core: fix cross validation

This commit is contained in:
Gary Rong 2026-03-31 14:22:39 +08:00 committed by CPerezz
parent d57dca07b1
commit a2496465f9
No known key found for this signature in database
GPG key ID: 62045F34B97177DD
3 changed files with 45 additions and 44 deletions

View file

@ -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.

View file

@ -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

View file

@ -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.