revert(net): remove header-accept RandomX check; keep nMinimumChainWork + #8
An adversarial re-review found the header-accept RandomX check (b9fdc7981+7e9b2c661header-PoW +d2124a303defer) to be a persistent source of consensus-liveness bugs: it derives the RandomX key from the ACTIVE chain (hush_chainactive), the wrong branch for reorg/side-branch/catch-up headers, so it repeatedly false-rejected validly-mined headers and DoS(100)-hard-banned honest peers (IBD-tail catch-up and deep-reorg cases); the defer fix and an extend-tip fix each addressed one case while leaving/creating others (an extend-tip variant re-opened an unbounded post-IBD side-branch flood). It only mitigated a low-harm resource DoS -- forged headers bloat mapBlockIndex memory/ disk but are never SELECTED (nMinimumChainWork) and the full RandomX + target check still runs at block-connect. Revert to fCheckPOW=0 at header-accept (original behavior). A comment in AcceptBlockHeader records that any re-attempt must derive the key from the header's OWN ancestry (pindexPrev->GetAncestor), never the active chain. Also hardens two issues the same review found: - #8 IBD header cap now bounds against the VALIDATED chainActive.Height() (attacker-hard) instead of pindexBestHeader, which a forward-extending flood advanced in lockstep, defeating the cap. - opreturn_burn only emits a change output above the dust threshold; a sub-dust change made the returned tx non-standard/unrelayable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
16
src/pow.cpp
16
src/pow.cpp
@@ -804,20 +804,8 @@ 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()) {
|
||||
// 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;
|
||||
}
|
||||
if (rxKey.empty())
|
||||
return error("CheckRandomXSolution(): cannot derive RandomX key for height %d", height);
|
||||
std::vector<unsigned char> ssInput = GetRandomXInput(*pblock);
|
||||
|
||||
char computedHash[RANDOMX_HASH_SIZE];
|
||||
|
||||
Reference in New Issue
Block a user