perf: verify each block's RandomX solution once, not twice, during sync
RandomX PoW verification is ~84% of block-connect wall time during network IBD, and CheckBlock was recomputing it TWICE per block: once in CheckBlockHeader and again in hush_checkPOW (which has no CBlockIndex, so it cannot use the fRandomXVerified dedup the parallel pre-verify pool relies on). Skip the redundant recompute inside hush_checkPOW: CheckBlockHeader runs first in CheckBlock and rejects an invalid solution before hush_checkPOW is reached, so the block is already verified once. Equihash, PoW-target and notary checks in hush_checkPOW still run. A scoped guard (ScopedRandomXSkip) SAVES and RESTORES the thread-local fSkipRandomXValidation, so it neither clobbers the miner's own skip (TestBlockValidity -> ConnectBlock re-entry, which would otherwise force the ~256MB inline RandomX alloc the miner deliberately avoids) nor leaks the flag on an exception. Measured on an isolated RandomX test chain: RandomX verifies per block 2.0 -> 1.03 (~40% faster network sync). The 2x behavior pre-exists in v1.0.2. Consensus-neutral: RandomXPreVerify.ConsensusEquivalence gtest passes; each block is still verified exactly once by CheckBlockHeader. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
20
src/main.cpp
20
src/main.cpp
@@ -5100,6 +5100,14 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,
|
||||
|
||||
int32_t hush_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height);
|
||||
|
||||
// RAII: save+restore the thread-local RandomX-skip flag around the verify-once dedup in CheckBlock,
|
||||
// so it can never clobber the miner's own fSkipRandomXValidation (TestBlockValidity -> ConnectBlock
|
||||
// re-entry) nor leak TRUE on an exception thrown out of hush_checkPOW.
|
||||
struct ScopedRandomXSkip {
|
||||
bool prev;
|
||||
ScopedRandomXSkip() : prev(GetSkipRandomXValidation()) { SetSkipRandomXValidation(true); }
|
||||
~ScopedRandomXSkip() { SetSkipRandomXValidation(prev); }
|
||||
};
|
||||
bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state,
|
||||
libzcash::ProofVerifier& verifier,
|
||||
bool fCheckPOW, bool fCheckMerkleRoot)
|
||||
@@ -5130,8 +5138,16 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
|
||||
fprintf(stderr," failed hash ht.%d\n",height);
|
||||
return state.DoS(50, error("CheckBlock: proof of work failed"),REJECT_INVALID, "high-hash");
|
||||
}
|
||||
if ( ASSETCHAINS_STAKED == 0 && hush_checkPOW(1,(CBlock *)&block,height) < 0 ) // checks Equihash
|
||||
return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW");
|
||||
if ( ASSETCHAINS_STAKED == 0 ) {
|
||||
// verify-once: CheckBlockHeader above already verified this block RandomX solution; skip the
|
||||
// redundant recompute inside hush_checkPOW (the un-deduped 2nd verify, ~half the RandomX cost
|
||||
// that dominates IBD). The scoped guard saves/restores the skip flag (never hardcodes false)
|
||||
// so the miner's own skip is preserved and nothing leaks on throw. Equihash + PoW-target in
|
||||
// hush_checkPOW still run.
|
||||
ScopedRandomXSkip _rxskip;
|
||||
if ( hush_checkPOW(1,(CBlock *)&block,height) < 0 )
|
||||
return state.DoS(100, error("CheckBlock: failed slow_checkPOW"),REJECT_INVALID, "failed-slow_checkPOW");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the merkle root.
|
||||
|
||||
@@ -715,6 +715,7 @@ static int64_t nTimeRandomX = 0; // cumulative RandomX validation time (us), r
|
||||
thread_local bool fSkipRandomXValidation = false;
|
||||
|
||||
void SetSkipRandomXValidation(bool skip) { fSkipRandomXValidation = skip; }
|
||||
bool GetSkipRandomXValidation() { return fSkipRandomXValidation; }
|
||||
|
||||
CBlockIndex *hush_chainactive(int32_t height);
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ void RandomXValidatorShutdown();
|
||||
|
||||
/** Set thread-local flag to skip RandomX validation (used by miner during TestBlockValidity) */
|
||||
void SetSkipRandomXValidation(bool skip);
|
||||
bool GetSkipRandomXValidation();
|
||||
|
||||
/** Return the RandomX key rotation interval in blocks */
|
||||
int GetRandomXInterval();
|
||||
|
||||
Reference in New Issue
Block a user