Skip to content

Commit 6b34bc6

Browse files
committed
Fix handling of invalid headers
We only disconnect outbound peers (excluding HB compact block peers and manual connections) when receiving a CACHED_INVALID header.
1 parent ef54b48 commit 6b34bc6

File tree

1 file changed

+30
-47
lines changed

1 file changed

+30
-47
lines changed

src/net_processing.cpp

Lines changed: 30 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,16 @@ struct CNodeState {
351351

352352
TxDownloadState m_tx_download;
353353

354-
CNodeState(CAddress addrIn, std::string addrNameIn) : address(addrIn), name(addrNameIn) {
354+
//! Whether this peer is an inbound connection
355+
bool m_is_inbound;
356+
357+
//! Whether this peer is a manual connection
358+
bool m_is_manual_connection;
359+
360+
CNodeState(CAddress addrIn, std::string addrNameIn, bool is_inbound, bool is_manual) :
361+
address(addrIn), name(std::move(addrNameIn)), m_is_inbound(is_inbound),
362+
m_is_manual_connection (is_manual)
363+
{
355364
fCurrentlyConnected = false;
356365
nMisbehavior = 0;
357366
fShouldBan = false;
@@ -747,7 +756,7 @@ void PeerLogicValidation::InitializeNode(CNode *pnode) {
747756
NodeId nodeid = pnode->GetId();
748757
{
749758
LOCK(cs_main);
750-
mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName)));
759+
mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName), pnode->fInbound, pnode->m_manual_connection));
751760
}
752761
if(!pnode->fInbound)
753762
PushNodeVersion(pnode, connman, GetTime());
@@ -994,9 +1003,22 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v
9941003
return true;
9951004
}
9961005
break;
997-
// Handled elsewhere for now
9981006
case ValidationInvalidReason::CACHED_INVALID:
999-
break;
1007+
{
1008+
LOCK(cs_main);
1009+
CNodeState *node_state = State(nodeid);
1010+
if (node_state == nullptr) {
1011+
break;
1012+
}
1013+
1014+
// Ban outbound (but not inbound) peers if on an invalid chain.
1015+
// Exempt HB compact block peers and manual connections.
1016+
if (!via_compact_block && !node_state->m_is_inbound && !node_state->m_is_manual_connection) {
1017+
Misbehaving(nodeid, 100, message);
1018+
return true;
1019+
}
1020+
break;
1021+
}
10001022
case ValidationInvalidReason::BLOCK_INVALID_HEADER:
10011023
case ValidationInvalidReason::BLOCK_CHECKPOINT:
10021024
case ValidationInvalidReason::BLOCK_INVALID_PREV:
@@ -1556,7 +1578,7 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac
15561578
connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp));
15571579
}
15581580

1559-
bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool punish_duplicate_invalid)
1581+
bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::vector<CBlockHeader>& headers, const CChainParams& chainparams, bool via_compact_block)
15601582
{
15611583
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
15621584
size_t nCount = headers.size();
@@ -1619,41 +1641,7 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
16191641
CBlockHeader first_invalid_header;
16201642
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
16211643
if (state.IsInvalid()) {
1622-
if (punish_duplicate_invalid && state.GetReason() == ValidationInvalidReason::CACHED_INVALID) {
1623-
// Goal: don't allow outbound peers to use up our outbound
1624-
// connection slots if they are on incompatible chains.
1625-
//
1626-
// We ask the caller to set punish_invalid appropriately based
1627-
// on the peer and the method of header delivery (compact
1628-
// blocks are allowed to be invalid in some circumstances,
1629-
// under BIP 152).
1630-
// Here, we try to detect the narrow situation that we have a
1631-
// valid block header (ie it was valid at the time the header
1632-
// was received, and hence stored in mapBlockIndex) but know the
1633-
// block is invalid, and that a peer has announced that same
1634-
// block as being on its active chain.
1635-
// Disconnect the peer in such a situation.
1636-
//
1637-
// Note: if the header that is invalid was not accepted to our
1638-
// mapBlockIndex at all, that may also be grounds for
1639-
// disconnecting the peer, as the chain they are on is likely
1640-
// to be incompatible. However, there is a circumstance where
1641-
// that does not hold: if the header's timestamp is more than
1642-
// 2 hours ahead of our current time. In that case, the header
1643-
// may become valid in the future, and we don't want to
1644-
// disconnect a peer merely for serving us one too-far-ahead
1645-
// block header, to prevent an attacker from splitting the
1646-
// network by mining a block right at the 2 hour boundary.
1647-
//
1648-
// TODO: update the DoS logic (or, rather, rewrite the
1649-
// DoS-interface between validation and net_processing) so that
1650-
// the interface is cleaner, and so that we disconnect on all the
1651-
// reasons that a peer's headers chain is incompatible
1652-
// with ours (eg block->nVersion softforks, MTP violations,
1653-
// etc), and not just the duplicate-invalid case.
1654-
pfrom->fDisconnect = true;
1655-
}
1656-
MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false, "invalid header received");
1644+
MaybePunishNode(pfrom->GetId(), state, via_compact_block, "invalid header received");
16571645
return false;
16581646
}
16591647
}
@@ -2781,7 +2769,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
27812769
// the peer if the header turns out to be for an invalid block.
27822770
// Note that if a peer tries to build on an invalid chain, that
27832771
// will be detected and the peer will be banned.
2784-
return ProcessHeadersMessage(pfrom, connman, {cmpctblock.header}, chainparams, /*punish_duplicate_invalid=*/false);
2772+
return ProcessHeadersMessage(pfrom, connman, {cmpctblock.header}, chainparams, /*via_compact_block=*/true);
27852773
}
27862774

27872775
if (fBlockReconstructed) {
@@ -2924,12 +2912,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
29242912
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
29252913
}
29262914

2927-
// Headers received via a HEADERS message should be valid, and reflect
2928-
// the chain the peer is on. If we receive a known-invalid header,
2929-
// disconnect the peer if it is using one of our outbound connection
2930-
// slots.
2931-
bool should_punish = !pfrom->fInbound && !pfrom->m_manual_connection;
2932-
return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish);
2915+
return ProcessHeadersMessage(pfrom, connman, headers, chainparams, /*via_compact_block=*/false);
29332916
}
29342917

29352918
if (strCommand == NetMsgType::BLOCK)

0 commit comments

Comments
 (0)