diff --git a/src/net.h b/src/net.h index beec58c389..9c41e960e4 100644 --- a/src/net.h +++ b/src/net.h @@ -942,6 +942,25 @@ public: void CopyStats(CNodeStats& stats) EXCLUSIVE_LOCKS_REQUIRED(!m_subver_mutex, !m_addr_local_mutex, !cs_vSend, !cs_vRecv); + bool PunishInvalidBlocks() const + { + if (HasPermission(NetPermissionFlags::NoBan)) { + return false; + } + switch (m_conn_type) { + case ConnectionType::INBOUND: + case ConnectionType::MANUAL: + case ConnectionType::FEELER: + return false; + case ConnectionType::OUTBOUND_FULL_RELAY: + case ConnectionType::BLOCK_RELAY: + case ConnectionType::ADDR_FETCH: + return true; + } // no default case, so the compiler can warn about missing cases + + assert(false); + } + std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); } /** A ping-pong round trip has completed successfully. Update latest and minimum ping times. */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 13ea3a29be..77508c741d 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1945,10 +1945,23 @@ void PeerManagerImpl::Misbehaving(Peer& peer, const std::string& message) LogPrint(BCLog::NET, "Misbehaving: peer=%d%s\n", peer.m_id, message_prefixed); } +static void HandleDoSPunishment(CConnman& connman, NodeId node_id, const int nDoS, const char * const what_is_it) { + // We never actually DoS ban for invalid blocks, merely disconnect nodes if we're relying on them as a primary node + const std::string msg = strprintf("peer=%d got DoS score %d on invalid %s", node_id, nDoS, what_is_it); + connman.ForNode(node_id, [msg](CNode* node) { + if (node->PunishInvalidBlocks()) { + LogPrint(BCLog::NET, "%s; simply disconnecting\n", msg); + node->fDisconnect = true; + } else { + LogPrint(BCLog::NET, "%s; tolerating\n", msg); + } + return true; + }); +} + void PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message) { - PeerRef peer{GetPeerRef(nodeid)}; switch (state.GetResult()) { case BlockValidationResult::BLOCK_RESULT_UNSET: break; @@ -1960,7 +1973,7 @@ void PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati case BlockValidationResult::BLOCK_CONSENSUS: case BlockValidationResult::BLOCK_MUTATED: if (!via_compact_block) { - if (peer) Misbehaving(*peer, message); + HandleDoSPunishment(m_connman, nodeid, 100, "block"); return; } break; @@ -1975,7 +1988,7 @@ void PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati // Discourage outbound (but not inbound) peers if on an invalid chain. // Exempt HB compact block peers. Manual connections are always protected from discouragement. if (!via_compact_block && !node_state->m_is_inbound) { - if (peer) Misbehaving(*peer, message); + HandleDoSPunishment(m_connman, nodeid, 100, "block"); return; } break; @@ -1983,11 +1996,11 @@ void PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati case BlockValidationResult::BLOCK_INVALID_HEADER: case BlockValidationResult::BLOCK_CHECKPOINT: case BlockValidationResult::BLOCK_INVALID_PREV: - if (peer) Misbehaving(*peer, message); + HandleDoSPunishment(m_connman, nodeid, 100, "block header"); return; // Conflicting (but not necessarily invalid) data or different policy: case BlockValidationResult::BLOCK_MISSING_PREV: - if (peer) Misbehaving(*peer, message); + HandleDoSPunishment(m_connman, nodeid, 100, "block header"); return; case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE: case BlockValidationResult::BLOCK_TIME_FUTURE: @@ -2006,7 +2019,7 @@ void PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationStat break; // The node is providing invalid data: case TxValidationResult::TX_CONSENSUS: - if (peer) Misbehaving(*peer, ""); + HandleDoSPunishment(m_connman, nodeid, 100, "transaction"); return; // Conflicting (but not necessarily invalid) data or different policy: case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE: @@ -2767,6 +2780,10 @@ void PeerManagerImpl::HandleUnconnectingHeaders(CNode& pfrom, Peer& peer, // eventually get the headers - even from a different peer - // we can use this peer to download. WITH_LOCK(cs_main, UpdateBlockAvailability(pfrom.GetId(), headers.back().GetHash())); + + if (pfrom.PunishInvalidBlocks()) { + pfrom.fDisconnect = true; + } } bool PeerManagerImpl::CheckHeadersAreContinuous(const std::vector& headers) const