From ff3c15ff90c005fd90d78859d43158de06398fac Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Tue, 12 Sep 2023 11:19:15 -0400 Subject: [PATCH] Full OnGasConsumed loop and added `GasChangeReason` (#16) --- core/state_transition.go | 16 +++++- core/vm/contract.go | 6 +-- core/vm/contracts.go | 2 +- core/vm/evm.go | 95 +++++++++++++++++++++++------------ core/vm/instructions.go | 34 ++++++++++++- core/vm/interpreter.go | 9 +++- core/vm/logger.go | 62 ++++++++++++++++++++++- core/vm/operations_acl.go | 2 +- eth/tracers/directory/noop.go | 4 +- eth/tracers/live/printer.go | 4 +- eth/tracers/native/mux.go | 4 +- 11 files changed, 189 insertions(+), 49 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 645fe669f2452..e82297963dd45 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.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance) + } + 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.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas) } 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 && refund > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds) + } + 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.gasRemaining > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxLeftOverReturned) + } + // 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 1d88b0e1b98bf..8a194bb12a9fd 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) + if logger != nil && reason != GasChangeIgnored { + logger.OnGasChange(c.Gas, c.Gas-gas, reason) } c.Gas -= gas return true diff --git a/core/vm/contracts.go b/core/vm/contracts.go index cb4cc76c02e91..7c1cf888bb31e 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.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract) } suppliedGas -= gasCost output, err := p.Run(input) diff --git a/core/vm/evm.go b/core/vm/evm.go index beadc501f1e0a..cf8dbb158160b 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -176,18 +176,10 @@ func (evm *EVM) SetBlockContext(blockCtx BlockContext) { func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { // Capture the tracer start/end events in debug mode if evm.Config.Tracer != nil { - if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value) - defer func(startGas uint64) { // Lazy evaluation of the parameters - evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err) - }(gas) - } else { - // Handle tracer events for entering and exiting a call frame - evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) - defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) - }(gas) - } + evm.captureBegin(evm.depth == 0, CALL, caller.Address(), addr, input, gas, value) + defer func(startGas uint64) { + evm.captureEnd(evm.depth == 0, CALL, startGas, leftOverGas, ret, err) + }(gas) } // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { @@ -233,6 +225,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.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } // TODO: consider clearing up unused snapshots: @@ -252,9 +248,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) + evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) + evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -285,6 +281,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.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } } @@ -303,9 +303,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by // that caller is something other than a Contract. parent := caller.(*Contract) // DELEGATECALL inherits value from parent call - evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) + evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value) defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) + evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -328,6 +328,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.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } } @@ -341,9 +345,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) { // Invoke tracer hooks that signal entering/exiting a call frame if evm.Config.Tracer != nil { - evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil) + evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) + evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -383,6 +387,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.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + gas = 0 } } @@ -402,19 +410,12 @@ func (c *codeAndHash) Hash() common.Hash { } // create creates a new contract using code as deployment code. -func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftoverGas uint64, err error) { +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { if evm.Config.Tracer != nil { - if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) - defer func() { - evm.Config.Tracer.CaptureEnd(ret, gas-leftoverGas, err) - }() - } else { - evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - defer func() { - evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) - }() - } + evm.captureBegin(evm.depth == 0, typ, caller.Address(), address, codeAndHash.code, gas, value) + defer func(startGas uint64) { + evm.captureEnd(evm.depth == 0, typ, startGas, leftOverGas, ret, err) + }(gas) } // Depth check execution. Fail if we're trying to execute above the // limit. @@ -437,6 +438,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.OnGasChange(gas, 0, GasChangeCallFailedExecution) + } + return nil, common.Address{}, 0, ErrContractAddressCollision } // Create a new account on the state @@ -470,7 +475,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, GasChangeCallCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -483,7 +488,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, GasChangeCallFailedExecution) } } @@ -508,3 +513,29 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment * // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } + +func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) { + tracer := evm.Config.Tracer + + if isRoot { + tracer.CaptureStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value) + } else { + tracer.CaptureEnter(typ, from, to, input, startGas, value) + } + + tracer.OnGasChange(0, startGas, GasChangeCallInitialBalance) +} + +func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) { + tracer := evm.Config.Tracer + + if leftOverGas != 0 { + tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) + } + + if isRoot { + tracer.CaptureEnd(ret, startGas-leftOverGas, err) + } else { + tracer.CaptureExit(ret, startGas-leftOverGas, err) + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 2e5fa9903cbbc..cba48aa5595e2 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, GasChangeCallContractCreation) //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 && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, 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, GasChangeCallContractCreation2) // 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 && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, 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 && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, 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 && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, 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 && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, 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 && returnGas > 0 { + interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, 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 14aa9cee2112c..82f1e187ac7b6 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -185,9 +185,10 @@ 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, GasChangeIgnored) { return nil, ErrOutOfGas } + if operation.dynamicGas != nil { // All ops with a dynamic memory usage also has a dynamic gas cost. var memorySize uint64 @@ -211,11 +212,13 @@ 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, GasChangeIgnored) { return nil, ErrOutOfGas } + // Do tracing before memory expansion if debug { + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode) in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } @@ -223,9 +226,11 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( mem.Resize(memorySize) } } else if debug { + in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, GasChangeCallOpCode) in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err) logged = true } + // execute the operation res, err = operation.execute(&pc, in, callContext) if err != nil { diff --git a/core/vm/logger.go b/core/vm/logger.go index 04084115000a6..a675563821b10 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -43,5 +43,65 @@ 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) + OnGasChange(old, new uint64, reason GasChangeReason) } + +// GasChangeReason is used to indicate the reason for a gas change, useful +// for tracing and reporting. +// +// There is essentially two types of gas changes, those that can be emitted once per transaction +// and those that can be emitted on a call basis, so possibly multiple times per transaction. +// +// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted +// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis. +type GasChangeReason byte + +const ( + GasChangeUnspecified GasChangeReason = iota + + // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per transaction. + GasChangeTxInitialBalance + // GasChangeTxIntrinsicGas 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. + GasChangeTxIntrinsicGas + // GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared) + // this generates an increase in gas. There is at most one of such gas change per transaction. + GasChangeTxRefunds + // GasChangeTxLeftOverReturned is the amount of gas left over at the end of transaction's execution that will be returned + // to the chain. This change will always be a negative change as we "drain" left over gas towards 0. If there was no gas + // left at the end of execution, no such even will be emitted. The returned gas's value in Wei is returned to caller. + // There is at most one of such gas change per transaction. + GasChangeTxLeftOverReturned + + // GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only + // one such gas change per call. + GasChangeCallInitialBalance + // GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always + // be a negative change as we "drain" left over gas towards 0. If there was no gas left at the end of execution, no such even + // will be emitted. + GasChangeCallLeftOverReturned + // GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it + // executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child. + // If there was no gas left to be refunded, no such even will be emitted. + GasChangeCallLeftOverRefunded + // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE. + GasChangeCallContractCreation + // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2. + GasChangeCallContractCreation2 + // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage. + GasChangeCallCodeStorage + // GasChangeCallOpCode 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. + GasChangeCallOpCode + // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution. + GasChangeCallPrecompiledContract + // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules. + GasChangeCallStorageColdAccess + // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert. + GasChangeCallFailedExecution + + // GasChangeIgnored is a special value that can be used to indicate that the gas change should be ignored as + // it will be "manually" tracked by a direct emit of the gas change event. + GasChangeIgnored GasChangeReason = 0xFF +) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 6dcd02bc05183..55c9bf34b9522 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, GasChangeCallStorageColdAccess) { return 0, ErrOutOfGas } } diff --git a/eth/tracers/directory/noop.go b/eth/tracers/directory/noop.go index 06902061f41c2..973b1abb4735b 100644 --- a/eth/tracers/directory/noop.go +++ b/eth/tracers/directory/noop.go @@ -58,8 +58,8 @@ func (t *NoopTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ * // CaptureKeccakPreimage is called during the KECCAK256 opcode. func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {} -// OnGasConsumed is called when gas is consumed. -func (t *NoopTracer) OnGasConsumed(gas, amount uint64) {} +// OnGasChange is called when gas is either consumed or refunded. +func (t *NoopTracer) OnGasChange(old, new 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/live/printer.go b/eth/tracers/live/printer.go index 3542c632936c3..1f51a6fc3f108 100644 --- a/eth/tracers/live/printer.go +++ b/eth/tracers/live/printer.go @@ -126,6 +126,6 @@ func (p *Printer) OnNewAccount(a common.Address) { fmt.Printf("OnNewAccount: a=%v\n", a) } -func (p *Printer) OnGasConsumed(gas, amount uint64) { - fmt.Printf("OnGasConsumed: gas=%v, amount=%v\n", gas, amount) +func (p *Printer) OnGasChange(old, new uint64, reason vm.GasChangeReason) { + fmt.Printf("OnGasChange: old=%v, new=%v, diff=%v\n", old, new, new-old) } diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index 880aca888da25..dab284a3a3186 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) OnGasChange(old, new uint64, reason vm.GasChangeReason) { for _, t := range t.tracers { - t.OnGasConsumed(gas, amount) + t.OnGasChange(old, new, reason) } }