@@ -278,12 +278,6 @@ struct CNodeState {
278
278
const CService address;
279
279
// ! Whether we have a fully established connection.
280
280
bool fCurrentlyConnected ;
281
- // ! Accumulated misbehaviour score for this peer.
282
- int nMisbehavior;
283
- // ! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
284
- bool m_should_discourage;
285
- // ! String name of this peer (debugging/logging purposes).
286
- const std::string name;
287
281
// ! The best known block we know this peer has announced.
288
282
const CBlockIndex *pindexBestKnownBlock;
289
283
// ! The hash of the last unknown block this peer has announced.
@@ -432,13 +426,10 @@ struct CNodeState {
432
426
// ! Whether this peer relays txs via wtxid
433
427
bool m_wtxid_relay{false };
434
428
435
- CNodeState (CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
436
- address (addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
437
- m_is_manual_connection (is_manual)
429
+ CNodeState (CAddress addrIn, bool is_inbound, bool is_manual)
430
+ : address(addrIn), m_is_inbound(is_inbound), m_is_manual_connection(is_manual)
438
431
{
439
432
fCurrentlyConnected = false ;
440
- nMisbehavior = 0 ;
441
- m_should_discourage = false ;
442
433
pindexBestKnownBlock = nullptr ;
443
434
hashLastUnknownBlock.SetNull ();
444
435
pindexLastCommonBlock = nullptr ;
@@ -476,6 +467,50 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
476
467
return &it->second ;
477
468
}
478
469
470
+ /* *
471
+ * Data structure for an individual peer. This struct is not protected by
472
+ * cs_main since it does not contain validation-critical data.
473
+ *
474
+ * Memory is owned by shared pointers and this object is destructed when
475
+ * the refcount drops to zero.
476
+ *
477
+ * TODO: move most members from CNodeState to this structure.
478
+ * TODO: move remaining application-layer data members from CNode to this structure.
479
+ */
480
+ struct Peer {
481
+ /* * Same id as the CNode object for this peer */
482
+ const NodeId m_id{0 };
483
+
484
+ /* * Protects misbehavior data members */
485
+ Mutex m_misbehavior_mutex;
486
+ /* * Accumulated misbehavior score for this peer */
487
+ int m_misbehavior_score GUARDED_BY (m_misbehavior_mutex){0 };
488
+ /* * Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */
489
+ bool m_should_discourage GUARDED_BY (m_misbehavior_mutex){false };
490
+
491
+ Peer (NodeId id) : m_id(id) {}
492
+ };
493
+
494
+ using PeerRef = std::shared_ptr<Peer>;
495
+
496
+ /* *
497
+ * Map of all Peer objects, keyed by peer id. This map is protected
498
+ * by the global g_peer_mutex. Once a shared pointer reference is
499
+ * taken, the lock may be released. Individual fields are protected by
500
+ * their own locks.
501
+ */
502
+ Mutex g_peer_mutex;
503
+ static std::map<NodeId, PeerRef> g_peer_map GUARDED_BY (g_peer_mutex);
504
+
505
+ /* * Get a shared pointer to the Peer object.
506
+ * May return nullptr if the Peer object can't be found. */
507
+ static PeerRef GetPeerRef (NodeId id)
508
+ {
509
+ LOCK (g_peer_mutex);
510
+ auto it = g_peer_map.find (id);
511
+ return it != g_peer_map.end () ? it->second : nullptr ;
512
+ }
513
+
479
514
static void UpdatePreferredDownload (const CNode& node, CNodeState* state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
480
515
{
481
516
nPreferredDownload -= state->fPreferredDownload ;
@@ -841,7 +876,12 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
841
876
NodeId nodeid = pnode->GetId ();
842
877
{
843
878
LOCK (cs_main);
844
- mapNodeState.emplace_hint (mapNodeState.end (), std::piecewise_construct, std::forward_as_tuple (nodeid), std::forward_as_tuple (addr, std::move (addrName), pnode->IsInboundConn (), pnode->IsManualConn ()));
879
+ mapNodeState.emplace_hint (mapNodeState.end (), std::piecewise_construct, std::forward_as_tuple (nodeid), std::forward_as_tuple (addr, pnode->IsInboundConn (), pnode->IsManualConn ()));
880
+ }
881
+ {
882
+ PeerRef peer = std::make_shared<Peer>(nodeid);
883
+ LOCK (g_peer_mutex);
884
+ g_peer_map.emplace_hint (g_peer_map.end (), nodeid, std::move (peer));
845
885
}
846
886
if (!pnode->IsInboundConn ())
847
887
PushNodeVersion (*pnode, m_connman, GetTime ());
@@ -870,13 +910,21 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
870
910
void PeerLogicValidation::FinalizeNode (NodeId nodeid, bool & fUpdateConnectionTime ) {
871
911
fUpdateConnectionTime = false ;
872
912
LOCK (cs_main);
913
+ int misbehavior{0 };
914
+ {
915
+ PeerRef peer = GetPeerRef (nodeid);
916
+ assert (peer != nullptr );
917
+ misbehavior = WITH_LOCK (peer->m_misbehavior_mutex , return peer->m_misbehavior_score );
918
+ LOCK (g_peer_mutex);
919
+ g_peer_map.erase (nodeid);
920
+ }
873
921
CNodeState *state = State (nodeid);
874
922
assert (state != nullptr );
875
923
876
924
if (state->fSyncStarted )
877
925
nSyncStarted--;
878
926
879
- if (state-> nMisbehavior == 0 && state->fCurrentlyConnected ) {
927
+ if (misbehavior == 0 && state->fCurrentlyConnected ) {
880
928
fUpdateConnectionTime = true ;
881
929
}
882
930
@@ -906,17 +954,23 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
906
954
}
907
955
908
956
bool GetNodeStateStats (NodeId nodeid, CNodeStateStats &stats) {
909
- LOCK (cs_main);
910
- CNodeState *state = State (nodeid);
911
- if (state == nullptr )
912
- return false ;
913
- stats.nMisbehavior = state->nMisbehavior ;
914
- stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock ->nHeight : -1 ;
915
- stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock ->nHeight : -1 ;
916
- for (const QueuedBlock& queue : state->vBlocksInFlight ) {
917
- if (queue.pindex )
918
- stats.vHeightInFlight .push_back (queue.pindex ->nHeight );
957
+ {
958
+ LOCK (cs_main);
959
+ CNodeState* state = State (nodeid);
960
+ if (state == nullptr )
961
+ return false ;
962
+ stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock ->nHeight : -1 ;
963
+ stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock ->nHeight : -1 ;
964
+ for (const QueuedBlock& queue : state->vBlocksInFlight ) {
965
+ if (queue.pindex )
966
+ stats.vHeightInFlight .push_back (queue.pindex ->nHeight );
967
+ }
919
968
}
969
+
970
+ PeerRef peer = GetPeerRef (nodeid);
971
+ if (peer == nullptr ) return false ;
972
+ stats.m_misbehavior_score = WITH_LOCK (peer->m_misbehavior_mutex , return peer->m_misbehavior_score );
973
+
920
974
return true ;
921
975
}
922
976
@@ -1060,21 +1114,21 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
1060
1114
* Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
1061
1115
* to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
1062
1116
*/
1063
- void Misbehaving (const NodeId pnode, const int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1117
+ void Misbehaving (const NodeId pnode, const int howmuch, const std::string& message)
1064
1118
{
1065
1119
assert (howmuch > 0 );
1066
1120
1067
- CNodeState* const state = State (pnode);
1068
- if (state == nullptr ) return ;
1121
+ PeerRef peer = GetPeerRef (pnode);
1122
+ if (peer == nullptr ) return ;
1069
1123
1070
- state->nMisbehavior += howmuch;
1124
+ LOCK (peer->m_misbehavior_mutex );
1125
+ peer->m_misbehavior_score += howmuch;
1071
1126
const std::string message_prefixed = message.empty () ? " " : (" : " + message);
1072
- if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
1073
- {
1074
- LogPrint (BCLog::NET, " Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n " , pnode, state->nMisbehavior - howmuch, state->nMisbehavior , message_prefixed);
1075
- state->m_should_discourage = true ;
1127
+ if (peer->m_misbehavior_score >= DISCOURAGEMENT_THRESHOLD && peer->m_misbehavior_score - howmuch < DISCOURAGEMENT_THRESHOLD) {
1128
+ LogPrint (BCLog::NET, " Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n " , pnode, peer->m_misbehavior_score - howmuch, peer->m_misbehavior_score , message_prefixed);
1129
+ peer->m_should_discourage = true ;
1076
1130
} else {
1077
- LogPrint (BCLog::NET, " Misbehaving: peer=%d (%d -> %d)%s\n " , pnode, state-> nMisbehavior - howmuch, state-> nMisbehavior , message_prefixed);
1131
+ LogPrint (BCLog::NET, " Misbehaving: peer=%d (%d -> %d)%s\n " , pnode, peer-> m_misbehavior_score - howmuch, peer-> m_misbehavior_score , message_prefixed);
1078
1132
}
1079
1133
}
1080
1134
@@ -1096,7 +1150,6 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
1096
1150
case BlockValidationResult::BLOCK_CONSENSUS:
1097
1151
case BlockValidationResult::BLOCK_MUTATED:
1098
1152
if (!via_compact_block) {
1099
- LOCK (cs_main);
1100
1153
Misbehaving (nodeid, 100 , message);
1101
1154
return true ;
1102
1155
}
@@ -1120,18 +1173,12 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
1120
1173
case BlockValidationResult::BLOCK_INVALID_HEADER:
1121
1174
case BlockValidationResult::BLOCK_CHECKPOINT:
1122
1175
case BlockValidationResult::BLOCK_INVALID_PREV:
1123
- {
1124
- LOCK (cs_main);
1125
- Misbehaving (nodeid, 100 , message);
1126
- }
1176
+ Misbehaving (nodeid, 100 , message);
1127
1177
return true ;
1128
1178
// Conflicting (but not necessarily invalid) data or different policy:
1129
1179
case BlockValidationResult::BLOCK_MISSING_PREV:
1130
- {
1131
- // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
1132
- LOCK (cs_main);
1133
- Misbehaving (nodeid, 10 , message);
1134
- }
1180
+ // TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
1181
+ Misbehaving (nodeid, 10 , message);
1135
1182
return true ;
1136
1183
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
1137
1184
case BlockValidationResult::BLOCK_TIME_FUTURE:
@@ -1155,11 +1202,8 @@ static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state,
1155
1202
break ;
1156
1203
// The node is providing invalid data:
1157
1204
case TxValidationResult::TX_CONSENSUS:
1158
- {
1159
- LOCK (cs_main);
1160
- Misbehaving (nodeid, 100 , message);
1161
- return true ;
1162
- }
1205
+ Misbehaving (nodeid, 100 , message);
1206
+ return true ;
1163
1207
// Conflicting (but not necessarily invalid) data or different policy:
1164
1208
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
1165
1209
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
@@ -1806,7 +1850,6 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
1806
1850
BlockTransactions resp (req);
1807
1851
for (size_t i = 0 ; i < req.indexes .size (); i++) {
1808
1852
if (req.indexes [i] >= block.vtx .size ()) {
1809
- LOCK (cs_main);
1810
1853
Misbehaving (pfrom.GetId (), 100 , " getblocktxn with out-of-bounds tx indices" );
1811
1854
return ;
1812
1855
}
@@ -2318,7 +2361,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
2318
2361
// Each connection can only send one version message
2319
2362
if (pfrom.nVersion != 0 )
2320
2363
{
2321
- LOCK (cs_main);
2322
2364
Misbehaving (pfrom.GetId (), 1 , " redundant version message" );
2323
2365
return ;
2324
2366
}
@@ -2478,7 +2520,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
2478
2520
2479
2521
if (pfrom.nVersion == 0 ) {
2480
2522
// Must have a version message before anything else
2481
- LOCK (cs_main);
2482
2523
Misbehaving (pfrom.GetId (), 1 , " non-version message before version handshake" );
2483
2524
return ;
2484
2525
}
@@ -2545,7 +2586,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
2545
2586
2546
2587
if (!pfrom.fSuccessfullyConnected ) {
2547
2588
// Must have a verack message before anything else
2548
- LOCK (cs_main);
2549
2589
Misbehaving (pfrom.GetId (), 1 , " non-verack message before version handshake" );
2550
2590
return ;
2551
2591
}
@@ -2559,7 +2599,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
2559
2599
}
2560
2600
if (vAddr.size () > MAX_ADDR_TO_SEND)
2561
2601
{
2562
- LOCK (cs_main);
2563
2602
Misbehaving (pfrom.GetId (), 20 , strprintf (" addr message size = %u" , vAddr.size ()));
2564
2603
return ;
2565
2604
}
@@ -2638,7 +2677,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
2638
2677
vRecv >> vInv;
2639
2678
if (vInv.size () > MAX_INV_SZ)
2640
2679
{
2641
- LOCK (cs_main);
2642
2680
Misbehaving (pfrom.GetId (), 20 , strprintf (" inv message size = %u" , vInv.size ()));
2643
2681
return ;
2644
2682
}
@@ -2714,7 +2752,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
2714
2752
vRecv >> vInv;
2715
2753
if (vInv.size () > MAX_INV_SZ)
2716
2754
{
2717
- LOCK (cs_main);
2718
2755
Misbehaving (pfrom.GetId (), 20 , strprintf (" getdata message size = %u" , vInv.size ()));
2719
2756
return ;
2720
2757
}
@@ -3439,7 +3476,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
3439
3476
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
3440
3477
unsigned int nCount = ReadCompactSize (vRecv);
3441
3478
if (nCount > MAX_HEADERS_RESULTS) {
3442
- LOCK (cs_main);
3443
3479
Misbehaving (pfrom.GetId (), 20 , strprintf (" headers message size = %u" , nCount));
3444
3480
return ;
3445
3481
}
@@ -3641,7 +3677,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
3641
3677
if (!filter.IsWithinSizeConstraints ())
3642
3678
{
3643
3679
// There is no excuse for sending a too-large filter
3644
- LOCK (cs_main);
3645
3680
Misbehaving (pfrom.GetId (), 100 , " too-large bloom filter" );
3646
3681
}
3647
3682
else if (pfrom.m_tx_relay != nullptr )
@@ -3675,7 +3710,6 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
3675
3710
}
3676
3711
}
3677
3712
if (bad) {
3678
- LOCK (cs_main);
3679
3713
Misbehaving (pfrom.GetId (), 100 , " bad filteradd message" );
3680
3714
}
3681
3715
return ;
@@ -3761,15 +3795,17 @@ void PeerLogicValidation::ProcessMessage(CNode& pfrom, const std::string& msg_ty
3761
3795
bool PeerLogicValidation::MaybeDiscourageAndDisconnect (CNode& pnode)
3762
3796
{
3763
3797
const NodeId peer_id{pnode.GetId ()};
3798
+ PeerRef peer = GetPeerRef (peer_id);
3799
+ if (peer == nullptr ) return false ;
3800
+
3764
3801
{
3765
- LOCK (cs_main);
3766
- CNodeState& state = *State (peer_id);
3802
+ LOCK (peer->m_misbehavior_mutex );
3767
3803
3768
3804
// There's nothing to do if the m_should_discourage flag isn't set
3769
- if (!state. m_should_discourage ) return false ;
3805
+ if (!peer-> m_should_discourage ) return false ;
3770
3806
3771
- state. m_should_discourage = false ;
3772
- } // cs_main
3807
+ peer-> m_should_discourage = false ;
3808
+ } // peer.m_misbehavior_mutex
3773
3809
3774
3810
if (pnode.HasPermission (PF_NOBAN)) {
3775
3811
// We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag
0 commit comments