@@ -490,9 +490,9 @@ class PeerManagerImpl final : public PeerManager
490
490
491
491
/* * Overridden from CValidationInterface. */
492
492
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 );
494
494
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 );
496
496
void UpdatedBlockTip (const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload ) override
497
497
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
498
498
void BlockChecked (const CBlock& block, const BlockValidationState& state) override
@@ -501,13 +501,13 @@ class PeerManagerImpl final : public PeerManager
501
501
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
502
502
503
503
/* * 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 );
506
506
bool HasAllDesirableServiceFlags (ServiceFlags services) const override ;
507
507
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 );
509
509
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 );
511
511
512
512
/* * Implement PeerManager */
513
513
void StartScheduledTasks (CScheduler& scheduler) override ;
@@ -526,7 +526,7 @@ class PeerManagerImpl final : public PeerManager
526
526
void UnitTestMisbehaving (NodeId peer_id) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving (*Assert (GetPeerRef (peer_id)), " " ); };
527
527
void ProcessMessage (CNode& pfrom, const std::string& msg_type, DataStream& vRecv,
528
528
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 );
530
530
void UpdateLastBlockAnnounceTime (NodeId node, int64_t time_in_seconds) override ;
531
531
ServiceFlags GetDesirableServiceFlags (ServiceFlags services) const override ;
532
532
@@ -585,12 +585,12 @@ class PeerManagerImpl final : public PeerManager
585
585
* Updates m_txrequest, m_recent_rejects, m_recent_rejects_reconsiderable, m_orphanage, and vExtraTxnForCompact. */
586
586
void ProcessInvalidTx (NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
587
587
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 );
589
589
590
590
/* * Handle a transaction whose result was MempoolAcceptResult::ResultType::VALID.
591
591
* Updates m_txrequest, m_orphanage, and vExtraTxnForCompact. Also queues the tx for relay. */
592
592
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 );
594
594
595
595
struct PackageToValidate {
596
596
const Package m_txns;
@@ -620,13 +620,13 @@ class PeerManagerImpl final : public PeerManager
620
620
* individual transactions, and caches rejection for the package as a group.
621
621
*/
622
622
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 );
624
624
625
625
/* * Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
626
626
* skipping any combinations that have already been tried. Return the resulting package along with
627
627
* the senders of its respective transactions, or std::nullopt if no package is found. */
628
628
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 );
630
630
631
631
/* *
632
632
* Reconsider orphan transactions after a parent has been accepted to the mempool.
@@ -640,7 +640,7 @@ class PeerManagerImpl final : public PeerManager
640
640
* will be empty.
641
641
*/
642
642
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 );
644
644
645
645
/* * Process a single headers message from a peer.
646
646
*
@@ -722,7 +722,7 @@ class PeerManagerImpl final : public PeerManager
722
722
* peer. The announcement parameters are decided in PeerManager and then
723
723
* passed to TxRequestTracker. */
724
724
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 );
726
726
727
727
/* * Send a message to a peer */
728
728
void PushMessage (CNode& node, CSerializedNetMsg&& msg) const { m_connman.PushMessage (&node, std::move (msg)); }
@@ -770,7 +770,19 @@ class PeerManagerImpl final : public PeerManager
770
770
BanMan* const m_banman;
771
771
ChainstateManager& m_chainman;
772
772
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);
774
786
std::unique_ptr<TxReconciliationTracker> m_txreconciliation;
775
787
776
788
/* * The height of the best chain */
@@ -851,7 +863,7 @@ class PeerManagerImpl final : public PeerManager
851
863
* chain tip has changed.
852
864
* */
853
865
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 );
855
867
856
868
/* *
857
869
* Filter for transactions that were recently rejected by the mempool.
@@ -887,10 +899,10 @@ class PeerManagerImpl final : public PeerManager
887
899
*
888
900
* Memory used: 1.3 MB
889
901
*/
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 };
891
903
/* * Block hash of chain tip the last time we reset m_recent_rejects and
892
904
* m_recent_rejects_reconsiderable. */
893
- uint256 hashRecentRejectsChainTip GUARDED_BY (cs_main );
905
+ uint256 hashRecentRejectsChainTip GUARDED_BY (m_tx_download_mutex );
894
906
895
907
/* *
896
908
* Filter for:
@@ -912,7 +924,7 @@ class PeerManagerImpl final : public PeerManager
912
924
*
913
925
* Parameters are picked to be the same as m_recent_rejects, with the same rationale.
914
926
*/
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 };
916
928
917
929
/*
918
930
* Filter for transactions that have been recently confirmed.
@@ -1067,7 +1079,7 @@ class PeerManagerImpl final : public PeerManager
1067
1079
int m_peers_downloading_from GUARDED_BY (cs_main) = 0;
1068
1080
1069
1081
/* * Storage for orphan information */
1070
- TxOrphanage m_orphanage;
1082
+ TxOrphanage m_orphanage GUARDED_BY (m_tx_download_mutex) ;
1071
1083
1072
1084
void AddToCompactExtraTransactions (const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
1073
1085
@@ -1630,7 +1642,8 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
1630
1642
1631
1643
void PeerManagerImpl::AddTxAnnouncement (const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
1632
1644
{
1633
- AssertLockHeld (::cs_main); // For m_txrequest
1645
+ AssertLockHeld (::cs_main); // for State
1646
+ AssertLockHeld (m_tx_download_mutex); // For m_txrequest
1634
1647
NodeId nodeid = node.GetId ();
1635
1648
if (!node.HasPermission (NetPermissionFlags::Relay) && m_txrequest.Count (nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
1636
1649
// Too many queued announcements from this peer
@@ -1666,8 +1679,11 @@ void PeerManagerImpl::InitializeNode(const CNode& node, ServiceFlags our_service
1666
1679
{
1667
1680
NodeId nodeid = node.GetId ();
1668
1681
{
1669
- LOCK (cs_main);
1682
+ LOCK (cs_main); // For m_node_states
1670
1683
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);
1671
1687
assert (m_txrequest.Count (nodeid) == 0 );
1672
1688
}
1673
1689
@@ -1735,8 +1751,11 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
1735
1751
}
1736
1752
}
1737
1753
}
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
+ }
1740
1759
if (m_txreconciliation) m_txreconciliation->ForgetPeer (nodeid);
1741
1760
m_num_preferred_download_peers -= state->fPreferredDownload ;
1742
1761
m_peers_downloading_from -= (!state->vBlocksInFlight .empty ());
@@ -1753,6 +1772,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
1753
1772
assert (m_peers_downloading_from == 0 );
1754
1773
assert (m_outbound_peers_with_protect_from_disconnect == 0 );
1755
1774
assert (m_wtxid_relay_peers == 0 );
1775
+ LOCK (m_tx_download_mutex);
1756
1776
assert (m_txrequest.Size () == 0 );
1757
1777
assert (m_orphanage.Size () == 0 );
1758
1778
}
@@ -2084,6 +2104,7 @@ void PeerManagerImpl::BlockConnected(
2084
2104
if (role == ChainstateRole::BACKGROUND) {
2085
2105
return ;
2086
2106
}
2107
+ LOCK (m_tx_download_mutex);
2087
2108
m_orphanage.EraseForBlock (*pblock);
2088
2109
2089
2110
{
@@ -2096,7 +2117,6 @@ void PeerManagerImpl::BlockConnected(
2096
2117
}
2097
2118
}
2098
2119
{
2099
- LOCK (cs_main);
2100
2120
for (const auto & ptx : pblock->vtx ) {
2101
2121
m_txrequest.ForgetTxHash (ptx->GetHash ());
2102
2122
m_txrequest.ForgetTxHash (ptx->GetWitnessHash ());
@@ -2254,6 +2274,9 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
2254
2274
2255
2275
bool PeerManagerImpl::AlreadyHaveTx (const GenTxid& gtxid, bool include_reconsiderable)
2256
2276
{
2277
+ AssertLockHeld (::cs_main);
2278
+ AssertLockHeld (m_tx_download_mutex);
2279
+
2257
2280
if (m_chainman.ActiveChain ().Tip ()->GetBlockHash () != hashRecentRejectsChainTip) {
2258
2281
// If the chain tip has changed previously rejected transactions
2259
2282
// 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
3154
3177
{
3155
3178
AssertLockNotHeld (m_peer_mutex);
3156
3179
AssertLockHeld (g_msgproc_mutex);
3157
- AssertLockHeld (cs_main );
3180
+ AssertLockHeld (m_tx_download_mutex );
3158
3181
3159
3182
LogDebug (BCLog::MEMPOOLREJ, " %s (wtxid=%s) from peer=%d was not accepted: %s\n " ,
3160
3183
ptx->GetHash ().ToString (),
@@ -3219,7 +3242,7 @@ void PeerManagerImpl::ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, c
3219
3242
{
3220
3243
AssertLockNotHeld (m_peer_mutex);
3221
3244
AssertLockHeld (g_msgproc_mutex);
3222
- AssertLockHeld (cs_main );
3245
+ AssertLockHeld (m_tx_download_mutex );
3223
3246
3224
3247
// As this version of the transaction was acceptable, we can forget about any requests for it.
3225
3248
// No-op if the tx is not in txrequest.
@@ -3247,7 +3270,7 @@ void PeerManagerImpl::ProcessPackageResult(const PackageToValidate& package_to_v
3247
3270
{
3248
3271
AssertLockNotHeld (m_peer_mutex);
3249
3272
AssertLockHeld (g_msgproc_mutex);
3250
- AssertLockHeld (cs_main );
3273
+ AssertLockHeld (m_tx_download_mutex );
3251
3274
3252
3275
const auto & package = package_to_validate.m_txns ;
3253
3276
const auto & senders = package_to_validate.m_senders ;
@@ -3303,7 +3326,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
3303
3326
{
3304
3327
AssertLockNotHeld (m_peer_mutex);
3305
3328
AssertLockHeld (g_msgproc_mutex);
3306
- AssertLockHeld (cs_main );
3329
+ AssertLockHeld (m_tx_download_mutex );
3307
3330
3308
3331
const auto & parent_wtxid{ptx->GetWitnessHash ()};
3309
3332
@@ -3356,7 +3379,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
3356
3379
bool PeerManagerImpl::ProcessOrphanTx (Peer& peer)
3357
3380
{
3358
3381
AssertLockHeld (g_msgproc_mutex);
3359
- LOCK ( cs_main);
3382
+ LOCK2 (:: cs_main, m_tx_download_mutex );
3360
3383
3361
3384
CTransactionRef porphanTx = nullptr ;
3362
3385
@@ -4173,7 +4196,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4173
4196
4174
4197
const bool reject_tx_invs{RejectIncomingTxs (pfrom)};
4175
4198
4176
- LOCK (cs_main);
4199
+ LOCK2 (cs_main, m_tx_download_mutex );
4177
4200
4178
4201
const auto current_time{GetTime<std::chrono::microseconds>()};
4179
4202
uint256* best_block{nullptr };
@@ -4506,7 +4529,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
4506
4529
const uint256& hash = peer->m_wtxid_relay ? wtxid : txid;
4507
4530
AddKnownTx (*peer, hash);
4508
4531
4509
- LOCK (cs_main);
4532
+ LOCK2 (cs_main, m_tx_download_mutex );
4510
4533
4511
4534
m_txrequest.ReceivedResponse (pfrom.GetId (), txid);
4512
4535
if (tx.HasWitness ()) m_txrequest.ReceivedResponse (pfrom.GetId (), wtxid);
@@ -5263,7 +5286,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
5263
5286
std::vector<CInv> vInv;
5264
5287
vRecv >> vInv;
5265
5288
if (vInv.size () <= MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
5266
- LOCK (::cs_main );
5289
+ LOCK (m_tx_download_mutex );
5267
5290
for (CInv &inv : vInv) {
5268
5291
if (inv.IsGenTxMsg ()) {
5269
5292
// 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
5388
5411
// by another peer that was already processed; in that case,
5389
5412
// the extra work may not be noticed, possibly resulting in an
5390
5413
// unnecessary 100ms delay)
5414
+ LOCK (m_tx_download_mutex);
5391
5415
if (m_orphanage.HaveTxToReconsider (peer->m_id )) fMoreWork = true ;
5392
5416
} catch (const std::exception& e) {
5393
5417
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)
6281
6305
//
6282
6306
// Message: getdata (transactions)
6283
6307
//
6308
+ LOCK (m_tx_download_mutex);
6284
6309
std::vector<std::pair<NodeId, GenTxid>> expired;
6285
6310
auto requestable = m_txrequest.GetRequestable (pto->GetId (), current_time, &expired);
6286
6311
for (const auto & entry : expired) {
0 commit comments