Skip to content

Commit 3eb1307

Browse files
committed
guard TxRequest and rejection caches with new mutex
We need to synchronize between various tx download structures. TxRequest does not inherently need cs_main for synchronization, and it's not appropriate to lock all of the tx download logic under cs_main.
1 parent 35dddbc commit 3eb1307

File tree

1 file changed

+57
-32
lines changed

1 file changed

+57
-32
lines changed

src/net_processing.cpp

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,9 @@ class PeerManagerImpl final : public PeerManager
490490

491491
/** Overridden from CValidationInterface. */
492492
void BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override
493-
EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex);
493+
EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex, !m_tx_download_mutex);
494494
void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override
495-
EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex);
495+
EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex, !m_tx_download_mutex);
496496
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override
497497
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
498498
void BlockChecked(const CBlock& block, const BlockValidationState& state) override
@@ -501,13 +501,13 @@ class PeerManagerImpl final : public PeerManager
501501
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
502502

503503
/** Implement NetEventsInterface */
504-
void InitializeNode(const CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
505-
void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
504+
void InitializeNode(const CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_tx_download_mutex);
505+
void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, !m_tx_download_mutex);
506506
bool HasAllDesirableServiceFlags(ServiceFlags services) const override;
507507
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
508-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
508+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex, !m_tx_download_mutex);
509509
bool SendMessages(CNode* pto) override
510-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, g_msgproc_mutex);
510+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, g_msgproc_mutex, !m_tx_download_mutex);
511511

512512
/** Implement PeerManager */
513513
void StartScheduledTasks(CScheduler& scheduler) override;
@@ -526,7 +526,7 @@ class PeerManagerImpl final : public PeerManager
526526
void UnitTestMisbehaving(NodeId peer_id) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), ""); };
527527
void ProcessMessage(CNode& pfrom, const std::string& msg_type, DataStream& vRecv,
528528
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
529-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
529+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex, !m_tx_download_mutex);
530530
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
531531
ServiceFlags GetDesirableServiceFlags(ServiceFlags services) const override;
532532

@@ -585,12 +585,12 @@ class PeerManagerImpl final : public PeerManager
585585
* Updates m_txrequest, m_recent_rejects, m_recent_rejects_reconsiderable, m_orphanage, and vExtraTxnForCompact. */
586586
void ProcessInvalidTx(NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
587587
bool maybe_add_extra_compact_tx)
588-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
588+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
589589

590590
/** Handle a transaction whose result was MempoolAcceptResult::ResultType::VALID.
591591
* Updates m_txrequest, m_orphanage, and vExtraTxnForCompact. Also queues the tx for relay. */
592592
void ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, const std::list<CTransactionRef>& replaced_transactions)
593-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
593+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
594594

595595
struct PackageToValidate {
596596
const Package m_txns;
@@ -620,13 +620,13 @@ class PeerManagerImpl final : public PeerManager
620620
* individual transactions, and caches rejection for the package as a group.
621621
*/
622622
void ProcessPackageResult(const PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
623-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
623+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
624624

625625
/** Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
626626
* skipping any combinations that have already been tried. Return the resulting package along with
627627
* the senders of its respective transactions, or std::nullopt if no package is found. */
628628
std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
629-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
629+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
630630

631631
/**
632632
* Reconsider orphan transactions after a parent has been accepted to the mempool.
@@ -640,7 +640,7 @@ class PeerManagerImpl final : public PeerManager
640640
* will be empty.
641641
*/
642642
bool ProcessOrphanTx(Peer& peer)
643-
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
643+
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, !m_tx_download_mutex);
644644

645645
/** Process a single headers message from a peer.
646646
*
@@ -722,7 +722,7 @@ class PeerManagerImpl final : public PeerManager
722722
* peer. The announcement parameters are decided in PeerManager and then
723723
* passed to TxRequestTracker. */
724724
void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
725-
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
725+
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_tx_download_mutex);
726726

727727
/** Send a message to a peer */
728728
void PushMessage(CNode& node, CSerializedNetMsg&& msg) const { m_connman.PushMessage(&node, std::move(msg)); }
@@ -770,7 +770,19 @@ class PeerManagerImpl final : public PeerManager
770770
BanMan* const m_banman;
771771
ChainstateManager& m_chainman;
772772
CTxMemPool& m_mempool;
773-
TxRequestTracker m_txrequest GUARDED_BY(::cs_main);
773+
774+
/** Synchronizes tx download including TxRequestTracker, rejection filters, and TxOrphanage.
775+
* Lock invariants:
776+
* - A txhash (txid or wtxid) in m_txrequest is not also in m_orphanage.
777+
* - A txhash (txid or wtxid) in m_txrequest is not also in m_recent_rejects.
778+
* - A txhash (txid or wtxid) in m_txrequest is not also in m_recent_rejects_reconsiderable.
779+
* - A txhash (txid or wtxid) in m_txrequest is not also in m_recent_confirmed_transactions.
780+
* - Each data structure's limits hold (m_orphanage max size, m_txrequest per-peer limits, etc).
781+
*
782+
* m_tx_download_mutex must be acquired before mempool.cs
783+
*/
784+
Mutex m_tx_download_mutex;
785+
TxRequestTracker m_txrequest GUARDED_BY(m_tx_download_mutex);
774786
std::unique_ptr<TxReconciliationTracker> m_txreconciliation;
775787

776788
/** The height of the best chain */
@@ -851,7 +863,7 @@ class PeerManagerImpl final : public PeerManager
851863
* chain tip has changed.
852864
* */
853865
bool AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
854-
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
866+
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex, m_tx_download_mutex);
855867

856868
/**
857869
* Filter for transactions that were recently rejected by the mempool.
@@ -887,10 +899,10 @@ class PeerManagerImpl final : public PeerManager
887899
*
888900
* Memory used: 1.3 MB
889901
*/
890-
CRollingBloomFilter m_recent_rejects GUARDED_BY(::cs_main){120'000, 0.000'001};
902+
CRollingBloomFilter m_recent_rejects GUARDED_BY(m_tx_download_mutex){120'000, 0.000'001};
891903
/** Block hash of chain tip the last time we reset m_recent_rejects and
892904
* m_recent_rejects_reconsiderable. */
893-
uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main);
905+
uint256 hashRecentRejectsChainTip GUARDED_BY(m_tx_download_mutex);
894906

895907
/**
896908
* Filter for:
@@ -912,7 +924,7 @@ class PeerManagerImpl final : public PeerManager
912924
*
913925
* Parameters are picked to be the same as m_recent_rejects, with the same rationale.
914926
*/
915-
CRollingBloomFilter m_recent_rejects_reconsiderable GUARDED_BY(::cs_main){120'000, 0.000'001};
927+
CRollingBloomFilter m_recent_rejects_reconsiderable GUARDED_BY(m_tx_download_mutex){120'000, 0.000'001};
916928

917929
/*
918930
* Filter for transactions that have been recently confirmed.
@@ -1067,7 +1079,7 @@ class PeerManagerImpl final : public PeerManager
10671079
int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
10681080

10691081
/** Storage for orphan information */
1070-
TxOrphanage m_orphanage;
1082+
TxOrphanage m_orphanage GUARDED_BY(m_tx_download_mutex);
10711083

10721084
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
10731085

@@ -1630,7 +1642,8 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
16301642

16311643
void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
16321644
{
1633-
AssertLockHeld(::cs_main); // For m_txrequest
1645+
AssertLockHeld(::cs_main); // for State
1646+
AssertLockHeld(m_tx_download_mutex); // For m_txrequest
16341647
NodeId nodeid = node.GetId();
16351648
if (!node.HasPermission(NetPermissionFlags::Relay) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
16361649
// Too many queued announcements from this peer
@@ -1666,8 +1679,11 @@ void PeerManagerImpl::InitializeNode(const CNode& node, ServiceFlags our_service
16661679
{
16671680
NodeId nodeid = node.GetId();
16681681
{
1669-
LOCK(cs_main);
1682+
LOCK(cs_main); // For m_node_states
16701683
m_node_states.emplace_hint(m_node_states.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(node.IsInboundConn()));
1684+
}
1685+
{
1686+
LOCK(m_tx_download_mutex);
16711687
assert(m_txrequest.Count(nodeid) == 0);
16721688
}
16731689

@@ -1735,8 +1751,11 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
17351751
}
17361752
}
17371753
}
1738-
m_orphanage.EraseForPeer(nodeid);
1739-
m_txrequest.DisconnectedPeer(nodeid);
1754+
{
1755+
LOCK(m_tx_download_mutex);
1756+
m_orphanage.EraseForPeer(nodeid);
1757+
m_txrequest.DisconnectedPeer(nodeid);
1758+
}
17401759
if (m_txreconciliation) m_txreconciliation->ForgetPeer(nodeid);
17411760
m_num_preferred_download_peers -= state->fPreferredDownload;
17421761
m_peers_downloading_from -= (!state->vBlocksInFlight.empty());
@@ -1753,6 +1772,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
17531772
assert(m_peers_downloading_from == 0);
17541773
assert(m_outbound_peers_with_protect_from_disconnect == 0);
17551774
assert(m_wtxid_relay_peers == 0);
1775+
LOCK(m_tx_download_mutex);
17561776
assert(m_txrequest.Size() == 0);
17571777
assert(m_orphanage.Size() == 0);
17581778
}
@@ -2084,6 +2104,7 @@ void PeerManagerImpl::BlockConnected(
20842104
if (role == ChainstateRole::BACKGROUND) {
20852105
return;
20862106
}
2107+
LOCK(m_tx_download_mutex);
20872108
m_orphanage.EraseForBlock(*pblock);
20882109

20892110
{
@@ -2096,7 +2117,6 @@ void PeerManagerImpl::BlockConnected(
20962117
}
20972118
}
20982119
{
2099-
LOCK(cs_main);
21002120
for (const auto& ptx : pblock->vtx) {
21012121
m_txrequest.ForgetTxHash(ptx->GetHash());
21022122
m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
@@ -2254,6 +2274,9 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
22542274

22552275
bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
22562276
{
2277+
AssertLockHeld(::cs_main);
2278+
AssertLockHeld(m_tx_download_mutex);
2279+
22572280
if (m_chainman.ActiveChain().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
22582281
// If the chain tip has changed previously rejected transactions
22592282
// might be now valid, e.g. due to a nLockTime'd tx becoming valid,
@@ -3154,7 +3177,7 @@ void PeerManagerImpl::ProcessInvalidTx(NodeId nodeid, const CTransactionRef& ptx
31543177
{
31553178
AssertLockNotHeld(m_peer_mutex);
31563179
AssertLockHeld(g_msgproc_mutex);
3157-
AssertLockHeld(cs_main);
3180+
AssertLockHeld(m_tx_download_mutex);
31583181

31593182
LogDebug(BCLog::MEMPOOLREJ, "%s (wtxid=%s) from peer=%d was not accepted: %s\n",
31603183
ptx->GetHash().ToString(),
@@ -3219,7 +3242,7 @@ void PeerManagerImpl::ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, c
32193242
{
32203243
AssertLockNotHeld(m_peer_mutex);
32213244
AssertLockHeld(g_msgproc_mutex);
3222-
AssertLockHeld(cs_main);
3245+
AssertLockHeld(m_tx_download_mutex);
32233246

32243247
// As this version of the transaction was acceptable, we can forget about any requests for it.
32253248
// No-op if the tx is not in txrequest.
@@ -3247,7 +3270,7 @@ void PeerManagerImpl::ProcessPackageResult(const PackageToValidate& package_to_v
32473270
{
32483271
AssertLockNotHeld(m_peer_mutex);
32493272
AssertLockHeld(g_msgproc_mutex);
3250-
AssertLockHeld(cs_main);
3273+
AssertLockHeld(m_tx_download_mutex);
32513274

32523275
const auto& package = package_to_validate.m_txns;
32533276
const auto& senders = package_to_validate.m_senders;
@@ -3303,7 +3326,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
33033326
{
33043327
AssertLockNotHeld(m_peer_mutex);
33053328
AssertLockHeld(g_msgproc_mutex);
3306-
AssertLockHeld(cs_main);
3329+
AssertLockHeld(m_tx_download_mutex);
33073330

33083331
const auto& parent_wtxid{ptx->GetWitnessHash()};
33093332

@@ -3356,7 +3379,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
33563379
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
33573380
{
33583381
AssertLockHeld(g_msgproc_mutex);
3359-
LOCK(cs_main);
3382+
LOCK2(::cs_main, m_tx_download_mutex);
33603383

33613384
CTransactionRef porphanTx = nullptr;
33623385

@@ -4173,7 +4196,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
41734196

41744197
const bool reject_tx_invs{RejectIncomingTxs(pfrom)};
41754198

4176-
LOCK(cs_main);
4199+
LOCK2(cs_main, m_tx_download_mutex);
41774200

41784201
const auto current_time{GetTime<std::chrono::microseconds>()};
41794202
uint256* best_block{nullptr};
@@ -4506,7 +4529,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
45064529
const uint256& hash = peer->m_wtxid_relay ? wtxid : txid;
45074530
AddKnownTx(*peer, hash);
45084531

4509-
LOCK(cs_main);
4532+
LOCK2(cs_main, m_tx_download_mutex);
45104533

45114534
m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
45124535
if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
@@ -5263,7 +5286,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
52635286
std::vector<CInv> vInv;
52645287
vRecv >> vInv;
52655288
if (vInv.size() <= MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
5266-
LOCK(::cs_main);
5289+
LOCK(m_tx_download_mutex);
52675290
for (CInv &inv : vInv) {
52685291
if (inv.IsGenTxMsg()) {
52695292
// If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as
@@ -5388,6 +5411,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
53885411
// by another peer that was already processed; in that case,
53895412
// the extra work may not be noticed, possibly resulting in an
53905413
// unnecessary 100ms delay)
5414+
LOCK(m_tx_download_mutex);
53915415
if (m_orphanage.HaveTxToReconsider(peer->m_id)) fMoreWork = true;
53925416
} catch (const std::exception& e) {
53935417
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());
@@ -6281,6 +6305,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
62816305
//
62826306
// Message: getdata (transactions)
62836307
//
6308+
LOCK(m_tx_download_mutex);
62846309
std::vector<std::pair<NodeId, GenTxid>> expired;
62856310
auto requestable = m_txrequest.GetRequestable(pto->GetId(), current_time, &expired);
62866311
for (const auto& entry : expired) {

0 commit comments

Comments
 (0)