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; }