@@ -232,6 +232,9 @@ struct Peer {
232
232
/* * Whether a ping has been requested by the user */
233
233
std::atomic<bool > m_ping_queued{false };
234
234
235
+ /* * Whether this peer relays txs via wtxid */
236
+ std::atomic<bool > m_wtxid_relay{false };
237
+
235
238
/* * A vector of addresses to send to the peer, limited to MAX_ADDR_TO_SEND. */
236
239
std::vector<CAddress> m_addrs_to_send;
237
240
/* * Probabilistic filter to track recent addr messages relayed with this
@@ -331,9 +334,6 @@ class PeerManagerImpl final : public PeerManager
331
334
const std::chrono::microseconds time_received, const std::atomic<bool >& interruptMsgProc) override ;
332
335
333
336
private:
334
- void _RelayTransaction (const uint256& txid, const uint256& wtxid)
335
- EXCLUSIVE_LOCKS_REQUIRED(cs_main);
336
-
337
337
/* * Consider evicting an outbound peer based on the amount of time they've been behind our tip */
338
338
void ConsiderEviction (CNode& pto, std::chrono::seconds time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
339
339
@@ -464,7 +464,7 @@ class PeerManagerImpl final : public PeerManager
464
464
std::map<uint256, std::pair<NodeId, bool >> mapBlockSource GUARDED_BY (cs_main);
465
465
466
466
/* * Number of peers with wtxid relay. */
467
- int m_wtxid_relay_peers GUARDED_BY (cs_main) = 0 ;
467
+ std::atomic< int > m_wtxid_relay_peers{ 0 } ;
468
468
469
469
/* * Number of outbound peers with m_chain_sync.m_protect. */
470
470
int m_outbound_peers_with_protect_from_disconnect GUARDED_BY (cs_main) = 0;
@@ -779,9 +779,6 @@ struct CNodeState {
779
779
// ! A rolling bloom filter of all announced tx CInvs to this peer.
780
780
CRollingBloomFilter m_recently_announced_invs = CRollingBloomFilter{INVENTORY_MAX_RECENT_RELAY, 0.000001 };
781
781
782
- // ! Whether this peer relays txs via wtxid
783
- bool m_wtxid_relay{false };
784
-
785
782
CNodeState (bool is_inbound) : m_is_inbound(is_inbound) {}
786
783
};
787
784
@@ -1211,8 +1208,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler)
1211
1208
CTransactionRef tx = m_mempool.get (txid);
1212
1209
1213
1210
if (tx != nullptr ) {
1214
- LOCK (cs_main);
1215
- _RelayTransaction (txid, tx->GetWitnessHash ());
1211
+ RelayTransaction (txid, tx->GetWitnessHash ());
1216
1212
} else {
1217
1213
m_mempool.RemoveUnbroadcastTx (txid, true );
1218
1214
}
@@ -1239,6 +1235,8 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
1239
1235
PeerRef peer = RemovePeer (nodeid);
1240
1236
assert (peer != nullptr );
1241
1237
misbehavior = WITH_LOCK (peer->m_misbehavior_mutex , return peer->m_misbehavior_score );
1238
+ m_wtxid_relay_peers -= peer->m_wtxid_relay ;
1239
+ assert (m_wtxid_relay_peers >= 0 );
1242
1240
}
1243
1241
CNodeState *state = State (nodeid);
1244
1242
assert (state != nullptr );
@@ -1256,8 +1254,6 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
1256
1254
assert (m_peers_downloading_from >= 0 );
1257
1255
m_outbound_peers_with_protect_from_disconnect -= state->m_chain_sync .m_protect ;
1258
1256
assert (m_outbound_peers_with_protect_from_disconnect >= 0 );
1259
- m_wtxid_relay_peers -= state->m_wtxid_relay ;
1260
- assert (m_wtxid_relay_peers >= 0 );
1261
1257
1262
1258
mapNodeState.erase (nodeid);
1263
1259
@@ -1742,21 +1738,22 @@ void PeerManagerImpl::SendPings()
1742
1738
1743
1739
void PeerManagerImpl::RelayTransaction (const uint256& txid, const uint256& wtxid)
1744
1740
{
1745
- WITH_LOCK (cs_main, _RelayTransaction (txid, wtxid););
1746
- }
1741
+ std::map<const NodeId, const uint256&> relay_peers;
1742
+ {
1743
+ // Don't hold m_peer_mutex while calling ForEachNode() to avoid an
1744
+ // m_peer_mutex/cs_vNodes lock inversion. During shutdown, FinalizeNode()
1745
+ // is called while holding cs_vNodes.
1746
+ LOCK (m_peer_mutex);
1747
+ for (auto & it : m_peer_map) {
1748
+ relay_peers.emplace (it.first , it.second ->m_wtxid_relay ? wtxid : txid);
1749
+ }
1750
+ }
1747
1751
1748
- void PeerManagerImpl::_RelayTransaction (const uint256& txid, const uint256& wtxid)
1749
- {
1750
- m_connman.ForEachNode ([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED (::cs_main) {
1751
- AssertLockHeld (::cs_main);
1752
+ m_connman.ForEachNode ([&relay_peers](CNode* node) {
1753
+ auto it = relay_peers.find (node->GetId ());
1754
+ if (it == relay_peers.end ()) return ; // Should never happen
1752
1755
1753
- CNodeState* state = State (pnode->GetId ());
1754
- if (state == nullptr ) return ;
1755
- if (state->m_wtxid_relay ) {
1756
- pnode->PushTxInventory (wtxid);
1757
- } else {
1758
- pnode->PushTxInventory (txid);
1759
- }
1756
+ node->PushTxInventory (it->second );
1760
1757
});
1761
1758
}
1762
1759
@@ -2317,7 +2314,7 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
2317
2314
2318
2315
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
2319
2316
LogPrint (BCLog::MEMPOOL, " accepted orphan tx %s\n " , orphanHash.ToString ());
2320
- _RelayTransaction (orphanHash, porphanTx->GetWitnessHash ());
2317
+ RelayTransaction (orphanHash, porphanTx->GetWitnessHash ());
2321
2318
m_orphanage.AddChildrenToWorkSet (*porphanTx, orphan_work_set);
2322
2319
m_orphanage.EraseTx (orphanHash);
2323
2320
for (const CTransactionRef& removedTx : result.m_replaced_transactions .value ()) {
@@ -2864,9 +2861,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
2864
2861
return ;
2865
2862
}
2866
2863
if (pfrom.GetCommonVersion () >= WTXID_RELAY_VERSION) {
2867
- LOCK (cs_main);
2868
- if (!State (pfrom.GetId ())->m_wtxid_relay ) {
2869
- State (pfrom.GetId ())->m_wtxid_relay = true ;
2864
+ if (!peer->m_wtxid_relay ) {
2865
+ peer->m_wtxid_relay = true ;
2870
2866
m_wtxid_relay_peers++;
2871
2867
} else {
2872
2868
LogPrint (BCLog::NET, " ignoring duplicate wtxidrelay from peer=%d\n " , pfrom.GetId ());
@@ -3020,7 +3016,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3020
3016
// Ignore INVs that don't match wtxidrelay setting.
3021
3017
// Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
3022
3018
// This is fine as no INV messages are involved in that process.
3023
- if (State (pfrom. GetId ()) ->m_wtxid_relay ) {
3019
+ if (peer ->m_wtxid_relay ) {
3024
3020
if (inv.IsMsgTx ()) continue ;
3025
3021
} else {
3026
3022
if (inv.IsMsgWtx ()) continue ;
@@ -3298,13 +3294,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3298
3294
const uint256& txid = ptx->GetHash ();
3299
3295
const uint256& wtxid = ptx->GetWitnessHash ();
3300
3296
3301
- LOCK2 (cs_main, g_cs_orphans);
3302
-
3303
- CNodeState* nodestate = State (pfrom.GetId ());
3304
-
3305
- const uint256& hash = nodestate->m_wtxid_relay ? wtxid : txid;
3297
+ const uint256& hash = peer->m_wtxid_relay ? wtxid : txid;
3306
3298
pfrom.AddKnownTx (hash);
3307
- if (nodestate ->m_wtxid_relay && txid != wtxid) {
3299
+ if (peer ->m_wtxid_relay && txid != wtxid) {
3308
3300
// Insert txid into filterInventoryKnown, even for
3309
3301
// wtxidrelay peers. This prevents re-adding of
3310
3302
// unconfirmed parents to the recently_announced
@@ -3313,6 +3305,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3313
3305
pfrom.AddKnownTx (txid);
3314
3306
}
3315
3307
3308
+ LOCK2 (cs_main, g_cs_orphans);
3309
+
3316
3310
m_txrequest.ReceivedResponse (pfrom.GetId (), txid);
3317
3311
if (tx.HasWitness ()) m_txrequest.ReceivedResponse (pfrom.GetId (), wtxid);
3318
3312
@@ -3337,7 +3331,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3337
3331
LogPrintf (" Not relaying non-mempool transaction %s from forcerelay peer=%d\n " , tx.GetHash ().ToString (), pfrom.GetId ());
3338
3332
} else {
3339
3333
LogPrintf (" Force relaying tx %s from peer=%d\n " , tx.GetHash ().ToString (), pfrom.GetId ());
3340
- _RelayTransaction (tx.GetHash (), tx.GetWitnessHash ());
3334
+ RelayTransaction (tx.GetHash (), tx.GetWitnessHash ());
3341
3335
}
3342
3336
}
3343
3337
return ;
@@ -3351,7 +3345,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3351
3345
// requests for it.
3352
3346
m_txrequest.ForgetTxHash (tx.GetHash ());
3353
3347
m_txrequest.ForgetTxHash (tx.GetWitnessHash ());
3354
- _RelayTransaction (tx.GetHash (), tx.GetWitnessHash ());
3348
+ RelayTransaction (tx.GetHash (), tx.GetWitnessHash ());
3355
3349
m_orphanage.AddChildrenToWorkSet (tx, peer->m_orphan_work_set );
3356
3350
3357
3351
pfrom.m_last_tx_time = GetTime<std::chrono::seconds>();
@@ -4841,8 +4835,8 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
4841
4835
LOCK (pto->m_tx_relay ->cs_filter );
4842
4836
4843
4837
for (const auto & txinfo : vtxinfo) {
4844
- const uint256& hash = state. m_wtxid_relay ? txinfo.tx ->GetWitnessHash () : txinfo.tx ->GetHash ();
4845
- CInv inv (state. m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
4838
+ const uint256& hash = peer-> m_wtxid_relay ? txinfo.tx ->GetWitnessHash () : txinfo.tx ->GetHash ();
4839
+ CInv inv (peer-> m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
4846
4840
pto->m_tx_relay ->setInventoryTxToSend .erase (hash);
4847
4841
// Don't send transactions that peers will not put into their mempool
4848
4842
if (txinfo.fee < filterrate.GetFee (txinfo.vsize )) {
@@ -4873,7 +4867,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
4873
4867
const CFeeRate filterrate{pto->m_tx_relay ->minFeeFilter .load ()};
4874
4868
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
4875
4869
// A heap is used so that not all items need sorting if only a few are being sent.
4876
- CompareInvMempoolOrder compareInvMempoolOrder (&m_mempool, state. m_wtxid_relay );
4870
+ CompareInvMempoolOrder compareInvMempoolOrder (&m_mempool, peer-> m_wtxid_relay );
4877
4871
std::make_heap (vInvTx.begin (), vInvTx.end (), compareInvMempoolOrder);
4878
4872
// No reason to drain out at many times the network's capacity,
4879
4873
// especially since we have many peers and some will draw much shorter delays.
@@ -4885,7 +4879,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
4885
4879
std::set<uint256>::iterator it = vInvTx.back ();
4886
4880
vInvTx.pop_back ();
4887
4881
uint256 hash = *it;
4888
- CInv inv (state. m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
4882
+ CInv inv (peer-> m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
4889
4883
// Remove it from the to-be-sent set
4890
4884
pto->m_tx_relay ->setInventoryTxToSend .erase (it);
4891
4885
// Check if not in the filter already
0 commit comments