From aad080db33f4458d0a4d2993a1274cc12019ead2 Mon Sep 17 00:00:00 2001 From: Matthieu Vachon Date: Fri, 1 Sep 2023 09:32:19 -0400 Subject: [PATCH] Normalized `GasChange` constats, emit `Refund/BuyBack` only if doing something, updated comments --- core/state_transition.go | 12 +++---- core/vm/contracts.go | 2 +- core/vm/evm.go | 34 ++++++++++---------- core/vm/instructions.go | 4 +-- core/vm/interpreter.go | 4 +-- core/vm/logger.go | 66 +++++++++++++++++++-------------------- core/vm/operations_acl.go | 2 +- 7 files changed, 62 insertions(+), 62 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 99e04a7fbb..79b076ab88 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -265,7 +265,7 @@ func (st *StateTransition) buyGas() error { } if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasInitialBalance) + st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance) } st.gasRemaining += st.msg.GasLimit @@ -391,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.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeIntrinsicGas) + t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas) } st.gasRemaining -= gas @@ -458,8 +458,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { refund = st.state.GetRefund() } - if st.evm.Config.Tracer != nil { - st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasRefunded) + if st.evm.Config.Tracer != nil && refund > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds) } st.gasRemaining += refund @@ -468,8 +468,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) { 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.OnGasChange(st.gasRemaining, 0, vm.GasBuyBack) + if st.evm.Config.Tracer != nil && st.gasRemaining > 0 { + st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxBuyBack) } // Also return remaining gas to the block gas counter so it is diff --git a/core/vm/contracts.go b/core/vm/contracts.go index 9f7b4e44d4..7c1cf888bb 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.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangePrecompiledContract) + 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 15addfa6ab..8266ed3aa9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -184,9 +184,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -236,7 +236,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -259,9 +259,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, // 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -294,7 +294,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -316,9 +316,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -343,7 +343,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -360,9 +360,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // 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, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func(startGas uint64) { - evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err) }(gas) } @@ -404,7 +404,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte evm.StateDB.RevertToSnapshot(snapshot) if err != ErrExecutionReverted { if evm.Config.Tracer != nil { - evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } gas = 0 @@ -435,9 +435,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, }() } else { evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) - evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance) + evm.Config.Tracer.OnGasChange(0, gas, GasChangeTxInitialBalance) defer func() { - evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasBuyBack) + evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasChangeTxBuyBack) evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) }() } @@ -464,7 +464,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, 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, GasChangeFailedExecution) + evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution) } return nil, common.Address{}, 0, ErrContractAddressCollision @@ -500,7 +500,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, GasChangeCodeStorage) { + if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCallCodeStorage) { evm.StateDB.SetCode(address, ret) } else { err = ErrCodeStoreOutOfGas @@ -513,7 +513,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, GasChangeFailedExecution) + contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution) } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0d5b253951..1146856cb0 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, GasChangeContractCreation) + scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation) //TODO: use uint256.Int instead of converting with toBig() var bigVal = big0 if !value.IsZero() { @@ -640,7 +640,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ) // Apply EIP150 gas -= gas / 64 - scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation2) + 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() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 31ad6d142b..b02011fa0f 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, GasChangeOpCode) { + if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeCallOpCode) { 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, GasChangeOpCode) { + if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeCallOpCode) { return nil, ErrOutOfGas } // Do tracing before memory expansion diff --git a/core/vm/logger.go b/core/vm/logger.go index 70da7ba9f1..bd3d2723ac 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -48,47 +48,47 @@ type EVMLogger interface { // 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 - // 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 + // 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 - GasChangeIntrinsicGas + 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. + 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 - // 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 + // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules + GasChangeCallContractCreation // 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 + 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 - 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 + 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 + // 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 call's execution. This can change can happen multiple times within a single transaction as + // each call is independent of each other. GasChangeCallLeftOverRefunded - // GasChangeFailedExecution is the burning of the remaining gas when the execution failed without a revert - GasChangeFailedExecution + // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert + GasChangeCallFailedExecution ) diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 00f46395fe..55c9bf34b9 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, GasChangeStorageColdAccess) { + if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) { return 0, ErrOutOfGas } }