Skip to content

Commit a79b720

Browse files
committed
Merge bitcoin/bitcoin#26295: Replace global g_cs_orphans lock with local
7082ce3 scripted-diff: rename and de-globalise g_cs_orphans (Anthony Towns) 733d85f Move all g_cs_orphans locking to txorphanage (Anthony Towns) a936f41 txorphanage: make m_peer_work_set private (Anthony Towns) 3614819 txorphange: move orphan workset to txorphanage (Anthony Towns) 6f8e442 net_processing: Localise orphan_work_set handling to ProcessOrphanTx (Anthony Towns) 0027174 net_processing: move ProcessOrphanTx docs to declaration (Anthony Towns) 9910ed7 net_processing: Pass a Peer& to ProcessOrphanTx (Anthony Towns) 89e2e0d net_processing: move extra transactions to msgproc mutex (Anthony Towns) ff8d44d Remove unnecessary includes of txorphange.h (Anthony Towns) Pull request description: Moves extra transactions to be under the `m_msgproc_mutex` lock rather than `g_cs_orphans` and refactors orphan handling so that the lock can be internal to the `TxOrphange` class. ACKs for top commit: dergoegge: Code review ACK 7082ce3 glozow: ACK 7082ce3 via code review and some [basic testing](https://github.com/glozow/bitcoin/blob/review-26295/src/test/orphanage_tests.cpp#L150). I think putting txorphanage in charge of handling peer work sets is the right direction. Tree-SHA512: 1ec454c3a69ebd45ff652770d6a55c6b183db71aba4d12639ed70f525f0035e069a81d06e9b65b66e87929c607080a1c5e5dcd2ca91eaa2cf202dc6c02aa6818
2 parents 9c2854c + 7082ce3 commit a79b720

File tree

8 files changed

+126
-99
lines changed

8 files changed

+126
-99
lines changed

src/init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
#include <torcontrol.h>
6868
#include <txdb.h>
6969
#include <txmempool.h>
70-
#include <txorphanage.h>
7170
#include <util/asmap.h>
7271
#include <util/check.h>
7372
#include <util/moneystr.h>

src/net_processing.cpp

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,6 @@ struct Peer {
365365
/** Total number of addresses that were processed (excludes rate-limited ones). */
366366
std::atomic<uint64_t> m_addr_processed{0};
367367

368-
/** Set of txids to reconsider once their parent transactions have been accepted **/
369-
std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
370-
371368
/** Whether we've sent this peer a getheaders in response to an inv prior to initial-headers-sync completing */
372369
bool m_inv_triggered_getheaders_before_sync GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
373370

@@ -584,8 +581,17 @@ class PeerManagerImpl final : public PeerManager
584581
*/
585582
bool MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer);
586583

587-
void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
588-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
584+
/**
585+
* Reconsider orphan transactions after a parent has been accepted to the mempool.
586+
*
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.
592+
*/
593+
bool ProcessOrphanTx(Peer& peer)
594+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
589595
/** Process a single headers message from a peer.
590596
*
591597
* @param[in] pfrom CNode of the peer
@@ -919,14 +925,14 @@ class PeerManagerImpl final : public PeerManager
919925
/** Storage for orphan information */
920926
TxOrphanage m_orphanage;
921927

922-
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
928+
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
923929

924930
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
925931
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
926932
* these are kept in a ring buffer */
927-
std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
933+
std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_msgproc_mutex);
928934
/** Offset into vExtraTxnForCompact to insert the next tx */
929-
size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
935+
size_t vExtraTxnForCompactIt GUARDED_BY(g_msgproc_mutex) = 0;
930936

931937
/** Check whether the last unknown block a peer advertised is not yet known. */
932938
void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
@@ -1490,7 +1496,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
14901496
for (const QueuedBlock& entry : state->vBlocksInFlight) {
14911497
mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
14921498
}
1493-
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
1499+
m_orphanage.EraseForPeer(nodeid);
14941500
m_txrequest.DisconnectedPeer(nodeid);
14951501
if (m_txreconciliation) m_txreconciliation->ForgetPeer(nodeid);
14961502
m_num_preferred_download_peers -= state->fPreferredDownload;
@@ -2880,33 +2886,24 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
28802886
return;
28812887
}
28822888

2883-
/**
2884-
* Reconsider orphan transactions after a parent has been accepted to the mempool.
2885-
*
2886-
* @param[in,out] orphan_work_set The set of orphan transactions to reconsider. Generally only one
2887-
* orphan will be reconsidered on each call of this function. This set
2888-
* may be added to if accepting an orphan causes its children to be
2889-
* reconsidered.
2890-
*/
2891-
void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
2889+
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
28922890
{
2891+
AssertLockHeld(g_msgproc_mutex);
28932892
AssertLockHeld(cs_main);
2894-
AssertLockHeld(g_cs_orphans);
2895-
2896-
while (!orphan_work_set.empty()) {
2897-
const uint256 orphanHash = *orphan_work_set.begin();
2898-
orphan_work_set.erase(orphan_work_set.begin());
28992893

2900-
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
2901-
if (porphanTx == nullptr) continue;
2894+
CTransactionRef porphanTx = nullptr;
2895+
NodeId from_peer = -1;
2896+
bool more = false;
29022897

2898+
while (CTransactionRef porphanTx = m_orphanage.GetTxToReconsider(peer.m_id, from_peer, more)) {
29032899
const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
29042900
const TxValidationState& state = result.m_state;
2901+
const uint256& orphanHash = porphanTx->GetHash();
29052902

29062903
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
29072904
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
29082905
RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
2909-
m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set);
2906+
m_orphanage.AddChildrenToWorkSet(*porphanTx, peer.m_id);
29102907
m_orphanage.EraseTx(orphanHash);
29112908
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
29122909
AddToCompactExtraTransactions(removedTx);
@@ -2957,6 +2954,8 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
29572954
break;
29582955
}
29592956
}
2957+
2958+
return more;
29602959
}
29612960

29622961
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
@@ -3990,7 +3989,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
39903989
AddKnownTx(*peer, txid);
39913990
}
39923991

3993-
LOCK2(cs_main, g_cs_orphans);
3992+
LOCK(cs_main);
39943993

39953994
m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
39963995
if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
@@ -4031,7 +4030,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
40314030
m_txrequest.ForgetTxHash(tx.GetHash());
40324031
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
40334032
RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
4034-
m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
4033+
m_orphanage.AddChildrenToWorkSet(tx, peer->m_id);
40354034

40364035
pfrom.m_last_tx_time = GetTime<std::chrono::seconds>();
40374036

@@ -4045,7 +4044,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
40454044
}
40464045

40474046
// Recursively process any orphan transactions that depended on this one
4048-
ProcessOrphanTx(peer->m_orphan_work_set);
4047+
ProcessOrphanTx(*peer);
40494048
}
40504049
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
40514050
{
@@ -4226,7 +4225,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42264225
bool fBlockReconstructed = false;
42274226

42284227
{
4229-
LOCK2(cs_main, g_cs_orphans);
4228+
LOCK(cs_main);
42304229
// If AcceptBlockHeader returned true, it set pindex
42314230
assert(pindex);
42324231
UpdateBlockAvailability(pfrom.GetId(), pindex->GetBlockHash());
@@ -4854,28 +4853,24 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
48544853
}
48554854
}
48564855

4856+
bool has_more_orphans;
48574857
{
4858-
LOCK2(cs_main, g_cs_orphans);
4859-
if (!peer->m_orphan_work_set.empty()) {
4860-
ProcessOrphanTx(peer->m_orphan_work_set);
4861-
}
4858+
LOCK(cs_main);
4859+
has_more_orphans = ProcessOrphanTx(*peer);
48624860
}
48634861

48644862
if (pfrom->fDisconnect)
48654863
return false;
48664864

4865+
if (has_more_orphans) return true;
4866+
48674867
// this maintains the order of responses
48684868
// and prevents m_getdata_requests to grow unbounded
48694869
{
48704870
LOCK(peer->m_getdata_requests_mutex);
48714871
if (!peer->m_getdata_requests.empty()) return true;
48724872
}
48734873

4874-
{
4875-
LOCK(g_cs_orphans);
4876-
if (!peer->m_orphan_work_set.empty()) return true;
4877-
}
4878-
48794874
// Don't bother if send buffer is too full to respond anyway
48804875
if (pfrom->fPauseSend) return false;
48814876

src/test/fuzz/process_message.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include <test/util/net.h>
2020
#include <test/util/setup_common.h>
2121
#include <test/util/validation.h>
22-
#include <txorphanage.h>
2322
#include <validationinterface.h>
2423
#include <version.h>
2524

src/test/fuzz/process_messages.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include <test/util/net.h>
1515
#include <test/util/setup_common.h>
1616
#include <test/util/validation.h>
17-
#include <txorphanage.h>
1817
#include <validation.h>
1918
#include <validationinterface.h>
2019

src/test/fuzz/txorphan.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
3636
SetMockTime(ConsumeTime(fuzzed_data_provider));
3737

3838
TxOrphanage orphanage;
39-
std::set<uint256> orphan_work_set;
4039
std::vector<COutPoint> outpoints;
4140
// initial outpoints used to construct transactions later
4241
for (uint8_t i = 0; i < 4; i++) {
@@ -86,30 +85,32 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
8685
CallOneOf(
8786
fuzzed_data_provider,
8887
[&] {
89-
LOCK(g_cs_orphans);
90-
orphanage.AddChildrenToWorkSet(*tx, orphan_work_set);
88+
orphanage.AddChildrenToWorkSet(*tx, peer_id);
9189
},
9290
[&] {
93-
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
9491
{
95-
LOCK(g_cs_orphans);
96-
bool get_tx = orphanage.GetTx(tx->GetHash()).first != nullptr;
97-
Assert(have_tx == get_tx);
92+
NodeId originator;
93+
bool more = true;
94+
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id, originator, more);
95+
if (!ref) {
96+
Assert(!more);
97+
} else {
98+
bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetHash()));
99+
Assert(have_tx);
100+
}
98101
}
99102
},
100103
[&] {
101104
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
102105
// AddTx should return false if tx is too big or already have it
103106
// tx weight is unknown, we only check when tx is already in orphanage
104107
{
105-
LOCK(g_cs_orphans);
106108
bool add_tx = orphanage.AddTx(tx, peer_id);
107109
// have_tx == true -> add_tx == false
108110
Assert(!have_tx || !add_tx);
109111
}
110112
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
111113
{
112-
LOCK(g_cs_orphans);
113114
bool add_tx = orphanage.AddTx(tx, peer_id);
114115
// if have_tx is still false, it must be too big
115116
Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
@@ -120,25 +121,22 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
120121
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
121122
// EraseTx should return 0 if m_orphans doesn't have the tx
122123
{
123-
LOCK(g_cs_orphans);
124124
Assert(have_tx == orphanage.EraseTx(tx->GetHash()));
125125
}
126126
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
127127
// have_tx should be false and EraseTx should fail
128128
{
129-
LOCK(g_cs_orphans);
130129
Assert(!have_tx && !orphanage.EraseTx(tx->GetHash()));
131130
}
132131
},
133132
[&] {
134-
LOCK(g_cs_orphans);
135133
orphanage.EraseForPeer(peer_id);
136134
},
137135
[&] {
138136
// test mocktime and expiry
139137
SetMockTime(ConsumeTime(fuzzed_data_provider));
140138
auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
141-
WITH_LOCK(g_cs_orphans, orphanage.LimitOrphans(limit));
139+
orphanage.LimitOrphans(limit);
142140
Assert(orphanage.Size() <= limit);
143141
});
144142
}

src/test/orphanage_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
2020
class TxOrphanageTest : public TxOrphanage
2121
{
2222
public:
23-
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
23+
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
2424
{
25+
LOCK(m_mutex);
2526
return m_orphans.size();
2627
}
2728

28-
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
29+
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
2930
{
31+
LOCK(m_mutex);
3032
std::map<uint256, OrphanTx>::iterator it;
3133
it = m_orphans.lower_bound(InsecureRand256());
3234
if (it == m_orphans.end())
@@ -59,8 +61,6 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
5961
FillableSigningProvider keystore;
6062
BOOST_CHECK(keystore.AddKey(key));
6163

62-
LOCK(g_cs_orphans);
63-
6464
// 50 orphan transactions:
6565
for (int i = 0; i < 50; i++)
6666
{

0 commit comments

Comments
 (0)