fix(net): verify PoW at header-accept once synced (header-flood DoS)

AcceptBlockHeader called CheckBlockHeader with fCheckPOW=0, so a synced node
stored any well-formed PoW-less header off the tip into mapBlockIndex without
bound (memory/disk DoS). nMinimumChainWork is defined but unenforced and would
not stop tip-siblings anyway (they inherit the tip's chain work). Verify PoW at
header-accept time when not in IBD: forged headers now fail RandomX and the peer
is DoS-banned. IBD keeps fCheckPOW=0 for fast header sync; the full-block
RandomX/target check at connect is unchanged, so no valid header is rejected
(not a consensus-rule change).

fCheckPOW=0 call site is Leto (6a30b40415).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 16:06:39 -05:00
parent 4351d5b733
commit b9fdc79818

View File

@@ -5443,7 +5443,10 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat
} }
return true; return true;
} }
if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->GetHeight():0,*ppindex, block, state,0)) { // SECURITY (header-flood DoS): once synced, verify PoW at header-accept time so a peer cannot
// flood unbounded PoW-less headers into mapBlockIndex (they now fail RandomX -> DoS-ban). During
// IBD keep fCheckPOW=0 for fast header sync; the full RandomX/target check runs at block connect.
if (!CheckBlockHeader(futureblockp,*ppindex!=0?(*ppindex)->GetHeight():0,*ppindex, block, state, IsInitialBlockDownload() ? 0 : 1)) {
if ( *futureblockp == 0 ) { if ( *futureblockp == 0 ) {
LogPrintf("%s: CheckBlockHeader futureblock=0\n", __func__); LogPrintf("%s: CheckBlockHeader futureblock=0\n", __func__);
return false; return false;