mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-17 13:36:37 +00:00
eth/tracers: trace block in block level
This commit is contained in:
parent
2872242045
commit
d400d75d52
1 changed files with 92 additions and 3 deletions
|
|
@ -153,9 +153,10 @@ func (api *API) blockByNumberAndHash(ctx context.Context, number rpc.BlockNumber
|
||||||
// TraceConfig holds extra parameters to trace functions.
|
// TraceConfig holds extra parameters to trace functions.
|
||||||
type TraceConfig struct {
|
type TraceConfig struct {
|
||||||
*logger.Config
|
*logger.Config
|
||||||
Tracer *string
|
Tracer *string
|
||||||
Timeout *string
|
Timeout *string
|
||||||
Reexec *uint64
|
Reexec *uint64
|
||||||
|
BlockLevel bool
|
||||||
// Config specific to given tracer. Note struct logger
|
// Config specific to given tracer. Note struct logger
|
||||||
// config are historically embedded in main object.
|
// config are historically embedded in main object.
|
||||||
TracerConfig json.RawMessage
|
TracerConfig json.RawMessage
|
||||||
|
|
@ -617,6 +618,12 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
return api.traceBlockParallel(ctx, block, statedb, config)
|
return api.traceBlockParallel(ctx, block, statedb, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if this is a block-level prestate tracing request
|
||||||
|
if config != nil && config.BlockLevel {
|
||||||
|
return api.traceBlockAsWhole(ctx, block, statedb, blockCtx, config)
|
||||||
|
}
|
||||||
|
|
||||||
// Native tracers have low overhead
|
// Native tracers have low overhead
|
||||||
var (
|
var (
|
||||||
txs = block.Transactions()
|
txs = block.Transactions()
|
||||||
|
|
@ -642,6 +649,88 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// traceBlockAsWhole executes all transactions in a block with a single reused prestate tracer
|
||||||
|
// to accumulate block-level prestate across all transactions.
|
||||||
|
func (api *API) traceBlockAsWhole(ctx context.Context, block *types.Block, statedb *state.StateDB, blockCtx vm.BlockContext, config *TraceConfig) ([]*txTraceResult, error) {
|
||||||
|
var (
|
||||||
|
tracer *Tracer
|
||||||
|
txs = block.Transactions()
|
||||||
|
txCtx = &Context{BlockHash: block.Hash(), BlockNumber: block.Number()}
|
||||||
|
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time())
|
||||||
|
timeout = defaultTraceTimeout * 10
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
config = &TraceConfig{}
|
||||||
|
}
|
||||||
|
// Default tracer is the struct logger
|
||||||
|
if config.Tracer == nil {
|
||||||
|
logger := logger.NewStructLogger(config.Config)
|
||||||
|
tracer = &Tracer{
|
||||||
|
Hooks: logger.Hooks(),
|
||||||
|
GetResult: logger.GetResult,
|
||||||
|
Stop: logger.Stop,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracer, err = DefaultDirectory.New(*config.Tracer, txCtx, config.TracerConfig, api.backend.ChainConfig())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute all transactions with the same tracer instance to accumulate state
|
||||||
|
tracingStateDB := state.NewHookedState(statedb, tracer.Hooks)
|
||||||
|
evm := vm.NewEVM(blockCtx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
|
||||||
|
|
||||||
|
if config.Timeout != nil {
|
||||||
|
if timeout, err = time.ParseDuration(*config.Timeout); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deadlineCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
|
go func() {
|
||||||
|
<-deadlineCtx.Done()
|
||||||
|
if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) {
|
||||||
|
tracer.Stop(errors.New("execution timeout"))
|
||||||
|
// Stop evm execution. Note cancellation is not necessarily immediate.
|
||||||
|
evm.Cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for i, tx := range txs {
|
||||||
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
|
if tracer.Hooks.OnTxStart != nil {
|
||||||
|
tracer.Hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute transaction %s: %w", tx.Hash().Hex(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tracer.Hooks.OnTxEnd != nil {
|
||||||
|
tracer.Hooks.OnTxEnd(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the accumulated prestate result from the single tracer
|
||||||
|
result, err := tracer.GetResult()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the merged result as a single element array
|
||||||
|
return []*txTraceResult{{
|
||||||
|
Result: result,
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue