Skip to content

Commit a8cf3b6

Browse files
committed
[refactor] move Find1P1CPackage to txdownload
Move-only.
1 parent f497414 commit a8cf3b6

File tree

4 files changed

+107
-104
lines changed

4 files changed

+107
-104
lines changed

src/net_processing.cpp

Lines changed: 9 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -564,40 +564,6 @@ class PeerManagerImpl final : public PeerManager
564564
*/
565565
bool MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer);
566566

567-
struct PackageToValidate {
568-
Package m_txns;
569-
std::vector<NodeId> m_senders;
570-
/** Construct a 1-parent-1-child package. */
571-
explicit PackageToValidate(const CTransactionRef& parent,
572-
const CTransactionRef& child,
573-
NodeId parent_sender,
574-
NodeId child_sender) :
575-
m_txns{parent, child},
576-
m_senders{parent_sender, child_sender}
577-
{}
578-
579-
// Move ctor
580-
PackageToValidate(PackageToValidate&& other) : m_txns{std::move(other.m_txns)}, m_senders{std::move(other.m_senders)} {}
581-
582-
// Move assignment
583-
PackageToValidate& operator=(PackageToValidate&& other) {
584-
this->m_txns = std::move(other.m_txns);
585-
this->m_senders = std::move(other.m_senders);
586-
return *this;
587-
}
588-
589-
std::string ToString() const {
590-
Assume(m_txns.size() == 2);
591-
return strprintf("parent %s (wtxid=%s, sender=%d) + child %s (wtxid=%s, sender=%d)",
592-
m_txns.front()->GetHash().ToString(),
593-
m_txns.front()->GetWitnessHash().ToString(),
594-
m_senders.front(),
595-
m_txns.back()->GetHash().ToString(),
596-
m_txns.back()->GetWitnessHash().ToString(),
597-
m_senders.back());
598-
}
599-
};
600-
601567
/** Handle a transaction whose result was not MempoolAcceptResult::ResultType::VALID.
602568
* @param[in] first_time_failure Whether we should consider inserting into vExtraTxnForCompact, adding
603569
* a new orphan to resolve, or looking for a package to submit.
@@ -609,8 +575,8 @@ class PeerManagerImpl final : public PeerManager
609575
* @returns a PackageToValidate if this transaction has a reconsiderable failure and an eligible package was found,
610576
* or std::nullopt otherwise.
611577
*/
612-
std::optional<PackageToValidate> ProcessInvalidTx(NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
613-
bool first_time_failure)
578+
std::optional<node::PackageToValidate> ProcessInvalidTx(NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
579+
bool first_time_failure)
614580
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
615581

616582
/** Handle a transaction whose result was MempoolAcceptResult::ResultType::VALID.
@@ -621,13 +587,7 @@ class PeerManagerImpl final : public PeerManager
621587
/** Handle the results of package validation: calls ProcessValidTx and ProcessInvalidTx for
622588
* individual transactions, and caches rejection for the package as a group.
623589
*/
624-
void ProcessPackageResult(const PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
625-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
626-
627-
/** Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
628-
* skipping any combinations that have already been tried. Return the resulting package along with
629-
* the senders of its respective transactions, or std::nullopt if no package is found. */
630-
std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
590+
void ProcessPackageResult(const node::PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
631591
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
632592

633593
/**
@@ -1946,7 +1906,7 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman,
19461906
m_banman(banman),
19471907
m_chainman(chainman),
19481908
m_mempool(pool),
1949-
m_txdownloadman(node::TxDownloadOptions{pool}),
1909+
m_txdownloadman(node::TxDownloadOptions{pool, m_rng}),
19501910
m_warnings{warnings},
19511911
m_opts{opts}
19521912
{
@@ -3015,7 +2975,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
30152975
return;
30162976
}
30172977

3018-
std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::ProcessInvalidTx(NodeId nodeid, const CTransactionRef& ptx, const TxValidationState& state,
2978+
std::optional<node::PackageToValidate> PeerManagerImpl::ProcessInvalidTx(NodeId nodeid, const CTransactionRef& ptx, const TxValidationState& state,
30192979
bool first_time_failure)
30202980
{
30212981
AssertLockNotHeld(m_peer_mutex);
@@ -3039,7 +2999,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::ProcessInvali
30392999
// Hashes to pass to AddKnownTx later
30403000
std::vector<uint256> unique_parents;
30413001
// Populated if failure is reconsiderable and eligible package is found.
3042-
std::optional<PackageToValidate> package_to_validate;
3002+
std::optional<node::PackageToValidate> package_to_validate;
30433003

30443004
if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) {
30453005
// Only process a new orphan if this is a first time failure, as otherwise it must be either
@@ -3145,7 +3105,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::ProcessInvali
31453105
// orphanage, as it is possible that they succeed as a package.
31463106
LogDebug(BCLog::TXPACKAGES, "tx %s (wtxid=%s) failed but reconsiderable, looking for child in orphanage\n",
31473107
ptx->GetHash().ToString(), ptx->GetWitnessHash().ToString());
3148-
package_to_validate = Find1P1CPackage(ptx, nodeid);
3108+
package_to_validate = m_txdownloadman.Find1P1CPackage(ptx, nodeid);
31493109
}
31503110
} else {
31513111
RecentRejectsFilter().insert(ptx->GetWitnessHash().ToUint256());
@@ -3215,7 +3175,7 @@ void PeerManagerImpl::ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, c
32153175
}
32163176
}
32173177

3218-
void PeerManagerImpl::ProcessPackageResult(const PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
3178+
void PeerManagerImpl::ProcessPackageResult(const node::PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
32193179
{
32203180
AssertLockNotHeld(m_peer_mutex);
32213181
AssertLockHeld(g_msgproc_mutex);
@@ -3271,61 +3231,6 @@ void PeerManagerImpl::ProcessPackageResult(const PackageToValidate& package_to_v
32713231
}
32723232
}
32733233

3274-
std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
3275-
{
3276-
AssertLockNotHeld(m_peer_mutex);
3277-
AssertLockHeld(g_msgproc_mutex);
3278-
AssertLockHeld(m_tx_download_mutex);
3279-
3280-
const auto& parent_wtxid{ptx->GetWitnessHash()};
3281-
auto& m_orphanage = m_txdownloadman.GetOrphanageRef();
3282-
3283-
Assume(RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256()));
3284-
3285-
// Prefer children from this peer. This helps prevent censorship attempts in which an attacker
3286-
// sends lots of fake children for the parent, and we (unluckily) keep selecting the fake
3287-
// children instead of the real one provided by the honest peer.
3288-
const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)};
3289-
3290-
// These children should be sorted from newest to oldest. In the (probably uncommon) case
3291-
// of children that replace each other, this helps us accept the highest feerate (probably the
3292-
// most recent) one efficiently.
3293-
for (const auto& child : cpfp_candidates_same_peer) {
3294-
Package maybe_cpfp_package{ptx, child};
3295-
if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package))) {
3296-
return PeerManagerImpl::PackageToValidate{ptx, child, nodeid, nodeid};
3297-
}
3298-
}
3299-
3300-
// If no suitable candidate from the same peer is found, also try children that were provided by
3301-
// a different peer. This is useful because sometimes multiple peers announce both transactions
3302-
// to us, and we happen to download them from different peers (we wouldn't have known that these
3303-
// 2 transactions are related). We still want to find 1p1c packages then.
3304-
//
3305-
// If we start tracking all announcers of orphans, we can restrict this logic to parent + child
3306-
// pairs in which both were provided by the same peer, i.e. delete this step.
3307-
const auto cpfp_candidates_different_peer{m_orphanage.GetChildrenFromDifferentPeer(ptx, nodeid)};
3308-
3309-
// Find the first 1p1c that hasn't already been rejected. We randomize the order to not
3310-
// create a bias that attackers can use to delay package acceptance.
3311-
//
3312-
// Create a random permutation of the indices.
3313-
std::vector<size_t> tx_indices(cpfp_candidates_different_peer.size());
3314-
std::iota(tx_indices.begin(), tx_indices.end(), 0);
3315-
std::shuffle(tx_indices.begin(), tx_indices.end(), m_rng);
3316-
3317-
for (const auto index : tx_indices) {
3318-
// If we already tried a package and failed for any reason, the combined hash was
3319-
// cached in m_lazy_recent_rejects_reconsiderable.
3320-
const auto [child_tx, child_sender] = cpfp_candidates_different_peer.at(index);
3321-
Package maybe_cpfp_package{ptx, child_tx};
3322-
if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package))) {
3323-
return PeerManagerImpl::PackageToValidate{ptx, child_tx, nodeid, child_sender};
3324-
}
3325-
}
3326-
return std::nullopt;
3327-
}
3328-
33293234
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
33303235
{
33313236
AssertLockHeld(g_msgproc_mutex);
@@ -4528,7 +4433,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
45284433
// possible that they succeed as a package.
45294434
LogDebug(BCLog::TXPACKAGES, "found tx %s (wtxid=%s) in reconsiderable rejects, looking for child in orphanage\n",
45304435
txid.ToString(), wtxid.ToString());
4531-
if (auto package_to_validate{Find1P1CPackage(ptx, pfrom.GetId())}) {
4436+
if (auto package_to_validate{m_txdownloadman.Find1P1CPackage(ptx, pfrom.GetId())}) {
45324437
const auto package_result{ProcessNewPackage(m_chainman.ActiveChainstate(), m_mempool, package_to_validate->m_txns, /*test_accept=*/false, /*client_maxfeerate=*/std::nullopt)};
45334438
LogDebug(BCLog::TXPACKAGES, "package evaluation for %s: %s\n", package_to_validate->ToString(),
45344439
package_result.m_state.IsValid() ? "package accepted" : "package rejected");

src/node/txdownloadman.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_NODE_TXDOWNLOADMAN_H
77

88
#include <net.h>
9+
#include <policy/packages.h>
910

1011
#include <cstdint>
1112
#include <memory>
@@ -38,6 +39,8 @@ static constexpr auto GETDATA_TX_INTERVAL{60s};
3839
struct TxDownloadOptions {
3940
/** Read-only reference to mempool. */
4041
const CTxMemPool& m_mempool;
42+
/** RNG provided by caller. */
43+
FastRandomContext& m_rng;
4144
};
4245
struct TxDownloadConnectionInfo {
4346
/** Whether this peer is preferred for transaction download. */
@@ -47,6 +50,39 @@ struct TxDownloadConnectionInfo {
4750
/** Whether this peer supports wtxid relay. */
4851
const bool m_wtxid_relay;
4952
};
53+
struct PackageToValidate {
54+
Package m_txns;
55+
std::vector<NodeId> m_senders;
56+
/** Construct a 1-parent-1-child package. */
57+
explicit PackageToValidate(const CTransactionRef& parent,
58+
const CTransactionRef& child,
59+
NodeId parent_sender,
60+
NodeId child_sender) :
61+
m_txns{parent, child},
62+
m_senders{parent_sender, child_sender}
63+
{}
64+
65+
// Move ctor
66+
PackageToValidate(PackageToValidate&& other) : m_txns{std::move(other.m_txns)}, m_senders{std::move(other.m_senders)} {}
67+
68+
// Move assignment
69+
PackageToValidate& operator=(PackageToValidate&& other) {
70+
this->m_txns = std::move(other.m_txns);
71+
this->m_senders = std::move(other.m_senders);
72+
return *this;
73+
}
74+
75+
std::string ToString() const {
76+
Assume(m_txns.size() == 2);
77+
return strprintf("parent %s (wtxid=%s, sender=%d) + child %s (wtxid=%s, sender=%d)",
78+
m_txns.front()->GetHash().ToString(),
79+
m_txns.front()->GetWitnessHash().ToString(),
80+
m_senders.front(),
81+
m_txns.back()->GetHash().ToString(),
82+
m_txns.back()->GetWitnessHash().ToString(),
83+
m_senders.back());
84+
}
85+
};
5086

5187
/**
5288
* Class responsible for deciding what transactions to request and, once
@@ -111,6 +147,11 @@ class TxDownloadManager {
111147

112148
/** Should be called when a notfound for a tx has been received. */
113149
void ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes);
150+
151+
/** Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
152+
* skipping any combinations that have already been tried. Return the resulting package along with
153+
* the senders of its respective transactions, or std::nullopt if no package is found. */
154+
std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid);
114155
};
115156
} // namespace node
116157
#endif // BITCOIN_NODE_TXDOWNLOADMAN_H

src/node/txdownloadman_impl.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ void TxDownloadManager::ReceivedNotFound(NodeId nodeid, const std::vector<uint25
7171
{
7272
m_impl->ReceivedNotFound(nodeid, txhashes);
7373
}
74+
std::optional<PackageToValidate> TxDownloadManager::Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
75+
{
76+
return m_impl->Find1P1CPackage(ptx, nodeid);
77+
}
7478

7579
// TxDownloadManagerImpl
7680
void TxDownloadManagerImpl::ActiveTipChange()
@@ -218,4 +222,54 @@ void TxDownloadManagerImpl::ReceivedNotFound(NodeId nodeid, const std::vector<ui
218222
m_txrequest.ReceivedResponse(nodeid, txhash);
219223
}
220224
}
225+
226+
std::optional<PackageToValidate> TxDownloadManagerImpl::Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
227+
{
228+
const auto& parent_wtxid{ptx->GetWitnessHash()};
229+
230+
Assume(RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256()));
231+
232+
// Prefer children from this peer. This helps prevent censorship attempts in which an attacker
233+
// sends lots of fake children for the parent, and we (unluckily) keep selecting the fake
234+
// children instead of the real one provided by the honest peer.
235+
const auto cpfp_candidates_same_peer{m_orphanage.GetChildrenFromSamePeer(ptx, nodeid)};
236+
237+
// These children should be sorted from newest to oldest. In the (probably uncommon) case
238+
// of children that replace each other, this helps us accept the highest feerate (probably the
239+
// most recent) one efficiently.
240+
for (const auto& child : cpfp_candidates_same_peer) {
241+
Package maybe_cpfp_package{ptx, child};
242+
if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package))) {
243+
return PackageToValidate{ptx, child, nodeid, nodeid};
244+
}
245+
}
246+
247+
// If no suitable candidate from the same peer is found, also try children that were provided by
248+
// a different peer. This is useful because sometimes multiple peers announce both transactions
249+
// to us, and we happen to download them from different peers (we wouldn't have known that these
250+
// 2 transactions are related). We still want to find 1p1c packages then.
251+
//
252+
// If we start tracking all announcers of orphans, we can restrict this logic to parent + child
253+
// pairs in which both were provided by the same peer, i.e. delete this step.
254+
const auto cpfp_candidates_different_peer{m_orphanage.GetChildrenFromDifferentPeer(ptx, nodeid)};
255+
256+
// Find the first 1p1c that hasn't already been rejected. We randomize the order to not
257+
// create a bias that attackers can use to delay package acceptance.
258+
//
259+
// Create a random permutation of the indices.
260+
std::vector<size_t> tx_indices(cpfp_candidates_different_peer.size());
261+
std::iota(tx_indices.begin(), tx_indices.end(), 0);
262+
std::shuffle(tx_indices.begin(), tx_indices.end(), m_opts.m_rng);
263+
264+
for (const auto index : tx_indices) {
265+
// If we already tried a package and failed for any reason, the combined hash was
266+
// cached in m_lazy_recent_rejects_reconsiderable.
267+
const auto [child_tx, child_sender] = cpfp_candidates_different_peer.at(index);
268+
Package maybe_cpfp_package{ptx, child_tx};
269+
if (!RecentRejectsReconsiderableFilter().contains(GetPackageHash(maybe_cpfp_package))) {
270+
return PackageToValidate{ptx, child_tx, nodeid, child_sender};
271+
}
272+
}
273+
return std::nullopt;
274+
}
221275
} // namespace node

src/node/txdownloadman_impl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <kernel/chain.h>
1111
#include <net.h>
1212
#include <primitives/transaction.h>
13+
#include <policy/packages.h>
1314
#include <txorphanage.h>
1415
#include <txrequest.h>
1516

@@ -159,6 +160,8 @@ class TxDownloadManagerImpl {
159160

160161
/** Marks a tx as ReceivedResponse in txrequest. */
161162
void ReceivedNotFound(NodeId nodeid, const std::vector<uint256>& txhashes);
163+
164+
std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid);
162165
};
163166
} // namespace node
164167
#endif // BITCOIN_NODE_TXDOWNLOADMAN_IMPL_H

0 commit comments

Comments
 (0)