eth/tracers: remove last duplication of tx execution logic in traceBlockParallel

This commit is contained in:
Jared Wasinger 2025-05-08 13:55:21 +08:00
parent 4a603a7fb3
commit 58b887bd62

View file

@ -480,7 +480,6 @@ func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *Tra
return api.instantiateTracer(config, ctx) return api.instantiateTracer(config, ctx)
} }
// TODO: investigate whether the canon field has different treatment when tracing bad blocks
env := envFromTraceConfig(api, config) env := envFromTraceConfig(api, config)
opt, err := traceExecOpt(config) opt, err := traceExecOpt(config)
if err != nil { if err != nil {
@ -676,7 +675,7 @@ func (api *API) traceBlockWithState(ctx context.Context, block *types.Block, sta
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.execTx(ctx, tx, msg, txctx, blockCtx, statedb, tracer, nil, execOpt.txTimeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -697,6 +696,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, env *traceEn
return api.traceBlockWithState(ctx, block, statedb, execOpt, instantiateTracer) return api.traceBlockWithState(ctx, block, statedb, execOpt, instantiateTracer)
} }
// TODO: this function has no test coverage
// 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
// runs along and executes txes without tracing enabled to generate their prestate. // runs along and executes txes without tracing enabled to generate their prestate.
// Worker threads take the tasks and the prestate and trace them. // Worker threads take the tasks and the prestate and trace them.
@ -738,7 +738,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// concurrent use. // concurrent use.
// See: https://github.com/ethereum/go-ethereum/issues/29114 // See: https://github.com/ethereum/go-ethereum/issues/29114
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, tracer, nil, txTimeout) res, err := api.execTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, tracer, nil, txTimeout)
if err != nil { if err != nil {
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
continue continue
@ -751,8 +751,6 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// Feed the transactions into the tracers and return // Feed the transactions into the tracers and return
var failed error var failed error
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{})
txloop: txloop:
for i, tx := range txs { for i, tx := range txs {
// Send the trace task over for execution // Send the trace task over for execution
@ -766,14 +764,16 @@ txloop:
// Generate the next state snapshot fast without tracing // Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
statedb.SetTxContext(tx.Hash(), i) traceCtx := Context{
if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { block.Hash(),
block.Number(),
i,
tx.Hash(),
}
if _, err := api.execTx(ctx, tx, msg, &traceCtx, blockCtx, statedb, nil, nil, defaultTraceTimeout); err != nil {
failed = err failed = err
break txloop break txloop
} }
// Finalize the state so any modifications are written to the trie
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number()))
} }
close(jobs) close(jobs)
@ -836,7 +836,7 @@ func (api *API) standardTraceTxToFile(ctx context.Context, block *types.Block, t
} }
traceTimeout := new(time.Duration) traceTimeout := new(time.Duration)
*traceTimeout = 1 * time.Hour *traceTimeout = 1 * time.Hour
_, err = api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, &tracer, nil, *traceTimeout) _, err = api.execTx(ctx, tx, msg, txctx, blockCtx, statedb, &tracer, nil, *traceTimeout)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -1036,7 +1036,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
if err != nil { if err != nil {
return nil, err return nil, err
} }
return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, tracer, nil, opt.txTimeout) return api.execTx(ctx, tx, msg, txctx, vmctx, statedb, tracer, nil, opt.txTimeout)
} }
// TraceCall lets you trace a given eth_call. It collects the structured logs // TraceCall lets you trace a given eth_call. It collects the structured logs
@ -1125,7 +1125,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if err != nil { if err != nil {
return nil, err return nil, err
} }
return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, tracer, precompiles, opt.txTimeout) return api.execTx(ctx, tx, msg, new(Context), vmctx, statedb, tracer, precompiles, opt.txTimeout)
} }
func (api *API) instantiateTracer(config *TraceConfig, txctx *Context) (*Tracer, error) { func (api *API) instantiateTracer(config *TraceConfig, txctx *Context) (*Tracer, error) {
@ -1153,20 +1153,19 @@ func (api *API) instantiateTracer(config *TraceConfig, txctx *Context) (*Tracer,
return tracer, err return tracer, err
} }
// TODO: modify traceTx to take a tracer directly. move logic for instantiation from TraceConfig to a separate function. // TODO: the following function takes two contexts... see if one of them can be removed.
// traceTx configures a new tracer according to the provided configuration, and // execTx executes a transaction against the given statedb, optionally configuring the execution to use a tracer
// executes the given message in the provided environment. The return value will // if tracer is non-nil. The result is nil if a tracer is not configured or the value returned from the tracer if it is
// be tracer dependent. // configured.
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, tracer *Tracer, precompiles vm.PrecompiledContracts, timeout time.Duration) (interface{}, error) { func (api *API) execTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, tracer *Tracer, precompiles vm.PrecompiledContracts, timeout time.Duration) (res interface{}, err error) {
var ( var (
err error
usedGas uint64 usedGas uint64
db vm.StateDB = statedb
evm *vm.EVM
vmConf = vm.Config{NoBaseFee: true}
) )
tracingStateDB := state.NewHookedState(statedb, tracer.Hooks) if tracer != nil {
evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) db = state.NewHookedState(statedb, tracer.Hooks)
if precompiles != nil {
evm.SetPrecompiles(precompiles)
}
deadlineCtx, cancel := context.WithTimeout(ctx, timeout) deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
go func() { go func() {
@ -1178,14 +1177,22 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
} }
}() }()
defer cancel() defer cancel()
vmConf.Tracer = tracer.Hooks
}
evm = vm.NewEVM(vmctx, db, api.backend.ChainConfig(), vmConf)
if precompiles != nil {
evm.SetPrecompiles(precompiles)
}
// Call Prepare to clear out the statedb access list
statedb.SetTxContext(txctx.TxHash, txctx.TxIndex) statedb.SetTxContext(txctx.TxHash, txctx.TxIndex)
_, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm) _, err = core.ApplyTransactionWithEVM(message, new(core.GasPool).AddGas(message.GasLimit), statedb, vmctx.BlockNumber, txctx.BlockHash, tx, &usedGas, evm)
if err != nil { if err != nil {
return nil, fmt.Errorf("tracing failed: %w", err) return nil, fmt.Errorf("tracing failed: %w", err)
} }
return tracer.GetResult() if tracer != nil {
res, err = tracer.GetResult()
}
return res, err
} }
// APIs return the collection of RPC services the tracer package offers. // APIs return the collection of RPC services the tracer package offers.