Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}

Expand All @@ -2949,6 +2947,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)
Expand Down
121 changes: 84 additions & 37 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package core

import (
"context"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -2587,32 +2632,35 @@ 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()
}
}
statedb.StartPrefetcher("chain", witness, witnessStats)
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())))
Expand Down
6 changes: 2 additions & 4 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
35 changes: 10 additions & 25 deletions eth/api_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -496,39 +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)
if err != nil {
return nil, err
config := core.ExecuteConfig{
WriteState: false,
EnableTracer: false,
MakeWitness: true,
}

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)
}

result, err := bc.ProcessBlock(parent.Root, block, false, true)
result, err := bc.ProcessBlock(context.Background(), parent.Root, block, config)
if err != nil {
return nil, err
}
Expand Down
5 changes: 3 additions & 2 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -371,6 +369,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 {
Expand Down
11 changes: 6 additions & 5 deletions internal/web3ext/web3ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -460,6 +455,12 @@ web3._extend({
params: 1,
inputFormatter: [null],
}),
new web3._extend.Method({
name: 'executionWitness',
call: 'debug_executionWitness',
params: 1,
inputFormatter: [null],
}),
],
properties: []
});
Expand Down
4 changes: 2 additions & 2 deletions tests/block_test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading