core, eth: use tracing.Hooks by value

the main motivation was to make Contract.UseGas and
Contract.RefundGas inlinable.
This commit is contained in:
Ömer Faruk IRMAK 2025-07-10 20:40:51 +03:00
parent 83aa643621
commit 57fa10b206
43 changed files with 241 additions and 254 deletions

View file

@ -255,7 +255,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
snapshot = statedb.Snapshot()
prevGas = gaspool.Gas()
)
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
if evm.Config.Tracer.OnTxStart != nil {
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
}
// (ret []byte, usedGas uint64, failed bool, err error)
@ -265,7 +265,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
gaspool.SetGas(prevGas)
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil {
if evm.Config.Tracer.OnTxEnd != nil {
evm.Config.Tracer.OnTxEnd(nil, err)
}
continue
@ -311,7 +311,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
//receipt.BlockNumber
receipt.TransactionIndex = uint(txIndex)
receipts = append(receipts, receipt)
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxEnd != nil {
if evm.Config.Tracer.OnTxEnd != nil {
evm.Config.Tracer.OnTxEnd(receipt, nil)
}
}

View file

@ -36,7 +36,7 @@ import (
// and on tx end it closes the file.
type fileWritingTracer struct {
txIndex int // transaction counter
inner *tracing.Hooks // inner hooks
inner tracing.Hooks // inner hooks
destination io.WriteCloser // the currently open file (if any)
baseDir string // baseDir to write output-files to
suffix string // suffix is the suffix to use when creating files
@ -58,7 +58,7 @@ func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
// newFileWriter creates a set of hooks which wraps inner hooks (typically a logger),
// and writes the output to a file, one file per transaction.
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracing.Hooks {
func newFileWriter(baseDir string, innerFn func(out io.Writer) tracing.Hooks) tracing.Hooks {
t := &fileWritingTracer{
baseDir: baseDir,
suffix: "jsonl",
@ -69,7 +69,7 @@ func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *
// newResultWriter creates a set of hooks wraps and invokes an underlying tracer,
// and writes the result (getResult-output) to file, one per transaction.
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracing.Hooks {
func newResultWriter(baseDir string, tracer *tracers.Tracer) tracing.Hooks {
t := &fileWritingTracer{
baseDir: baseDir,
getResult: tracer.GetResult,
@ -91,7 +91,7 @@ func (l *fileWritingTracer) OnTxStart(env *tracing.VMContext, tx *types.Transact
log.Info("Created tracing-file", "path", fname)
l.destination = traceFile
}
if l.inner != nil && l.inner.OnTxStart != nil {
if l.inner.OnTxStart != nil {
l.inner.OnTxStart(env, tx, from)
}
}
@ -99,7 +99,7 @@ func (l *fileWritingTracer) OnTxStart(env *tracing.VMContext, tx *types.Transact
// OnTxEnd writes result (if getResult exist), closes any currently open output-file,
// and invokes the inner OnTxEnd handler.
func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
if l.inner != nil && l.inner.OnTxEnd != nil {
if l.inner.OnTxEnd != nil {
l.inner.OnTxEnd(receipt, err)
}
if l.getResult != nil && l.destination != nil {
@ -114,37 +114,37 @@ func (l *fileWritingTracer) OnTxEnd(receipt *types.Receipt, err error) {
l.txIndex++
}
func (l *fileWritingTracer) hooks() *tracing.Hooks {
return &tracing.Hooks{
func (l *fileWritingTracer) hooks() tracing.Hooks {
return tracing.Hooks{
OnTxStart: l.OnTxStart,
OnTxEnd: l.OnTxEnd,
OnEnter: func(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if l.inner != nil && l.inner.OnEnter != nil {
if l.inner.OnEnter != nil {
l.inner.OnEnter(depth, typ, from, to, input, gas, value)
}
},
OnExit: func(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if l.inner != nil && l.inner.OnExit != nil {
if l.inner.OnExit != nil {
l.inner.OnExit(depth, output, gasUsed, err, reverted)
}
},
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
if l.inner != nil && l.inner.OnOpcode != nil {
if l.inner.OnOpcode != nil {
l.inner.OnOpcode(pc, op, gas, cost, scope, rData, depth, err)
}
},
OnFault: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error) {
if l.inner != nil && l.inner.OnFault != nil {
if l.inner.OnFault != nil {
l.inner.OnFault(pc, op, gas, cost, scope, depth, err)
}
},
OnSystemCallStart: func() {
if l.inner != nil && l.inner.OnSystemCallStart != nil {
if l.inner.OnSystemCallStart != nil {
l.inner.OnSystemCallStart()
}
},
OnSystemCallEnd: func() {
if l.inner != nil && l.inner.OnSystemCallEnd != nil {
if l.inner.OnSystemCallEnd != nil {
l.inner.OnSystemCallEnd()
}
},

View file

@ -167,11 +167,11 @@ func Transition(ctx *cli.Context) error {
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
}
if ctx.Bool(TraceEnableCallFramesFlag.Name) {
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) tracing.Hooks {
return logger.NewJSONLoggerWithCallFrames(logConfig, out)
})
} else {
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) tracing.Hooks {
return logger.NewJSONLogger(logConfig, out)
})
}

View file

@ -229,7 +229,7 @@ func main() {
}
// tracerFromFlags parses the cli flags and returns the specified tracer.
func tracerFromFlags(ctx *cli.Context) *tracing.Hooks {
func tracerFromFlags(ctx *cli.Context) tracing.Hooks {
config := &logger.Config{
EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
@ -248,7 +248,7 @@ func tracerFromFlags(ctx *cli.Context) *tracing.Hooks {
default:
fmt.Fprintf(os.Stderr, "unknown trace format: %q\n", format)
os.Exit(1)
return nil
return tracing.Hooks{}
}
// Deprecated ways of configuring tracing.
case ctx.Bool(MachineFlag.Name):
@ -256,7 +256,7 @@ func tracerFromFlags(ctx *cli.Context) *tracing.Hooks {
case ctx.Bool(DebugFlag.Name):
return logger.NewStreamingStructLogger(config, os.Stderr).Hooks()
default:
return nil
return tracing.Hooks{}
}
}

View file

@ -198,7 +198,7 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
func runCmd(ctx *cli.Context) error {
var (
tracer *tracing.Hooks
tracer tracing.Hooks
prestate *state.StateDB
chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender"))
@ -363,12 +363,12 @@ allocations: %d
allocated bytes: %d
`, stats.GasUsed, stats.Time, stats.Allocs, stats.BytesAllocated)
}
if tracer == nil {
if len(output) > 0 {
fmt.Printf("%#x\n", output)
}
if err != nil {
fmt.Printf(" error: %v\n", err)
}
}
return nil
}

View file

@ -329,7 +329,7 @@ type BlockChain struct {
validator Validator // Block and state validator interface
prefetcher Prefetcher
processor Processor // Block transaction processor interface
logger *tracing.Hooks
logger tracing.Hooks
lastForkReadyAlert time.Time // Last time there was a fork readiness print out
}
@ -491,10 +491,10 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
// it in advance.
bc.engine.VerifyHeader(bc, bc.CurrentHeader())
if bc.logger != nil && bc.logger.OnBlockchainInit != nil {
if bc.logger.OnBlockchainInit != nil {
bc.logger.OnBlockchainInit(chainConfig)
}
if bc.logger != nil && bc.logger.OnGenesisBlock != nil {
if bc.logger.OnGenesisBlock != nil {
if block := bc.CurrentBlock(); block.Number.Uint64() == 0 {
alloc, err := getGenesisState(bc.db, block.Hash())
if err != nil {
@ -1311,7 +1311,7 @@ func (bc *BlockChain) Stop() {
}
}
// Allow tracers to clean-up and release resources.
if bc.logger != nil && bc.logger.OnClose != nil {
if bc.logger.OnClose != nil {
bc.logger.OnClose()
}
// Close the trie database, release all the held resources as the last step.
@ -1862,7 +1862,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
return nil, it.index, err
}
stats.processed++
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
if bc.logger.OnSkippedBlock != nil {
bc.logger.OnSkippedBlock(tracing.BlockEvent{
Block: block,
Finalized: bc.CurrentFinalBlock(),
@ -1998,7 +1998,7 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
// Disable tracing for prefetcher executions.
vmCfg := bc.cfg.VmConfig
vmCfg.Tracer = nil
vmCfg.Tracer = tracing.Hooks{}
bc.prefetcher.Prefetch(block, throwaway, vmCfg, &interrupt)
blockPrefetchExecuteTimer.Update(time.Since(start))
@ -2026,14 +2026,14 @@ func (bc *BlockChain) processBlock(parentRoot common.Hash, block *types.Block, s
defer statedb.StopPrefetcher()
}
if bc.logger != nil && bc.logger.OnBlockStart != nil {
if bc.logger.OnBlockStart != nil {
bc.logger.OnBlockStart(tracing.BlockEvent{
Block: block,
Finalized: bc.CurrentFinalBlock(),
Safe: bc.CurrentSafeBlock(),
})
}
if bc.logger != nil && bc.logger.OnBlockEnd != nil {
if bc.logger.OnBlockEnd != nil {
defer func() {
bc.logger.OnBlockEnd(blockEndErr)
}()

View file

@ -33,16 +33,12 @@ import (
// on state operations.
type hookedStateDB struct {
inner *StateDB
hooks *tracing.Hooks
hooks tracing.Hooks
}
// NewHookedState wraps the given stateDb with the given hooks
func NewHookedState(stateDb *StateDB, hooks *tracing.Hooks) *hookedStateDB {
s := &hookedStateDB{stateDb, hooks}
if s.hooks == nil {
s.hooks = new(tracing.Hooks)
}
return s
func NewHookedState(stateDb *StateDB, hooks tracing.Hooks) *hookedStateDB {
return &hookedStateDB{stateDb, hooks}
}
func (s *hookedStateDB) CreateAccount(addr common.Address) {

View file

@ -39,7 +39,7 @@ func TestBurn(t *testing.T) {
var burned = new(uint256.Int)
s, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
hooked := NewHookedState(s, &tracing.Hooks{
hooked := NewHookedState(s, tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
if reason == tracing.BalanceDecreaseSelfdestructBurn {
burned.Add(burned, uint256.MustFromBig(prev))
@ -94,7 +94,7 @@ func TestHooks(t *testing.T) {
emitF := func(format string, a ...any) {
result = append(result, fmt.Sprintf(format, a...))
}
sdb := NewHookedState(inner, &tracing.Hooks{
sdb := NewHookedState(inner, tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
emitF("%v.balance: %v->%v (%v)", addr, prev, new, reason)
},

View file

@ -76,8 +76,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// Apply pre-execution system calls.
var tracingStateDB = vm.StateDB(statedb)
if hooks := cfg.Tracer; hooks != nil {
tracingStateDB = state.NewHookedState(statedb, hooks)
if cfg.Tracer.HasStateHooks() {
tracingStateDB = state.NewHookedState(statedb, cfg.Tracer)
}
context = NewEVMBlockContext(header, p.chain, nil)
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
@ -137,13 +137,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// and uses the input parameters for its environment similar to ApplyTransaction. However,
// this method takes an already created EVM instance as input.
func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, blockTime uint64, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
if hooks := evm.Config.Tracer; hooks != nil {
if hooks.OnTxStart != nil {
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
}
if hooks.OnTxEnd != nil {
defer func() { hooks.OnTxEnd(receipt, err) }()
if evm.Config.Tracer.OnTxStart != nil {
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
}
if evm.Config.Tracer.OnTxEnd != nil {
defer func() { evm.Config.Tracer.OnTxEnd(receipt, err) }()
}
// Apply the transaction to the current state (included in the env).
result, err := ApplyMessage(evm, msg, gp)
@ -215,11 +213,9 @@ func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
// contract. This method is exported to be used in tests.
func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
if tracer := evm.Config.Tracer; tracer != nil {
onSystemCallStart(tracer, evm.GetVMContext())
if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd()
}
onSystemCallStart(&evm.Config.Tracer, evm.GetVMContext())
if evm.Config.Tracer.OnSystemCallEnd != nil {
defer evm.Config.Tracer.OnSystemCallEnd()
}
msg := &Message{
From: params.SystemAddress,
@ -239,11 +235,9 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
// ProcessParentBlockHash stores the parent block hash in the history storage contract
// as per EIP-2935/7709.
func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
if tracer := evm.Config.Tracer; tracer != nil {
onSystemCallStart(tracer, evm.GetVMContext())
if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd()
}
onSystemCallStart(&evm.Config.Tracer, evm.GetVMContext())
if evm.Config.Tracer.OnSystemCallEnd != nil {
defer evm.Config.Tracer.OnSystemCallEnd()
}
msg := &Message{
From: params.SystemAddress,
@ -279,11 +273,9 @@ func ProcessConsolidationQueue(requests *[][]byte, evm *vm.EVM) error {
}
func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte, addr common.Address) error {
if tracer := evm.Config.Tracer; tracer != nil {
onSystemCallStart(tracer, evm.GetVMContext())
if tracer.OnSystemCallEnd != nil {
defer tracer.OnSystemCallEnd()
}
onSystemCallStart(&evm.Config.Tracer, evm.GetVMContext())
if evm.Config.Tracer.OnSystemCallEnd != nil {
defer evm.Config.Tracer.OnSystemCallEnd()
}
msg := &Message{
From: params.SystemAddress,

View file

@ -292,7 +292,7 @@ func (st *stateTransition) buyGas() error {
return err
}
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
if st.evm.Config.Tracer.OnGasChange != nil {
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
}
st.gasRemaining = st.msg.GasLimit
@ -456,8 +456,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
return nil, fmt.Errorf("%w: have %d, want %d", ErrFloorDataGas, msg.GasLimit, floorDataGas)
}
}
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas)
if st.evm.Config.Tracer.OnGasChange != nil {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas)
}
st.gasRemaining -= gas
@ -530,8 +530,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
if st.gasUsed() < floorDataGas {
prev := st.gasRemaining
st.gasRemaining = st.initialGas - floorDataGas
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
if st.evm.Config.Tracer.OnGasChange != nil {
st.evm.Config.Tracer.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
}
}
if peakGasUsed < floorDataGas {
@ -640,7 +640,7 @@ func (st *stateTransition) calcRefund() uint64 {
if refund > st.state.GetRefund() {
refund = st.state.GetRefund()
}
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
if st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, tracing.GasChangeTxRefunds)
}
return refund
@ -653,7 +653,7 @@ func (st *stateTransition) returnGas() {
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
if st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, tracing.GasChangeTxLeftOverReturned)
}

View file

@ -188,7 +188,8 @@ type (
)
type Hooks struct {
// VM events
// VM events, make sure to update HasVMHooks
// if you ever add a new hook here
OnTxStart TxStartHook
OnTxEnd TxEndHook
OnEnter EnterHook
@ -206,7 +207,8 @@ type Hooks struct {
OnSystemCallStart OnSystemCallStartHook
OnSystemCallStartV2 OnSystemCallStartHookV2
OnSystemCallEnd OnSystemCallEndHook
// State events
// State events, make sure to update HasStateHooks
// if you ever add a new hook here
OnBalanceChange BalanceChangeHook
OnNonceChange NonceChangeHook
OnNonceChangeV2 NonceChangeHookV2
@ -217,6 +219,27 @@ type Hooks struct {
OnBlockHashRead BlockHashReadHook
}
// HasVMHooks returns if any of the VM events are being hooked
func (h *Hooks) HasVMHooks() bool {
return h.OnTxStart != nil ||
h.OnTxEnd != nil ||
h.OnEnter != nil ||
h.OnExit != nil ||
h.OnOpcode != nil ||
h.OnFault != nil ||
h.OnGasChange != nil
}
// HasStateHooks returns if any of the state events are being hooked
func (h *Hooks) HasStateHooks() bool {
return h.OnBalanceChange != nil ||
h.OnNonceChange != nil ||
h.OnNonceChangeV2 != nil ||
h.OnCodeChange != nil ||
h.OnStorageChange != nil ||
h.OnLog != nil
}
// BalanceChangeReason is used to indicate the reason for a balance change, useful
// for tracing and reporting.
type BalanceChangeReason byte

View file

@ -126,24 +126,24 @@ func (c *Contract) Caller() common.Address {
}
// UseGas attempts the use gas and subtracts it and returns true on success
func (c *Contract) UseGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) (ok bool) {
func (c *Contract) UseGas(gas uint64, onGasChange tracing.GasChangeHook, reason tracing.GasChangeReason) (ok bool) {
if c.Gas < gas {
return false
}
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
logger.OnGasChange(c.Gas, c.Gas-gas, reason)
if onGasChange != nil && reason != tracing.GasChangeIgnored {
onGasChange(c.Gas, c.Gas-gas, reason)
}
c.Gas -= gas
return true
}
// RefundGas refunds gas to the contract
func (c *Contract) RefundGas(gas uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
func (c *Contract) RefundGas(gas uint64, onGasChange tracing.GasChangeHook, reason tracing.GasChangeReason) {
if gas == 0 {
return
}
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
logger.OnGasChange(c.Gas, c.Gas+gas, reason)
if onGasChange != nil && reason != tracing.GasChangeIgnored {
onGasChange(c.Gas, c.Gas+gas, reason)
}
c.Gas += gas
}

View file

@ -257,13 +257,13 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, onGasChange tracing.GasChangeHook) (ret []byte, remainingGas uint64, err error) {
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
}
if logger != nil && logger.OnGasChange != nil {
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
if onGasChange != nil {
onGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
}
suppliedGas -= gasCost
output, err := p.Run(input)

View file

@ -358,7 +358,7 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
code := interpreter.evm.StateDB.GetCode(addr)
paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64())
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(addr, copyOffset, nonPaddedCopyLength, uint64(len(code)), false, scope.Contract.Gas)
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeUnspecified)
if consumed < wanted {
return nil, ErrOutOfGas
}
@ -384,7 +384,7 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
// advanced past this boundary.
contractAddr := scope.Contract.Address()
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
scope.Contract.UseGas(wanted, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeUnspecified)
if consumed < wanted {
return nil, ErrOutOfGas
}
@ -412,7 +412,7 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
contractAddr := scope.Contract.Address()
consumed, wanted := interpreter.evm.AccessEvents.CodeChunksRangeGas(contractAddr, uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false, scope.Contract.Gas)
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified)
scope.Contract.UseGas(consumed, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeUnspecified)
if consumed < wanted {
return nil, ErrOutOfGas
}

View file

@ -125,6 +125,9 @@ type EVM struct {
// jumpDests is the aggregated result of JUMPDEST analysis made through
// the life cycle of EVM.
jumpDests map[common.Hash]bitvec
// tracingEnabled is used to shortcircuit some of the tracing calls
tracingEnabled bool
}
// NewEVM constructs an EVM instance with the supplied block context, state
@ -139,6 +142,7 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
jumpDests: make(map[common.Hash]bitvec),
tracingEnabled: config.Tracer.HasVMHooks(),
}
evm.precompiles = activePrecompiledContracts(evm.chainRules)
evm.interpreter = NewEVMInterpreter(evm)
@ -186,8 +190,8 @@ func isSystemCall(caller common.Address) bool {
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.tracingEnabled {
// Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALL, caller, addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
@ -230,7 +234,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
evm.Context.Transfer(evm.StateDB, caller, addr, value)
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer.OnGasChange)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
code := evm.resolveCode(addr)
@ -251,7 +255,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
@ -272,8 +276,8 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.tracingEnabled {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, CALLCODE, caller, addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
@ -294,7 +298,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer.OnGasChange)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
@ -306,7 +310,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
gas = 0
@ -321,8 +325,8 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.tracingEnabled {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
// DELEGATECALL inherits value from parent call
evm.captureBegin(evm.depth, DELEGATECALL, caller, addr, input, gas, value.ToBig())
defer func(startGas uint64) {
@ -337,7 +341,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer.OnGasChange)
} else {
// Initialise a new contract and make initialise the delegate values
//
@ -350,7 +354,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
gas = 0
@ -364,8 +368,8 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if evm.tracingEnabled {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth, STATICCALL, caller, addr, input, gas, nil)
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
@ -389,7 +393,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer.OnGasChange)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
@ -405,7 +409,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
@ -417,7 +421,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
if evm.Config.Tracer != nil {
if evm.tracingEnabled {
evm.captureBegin(evm.depth, typ, caller, address, code, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
@ -443,7 +447,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
if statelessGas > gas {
return nil, common.Address{}, 0, ErrOutOfGas
}
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, gas-statelessGas, tracing.GasChangeWitnessContractCollisionCheck)
}
gas = gas - statelessGas
@ -464,7 +468,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
if evm.StateDB.GetNonce(address) != 0 ||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
(storageRoot != (common.Hash{}) && storageRoot != types.EmptyRootHash) { // non-empty storage
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
}
return nil, common.Address{}, 0, ErrContractAddressCollision
@ -491,7 +495,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
if consumed < wanted {
return nil, common.Address{}, 0, ErrOutOfGas
}
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(gas, gas-consumed, tracing.GasChangeWitnessContractInit)
}
gas = gas - consumed
@ -511,7 +515,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
contract.UseGas(contract.Gas, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallFailedExecution)
}
}
return ret, address, contract.Gas, err
@ -537,12 +541,12 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
if !evm.chainRules.IsEIP4762 {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if !contract.UseGas(createDataGas, evm.Config.Tracer, tracing.GasChangeCallCodeStorage) {
if !contract.UseGas(createDataGas, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallCodeStorage) {
return ret, ErrCodeStoreOutOfGas
}
} else {
consumed, wanted := evm.AccessEvents.CodeChunksRangeGas(address, 0, uint64(len(ret)), uint64(len(ret)), true, contract.Gas)
contract.UseGas(consumed, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
contract.UseGas(consumed, evm.Config.Tracer.OnGasChange, tracing.GasChangeWitnessCodeChunk)
if len(ret) > 0 && (consumed < wanted) {
return ret, ErrCodeStoreOutOfGas
}
@ -601,29 +605,21 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash {
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
tracer := evm.Config.Tracer
if tracer.OnEnter != nil {
tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
if evm.Config.Tracer.OnEnter != nil {
evm.Config.Tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
}
if tracer.OnGasChange != nil {
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
if evm.Config.Tracer.OnGasChange != nil {
evm.Config.Tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
}
}
func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) {
tracer := evm.Config.Tracer
if leftOverGas != 0 && tracer.OnGasChange != nil {
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
if evm.Config.Tracer.OnGasChange != nil && leftOverGas != 0 {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
}
var reverted bool
if err != nil {
reverted = true
}
if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) {
reverted = false
}
if tracer.OnExit != nil {
tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
if evm.Config.Tracer.OnExit != nil {
reverted := err != nil && (evm.chainRules.IsHomestead || !errors.Is(err, ErrCodeStoreOutOfGas))
evm.Config.Tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
}
}

View file

@ -443,8 +443,8 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
if witness := interpreter.evm.StateDB.Witness(); witness != nil {
witness.AddBlockHash(num64)
}
if tracer := interpreter.evm.Config.Tracer; tracer != nil && tracer.OnBlockHashRead != nil {
tracer.OnBlockHashRead(num64, res)
if interpreter.evm.Config.Tracer.OnBlockHashRead != nil {
interpreter.evm.Config.Tracer.OnBlockHashRead(num64, res)
}
num.SetBytes(res[:])
} else {
@ -670,7 +670,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// reuse size int for stackvalue
stackvalue := size
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation)
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallContractCreation)
res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract.Address(), input, gas, &value)
// Push item on the stack based on the returned error. If the ruleset is
@ -686,7 +686,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
scope.Stack.push(&stackvalue)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded)
if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer
@ -710,7 +710,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
// Apply EIP150
gas -= gas / 64
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallContractCreation2)
// reuse size int for stackvalue
stackvalue := size
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract.Address(), input, gas,
@ -722,7 +722,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
stackvalue.SetBytes(addr.Bytes())
}
scope.Stack.push(&stackvalue)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded)
if suberr == ErrExecutionReverted {
interpreter.returnData = res // set REVERT data to return data buffer
@ -762,7 +762,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@ -795,7 +795,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@ -824,7 +824,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@ -853,7 +853,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded)
interpreter.returnData = ret
return ret, nil
@ -890,13 +890,11 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil {
tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
}
if tracer.OnExit != nil {
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
if interpreter.evm.Config.Tracer.OnEnter != nil {
interpreter.evm.Config.Tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
}
if interpreter.evm.Config.Tracer.OnExit != nil {
interpreter.evm.Config.Tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
}
return nil, errStopToken
}
@ -910,13 +908,11 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct)
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct)
interpreter.evm.StateDB.SelfDestruct6780(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil {
tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
}
if tracer.OnExit != nil {
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
if interpreter.evm.Config.Tracer.OnEnter != nil {
interpreter.evm.Config.Tracer.OnEnter(interpreter.evm.depth, byte(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
}
if interpreter.evm.Config.Tracer.OnExit != nil {
interpreter.evm.Config.Tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
}
return nil, errStopToken
}

View file

@ -29,7 +29,7 @@ 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
@ -202,7 +202,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
gasCopy uint64 // for EVMLogger to log gas remaining before execution
logged bool // deferred EVMLogger should ignore already logged steps
res []byte // result of the opcode execution function
debug = in.evm.Config.Tracer != nil
)
// Don't move this deferred function, it's placed before the OnOpcode-deferred method,
// so that it gets executed _after_: the OnOpcode needs the stacks before
@ -213,7 +212,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}()
contract.Input = input
if debug {
defer func() { // this deferred method handles exit-with-error
if err == nil {
return
@ -225,24 +223,21 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err))
}
}()
}
// The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
// parent context.
_ = jumpTable[0] // nil-check the jumpTable out of the loop
for {
if debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
}
if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
contractAddr := contract.Address()
consumed, wanted := in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
contract.UseGas(consumed, in.evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk)
contract.UseGas(consumed, in.evm.Config.Tracer.OnGasChange, tracing.GasChangeWitnessCodeChunk)
if consumed < wanted {
return nil, ErrOutOfGas
}
@ -301,7 +296,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
// Do tracing before potential memory expansion
if debug {
if in.evm.Config.Tracer.OnGasChange != nil {
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
}
@ -309,7 +303,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
logged = true
}
}
if memorySize > 0 {
mem.Resize(memorySize)
}

View file

@ -164,7 +164,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) g
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.UseGas(coldCost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
}
@ -265,7 +265,7 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.UseGas(coldCost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += coldCost
@ -280,7 +280,7 @@ func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
evm.StateDB.AddAddressToAccessList(target)
cost = params.ColdAccountAccessCostEIP2929
}
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
if !contract.UseGas(cost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost

View file

@ -130,7 +130,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
vmenv = NewEnv(cfg)
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
if cfg.EVMConfig.Tracer.OnTxStart != nil {
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
}
// Execute the preparatory steps for state transition which includes:
@ -148,7 +148,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
cfg.GasLimit,
uint256.MustFromBig(cfg.Value),
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
if cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err)
}
return ret, cfg.State, err
@ -168,7 +168,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
vmenv = NewEnv(cfg)
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
if cfg.EVMConfig.Tracer.OnTxStart != nil {
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
}
// Execute the preparatory steps for state transition which includes:
@ -182,7 +182,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
cfg.GasLimit,
uint256.MustFromBig(cfg.Value),
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
if cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err)
}
return code, address, leftOverGas, err
@ -201,7 +201,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
statedb = cfg.State
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxStart != nil {
if cfg.EVMConfig.Tracer.OnTxStart != nil {
cfg.EVMConfig.Tracer.OnTxStart(vmenv.GetVMContext(), types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit}), cfg.Origin)
}
// Execute the preparatory steps for state transition which includes:
@ -217,7 +217,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
cfg.GasLimit,
uint256.MustFromBig(cfg.Value),
)
if cfg.EVMConfig.Tracer != nil && cfg.EVMConfig.Tracer.OnTxEnd != nil {
if cfg.EVMConfig.Tracer.OnTxEnd != nil {
cfg.EVMConfig.Tracer.OnTxEnd(&types.Receipt{GasUsed: cfg.GasLimit - leftOverGas}, err)
}
return ret, leftOverGas, err

View file

@ -666,7 +666,7 @@ func TestColdAccountAccessCost(t *testing.T) {
var have = uint64(0)
Execute(tc.code, nil, &Config{
EVMConfig: vm.Config{
Tracer: &tracing.Hooks{
Tracer: tracing.Hooks{
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
// Uncomment to investigate failures:
//t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)
@ -920,7 +920,7 @@ func TestDelegatedAccountAccessCost(t *testing.T) {
ChainConfig: params.MergedTestChainConfig,
State: statedb,
EVMConfig: vm.Config{
Tracer: &tracing.Hooks{
Tracer: tracing.Hooks{
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
// Uncomment to investigate failures:
t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)

View file

@ -208,7 +208,7 @@ func newStateTracer(ctx *Context, cfg json.RawMessage, chainCfg *params.ChainCon
GetResult: func() (json.RawMessage, error) {
return json.Marshal(t)
},
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnBalanceChange: func(addr common.Address, prev, new *big.Int, reason tracing.BalanceChangeReason) {
t.Balance[addr] = (*hexutil.Big)(new)
},

View file

@ -39,7 +39,7 @@ type Context struct {
// This involves a method to retrieve results and one to
// stop tracing.
type Tracer struct {
*tracing.Hooks
tracing.Hooks
GetResult func() (json.RawMessage, error)
// Stop terminates execution of the tracer at the first opportune moment.
Stop func(err error)

View file

@ -121,10 +121,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
logState := vm.StateDB(st.StateDB)
if tracer.Hooks != nil {
logState = state.NewHookedState(st.StateDB, tracer.Hooks)
}
logState := state.NewHookedState(st.StateDB, tracer.Hooks)
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
@ -355,11 +352,7 @@ func TestInternals(t *testing.T) {
}, false, rawdb.HashScheme)
defer st.Close()
logState := vm.StateDB(st.StateDB)
if hooks := tc.tracer.Hooks; hooks != nil {
logState = state.NewHookedState(st.StateDB, hooks)
}
logState := state.NewHookedState(st.StateDB, tc.tracer.Hooks)
tx, err := types.SignNewTx(key, signer, &types.LegacyTx{
To: &to,
Value: big.NewInt(0),

View file

@ -114,10 +114,7 @@ func TestErc7562Tracer(t *testing.T) {
if err != nil {
t.Fatalf("failed to create erc7562 tracer: %v", err)
}
logState := vm.StateDB(st.StateDB)
if tracer.Hooks != nil {
logState = state.NewHookedState(st.StateDB, tracer.Hooks)
}
logState := state.NewHookedState(st.StateDB, tracer.Hooks)
msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)

View file

@ -235,7 +235,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage, chainCo
t.logValue = t.log.setupObject()
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnEnter: t.OnEnter,

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
)
type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error)
type ctorFunc func(config json.RawMessage) (tracing.Hooks, error)
// LiveDirectory is the collection of tracers which can be used
// during normal block import operations.
@ -39,12 +39,12 @@ func (d *liveDirectory) Register(name string, f ctorFunc) {
}
// New instantiates a tracer by name.
func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) {
func (d *liveDirectory) New(name string, config json.RawMessage) (tracing.Hooks, error) {
if len(config) == 0 {
config = json.RawMessage("{}")
}
if f, ok := d.elems[name]; ok {
return f(config)
}
return nil, errors.New("not found")
return tracing.Hooks{}, errors.New("not found")
}

View file

@ -37,9 +37,9 @@ func init() {
// as soon as we have a real live tracer.
type noop struct{}
func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
func newNoopTracer(_ json.RawMessage) (tracing.Hooks, error) {
t := &noop{}
return &tracing.Hooks{
return tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnEnter: t.OnEnter,

View file

@ -92,13 +92,13 @@ type supplyTracerConfig struct {
MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes.
}
func newSupplyTracer(cfg json.RawMessage) (*tracing.Hooks, error) {
func newSupplyTracer(cfg json.RawMessage) (tracing.Hooks, error) {
var config supplyTracerConfig
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, fmt.Errorf("failed to parse config: %v", err)
return tracing.Hooks{}, fmt.Errorf("failed to parse config: %v", err)
}
if config.Path == "" {
return nil, errors.New("supply tracer output path is required")
return tracing.Hooks{}, errors.New("supply tracer output path is required")
}
// Store traces in a rotating file
@ -113,7 +113,7 @@ func newSupplyTracer(cfg json.RawMessage) (*tracing.Hooks, error) {
delta: newSupplyInfo(),
logger: logger,
}
return &tracing.Hooks{
return tracing.Hooks{
OnBlockchainInit: t.onBlockchainInit,
OnBlockStart: t.onBlockStart,
OnBlockEnd: t.onBlockEnd,

View file

@ -119,8 +119,8 @@ func NewAccessListTracer(acl types.AccessList, addressesToExclude map[common.Add
}
}
func (a *AccessListTracer) Hooks() *tracing.Hooks {
return &tracing.Hooks{
func (a *AccessListTracer) Hooks() tracing.Hooks {
return tracing.Hooks{
OnOpcode: a.OnOpcode,
}
}

View file

@ -244,8 +244,8 @@ func NewStructLogger(cfg *Config) *StructLogger {
return logger
}
func (l *StructLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{
func (l *StructLogger) Hooks() tracing.Hooks {
return tracing.Hooks{
OnTxStart: l.OnTxStart,
OnTxEnd: l.OnTxEnd,
OnSystemCallStartV2: l.OnSystemCallStart,
@ -426,8 +426,8 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
return l
}
func (t *mdLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{
func (t *mdLogger) Hooks() tracing.Hooks {
return tracing.Hooks{
OnTxStart: t.OnTxStart,
OnSystemCallStartV2: t.OnSystemCallStart,
OnSystemCallEnd: t.OnSystemCallEnd,

View file

@ -58,17 +58,17 @@ type jsonLogger struct {
encoder *json.Encoder
cfg *Config
env *tracing.VMContext
hooks *tracing.Hooks
hooks tracing.Hooks
}
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream.
func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks {
func NewJSONLogger(cfg *Config, writer io.Writer) tracing.Hooks {
l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
if l.cfg == nil {
l.cfg = &Config{}
}
l.hooks = &tracing.Hooks{
l.hooks = tracing.Hooks{
OnTxStart: l.OnTxStart,
OnSystemCallStart: l.onSystemCallStart,
OnExit: l.OnExit,
@ -80,12 +80,12 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks {
// NewJSONLoggerWithCallFrames creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream. It also includes call frames in the output.
func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) *tracing.Hooks {
func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) tracing.Hooks {
l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
if l.cfg == nil {
l.cfg = &Config{}
}
l.hooks = &tracing.Hooks{
l.hooks = tracing.Hooks{
OnTxStart: l.OnTxStart,
OnSystemCallStart: l.onSystemCallStart,
OnEnter: l.OnEnter,
@ -129,10 +129,10 @@ func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracin
func (l *jsonLogger) onSystemCallStart() {
// Process no events while in system call.
hooks := *l.hooks
*l.hooks = tracing.Hooks{
hooks := l.hooks
l.hooks = tracing.Hooks{
OnSystemCallEnd: func() {
*l.hooks = hooks
l.hooks = hooks
},
}
}

View file

@ -64,7 +64,7 @@ func newFourByteTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *p
chainConfig: chainConfig,
}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnEnter: t.OnEnter,
},

View file

@ -132,7 +132,7 @@ func newCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *param
return nil, err
}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnEnter: t.OnEnter,

View file

@ -143,7 +143,7 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *p
ft := &flatCallTracer{tracer: t, ctx: ctx, config: config, chainConfig: chainConfig}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: ft.OnTxStart,
OnTxEnd: ft.OnTxEnd,
OnEnter: ft.OnEnter,

View file

@ -153,7 +153,7 @@ func newErc7562Tracer(ctx *tracers.Context, cfg json.RawMessage, _ *params.Chain
return nil, err
}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnOpcode: t.OnOpcode,
OnTxEnd: t.OnTxEnd,

View file

@ -57,7 +57,7 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params
t := &muxTracer{names: names, tracers: objects}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnEnter: t.OnEnter,

View file

@ -39,7 +39,7 @@ type noopTracer struct{}
func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) {
t := &noopTracer{}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnEnter: t.OnEnter,

View file

@ -99,7 +99,7 @@ func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *p
deleted: make(map[common.Address]bool),
}
return &tracers.Tracer{
Hooks: &tracing.Hooks{
Hooks: tracing.Hooks{
OnTxStart: t.OnTxStart,
OnTxEnd: t.OnTxEnd,
OnOpcode: t.OnOpcode,

View file

@ -68,8 +68,8 @@ func newTracer(traceTransfers bool, blockNumber uint64, blockHash, txHash common
}
}
func (t *tracer) Hooks() *tracing.Hooks {
return &tracing.Hooks{
func (t *tracer) Hooks() tracing.Hooks {
return tracing.Hooks{
OnEnter: t.onEnter,
OnExit: t.onExit,
OnLog: t.onLog,

View file

@ -255,8 +255,8 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
senders = make(map[common.Hash]common.Address)
)
tracingStateDB := vm.StateDB(sim.state)
if hooks := tracer.Hooks(); hooks != nil {
tracingStateDB = state.NewHookedState(sim.state, hooks)
if tracer != nil {
tracingStateDB = state.NewHookedState(sim.state, tracer.Hooks())
}
evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig)
// It is possible to override precompiles with EVM bytecode, or

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/tracing"
)
func TestBlockchain(t *testing.T) {
@ -105,7 +106,7 @@ func execBlockTest(t *testing.T, bt *testMatcher, test *BlockTest) {
}
for _, snapshot := range snapshotConf {
for _, dbscheme := range dbschemeConf {
if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, nil, nil)); err != nil {
if err := bt.checkFailure(t, test.Run(snapshot, dbscheme, true, tracing.Hooks{}, nil)); err != nil {
t.Errorf("test with config {snapshotter:%v, scheme:%v} failed: %v", snapshot, dbscheme, err)
return
}

View file

@ -111,7 +111,7 @@ type btHeaderMarshaling struct {
ExcessBlobGas *math.HexOrDecimal64
}
func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer *tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) {
func (t *BlockTest) Run(snapshotter bool, scheme string, witness bool, tracer tracing.Hooks, postCheck func(error, *core.BlockChain)) (result error) {
config, ok := Forks[t.json.Network]
if !ok {
return UnsupportedForkError{t.json.Network}

View file

@ -323,8 +323,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
evm := vm.NewEVM(context, st.StateDB, config, vmconfig)
if tracer := vmconfig.Tracer; tracer != nil && tracer.OnTxStart != nil {
tracer.OnTxStart(evm.GetVMContext(), nil, msg.From)
if evm.Config.Tracer.OnTxStart != nil {
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), nil, msg.From)
}
// Execute the message.
snapshot := st.StateDB.Snapshot()
@ -333,7 +333,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
vmRet, err := core.ApplyMessage(evm, msg, gaspool)
if err != nil {
st.StateDB.RevertToSnapshot(snapshot)
if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil {
if evm.Config.Tracer.OnTxEnd != nil {
evm.Config.Tracer.OnTxEnd(nil, err)
}
return st, common.Hash{}, 0, err
@ -347,9 +347,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
// Commit state mutations into database.
root, _ = st.StateDB.Commit(block.NumberU64(), config.IsEIP158(block.Number()), config.IsCancun(block.Number(), block.Time()))
if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil {
if evm.Config.Tracer.OnTxEnd != nil {
receipt := &types.Receipt{GasUsed: vmRet.UsedGas}
tracer.OnTxEnd(receipt, nil)
evm.Config.Tracer.OnTxEnd(receipt, nil)
}
return st, root, vmRet.UsedGas, nil
}