Skip to content

Commit 1f96d2e

Browse files
committed
[net processing] Move misbehavior tracking state to Peer
Misbehavior tracking state is now contained in Peer instead of CNode. It is no longer guarded by cs_main, but instead by a dedicated m_misbehavior_mutex lock. This allows us to remove 14 cs_main locks from net_processing.
1 parent 7cd4159 commit 1f96d2e

File tree

2 files changed

+53
-72
lines changed

2 files changed

+53
-72
lines changed

src/net_processing.cpp

+49-56
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,6 @@ struct CNodeState {
276276
const CService address;
277277
//! Whether we have a fully established connection.
278278
bool fCurrentlyConnected;
279-
//! Accumulated misbehaviour score for this peer.
280-
int nMisbehavior;
281-
//! Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission).
282-
bool m_should_discourage;
283279
//! The best known block we know this peer has announced.
284280
const CBlockIndex *pindexBestKnownBlock;
285281
//! The hash of the last unknown block this peer has announced.
@@ -432,8 +428,6 @@ struct CNodeState {
432428
: address(addrIn), m_is_inbound(is_inbound), m_is_manual_connection(is_manual)
433429
{
434430
fCurrentlyConnected = false;
435-
nMisbehavior = 0;
436-
m_should_discourage = false;
437431
pindexBestKnownBlock = nullptr;
438432
hashLastUnknownBlock.SetNull();
439433
pindexLastCommonBlock = nullptr;
@@ -485,6 +479,13 @@ struct Peer {
485479
/** Same id as the CNode object for this peer */
486480
const NodeId m_id{0};
487481

482+
/** Protects misbehavior data members */
483+
Mutex m_misbehavior_mutex;
484+
/** Accumulated misbehavior score for this peer */
485+
int nMisbehavior GUARDED_BY(m_misbehavior_mutex){0};
486+
/** Whether this peer should be disconnected and marked as discouraged (unless it has the noban permission). */
487+
bool m_should_discourage GUARDED_BY(m_misbehavior_mutex){false};
488+
488489
Peer(NodeId id) : m_id(id) {}
489490
};
490491

@@ -907,7 +908,11 @@ void PeerLogicValidation::ReattemptInitialBroadcast(CScheduler& scheduler) const
907908
void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
908909
fUpdateConnectionTime = false;
909910
LOCK(cs_main);
911+
int misbehavior{0};
910912
{
913+
PeerRef peer = GetPeerRef(nodeid);
914+
assert(peer != nullptr);
915+
misbehavior = WITH_LOCK(peer->m_misbehavior_mutex, return peer->nMisbehavior);
911916
LOCK(g_peer_mutex);
912917
g_peer_map.erase(nodeid);
913918
}
@@ -917,7 +922,7 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
917922
if (state->fSyncStarted)
918923
nSyncStarted--;
919924

920-
if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
925+
if (misbehavior == 0 && state->fCurrentlyConnected) {
921926
fUpdateConnectionTime = true;
922927
}
923928

@@ -947,17 +952,23 @@ void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTim
947952
}
948953

949954
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
950-
LOCK(cs_main);
951-
CNodeState *state = State(nodeid);
952-
if (state == nullptr)
953-
return false;
954-
stats.nMisbehavior = state->nMisbehavior;
955-
stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
956-
stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
957-
for (const QueuedBlock& queue : state->vBlocksInFlight) {
958-
if (queue.pindex)
959-
stats.vHeightInFlight.push_back(queue.pindex->nHeight);
955+
{
956+
LOCK(cs_main);
957+
CNodeState* state = State(nodeid);
958+
if (state == nullptr)
959+
return false;
960+
stats.nSyncHeight = state->pindexBestKnownBlock ? state->pindexBestKnownBlock->nHeight : -1;
961+
stats.nCommonHeight = state->pindexLastCommonBlock ? state->pindexLastCommonBlock->nHeight : -1;
962+
for (const QueuedBlock& queue : state->vBlocksInFlight) {
963+
if (queue.pindex)
964+
stats.vHeightInFlight.push_back(queue.pindex->nHeight);
965+
}
960966
}
967+
968+
PeerRef peer = GetPeerRef(nodeid);
969+
if (peer == nullptr) return false;
970+
stats.nMisbehavior = WITH_LOCK(peer->m_misbehavior_mutex, return peer->nMisbehavior);
971+
961972
return true;
962973
}
963974

@@ -1101,21 +1112,21 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
11011112
* Increment peer's misbehavior score. If the new value >= DISCOURAGEMENT_THRESHOLD, mark the node
11021113
* to be discouraged, meaning the peer might be disconnected and added to the discouragement filter.
11031114
*/
1104-
void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
1115+
void Misbehaving(const NodeId pnode, const int howmuch, const std::string& message)
11051116
{
11061117
assert(howmuch > 0);
11071118

1108-
CNodeState* const state = State(pnode);
1109-
if (state == nullptr) return;
1119+
PeerRef peer = GetPeerRef(pnode);
1120+
if (peer == nullptr) return;
11101121

1111-
state->nMisbehavior += howmuch;
1122+
LOCK(peer->m_misbehavior_mutex);
1123+
peer->nMisbehavior += howmuch;
11121124
const std::string message_prefixed = message.empty() ? "" : (": " + message);
1113-
if (state->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && state->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD)
1114-
{
1115-
LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
1116-
state->m_should_discourage = true;
1125+
if (peer->nMisbehavior >= DISCOURAGEMENT_THRESHOLD && peer->nMisbehavior - howmuch < DISCOURAGEMENT_THRESHOLD) {
1126+
LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d) DISCOURAGE THRESHOLD EXCEEDED%s\n", pnode, peer->nMisbehavior - howmuch, peer->nMisbehavior, message_prefixed);
1127+
peer->m_should_discourage = true;
11171128
} else {
1118-
LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, state->nMisbehavior - howmuch, state->nMisbehavior, message_prefixed);
1129+
LogPrint(BCLog::NET, "Misbehaving: peer=%d (%d -> %d)%s\n", pnode, peer->nMisbehavior - howmuch, peer->nMisbehavior, message_prefixed);
11191130
}
11201131
}
11211132

@@ -1137,7 +1148,6 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
11371148
case BlockValidationResult::BLOCK_CONSENSUS:
11381149
case BlockValidationResult::BLOCK_MUTATED:
11391150
if (!via_compact_block) {
1140-
LOCK(cs_main);
11411151
Misbehaving(nodeid, 100, message);
11421152
return true;
11431153
}
@@ -1161,18 +1171,12 @@ static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& s
11611171
case BlockValidationResult::BLOCK_INVALID_HEADER:
11621172
case BlockValidationResult::BLOCK_CHECKPOINT:
11631173
case BlockValidationResult::BLOCK_INVALID_PREV:
1164-
{
1165-
LOCK(cs_main);
1166-
Misbehaving(nodeid, 100, message);
1167-
}
1174+
Misbehaving(nodeid, 100, message);
11681175
return true;
11691176
// Conflicting (but not necessarily invalid) data or different policy:
11701177
case BlockValidationResult::BLOCK_MISSING_PREV:
1171-
{
1172-
// TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
1173-
LOCK(cs_main);
1174-
Misbehaving(nodeid, 10, message);
1175-
}
1178+
// TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
1179+
Misbehaving(nodeid, 10, message);
11761180
return true;
11771181
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
11781182
case BlockValidationResult::BLOCK_TIME_FUTURE:
@@ -1196,11 +1200,8 @@ static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state,
11961200
break;
11971201
// The node is providing invalid data:
11981202
case TxValidationResult::TX_CONSENSUS:
1199-
{
1200-
LOCK(cs_main);
1201-
Misbehaving(nodeid, 100, message);
1202-
return true;
1203-
}
1203+
Misbehaving(nodeid, 100, message);
1204+
return true;
12041205
// Conflicting (but not necessarily invalid) data or different policy:
12051206
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
12061207
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
@@ -1847,7 +1848,6 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
18471848
BlockTransactions resp(req);
18481849
for (size_t i = 0; i < req.indexes.size(); i++) {
18491850
if (req.indexes[i] >= block.vtx.size()) {
1850-
LOCK(cs_main);
18511851
Misbehaving(pfrom.GetId(), 100, "getblocktxn with out-of-bounds tx indices");
18521852
return;
18531853
}
@@ -2368,7 +2368,6 @@ void ProcessMessage(
23682368
// Each connection can only send one version message
23692369
if (pfrom.nVersion != 0)
23702370
{
2371-
LOCK(cs_main);
23722371
Misbehaving(pfrom.GetId(), 1, "redundant version message");
23732372
return;
23742373
}
@@ -2528,7 +2527,6 @@ void ProcessMessage(
25282527

25292528
if (pfrom.nVersion == 0) {
25302529
// Must have a version message before anything else
2531-
LOCK(cs_main);
25322530
Misbehaving(pfrom.GetId(), 1, "non-version message before version handshake");
25332531
return;
25342532
}
@@ -2595,7 +2593,6 @@ void ProcessMessage(
25952593

25962594
if (!pfrom.fSuccessfullyConnected) {
25972595
// Must have a verack message before anything else
2598-
LOCK(cs_main);
25992596
Misbehaving(pfrom.GetId(), 1, "non-verack message before version handshake");
26002597
return;
26012598
}
@@ -2609,7 +2606,6 @@ void ProcessMessage(
26092606
}
26102607
if (vAddr.size() > MAX_ADDR_TO_SEND)
26112608
{
2612-
LOCK(cs_main);
26132609
Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
26142610
return;
26152611
}
@@ -2688,7 +2684,6 @@ void ProcessMessage(
26882684
vRecv >> vInv;
26892685
if (vInv.size() > MAX_INV_SZ)
26902686
{
2691-
LOCK(cs_main);
26922687
Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
26932688
return;
26942689
}
@@ -2764,7 +2759,6 @@ void ProcessMessage(
27642759
vRecv >> vInv;
27652760
if (vInv.size() > MAX_INV_SZ)
27662761
{
2767-
LOCK(cs_main);
27682762
Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
27692763
return;
27702764
}
@@ -3489,7 +3483,6 @@ void ProcessMessage(
34893483
// Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks.
34903484
unsigned int nCount = ReadCompactSize(vRecv);
34913485
if (nCount > MAX_HEADERS_RESULTS) {
3492-
LOCK(cs_main);
34933486
Misbehaving(pfrom.GetId(), 20, strprintf("headers message size = %u", nCount));
34943487
return;
34953488
}
@@ -3691,7 +3684,6 @@ void ProcessMessage(
36913684
if (!filter.IsWithinSizeConstraints())
36923685
{
36933686
// There is no excuse for sending a too-large filter
3694-
LOCK(cs_main);
36953687
Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
36963688
}
36973689
else if (pfrom.m_tx_relay != nullptr)
@@ -3725,7 +3717,6 @@ void ProcessMessage(
37253717
}
37263718
}
37273719
if (bad) {
3728-
LOCK(cs_main);
37293720
Misbehaving(pfrom.GetId(), 100, "bad filteradd message");
37303721
}
37313722
return;
@@ -3811,15 +3802,17 @@ void ProcessMessage(
38113802
bool PeerLogicValidation::MaybeDiscourageAndDisconnect(CNode& pnode)
38123803
{
38133804
const NodeId peer_id{pnode.GetId()};
3805+
PeerRef peer = GetPeerRef(peer_id);
3806+
if (peer == nullptr) return false;
3807+
38143808
{
3815-
LOCK(cs_main);
3816-
CNodeState& state = *State(peer_id);
3809+
LOCK(peer->m_misbehavior_mutex);
38173810

38183811
// There's nothing to do if the m_should_discourage flag isn't set
3819-
if (!state.m_should_discourage) return false;
3812+
if (!peer->m_should_discourage) return false;
38203813

3821-
state.m_should_discourage = false;
3822-
} // cs_main
3814+
peer->m_should_discourage = false;
3815+
} // peer.m_misbehavior_mutex
38233816

38243817
if (pnode.HasPermission(PF_NOBAN)) {
38253818
// We never disconnect or discourage peers for bad behavior if they have the NOBAN permission flag

src/test/denialofservice_tests.cpp

+4-16
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
232232
peerLogic->InitializeNode(&dummyNode1);
233233
dummyNode1.nVersion = 1;
234234
dummyNode1.fSuccessfullyConnected = true;
235-
{
236-
LOCK(cs_main);
237-
Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
238-
}
235+
Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD); // Should be discouraged
239236
{
240237
LOCK(dummyNode1.cs_sendProcessing);
241238
BOOST_CHECK(peerLogic->SendMessages(&dummyNode1));
@@ -249,20 +246,14 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
249246
peerLogic->InitializeNode(&dummyNode2);
250247
dummyNode2.nVersion = 1;
251248
dummyNode2.fSuccessfullyConnected = true;
252-
{
253-
LOCK(cs_main);
254-
Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
255-
}
249+
Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1);
256250
{
257251
LOCK(dummyNode2.cs_sendProcessing);
258252
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
259253
}
260254
BOOST_CHECK(!banman->IsDiscouraged(addr2)); // 2 not discouraged yet...
261255
BOOST_CHECK(banman->IsDiscouraged(addr1)); // ... but 1 still should be
262-
{
263-
LOCK(cs_main);
264-
Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
265-
}
256+
Misbehaving(dummyNode2.GetId(), 1); // 2 reaches discouragement threshold
266257
{
267258
LOCK(dummyNode2.cs_sendProcessing);
268259
BOOST_CHECK(peerLogic->SendMessages(&dummyNode2));
@@ -292,10 +283,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
292283
dummyNode.nVersion = 1;
293284
dummyNode.fSuccessfullyConnected = true;
294285

295-
{
296-
LOCK(cs_main);
297-
Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
298-
}
286+
Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD);
299287
{
300288
LOCK(dummyNode.cs_sendProcessing);
301289
BOOST_CHECK(peerLogic->SendMessages(&dummyNode));

0 commit comments

Comments
 (0)