From b8ae09337dfbe38b85e3bd3a96032e35609e0395 Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Wed, 4 Mar 2026 14:40:45 +0800 Subject: [PATCH 1/2] cmd, core, eth, tests: prevent state flushing in RPC (#33931) Fixes https://github.com/ethereum/go-ethereum/issues/33572 --- cmd/utils/flags.go | 3 + core/blockchain.go | 121 +++++++++++++++++++++++++----------- eth/api_debug.go | 25 +++++--- eth/backend.go | 3 + internal/web3ext/web3ext.go | 11 ++-- tests/block_test_util.go | 4 +- 6 files changed, 115 insertions(+), 52 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 6b73c7111f..f30cad920c 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2949,6 +2949,9 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } options.VmConfig = vmcfg + options.StatelessSelfValidation = ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name) + options.EnableWitnessStats = ctx.Bool(VMWitnessStatsFlag.Name) + chain, err := core.NewBlockChain(chainDb, gspec, engine, options) if err != nil { Fatalf("Can't create BlockChain: %v", err) diff --git a/core/blockchain.go b/core/blockchain.go index 6150fa2fc8..328ed6b0c6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -18,6 +18,7 @@ package core import ( + "context" "errors" "fmt" "io" @@ -233,6 +234,15 @@ type BlockChainConfig struct { // EnableBAL enables the block access list feature EnableBAL bool + + // SlowBlockThreshold is the block execution time threshold beyond which + // detailed statistics will be logged. Negative value means disabled (default), + // zero logs all blocks, positive value filters blocks by execution time. + SlowBlockThreshold time.Duration + + // Execution configs + StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose) + EnableWitnessStats bool // Whether trie access statistics collection is enabled } // DefaultConfig returns the default config. @@ -2404,7 +2414,15 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bc.updateHighestVerifiedHeader(block.Header()) // The traced section of block import. start := time.Now() - res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1) + config := ExecuteConfig{ + WriteState: true, + WriteHead: setHead, + EnableTracer: true, + MakeWitness: makeWitness && len(chain) == 1, + StatelessSelfValidation: bc.cfg.StatelessSelfValidation, + EnableWitnessStats: bc.cfg.EnableWitnessStats, + } + res, err := bc.ProcessBlock(context.Background(), parent.Root, block, config) if err != nil { return nil, it.index, err } @@ -2507,9 +2525,36 @@ func (bpr *blockProcessingResult) Witness() *stateless.Witness { return bpr.witness } +// ExecuteConfig defines optional behaviors during execution. +type ExecuteConfig struct { + // WriteState controls whether the computed state changes are persisted to + // the underlying storage. If false, execution is performed in-memory only. + WriteState bool + + // WriteHead indicates whether the execution result should update the canonical + // chain head. It's only relevant with WriteState == True. + WriteHead bool + + // EnableTracer enables execution tracing. This is typically used for debugging + // or analysis and may significantly impact performance. + EnableTracer bool + + // MakeWitness indicates whether to generate execution witness data during + // execution. Enabling this may introduce additional memory and CPU overhead. + MakeWitness bool + + // StatelessSelfValidation indicates whether the execution witnesses generation + // and self-validation (testing purpose) is enabled. + StatelessSelfValidation bool + + // EnableWitnessStats indicates whether to enable collection of witness trie + // access statistics + EnableWitnessStats bool +} + // ProcessBlock executes and validates the given block. If there was no error // it writes the block and associated state to database. -func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (_ *blockProcessingResult, blockEndErr error) { +func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, config ExecuteConfig) (result *blockProcessingResult, blockEndErr error) { var ( err error startTime = time.Now() @@ -2587,12 +2632,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s // Generate witnesses either if we're self-testing, or if it's the // only block being inserted. A bit crude, but witnesses are huge, // so we refuse to make an entire chain of them. - if bc.cfg.VmConfig.StatelessSelfValidation || makeWitness { + if config.StatelessSelfValidation || config.MakeWitness { witness, err = stateless.NewWitness(block.Header(), bc) if err != nil { return nil, err } - if bc.cfg.VmConfig.EnableWitnessStats { + if config.EnableWitnessStats { witnessStats = stateless.NewWitnessStats() } } @@ -2600,19 +2645,22 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s defer statedb.StopPrefetcher() } - if bc.logger != nil && bc.logger.OnBlockStart != nil { - td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) - bc.logger.OnBlockStart(tracing.BlockEvent{ - Block: block, - TD: td, - Finalized: bc.CurrentFinalBlock(), - Safe: bc.CurrentSafeBlock(), - }) - } - if bc.logger != nil && bc.logger.OnBlockEnd != nil { - defer func() { - bc.logger.OnBlockEnd(blockEndErr) - }() + // Instrument the blockchain tracing + if config.EnableTracer { + if bc.logger != nil && bc.logger.OnBlockStart != nil { + td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) + bc.logger.OnBlockStart(tracing.BlockEvent{ + Block: block, + TD: td, + Finalized: bc.CurrentFinalBlock(), + Safe: bc.CurrentSafeBlock(), + }) + } + if bc.logger != nil && bc.logger.OnBlockEnd != nil { + defer func() { + bc.logger.OnBlockEnd(blockEndErr) + }() + } } // Process block using the parent state as reference point @@ -2640,7 +2688,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s // 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 && bc.cfg.VmConfig.StatelessSelfValidation { + 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 @@ -2685,30 +2733,29 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation // Write the block to the chain and get the status. - var ( - wstart = time.Now() - status WriteStatus - ) - if !setHead { - // Don't set the head, only insert the block - err = bc.writeBlockWithState(block, res.Receipts, statedb) - } else { - status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, nil) - } - if err != nil { - return nil, err + var status WriteStatus + if config.WriteState { + wstart := time.Now() + if !config.WriteHead { + // Don't set the head, only insert the block + err = bc.writeBlockWithState(block, res.Receipts, statedb) + } else { + status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, nil) + } + if err != nil { + return nil, err + } + // Update the metrics touched during block commit + accountCommitTimer.Update(statedb.AccountCommits) + storageCommitTimer.Update(statedb.StorageCommits) + snapshotCommitTimer.Update(statedb.SnapshotCommits) + triedbCommitTimer.Update(statedb.TrieDBCommits) + blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits) } // Report the collected witness statistics if witnessStats != nil { witnessStats.ReportMetrics(block.NumberU64()) } - - // Update the metrics touched during block commit - accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them - storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them - snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them - triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them - blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits) elapsed := time.Since(startTime) + 1 // prevent zero division blockInsertTimer.Update(elapsed) blockInsertTxSizeGauge.Update(int64(len(block.Transactions()))) diff --git a/eth/api_debug.go b/eth/api_debug.go index 63cfa0ea23..4995710793 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/stateless" @@ -496,19 +497,23 @@ func (api *DebugAPI) StateSize(blockHashOrNumber *rpc.BlockNumberOrHash) (interf }, nil } -func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumber) (*stateless.ExtWitness, error) { +func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumberOrHash) (*stateless.ExtWitness, error) { bc := api.eth.blockchain - block, err := api.eth.APIBackend.BlockByNumber(context.Background(), bn) + block, err := api.eth.APIBackend.BlockByNumberOrHash(context.Background(), bn) if err != nil { - return &stateless.ExtWitness{}, fmt.Errorf("block number %v not found", bn) + return &stateless.ExtWitness{}, fmt.Errorf("block %v not found", bn) } parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) if parent == nil { - return &stateless.ExtWitness{}, fmt.Errorf("block number %v found, but parent missing", bn) + return &stateless.ExtWitness{}, fmt.Errorf("block %v found, but parent missing", bn) } - - result, err := bc.ProcessBlock(parent.Root, block, false, true) + config := core.ExecuteConfig{ + WriteState: false, + EnableTracer: false, + MakeWitness: true, + } + result, err := bc.ProcessBlock(context.Background(), parent.Root, block, config) if err != nil { return nil, err } @@ -527,8 +532,12 @@ func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWit if parent == nil { return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash) } - - result, err := bc.ProcessBlock(parent.Root, block, false, true) + config := core.ExecuteConfig{ + WriteState: false, + EnableTracer: false, + MakeWitness: true, + } + result, err := bc.ProcessBlock(context.Background(), parent.Root, block, config) if err != nil { return nil, err } diff --git a/eth/backend.go b/eth/backend.go index d5ac85ddaa..e671d23261 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -371,6 +371,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { // - DATADIR/triedb/verkle.journal TrieJournalDirectory: stack.ResolvePath("triedb"), StateSizeTracking: config.EnableStateSizeTracking, + + StatelessSelfValidation: config.StatelessSelfValidation, + EnableWitnessStats: config.EnableWitnessStats, } ) if config.DisableTxIndexer { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 99b9214684..fb9c4e25b7 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -413,11 +413,6 @@ web3._extend({ params: 2, inputFormatter:[null, null], }), - new web3._extend.Method({ - name: 'freezeClient', - call: 'debug_freezeClient', - params: 1, - }), new web3._extend.Method({ name: 'getAccessibleState', call: 'debug_getAccessibleState', @@ -460,6 +455,12 @@ web3._extend({ params: 1, inputFormatter: [null], }), + new web3._extend.Method({ + name: 'executionWitness', + call: 'debug_executionWitness', + params: 1, + inputFormatter: [null], + }), ], properties: [] }); diff --git a/tests/block_test_util.go b/tests/block_test_util.go index bd9639474c..2bcaaca16a 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -158,9 +158,9 @@ func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *t Preimages: true, TxLookupLimit: -1, // disable tx indexing VmConfig: vm.Config{ - Tracer: tracer, - StatelessSelfValidation: witness, + Tracer: tracer, }, + StatelessSelfValidation: witness, } if snapshotter { options.SnapshotLimit = 1 From 4a863e77b3a0e4293a852e039d100d11abf9f7e5 Mon Sep 17 00:00:00 2001 From: allformless <213398294+allformless@users.noreply.github.com> Date: Tue, 12 May 2026 15:48:59 +0800 Subject: [PATCH 2/2] core/vm: clean vm.Config --- cmd/utils/flags.go | 2 -- core/vm/interpreter.go | 6 ++---- eth/api_debug.go | 24 ------------------------ eth/backend.go | 2 -- 4 files changed, 2 insertions(+), 32 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index f30cad920c..5b39f06768 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2929,8 +2929,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh } vmcfg := vm.Config{ EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name), - EnableWitnessStats: ctx.Bool(VMWitnessStatsFlag.Name), - StatelessSelfValidation: ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name), EnableOpcodeOptimizations: ctx.Bool(VMOpcodeOptimizeFlag.Name), } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 27cbb46c03..6762ecb087 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -29,14 +29,12 @@ import ( // Config are the configuration options for the Interpreter type Config struct { - Tracer *tracing.Hooks + Tracer *tracing.Hooks + NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages ExtraEips []int // Additional EIPS that are to be enabled EnableOpcodeOptimizations bool // Enable opcode optimization - - StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose) - EnableWitnessStats bool // Whether trie access statistics collection is enabled } // ScopeContext contains the things that are per-call, such as stack and memory, diff --git a/eth/api_debug.go b/eth/api_debug.go index 4995710793..123b816a5e 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -520,27 +520,3 @@ func (api *DebugAPI) ExecutionWitness(bn rpc.BlockNumberOrHash) (*stateless.ExtW return result.Witness().ToExtWitness(), nil } - -func (api *DebugAPI) ExecutionWitnessByHash(hash common.Hash) (*stateless.ExtWitness, error) { - bc := api.eth.blockchain - block := bc.GetBlockByHash(hash) - if block == nil { - return &stateless.ExtWitness{}, fmt.Errorf("block hash %x not found", hash) - } - - parent := bc.GetHeader(block.ParentHash(), block.NumberU64()-1) - if parent == nil { - return &stateless.ExtWitness{}, fmt.Errorf("block number %x found, but parent missing", hash) - } - config := core.ExecuteConfig{ - WriteState: false, - EnableTracer: false, - MakeWitness: true, - } - result, err := bc.ProcessBlock(context.Background(), parent.Root, block, config) - if err != nil { - return nil, err - } - - return result.Witness().ToExtWitness(), nil -} diff --git a/eth/backend.go b/eth/backend.go index e671d23261..7bc14e21d4 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -361,8 +361,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { TxLookupLimit: int64(min(config.TransactionHistory, math.MaxInt64)), VmConfig: vm.Config{ EnablePreimageRecording: config.EnablePreimageRecording, - EnableWitnessStats: config.EnableWitnessStats, - StatelessSelfValidation: config.StatelessSelfValidation, EnableOpcodeOptimizations: config.EnableOpcodeOptimizing, }, // Enables file journaling for the trie database. The journal files will be stored