From a2496465f9e9b817060dd7be7624645de1ffb631 Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Tue, 31 Mar 2026 14:22:39 +0800 Subject: [PATCH] core: fix cross validation --- core/blockchain.go | 67 ++++++++++++++++++++++------------------ core/blockchain_stats.go | 17 ++++------ core/state/statedb.go | 5 ++- 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 2b11084cb8..da4e463da2 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2225,38 +2225,9 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, } vtime := time.Since(vstart) - // If witnesses was generated and stateless self-validation requested, do - // that now. Self validation should *never* run in production, it's more of - // a tight integration to enable running *all* consensus tests through the - // witness builder/runner, which would otherwise be impossible due to the - // various invalid chain states/behaviors being contained in those tests. - xvstart := time.Now() - if witness := statedb.Witness(); witness != nil && config.StatelessSelfValidation { - log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash()) - - // Remove critical computed fields from the block to force true recalculation - context := block.Header() - context.Root = common.Hash{} - context.ReceiptHash = common.Hash{} - - task := types.NewBlockWithHeader(context).WithBody(*block.Body()) - - // Run the stateless self-cross-validation - crossStateRoot, crossReceiptRoot, err := ExecuteStateless(ctx, bc.chainConfig, bc.cfg.VmConfig, task, witness) - if err != nil { - return nil, fmt.Errorf("stateless self-validation failed: %v", err) - } - if crossStateRoot != block.Root() { - return nil, fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, block.Root()) - } - if crossReceiptRoot != block.ReceiptHash() { - return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash()) - } - } - var ( proctime = time.Since(startTime) // processing + validation + cross validation - stats = NewExecuteStats(statedb, ptime, vtime, time.Since(xvstart)) + stats = NewExecuteStats(statedb, ptime, vtime) ) // Write the block to the chain and get the status. var status WriteStatus @@ -2284,6 +2255,9 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, stats.TotalTime = elapsed stats.MgasPerSecond = float64(res.GasUsed) * 1000 / float64(elapsed) + if config.StatelessSelfValidation { + bc.crossValidation(ctx, statedb, block) + } return &blockProcessingResult{ usedGas: res.GasUsed, procTime: proctime, @@ -2293,6 +2267,39 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, }, nil } +func (bc *BlockChain) crossValidation(ctx context.Context, statedb *state.StateDB, block *types.Block) error { + // If witnesses was generated and stateless self-validation requested, do + // that now. Self validation should *never* run in production, it's more of + // a tight integration to enable running *all* consensus tests through the + // witness builder/runner, which would otherwise be impossible due to the + // various invalid chain states/behaviors being contained in those tests. + if witness := statedb.Witness(); witness != nil { + xvstart := time.Now() + log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash()) + + // Remove critical computed fields from the block to force true recalculation + context := block.Header() + context.Root = common.Hash{} + context.ReceiptHash = common.Hash{} + + task := types.NewBlockWithHeader(context).WithBody(*block.Body()) + + // Run the stateless self-cross-validation + crossStateRoot, crossReceiptRoot, err := ExecuteStateless(ctx, bc.chainConfig, bc.cfg.VmConfig, task, witness) + if err != nil { + return fmt.Errorf("stateless self-validation failed: %v", err) + } + if crossStateRoot != block.Root() { + return fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, block.Root()) + } + if crossReceiptRoot != block.ReceiptHash() { + return fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash()) + } + blockCrossValidationTimer.UpdateSince(xvstart) + } + return nil +} + // insertSideChain is called when an import batch hits upon a pruned ancestor // error, which happens when a sidechain with a sufficiently old fork-block is // found. diff --git a/core/blockchain_stats.go b/core/blockchain_stats.go index 4608151654..fc33ef6fe4 100644 --- a/core/blockchain_stats.go +++ b/core/blockchain_stats.go @@ -38,12 +38,9 @@ type ExecuteStats struct { AccountUpdates time.Duration // Time spent on the account trie update StorageUpdates time.Duration // Time spent on the storage trie update - // EVM execution time - Execution time.Duration // Time spent on the EVM execution - - // Validation times - Validation time.Duration // Time spent on the block validation - CrossValidation time.Duration // Optional, time spent on the block cross validation + // EVM execution and validation time + Execution time.Duration // Time spent on the EVM execution + Validation time.Duration // Time spent on the block validation // Commit times HasherCommit time.Duration // Time spent on trie commit @@ -70,7 +67,7 @@ type ExecuteStats struct { StatePrefetchCacheStats state.ReaderStats } -func NewExecuteStats(stateDB *state.StateDB, process time.Duration, validation time.Duration, crossValidation time.Duration) *ExecuteStats { +func NewExecuteStats(stateDB *state.StateDB, process time.Duration, validation time.Duration) *ExecuteStats { return &ExecuteStats{ // State read times AccountReads: stateDB.AccountReads, @@ -82,9 +79,8 @@ func NewExecuteStats(stateDB *state.StateDB, process time.Duration, validation t AccountUpdates: stateDB.AccountUpdates, StorageUpdates: stateDB.StorageUpdates, - Execution: process - stateDB.StateReadTime(), - Validation: validation - stateDB.StateHashTime(), - CrossValidation: crossValidation, + Execution: process - stateDB.StateReadTime(), + Validation: validation - stateDB.StateHashTime(), AccountLoaded: stateDB.AccountLoaded, AccountUpdated: stateDB.AccountUpdated, @@ -121,7 +117,6 @@ func (s *ExecuteStats) reportMetrics() { blockExecutionTimer.Update(s.Execution) // The time spent on EVM processing blockValidationTimer.Update(s.Validation) // The time spent on block validation - blockCrossValidationTimer.Update(s.CrossValidation) // The time spent on stateless cross validation triedbCommitTimer.Update(s.DatabaseCommit) // Trie database commits are complete, we can mark them blockWriteTimer.Update(s.BlockWrite) // The time spent on block write blockInsertTimer.Update(s.TotalTime) // The total time spent on block execution diff --git a/core/state/statedb.go b/core/state/statedb.go index 6f428d0715..01fd13fb59 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1020,11 +1020,10 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorag } s.DatabaseCommits = time.Since(start) - // The reader update must be performed as the final step, otherwise, - // the new state would not be visible before db.commit. + // The reader/hasher update must be performed as the final step s.reader, _ = s.db.Reader(s.originalRoot) s.hasher, _ = s.db.Hasher(s.originalRoot) - return ret, err + return ret, nil } // Commit writes the state mutations into the configured data stores.