Skip to content

Commit 8818729

Browse files
TheBlueMattjnewbery
authored andcommitted
[refactor] Refactor misbehavior ban decisions to MaybePunishNode()
Isolate the decision of whether to ban a peer to one place in the code, rather than having it sprinkled throughout net_processing. Co-authored-by: Anthony Towns <[email protected]> Suhas Daftuar <[email protected]> John Newbery <[email protected]>
1 parent 00e11e6 commit 8818729

File tree

2 files changed

+58
-34
lines changed

2 files changed

+58
-34
lines changed

src/consensus/validation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class CValidationState {
8484
void SetCorruptionPossible() {
8585
corruptionPossible = true;
8686
}
87+
int GetDoS(void) const { return nDoS; }
8788
unsigned int GetRejectCode() const { return chRejectCode; }
8889
std::string GetRejectReason() const { return strRejectReason; }
8990
std::string GetDebugMessage() const { return strDebugMessage; }

src/net_processing.cpp

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,34 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
959959
LogPrint(BCLog::NET, "%s: %s peer=%d (%d -> %d)%s\n", __func__, state->name, pnode, state->nMisbehavior-howmuch, state->nMisbehavior, message_prefixed);
960960
}
961961

962+
static bool TxRelayMayResultInDisconnect(const CValidationState& state)
963+
{
964+
return (state.GetDoS() > 0);
965+
}
966+
967+
/**
968+
* Potentially ban a node based on the contents of a CValidationState object
969+
* TODO: net_processing should make the punish decision based on the reason
970+
* a tx/block was invalid, rather than just the nDoS score handed back by validation.
971+
*
972+
* @parameter via_compact_block: this bool is passed in because net_processing should
973+
* punish peers differently depending on whether the data was provided in a compact
974+
* block message or not. If the compact block had a valid header, but contained invalid
975+
* txs, the peer should not be punished. See BIP 152.
976+
*/
977+
static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
978+
int nDoS = state.GetDoS();
979+
if (nDoS > 0 && !via_compact_block) {
980+
LOCK(cs_main);
981+
Misbehaving(nodeid, nDoS, message);
982+
return true;
983+
}
984+
if (message != "") {
985+
LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message);
986+
}
987+
return false;
988+
}
989+
962990

963991

964992

@@ -1132,14 +1160,12 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
11321160
const uint256 hash(block.GetHash());
11331161
std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
11341162

1135-
int nDoS = 0;
1136-
if (state.IsInvalid(nDoS)) {
1163+
if (state.IsInvalid()) {
11371164
// Don't send reject message with code 0 or an internal reject code.
11381165
if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
11391166
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash};
11401167
State(it->second.first)->rejects.push_back(reject);
1141-
if (nDoS > 0 && it->second.second)
1142-
Misbehaving(it->second.first, nDoS);
1168+
MaybePunishNode(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);
11431169
}
11441170
}
11451171
// Check that:
@@ -1551,14 +1577,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
15511577
CValidationState state;
15521578
CBlockHeader first_invalid_header;
15531579
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
1554-
int nDoS;
1555-
if (state.IsInvalid(nDoS)) {
1556-
LOCK(cs_main);
1557-
if (nDoS > 0) {
1558-
Misbehaving(pfrom->GetId(), nDoS, "invalid header received");
1559-
} else {
1560-
LogPrint(BCLog::NET, "peer=%d: invalid header received\n", pfrom->GetId());
1561-
}
1580+
if (state.IsInvalid()) {
15621581
if (punish_duplicate_invalid && LookupBlockIndex(first_invalid_header.GetHash())) {
15631582
// Goal: don't allow outbound peers to use up our outbound
15641583
// connection slots if they are on incompatible chains.
@@ -1593,6 +1612,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
15931612
// etc), and not just the duplicate-invalid case.
15941613
pfrom->fDisconnect = true;
15951614
}
1615+
MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false, "invalid header received");
15961616
return false;
15971617
}
15981618
}
@@ -1727,9 +1747,9 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
17271747
const CTransaction& orphanTx = *porphanTx;
17281748
NodeId fromPeer = orphan_it->second.fromPeer;
17291749
bool fMissingInputs2 = false;
1730-
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
1731-
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
1732-
// anyone relaying LegitTxX banned)
1750+
// Use a new CValidationState because orphans come from different peers (and we call
1751+
// MaybePunishNode based on the source peer from the orphan map, not based on the peer
1752+
// that relayed the previous transaction).
17331753
CValidationState orphan_state;
17341754

17351755
if (setMisbehaving.count(fromPeer)) continue;
@@ -1747,11 +1767,11 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
17471767
EraseOrphanTx(orphanHash);
17481768
done = true;
17491769
} else if (!fMissingInputs2) {
1750-
int nDos = 0;
1751-
if (orphan_state.IsInvalid(nDos) && nDos > 0) {
1770+
if (orphan_state.IsInvalid()) {
17521771
// Punish peer that gave us an invalid orphan tx
1753-
Misbehaving(fromPeer, nDos);
1754-
setMisbehaving.insert(fromPeer);
1772+
if (MaybePunishNode(fromPeer, orphan_state, /*via_compact_block*/ false)) {
1773+
setMisbehaving.insert(fromPeer);
1774+
}
17551775
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
17561776
}
17571777
// Has inputs but not accepted to mempool
@@ -2496,8 +2516,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
24962516
// Never relay transactions that we would assign a non-zero DoS
24972517
// score for, as we expect peers to do the same with us in that
24982518
// case.
2499-
int nDoS = 0;
2500-
if (!state.IsInvalid(nDoS) || nDoS == 0) {
2519+
if (!state.IsInvalid() || !TxRelayMayResultInDisconnect(state)) {
25012520
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->GetId());
25022521
RelayTransaction(tx, connman);
25032522
} else {
@@ -2526,8 +2545,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25262545
// peer simply for relaying a tx that our recentRejects has caught,
25272546
// regardless of false positives.
25282547

2529-
int nDoS = 0;
2530-
if (state.IsInvalid(nDoS))
2548+
if (state.IsInvalid())
25312549
{
25322550
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
25332551
pfrom->GetId(),
@@ -2536,9 +2554,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25362554
connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
25372555
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash));
25382556
}
2539-
if (nDoS > 0) {
2540-
Misbehaving(pfrom->GetId(), nDoS);
2541-
}
2557+
MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false);
25422558
}
25432559
return true;
25442560
}
@@ -2574,14 +2590,21 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25742590
const CBlockIndex *pindex = nullptr;
25752591
CValidationState state;
25762592
if (!ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &pindex)) {
2577-
int nDoS;
2578-
if (state.IsInvalid(nDoS)) {
2579-
if (nDoS > 0) {
2580-
LOCK(cs_main);
2581-
Misbehaving(pfrom->GetId(), nDoS, strprintf("Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId()));
2582-
} else {
2583-
LogPrint(BCLog::NET, "Peer %d sent us invalid header via cmpctblock\n", pfrom->GetId());
2584-
}
2593+
if (state.IsInvalid() && received_new_header) {
2594+
// In this situation, the block header is known to be invalid.
2595+
// If we never created a CBlockIndex entry for it, then the
2596+
// header must be bad just by inspection (and is not one that
2597+
// looked okay but the block later turned out to be invalid for
2598+
// some other reason).
2599+
// We should punish compact block peers that give us an invalid
2600+
// header (other than a "duplicate-invalid" one, see
2601+
// ProcessHeadersMessage), so set via_compact_block to false
2602+
// here.
2603+
// TODO: when we switch from DoS scores to reasons that
2604+
// tx/blocks are invalid, this call should set
2605+
// via_compact_block to true, since MaybePunishNode will have
2606+
// sufficient information to act correctly.
2607+
MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false, "invalid header via cmpctblock");
25852608
return true;
25862609
}
25872610
}

0 commit comments

Comments
 (0)