diff --git a/src/pow.cpp b/src/pow.cpp index 1daf60e61..1996307dd 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -804,8 +804,20 @@ bool CheckRandomXSolution(const CBlockHeader *pblock, int32_t height) // Derive the key (shared helper) and serialize the input (identical bytes to the pool path). std::string rxKey = GetRandomXKey(height); - if (rxKey.empty()) - return error("CheckRandomXSolution(): cannot derive RandomX key for height %d", height); + if (rxKey.empty()) { + // The RandomX key block (keyHeight = ((height-lag)/interval)*interval, looked up on the + // ACTIVE chain) is not yet connected. This happens ONLY at header-accept when headers run + // ahead of our connected block tip (the IBD tail / catch-up) -- block-connect always has it, + // since blocks connect in order and keyHeight <= height-lag < the connected tip. The header + // is NOT invalid; we simply cannot verify it YET. Defer to block-connect (which re-checks + // with the key present) rather than returning an error -- returning an error here makes + // CheckBlockHeader DoS(100)-ban the honest peer that sent a perfectly valid tip header we + // just can't check yet (observed live: a post-reindex node banned the whole fleet and stalled + // ~2000 blocks short of the tip). Flood protection is preserved for synced nodes (key present + // -> real RandomX check) and bounded during catch-up by the per-peer IBD header cap + nMinimumChainWork. + LogPrint("net", "CheckRandomXSolution: RandomX key block for height %d not yet connected; deferring verification to block-connect\n", height); + return true; + } std::vector ssInput = GetRandomXInput(*pblock); char computedHash[RANDOMX_HASH_SIZE];