Skip to content

Commit 77a3603

Browse files
committed
Merge bitcoin/bitcoin#26551: p2p: Track orphans by who provided them
c58c249 net_processing: indicate more work to do when orphans are ready to reconsider (Anthony Towns) ecb0a3e net_processing: Don't process tx after processing orphans (Anthony Towns) c583775 net_processing: only process orphans before messages (Anthony Towns) be23046 txorphange: Drop redundant originator arg from GetTxToReconsider (Anthony Towns) a4fe099 txorphanage: index workset by originating peer (Anthony Towns) Pull request description: We currently process orphans by assigning them to the peer that provided a missing parent; instead assign them to the peer that provided the orphan in the first place. This prevents a peer from being able to marginally delay another peer's transactions and also simplifies the internal API slightly. Because we're now associating orphan processing with the peer that provided the orphan originally, we no longer process orphans immediately after receiving the parent, but defer until a future call to `ProcessMessage`. Based on #26295 ACKs for top commit: naumenkogs: utACK c58c249 glozow: ACK c58c249 mzumsande: Code Review ACK c58c249 Tree-SHA512: 3186c346f21e60440266a2a80a9d23d7b96071414e14b2b3bfe50457c04c18b1eab109c3d8c2a7726a6b10a2eda1f0512510a52c102da112820a26f5d96f12de
2 parents d4c180e + c58c249 commit 77a3603

File tree

4 files changed

+57
-49
lines changed

4 files changed

+57
-49
lines changed

src/net_processing.cpp

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -584,14 +584,17 @@ class PeerManagerImpl final : public PeerManager
584584
/**
585585
* Reconsider orphan transactions after a parent has been accepted to the mempool.
586586
*
587-
* @peer[in] peer The peer whose orphan transactions we will reconsider. Generally only one
588-
* orphan will be reconsidered on each call of this function. This set
589-
* may be added to if accepting an orphan causes its children to be
590-
* reconsidered.
591-
* @return True if there are still orphans in this peer's work set.
587+
* @peer[in] peer The peer whose orphan transactions we will reconsider. Generally only
588+
* one orphan will be reconsidered on each call of this function. If an
589+
* accepted orphan has orphaned children, those will need to be
590+
* reconsidered, creating more work, possibly for other peers.
591+
* @return True if meaningful work was done (an orphan was accepted/rejected).
592+
* If no meaningful work was done, then the work set for this peer
593+
* will be empty.
592594
*/
593595
bool ProcessOrphanTx(Peer& peer)
594-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
596+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
597+
595598
/** Process a single headers message from a peer.
596599
*
597600
* @param[in] pfrom CNode of the peer
@@ -2897,34 +2900,32 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
28972900
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
28982901
{
28992902
AssertLockHeld(g_msgproc_mutex);
2900-
AssertLockHeld(cs_main);
2903+
LOCK(cs_main);
29012904

29022905
CTransactionRef porphanTx = nullptr;
2903-
NodeId from_peer = -1;
2904-
bool more = false;
29052906

2906-
while (CTransactionRef porphanTx = m_orphanage.GetTxToReconsider(peer.m_id, from_peer, more)) {
2907+
while (CTransactionRef porphanTx = m_orphanage.GetTxToReconsider(peer.m_id)) {
29072908
const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
29082909
const TxValidationState& state = result.m_state;
29092910
const uint256& orphanHash = porphanTx->GetHash();
29102911

29112912
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
29122913
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
29132914
RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
2914-
m_orphanage.AddChildrenToWorkSet(*porphanTx, peer.m_id);
2915+
m_orphanage.AddChildrenToWorkSet(*porphanTx);
29152916
m_orphanage.EraseTx(orphanHash);
29162917
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
29172918
AddToCompactExtraTransactions(removedTx);
29182919
}
2919-
break;
2920+
return true;
29202921
} else if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
29212922
if (state.IsInvalid()) {
29222923
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
29232924
orphanHash.ToString(),
2924-
from_peer,
2925+
peer.m_id,
29252926
state.ToString());
29262927
// Maybe punish peer that gave us an invalid orphan tx
2927-
MaybePunishNodeForTx(from_peer, state);
2928+
MaybePunishNodeForTx(peer.m_id, state);
29282929
}
29292930
// Has inputs but not accepted to mempool
29302931
// Probably non-standard or insufficient fee
@@ -2959,11 +2960,11 @@ bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
29592960
}
29602961
}
29612962
m_orphanage.EraseTx(orphanHash);
2962-
break;
2963+
return true;
29632964
}
29642965
}
29652966

2966-
return more;
2967+
return false;
29672968
}
29682969

29692970
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
@@ -4033,7 +4034,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
40334034
m_txrequest.ForgetTxHash(tx.GetHash());
40344035
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
40354036
RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
4036-
m_orphanage.AddChildrenToWorkSet(tx, peer->m_id);
4037+
m_orphanage.AddChildrenToWorkSet(tx);
40374038

40384039
pfrom.m_last_tx_time = GetTime<std::chrono::seconds>();
40394040

@@ -4045,9 +4046,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
40454046
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
40464047
AddToCompactExtraTransactions(removedTx);
40474048
}
4048-
4049-
// Recursively process any orphan transactions that depended on this one
4050-
ProcessOrphanTx(*peer);
40514049
}
40524050
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
40534051
{
@@ -4856,16 +4854,12 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
48564854
}
48574855
}
48584856

4859-
bool has_more_orphans;
4860-
{
4861-
LOCK(cs_main);
4862-
has_more_orphans = ProcessOrphanTx(*peer);
4863-
}
4857+
const bool processed_orphan = ProcessOrphanTx(*peer);
48644858

48654859
if (pfrom->fDisconnect)
48664860
return false;
48674861

4868-
if (has_more_orphans) return true;
4862+
if (processed_orphan) return true;
48694863

48704864
// this maintains the order of responses
48714865
// and prevents m_getdata_requests to grow unbounded
@@ -4911,6 +4905,12 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
49114905
LOCK(peer->m_getdata_requests_mutex);
49124906
if (!peer->m_getdata_requests.empty()) fMoreWork = true;
49134907
}
4908+
// Does this peer has an orphan ready to reconsider?
4909+
// (Note: we may have provided a parent for an orphan provided
4910+
// by another peer that was already processed; in that case,
4911+
// the extra work may not be noticed, possibly resulting in an
4912+
// unnecessary 100ms delay)
4913+
if (m_orphanage.HaveTxToReconsider(peer->m_id)) fMoreWork = true;
49144914
} catch (const std::exception& e) {
49154915
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg.m_type), msg.m_message_size, e.what(), typeid(e).name());
49164916
} catch (...) {

src/test/fuzz/txorphan.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,12 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
8585
CallOneOf(
8686
fuzzed_data_provider,
8787
[&] {
88-
orphanage.AddChildrenToWorkSet(*tx, peer_id);
88+
orphanage.AddChildrenToWorkSet(*tx);
8989
},
9090
[&] {
9191
{
92-
NodeId originator;
93-
bool more = true;
94-
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id, originator, more);
95-
if (!ref) {
96-
Assert(!more);
97-
} else {
92+
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
93+
if (ref) {
9894
bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetHash()));
9995
Assert(have_tx);
10096
}

src/txorphanage.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,19 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans)
145145
if (nEvicted > 0) LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
146146
}
147147

148-
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, NodeId peer)
148+
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
149149
{
150150
LOCK(m_mutex);
151151

152-
// Get this peer's work set, emplacing an empty set it didn't exist
153-
std::set<uint256>& orphan_work_set = m_peer_work_set.try_emplace(peer).first->second;
154152

155153
for (unsigned int i = 0; i < tx.vout.size(); i++) {
156154
const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
157155
if (it_by_prev != m_outpoint_to_orphan_it.end()) {
158156
for (const auto& elem : it_by_prev->second) {
157+
// Get this source peer's work set, emplacing an empty set if it didn't exist
158+
// (note: if this peer wasn't still connected, we would have removed the orphan tx already)
159+
std::set<uint256>& orphan_work_set = m_peer_work_set.try_emplace(elem->second.fromPeer).first->second;
160+
// Add this tx to the work set
159161
orphan_work_set.insert(elem->first);
160162
}
161163
}
@@ -172,7 +174,7 @@ bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
172174
}
173175
}
174176

175-
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer, NodeId& originator, bool& more)
177+
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
176178
{
177179
LOCK(m_mutex);
178180

@@ -185,16 +187,25 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer, NodeId& originator,
185187

186188
const auto orphan_it = m_orphans.find(txid);
187189
if (orphan_it != m_orphans.end()) {
188-
more = !work_set.empty();
189-
originator = orphan_it->second.fromPeer;
190190
return orphan_it->second.tx;
191191
}
192192
}
193193
}
194-
more = false;
195194
return nullptr;
196195
}
197196

197+
bool TxOrphanage::HaveTxToReconsider(NodeId peer)
198+
{
199+
LOCK(m_mutex);
200+
201+
auto work_set_it = m_peer_work_set.find(peer);
202+
if (work_set_it != m_peer_work_set.end()) {
203+
auto& work_set = work_set_it->second;
204+
return !work_set.empty();
205+
}
206+
return false;
207+
}
208+
198209
void TxOrphanage::EraseForBlock(const CBlock& block)
199210
{
200211
LOCK(m_mutex);

src/txorphanage.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,11 @@ class TxOrphanage {
2727
bool HaveTx(const GenTxid& gtxid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
2828

2929
/** Extract a transaction from a peer's work set
30-
* Returns nullptr and sets more to false if there are no transactions
31-
* to work on. Otherwise returns the transaction reference, removes
32-
* the transaction from the work set, and populates its arguments with
33-
* the originating peer, and whether there are more orphans for this peer
34-
* to work on after this tx.
30+
* Returns nullptr if there are no transactions to work on.
31+
* Otherwise returns the transaction reference, and removes
32+
* it from the work set.
3533
*/
36-
CTransactionRef GetTxToReconsider(NodeId peer, NodeId& originator, bool& more) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
34+
CTransactionRef GetTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
3735

3836
/** Erase an orphan by txid */
3937
int EraseTx(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
@@ -47,8 +45,11 @@ class TxOrphanage {
4745
/** Limit the orphanage to the given maximum */
4846
void LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
4947

50-
/** Add any orphans that list a particular tx as a parent into a peer's work set */
51-
void AddChildrenToWorkSet(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
48+
/** Add any orphans that list a particular tx as a parent into the from peer's work set */
49+
void AddChildrenToWorkSet(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);;
50+
51+
/** Does this peer have any work to do? */
52+
bool HaveTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);;
5253

5354
/** Return how many entries exist in the orphange */
5455
size_t Size() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
@@ -72,7 +73,7 @@ class TxOrphanage {
7273
* -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
7374
std::map<uint256, OrphanTx> m_orphans GUARDED_BY(m_mutex);
7475

75-
/** Which peer provided a parent tx of orphans that need to be reconsidered */
76+
/** Which peer provided the orphans that need to be reconsidered */
7677
std::map<NodeId, std::set<uint256>> m_peer_work_set GUARDED_BY(m_mutex);
7778

7879
using OrphanMap = decltype(m_orphans);

0 commit comments

Comments
 (0)