From 32c14945daeb4927360f7693d99c265ed13d8a7e Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Wed, 2 Aug 2023 09:11:46 -0400 Subject: [PATCH] Full OnGasConsumed loop and added `GasChangeReason` With this change, the full gas loop of a transaction can be tracked going from initial balance (`gasLimit`) back down to 0. I validated on my regression test suite that those conditions applied to all transactions I have: - First `OnGasConsumed` is always going from 0 -> `trx.gasLimit` - Last `OnGasConsumed` "new value" is always 0 - trx.gasLimit - last balance of last `OnGasConsumed` is equal to `trx.gasUsed` The addition of `reason` make it possible for a logger to filter out unwanted signal, for example one could want to drop all OpCode related gas change and only cares about "transaction" level. The reason is also a good thing for visibility and for creating powerful debug tools for Ethereum transaction execution. --- core/state_transition.go | 16 +++++++- core/vm/contract.go | 4 +- core/vm/contracts.go | 2 +- core/vm/evm.go | 41 +++++++++++++++++++- core/vm/instructions.go | 34 +++++++++++++++- core/vm/interpreter.go | 4 +- core/vm/logger.go | 49 +++++++++++++++++++++++- core/vm/operations_acl.go | 2 +- eth/tracers/logger/access_list_tracer.go | 2 +- eth/tracers/logger/logger.go | 4 +- eth/tracers/logger/logger_json.go | 2 +- eth/tracers/native/mux.go | 4 +- eth/tracers/noop.go | 2 +- eth/tracers/printer.go | 2 +- 14 files changed, 148 insertions(+), 20 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 645fe669f2..cd406c0107 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -263,6 +263,11 @@ func (st *StateTransition) buyGas() error { if err := st.gp.SubGas(st.msg.GasLimit); err != nil { return err } + + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasConsumed(0, -st.msg.GasLimit, vm.GasInitialBalance) + } + st.gasRemaining += st.msg.GasLimit st.initialGas = st.msg.GasLimit @@ -386,7 +391,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } if t := st.evm.Config.Tracer; t != nil { - t.OnGasConsumed(st.gasRemaining, gas) + t.OnGasConsumed(st.gasRemaining, gas, vm.GasChangeIntrinsicGas) } st.gasRemaining -= gas @@ -452,12 +457,21 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { if refund > st.state.GetRefund() { refund = st.state.GetRefund() } + + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasConsumed(st.gasRemaining, -refund, vm.GasRefunded) + } + st.gasRemaining += refund // Return ETH for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice) st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund) + if st.evm.Config.Tracer != nil { + st.evm.Config.Tracer.OnGasConsumed(st.gasRemaining, st.gasRemaining, vm.GasBuyBack) + } + // Also return remaining gas to the block gas counter so it is // available for the next transaction. st.gp.AddGas(st.gasRemaining) diff --git a/core/vm/contract.go b/core/vm/contract.go index 1d88b0e1b9..6875e01635 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -159,12 +159,12 @@ 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 EVMLogger) (ok bool) { +func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) (ok bool) { if c.Gas < gas { return false } if logger != nil { - logger.OnGasConsumed(c.Gas, gas) + logger.OnGasConsumed(c.Gas, gas, reason) } c.Gas -= gas return true diff --git a/core/vm/contracts.go b/core/vm/contracts.go index cb4cc76c02..662bce1b07 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -174,7 +174,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin return nil, 0, ErrOutOfGas } if logger != nil { - logger.OnGasConsumed(suppliedGas, gasCost) + logger.OnGasConsumed(suppliedGas, gasCost, GasChangePrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index be7ffebf53..93c1279e64 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -17,7 +17,9 @@ package vm import ( + "fmt" "math/big" + "os" "sync/atomic" "github.com/ethereum/go-ethereum/common" @@ -188,7 +190,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } else { // Handle tracer events for entering and exiting a call frame tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) + tracer.OnGasConsumed(0, -gas, GasInitialBalance) + defer func(startGas uint64) { + tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) tracer.CaptureExit(ret, startGas-leftOverGas, err) }(gas) } @@ -237,6 +242,10 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } // TODO: consider clearing up unused snapshots: @@ -257,7 +266,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // Invoke tracer hooks that signal entering/exiting a call frame if tracer := evm.Config.Tracer; tracer != nil { tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) + tracer.OnGasConsumed(0, -gas, GasInitialBalance) + defer func(startGas uint64) { + tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) tracer.CaptureExit(ret, startGas-leftOverGas, err) }(gas) } @@ -290,6 +302,10 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } } @@ -309,7 +325,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by parent := caller.(*Contract) // DELEGATECALL inherits value from parent call tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) + tracer.OnGasConsumed(0, -gas, GasInitialBalance) + defer func(startGas uint64) { + tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) tracer.CaptureExit(ret, startGas-leftOverGas, err) }(gas) } @@ -334,6 +353,10 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } } @@ -348,7 +371,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // Invoke tracer hooks that signal entering/exiting a call frame if tracer := evm.Config.Tracer; tracer != nil { tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) + tracer.OnGasConsumed(0, -gas, GasInitialBalance) + defer func(startGas uint64) { + tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) tracer.CaptureExit(ret, startGas-leftOverGas, err) }(gas) } @@ -390,6 +416,10 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + gas = 0 } } @@ -418,7 +448,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, }(gas) } else { tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) + tracer.OnGasConsumed(0, -gas, GasInitialBalance) + defer func(startGas uint64) { + tracer.OnGasConsumed(leftOverGas, leftOverGas, GasBuyBack) tracer.CaptureExit(ret, startGas-leftOverGas, err) }(gas) } @@ -445,6 +478,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Ensure there's no existing contract already at the designated address contractHash := evm.StateDB.GetCodeHash(address) if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { + if evm.Config.Tracer != nil { + evm.Config.Tracer.OnGasConsumed(gas, gas, GasChangeFailedExecution) + } + return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state @@ -478,7 +515,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // by the error checking condition below. if err == nil { createDataGas := uint64(len(ret)) * params.CreateDataGas - if contract.UseGas(createDataGas, evm.Config.Tracer) { + if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -491,7 +528,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { - contract.UseGas(contract.Gas, evm.Config.Tracer) + contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeFailedExecution) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 2e5fa9903c..5a718992db 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -592,7 +592,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) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -612,6 +612,11 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { @@ -635,7 +640,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation2) // reuse size int for stackvalue stackvalue := size //TODO: use uint256.Int instead of converting with toBig() @@ -652,6 +657,11 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] stackvalue.SetBytes(addr.Bytes()) } scope.Stack.push(&stackvalue) + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas if suberr == ErrExecutionReverted { @@ -697,6 +707,11 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -732,6 +747,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -760,6 +780,11 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret @@ -788,6 +813,11 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + + if interpreter.evm.Config.Tracer != nil { + interpreter.evm.Config.Tracer.OnGasConsumed(scope.Contract.Gas, -returnGas, GasChangeCallLeftOverRefunded) + } + scope.Contract.Gas += returnGas interpreter.returnData = ret diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 14aa9cee21..31ad6d142b 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -185,7 +185,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( } else if sLen > operation.maxStack { return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} } - if !contract.UseGas(cost, in.evm.Config.Tracer) { + if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeOpCode) { return nil, ErrOutOfGas } if operation.dynamicGas != nil { @@ -211,7 +211,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( var dynamicCost uint64 dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) cost += dynamicCost // for tracing - if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeOpCode) { return nil, ErrOutOfGas } // Do tracing before memory expansion diff --git a/core/vm/logger.go b/core/vm/logger.go index 0408411500..1f2469777d 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -43,5 +43,52 @@ type EVMLogger interface { CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) CaptureKeccakPreimage(hash common.Hash, data []byte) // Misc - OnGasConsumed(gas, amount uint64) + OnGasConsumed(gas, amount uint64, reason GasChangeReason) } + +// GasChangeReason is used to indicate the reason for a gas change, useful +// for tracing and reporting. +type GasChangeReason byte + +const ( + GasChangeUnspecified GasChangeReason = iota + + // GasInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call + GasInitialBalance + // GasRefunded is the amount of gas that will be refunded to the caller for data returned to the chain + // PR Review: Is that the right description? Called in core/state_transition.go#StateTransition.refundGas + GasRefunded + // GasBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller + GasBuyBack + // GasChangeIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is + // always exactly one of those per transaction + GasChangeIntrinsicGas + + // PR Review: `GasChangeContractCreation/2` are actually the EIP150 burn cost of CREATE/CREATE2 respectively. + // I think our old name `GasChangeContractCreation/2` is not really accurate. I don't + // think that using EIP150 as a name is a good idea as rules can change in the future. So + // maybe we can just call them `GasChangeCreateBurn/GasChangeCreate2Burn`? Burn might + // feels like the wrong term, `Stipend` came to mind also but I'm unsure. + // + // I would like also to keep the distinction between CREATE and CREATE2 however, it's an + // important thing IMO as they are different and could use different rules in the future. + + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules + GasChangeContractCreation + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules + GasChangeContractCreation2 + // GasChangeCodeStorage is the amount of gas that will be charged for code storage + GasChangeCodeStorage + // GasChangeOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was + // performed can be check by `CaptureState` handling + GasChangeOpCode + // GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution + GasChangePrecompiledContract + // GasChangeStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules + GasChangeStorageColdAccess + + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if there is left over at the end of execution + GasChangeCallLeftOverRefunded + // GasChangeFailedExecution is the burning of the remaining gas when the execution failed without a revert + GasChangeFailedExecution +) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 6dcd02bc05..00f46395fe 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -169,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc { evm.StateDB.AddAddressToAccessList(addr) // Charge the remaining difference here already, to correctly calculate available // gas for call - if !contract.UseGas(coldCost, evm.Config.Tracer) { + if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeStorageColdAccess) { return 0, ErrOutOfGas } } diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index ff7715f2ee..9c5e1d85c0 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -163,7 +163,7 @@ func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, func (*AccessListTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (*AccessListTracer) OnGasConsumed(gas, amount uint64) {} +func (*AccessListTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {} diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index 5885782474..a653b57f7b 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -219,7 +219,7 @@ func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, s // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (l *StructLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (l *StructLogger) OnGasConsumed(gas, amount uint64) {} +func (l *StructLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} // CaptureEnd is called after the call finishes to finalize the tracing. func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { @@ -410,7 +410,7 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope func (t *mdLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (t *mdLogger) OnGasConsumed(gas, amount uint64) {} +func (t *mdLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n", diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index c1e25a1c3a..17e1777c31 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -81,7 +81,7 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (l *JSONLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -func (l *JSONLogger) OnGasConsumed(gas, amount uint64) {} +func (l *JSONLogger) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} // CaptureEnd is triggered at end of execution. func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) { diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 2dcb8f2b2d..9476ab55f0 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -96,9 +96,9 @@ func (t *muxTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) { } // CaptureGasConsumed is called when gas is consumed. -func (t *muxTracer) OnGasConsumed(gas, amount uint64) { +func (t *muxTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) { for _, t := range t.tracers { - t.OnGasConsumed(gas, amount) + t.OnGasConsumed(gas, amount, reason) } } diff --git a/eth/tracers/noop.go b/eth/tracers/noop.go index 695c99c84b..1e6ba04dd5 100644 --- a/eth/tracers/noop.go +++ b/eth/tracers/noop.go @@ -59,7 +59,7 @@ func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ * func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} // OnGasConsumed is called when gas is consumed. -func (t *NoopTracer) OnGasConsumed(gas, amount uint64) {} +func (t *NoopTracer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) {} // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). func (t *NoopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { diff --git a/eth/tracers/printer.go b/eth/tracers/printer.go index ff46326e9c..4122d1bc05 100644 --- a/eth/tracers/printer.go +++ b/eth/tracers/printer.go @@ -120,6 +120,6 @@ func (p *Printer) OnNewAccount(a common.Address) { fmt.Printf("OnNewAccount: a=%v\n", a) } -func (p *Printer) OnGasConsumed(gas, amount uint64) { +func (p *Printer) OnGasConsumed(gas, amount uint64, reason vm.GasChangeReason) { fmt.Printf("OnGasConsumed: gas=%v, amount=%v\n", gas, amount) }