fix traceChain, with note about improving test case

This commit is contained in:
Jared Wasinger 2025-05-07 15:19:26 +08:00
parent 655ce1733c
commit 9c901e862b
2 changed files with 22 additions and 24 deletions

View file

@ -262,7 +262,6 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
log.Warn("invalid trace configuration", "err", err) log.Warn("invalid trace configuration", "err", err)
return nil return nil
} }
env := envFromTraceConfig(api, config)
instantiateTracer := func(ctx *Context) (*Tracer, error) { instantiateTracer := func(ctx *Context) (*Tracer, error) {
return api.instantiateTracer(config, ctx) return api.instantiateTracer(config, ctx)
@ -275,7 +274,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
// Fetch and execute the block trace taskCh // Fetch and execute the block trace taskCh
for task := range taskCh { for task := range taskCh {
task.results, err = api.traceBlock(ctx, task.block, env, opt, instantiateTracer) task.results, err = api.traceBlockWithState(ctx, task.block, task.statedb, opt, instantiateTracer)
if err != nil { if err != nil {
log.Warn("Tracing failed", "err", err) log.Warn("Tracing failed", "err", err)
break break
@ -325,7 +324,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
close(resCh) close(resCh)
}() }()
// Feed all the blocks both into the tracer, as well as fast process concurrently // Feed all the blocks both into the tracer, as well as fast process concurrently
for number = start.NumberU64(); number < end.NumberU64(); number++ { for number = start.NumberU64() + 1; number <= end.NumberU64(); number++ {
// Stop tracing if interruption was requested // Stop tracing if interruption was requested
select { select {
case <-closed: case <-closed:
@ -343,11 +342,6 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
failed = err failed = err
break break
} }
next, err := api.blockByNumber(ctx, rpc.BlockNumber(number+1))
if err != nil {
failed = err
break
}
// Make sure the state creator doesn't go too far. Too many unprocessed // Make sure the state creator doesn't go too far. Too many unprocessed
// trace state may cause the oldest state to become stale(e.g. in // trace state may cause the oldest state to become stale(e.g. in
// path-based scheme). // path-based scheme).
@ -365,7 +359,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
s1, s2, s3 := statedb.Database().TrieDB().Size() s1, s2, s3 := statedb.Database().TrieDB().Size()
preferDisk = s1+s2+s3 > defaultTracechainMemLimit preferDisk = s1+s2+s3 > defaultTracechainMemLimit
} }
statedb, _, release, err = api.retrieveBlockState(ctx, block, opt, false, preferDisk) statedb, release, err = api.retrieveBlockPrestate(ctx, block, opt, false, preferDisk)
if err != nil { if err != nil {
failed = err failed = err
break break
@ -377,9 +371,9 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
tracker.callReleases() tracker.callReleases()
// Send the block over to the concurrent tracers (if not in the fast-forward phase) // Send the block over to the concurrent tracers (if not in the fast-forward phase)
txs := next.Transactions() txs := block.Transactions()
select { select {
case taskCh <- &blockTraceTask{statedb: statedb.Copy(), block: next, release: release, results: make([]*txTraceResult, len(txs))}: case taskCh <- &blockTraceTask{statedb: statedb.Copy(), block: block, release: release, results: make([]*txTraceResult, len(txs))}:
case <-closed: case <-closed:
tracker.releaseState(number, release) tracker.releaseState(number, release)
return return
@ -631,20 +625,26 @@ func envFromLoggerConfig(api *API, config *logger.Config) *traceEnv {
} }
} }
func (api *API) retrieveBlockState(ctx context.Context, block *types.Block, execOpt *traceExecOptions, readOnly, preferDisk bool) (db *state.StateDB, blockCtx *vm.BlockContext, stateRelease StateReleaseFunc, err error) { func (api *API) retrieveBlockPrestate(ctx context.Context, block *types.Block, execOpt *traceExecOptions, readOnly, preferDisk bool) (db *state.StateDB, stateRelease StateReleaseFunc, err error) {
if block.NumberU64() == 0 {
return nil, nil, fmt.Errorf("cannot trace genesis block")
}
// Prepare base state // Prepare base state
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
statedb, release, err := api.backend.StateAtBlock(ctx, parent, execOpt.reexec, nil, readOnly, preferDisk) statedb, release, err := api.backend.StateAtBlock(ctx, parent, execOpt.reexec, nil, readOnly, preferDisk)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
}
return statedb, release, nil
} }
// TODO: we can move these into execBlockTrace? it changes the internal semantics of traceChain but it shouldn't matter? func (api *API) traceBlockWithState(ctx context.Context, block *types.Block, statedb *state.StateDB, execOpt *traceExecOptions, instantiateTracer func(*Context) (*Tracer, error)) ([]*txTraceResult, error) {
bCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) // TODO: this changes the semantics of traceChain a bit. double-check that it's alright to call ProcessBeaconBlockRoot and ProcessParentBlockHash here
evm := vm.NewEVM(bCtx, statedb, api.backend.ChainConfig(), vm.Config{}) blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
if beaconRoot := block.BeaconRoot(); beaconRoot != nil { if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
core.ProcessBeaconBlockRoot(*beaconRoot, evm) core.ProcessBeaconBlockRoot(*beaconRoot, evm)
} }
@ -652,13 +652,10 @@ func (api *API) retrieveBlockState(ctx context.Context, block *types.Block, exec
core.ProcessParentBlockHash(block.ParentHash(), evm) core.ProcessParentBlockHash(block.ParentHash(), evm)
} }
return statedb, &bCtx, release, nil
}
func (api *API) execBlockTrace(ctx context.Context, block *types.Block, blockCtx *vm.BlockContext, statedb *state.StateDB, execOpt *traceExecOptions, instantiateTracer func(*Context) (*Tracer, error)) ([]*txTraceResult, error) {
if execOpt.parallel { if execOpt.parallel {
return api.traceBlockParallel(ctx, block, statedb, execOpt.txTimeout, instantiateTracer) return api.traceBlockParallel(ctx, block, statedb, execOpt.txTimeout, instantiateTracer)
} }
// Native tracers have low overhead // Native tracers have low overhead
var ( var (
txs = block.Transactions() txs = block.Transactions()
@ -679,7 +676,7 @@ func (api *API) execBlockTrace(ctx context.Context, block *types.Block, blockCtx
if err != nil { if err != nil {
return nil, err return nil, err
} }
res, err := api.traceTx(ctx, tx, msg, txctx, *blockCtx, statedb, tracer, nil, execOpt.txTimeout) res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, tracer, nil, execOpt.txTimeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -695,12 +692,12 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, env *traceEn
if block.NumberU64() == 0 { if block.NumberU64() == 0 {
return nil, fmt.Errorf("cannot trace genesis block") return nil, fmt.Errorf("cannot trace genesis block")
} }
statedb, blockCtx, release, err := api.retrieveBlockState(ctx, block, execOpt, true, false) statedb, release, err := api.retrieveBlockPrestate(ctx, block, execOpt, true, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer release() defer release()
return api.execBlockTrace(ctx, block, blockCtx, statedb, execOpt, instantiateTracer) return api.traceBlockWithState(ctx, block, statedb, execOpt, instantiateTracer)
} }
// traceBlockParallel is for tracers that have a high overhead (read JS tracers). One thread // traceBlockParallel is for tracers that have a high overhead (read JS tracers). One thread

View file

@ -1056,6 +1056,7 @@ func newStates(keys []common.Hash, vals []common.Hash) map[common.Hash]common.Ha
return m return m
} }
// TODO: modify tracechain so that each transaction in the chain prints out a different trace dependent on the current state
func TestTraceChain(t *testing.T) { func TestTraceChain(t *testing.T) {
// Initialize test accounts // Initialize test accounts
accounts := newAccounts(3) accounts := newAccounts(3)