diff --git a/core/state_transition.go b/core/state_transition.go index 79b076ab88..e82297963d 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -469,7 +469,7 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { 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.GasChangeTxBuyBack) + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxLeftOverReturned) } // Also return remaining gas to the block gas counter so it is diff --git a/core/vm/contract.go b/core/vm/contract.go index f2cdb40a4c..8a194bb12a 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -163,7 +163,7 @@ func (c *Contract) UseGas(gas uint64, logger EVMLogger, reason GasChangeReason) if c.Gas < gas { return false } - if logger != nil { + if logger != nil && reason != GasChangeIgnored { logger.OnGasChange(c.Gas, c.Gas-gas, reason) } c.Gas -= gas diff --git a/core/vm/evm.go b/core/vm/evm.go index cb8bc98838..70fdb19997 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -181,26 +181,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) - evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) - defer func(startGas uint64) { // Lazy evaluation of the parameters - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureEnd(ret, startGas-leftOverGas, err) - }(gas) - } else { - // Handle tracer events for entering and exiting a call frame - evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) - evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) - defer func(startGas uint64) { - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, 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) { @@ -269,13 +253,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.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) + evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value) defer func(startGas uint64) { - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err) + evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -328,13 +308,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.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) + evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value) defer func(startGas uint64) { - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err) + evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -374,13 +350,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.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) + evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil) defer func(startGas uint64) { - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err) + evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err) }(gas) } // Fail if we're trying to execute above the call depth limit @@ -445,25 +417,10 @@ 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) { if evm.Config.Tracer != nil { - if evm.depth == 0 { - evm.Config.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value) - evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) - defer func(startGas uint64) { - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureEnd(ret, startGas-leftOverGas, err) - }(gas) - } else { - evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance) - defer func(startGas uint64) { - if leftOverGas != 0 { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverReturned) - } - evm.Config.Tracer.CaptureExit(ret, startGas-leftOverGas, err) - }(gas) - } + 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. @@ -561,3 +518,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 1146856cb0..cba48aa559 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -613,7 +613,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Stack.push(&stackvalue) - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } @@ -658,7 +658,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } scope.Stack.push(&stackvalue) - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } @@ -708,7 +708,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } @@ -748,7 +748,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } @@ -781,7 +781,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } @@ -814,7 +814,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } - if interpreter.evm.Config.Tracer != nil { + if interpreter.evm.Config.Tracer != nil && returnGas > 0 { interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index b02011fa0f..82f1e187ac 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, GasChangeCallOpCode) { + 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, GasChangeCallOpCode) { + 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 2175c32ad2..a675563821 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -63,27 +63,31 @@ const ( // 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 + // 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 only one such gas change per transaction. + // this generates an increase in gas. There is at most one of such gas change per transaction. GasChangeTxRefunds - // GasChangeTxBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller at the very - // end of the transaction's execution. There is only one such gas change per transaction. - GasChangeTxBuyBack + // 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. + // 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, today controlled by EIP150 rules. + // 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, today controlled by EIP150 rules. + // 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 @@ -96,4 +100,8 @@ const ( 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/eth/tracers/firehose.go b/eth/tracers/firehose.go index 0358abc38c..2bfd7b38bc 100644 --- a/eth/tracers/firehose.go +++ b/eth/tracers/firehose.go @@ -782,7 +782,7 @@ func (f *Firehose) OnGasChange(old, new uint64, reason vm.GasChangeReason) { // Ref eb1916a67d9bea03df16a7a3e2cfac72 if reason == vm.GasChangeTxInitialBalance || reason == vm.GasChangeTxRefunds || - reason == vm.GasChangeTxBuyBack || + reason == vm.GasChangeTxLeftOverReturned || reason == vm.GasChangeCallInitialBalance || reason == vm.GasChangeCallLeftOverReturned { return @@ -1128,12 +1128,12 @@ var gasChangeReasonToPb = map[vm.GasChangeReason]pbeth.GasChange_Reason{ // // vm.GasChangeTxInitialBalance: pbeth.GasChange_REASON_TX_INITIAL_BALANCE, // vm.GasChangeTxRefunds: pbeth.GasChange_REASON_TX_REFUNDS, - // vm.GasChangeTxBuyBack: pbeth.GasChange_REASON_TX_BUY_BACK, + // vm.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_TX_LEFT_OVER_RETURNED, // vm.GasChangeCallInitialBalance: pbeth.GasChange_REASON_CALL_INITIAL_BALANCE, // vm.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_CALL_LEFT_OVER_RETURNED, vm.GasChangeTxInitialBalance: pbeth.GasChange_REASON_UNKNOWN, vm.GasChangeTxRefunds: pbeth.GasChange_REASON_UNKNOWN, - vm.GasChangeTxBuyBack: pbeth.GasChange_REASON_UNKNOWN, + vm.GasChangeTxLeftOverReturned: pbeth.GasChange_REASON_UNKNOWN, vm.GasChangeCallInitialBalance: pbeth.GasChange_REASON_UNKNOWN, vm.GasChangeCallLeftOverReturned: pbeth.GasChange_REASON_UNKNOWN, diff --git a/go.mod b/go.mod index e0fff233e1..30affdab44 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/rs/cors v1.7.0 github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 - github.com/streamingfast/firehose-ethereum/types v0.0.0-20230911154550-654ed552f285 + github.com/streamingfast/firehose-ethereum/types v0.0.0-20230912180331-359d900a4e1e github.com/stretchr/testify v1.8.1 github.com/supranational/blst v0.3.11-0.20230406105308-e9dfc5ee724b github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 diff --git a/go.sum b/go.sum index d82eac20b3..cabbddb1b4 100644 --- a/go.sum +++ b/go.sum @@ -679,8 +679,8 @@ github.com/streamingfast/dstore v0.1.1-0.20220607202639-35118aeaf648 h1:xpy3HNXe github.com/streamingfast/dstore v0.1.1-0.20220607202639-35118aeaf648/go.mod h1:SHSEIPowGeE1TfNNmGeAUUnlO3dwevmX5kFOSazU60M= github.com/streamingfast/eth-go v0.0.0-20220421042603-ebe2c94fcc92 h1:jMLyqSyOP0Wf1wbC0qhTW8GdFr+NM5WBwBzNc74TAk8= github.com/streamingfast/eth-go v0.0.0-20220421042603-ebe2c94fcc92/go.mod h1:GSWB6tldvuaUekmehj7yCv/8o6MIWPb+9Hf+m4uZdDM= -github.com/streamingfast/firehose-ethereum/types v0.0.0-20230911154550-654ed552f285 h1:CHi8MSyRFe4ngApM2XbR5AueTd/6pSxAvzxFhqdy08Q= -github.com/streamingfast/firehose-ethereum/types v0.0.0-20230911154550-654ed552f285/go.mod h1:Kh5fptEEMKVw/QXrdfr49ldY3SP5+/MJoUPQfUtByUU= +github.com/streamingfast/firehose-ethereum/types v0.0.0-20230912180331-359d900a4e1e h1:H7Re+AlOEonzy9/KxHa0dj6oswXjCDU8eODHG337jUU= +github.com/streamingfast/firehose-ethereum/types v0.0.0-20230912180331-359d900a4e1e/go.mod h1:Kh5fptEEMKVw/QXrdfr49ldY3SP5+/MJoUPQfUtByUU= github.com/streamingfast/jsonpb v0.0.0-20210811021341-3670f0aa02d0 h1:g8eEYbFSykyzIyuxNMmHEUGGUvJE0ivmqZagLDK42gw= github.com/streamingfast/jsonpb v0.0.0-20210811021341-3670f0aa02d0/go.mod h1:cTNObq2Uofb330y05JbbZZ6RwE6QUXw5iVcHk1Fx3fk= github.com/streamingfast/logging v0.0.0-20210811175431-f3b44b61606a/go.mod h1:4GdqELhZOXj4xwc4IaBmzofzdErGynnaSzuzxy0ZIBo=