@@ -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