From 7914fca0f2ba926d6020027b8d946e68017920e1 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 8 Jul 2026 05:48:39 +0200 Subject: [PATCH] fix: gate the RandomX PoW-verification skip on the in-index checkpoint RandomXValidationRequired skipped RandomX verification for blocks below the static top checkpoint (GetTotalBlocksEstimate), while the fork-rejection guard uses the in-index checkpoint (GetLastCheckpoint). Once the checkpoint list extends above the RandomX activation height, that asymmetry opens a gap during IBD/eclipse in which a peer with no RandomX hashpower can get SHA256-grinded, RandomX-forged blocks accepted (CheckProofOfWork hashes the header including the attacker-controlled nSolution). Gate the skip on GetLastCheckpoint()->GetHeight() so a block is PoW-exempt only when provably below a checkpoint the node has locked into its index -- the same boundary the fork guard uses. Currently inert (top checkpoint 2838000 < activation 2838976) but becomes live at the next checkpoint refresh; the check runs under cs_main and only ever adds verification. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/pow.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pow.cpp b/src/pow.cpp index fcd55033c..398c84751 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -739,8 +739,18 @@ bool RandomXValidationRequired(int32_t height) if (HUSH_LOADINGBLOCKS != 0) return false; extern bool fCheckpointsEnabled; - if (fCheckpointsEnabled && height < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())) - return false; + // Gate the RandomX skip on the last checkpoint actually LOCKED INTO this node's block index + // (GetLastCheckpoint), NOT the static top checkpoint (GetTotalBlocksEstimate). The fork-rejection + // guard uses this same in-index boundary, so a block below it is provably on the checkpoint-pinned + // chain and cannot be a forged fork. Using the static boundary would, once checkpoints extend above + // the RandomX activation height, leave a gap (in-index checkpoint .. static top) during IBD/eclipse + // where a no-hashpower peer could get SHA256-grinded, RandomX-forged blocks accepted. Safe to walk + // mapBlockIndex here: called only under cs_main (ActivateBestChainStep + inline CheckRandomXSolution). + if (fCheckpointsEnabled) { + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(Params().Checkpoints()); + if (pcheckpoint != NULL && height < pcheckpoint->GetHeight()) + return false; + } return true; }