@@ -959,6 +959,34 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
959
959
LogPrint (BCLog::NET, " %s: %s peer=%d (%d -> %d)%s\n " , __func__, state->name , pnode, state->nMisbehavior -howmuch, state->nMisbehavior , message_prefixed);
960
960
}
961
961
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
+
962
990
963
991
964
992
@@ -1132,14 +1160,12 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
1132
1160
const uint256 hash (block.GetHash ());
1133
1161
std::map<uint256, std::pair<NodeId, bool >>::iterator it = mapBlockSource.find (hash);
1134
1162
1135
- int nDoS = 0 ;
1136
- if (state.IsInvalid (nDoS)) {
1163
+ if (state.IsInvalid ()) {
1137
1164
// Don't send reject message with code 0 or an internal reject code.
1138
1165
if (it != mapBlockSource.end () && State (it->second .first ) && state.GetRejectCode () > 0 && state.GetRejectCode () < REJECT_INTERNAL) {
1139
1166
CBlockReject reject = {(unsigned char )state.GetRejectCode (), state.GetRejectReason ().substr (0 , MAX_REJECT_MESSAGE_LENGTH), hash};
1140
1167
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 );
1143
1169
}
1144
1170
}
1145
1171
// Check that:
@@ -1551,14 +1577,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
1551
1577
CValidationState state;
1552
1578
CBlockHeader first_invalid_header;
1553
1579
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 ()) {
1562
1581
if (punish_duplicate_invalid && LookupBlockIndex (first_invalid_header.GetHash ())) {
1563
1582
// Goal: don't allow outbound peers to use up our outbound
1564
1583
// connection slots if they are on incompatible chains.
@@ -1593,6 +1612,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
1593
1612
// etc), and not just the duplicate-invalid case.
1594
1613
pfrom->fDisconnect = true ;
1595
1614
}
1615
+ MaybePunishNode (pfrom->GetId (), state, /* via_compact_block*/ false , " invalid header received" );
1596
1616
return false ;
1597
1617
}
1598
1618
}
@@ -1727,9 +1747,9 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
1727
1747
const CTransaction& orphanTx = *porphanTx;
1728
1748
NodeId fromPeer = orphan_it->second .fromPeer ;
1729
1749
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).
1733
1753
CValidationState orphan_state;
1734
1754
1735
1755
if (setMisbehaving.count (fromPeer)) continue ;
@@ -1747,11 +1767,11 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
1747
1767
EraseOrphanTx (orphanHash);
1748
1768
done = true ;
1749
1769
} else if (!fMissingInputs2 ) {
1750
- int nDos = 0 ;
1751
- if (orphan_state.IsInvalid (nDos) && nDos > 0 ) {
1770
+ if (orphan_state.IsInvalid ()) {
1752
1771
// 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
+ }
1755
1775
LogPrint (BCLog::MEMPOOL, " invalid orphan tx %s\n " , orphanHash.ToString ());
1756
1776
}
1757
1777
// Has inputs but not accepted to mempool
@@ -2496,8 +2516,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2496
2516
// Never relay transactions that we would assign a non-zero DoS
2497
2517
// score for, as we expect peers to do the same with us in that
2498
2518
// case.
2499
- int nDoS = 0 ;
2500
- if (!state.IsInvalid (nDoS) || nDoS == 0 ) {
2519
+ if (!state.IsInvalid () || !TxRelayMayResultInDisconnect (state)) {
2501
2520
LogPrintf (" Force relaying tx %s from whitelisted peer=%d\n " , tx.GetHash ().ToString (), pfrom->GetId ());
2502
2521
RelayTransaction (tx, connman);
2503
2522
} else {
@@ -2526,8 +2545,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2526
2545
// peer simply for relaying a tx that our recentRejects has caught,
2527
2546
// regardless of false positives.
2528
2547
2529
- int nDoS = 0 ;
2530
- if (state.IsInvalid (nDoS))
2548
+ if (state.IsInvalid ())
2531
2549
{
2532
2550
LogPrint (BCLog::MEMPOOLREJ, " %s from peer=%d was not accepted: %s\n " , tx.GetHash ().ToString (),
2533
2551
pfrom->GetId (),
@@ -2536,9 +2554,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2536
2554
connman->PushMessage (pfrom, msgMaker.Make (NetMsgType::REJECT, strCommand, (unsigned char )state.GetRejectCode (),
2537
2555
state.GetRejectReason ().substr (0 , MAX_REJECT_MESSAGE_LENGTH), inv.hash ));
2538
2556
}
2539
- if (nDoS > 0 ) {
2540
- Misbehaving (pfrom->GetId (), nDoS);
2541
- }
2557
+ MaybePunishNode (pfrom->GetId (), state, /* via_compact_block*/ false );
2542
2558
}
2543
2559
return true ;
2544
2560
}
@@ -2574,14 +2590,21 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
2574
2590
const CBlockIndex *pindex = nullptr ;
2575
2591
CValidationState state;
2576
2592
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" );
2585
2608
return true ;
2586
2609
}
2587
2610
}
0 commit comments