Normalized GasChange constats, emit Refund/BuyBack only if doing something, updated comments

This commit is contained in:
Matthieu Vachon 2023-09-01 09:32:19 -04:00
parent 01ccd7126e
commit aad080db33
7 changed files with 62 additions and 62 deletions

View file

@ -265,7 +265,7 @@ func (st *StateTransition) buyGas() error {
} }
if st.evm.Config.Tracer != nil { 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 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) return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
} }
if t := st.evm.Config.Tracer; t != nil { 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 st.gasRemaining -= gas
@ -458,8 +458,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
refund = st.state.GetRefund() refund = st.state.GetRefund()
} }
if st.evm.Config.Tracer != nil { if st.evm.Config.Tracer != nil && refund > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasRefunded) st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds)
} }
st.gasRemaining += refund 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) remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice)
st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund) st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund)
if st.evm.Config.Tracer != nil { if st.evm.Config.Tracer != nil && st.gasRemaining > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasBuyBack) st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxBuyBack)
} }
// Also return remaining gas to the block gas counter so it is // Also return remaining gas to the block gas counter so it is

View file

@ -174,7 +174,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin
return nil, 0, ErrOutOfGas return nil, 0, ErrOutOfGas
} }
if logger != nil { if logger != nil {
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangePrecompiledContract) logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract)
} }
suppliedGas -= gasCost suppliedGas -= gasCost
output, err := p.Run(input) output, err := p.Run(input)

View file

@ -184,9 +184,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
} else { } else {
// Handle tracer events for entering and exiting a call frame // Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value) 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) { 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) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
} }
@ -236,7 +236,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
} }
gas = 0 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value) 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) { 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) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
} }
@ -294,7 +294,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
} }
gas = 0 gas = 0
@ -316,9 +316,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
parent := caller.(*Contract) parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call // DELEGATECALL inherits value from parent call
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value) 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) { 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) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
} }
@ -343,7 +343,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
} }
gas = 0 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, 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) { 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) evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas) }(gas)
} }
@ -404,7 +404,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
if evm.Config.Tracer != nil { if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution) evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
} }
gas = 0 gas = 0
@ -435,9 +435,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}() }()
} else { } else {
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value) 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() { defer func() {
evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasBuyBack) evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasChangeTxBuyBack)
evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err) 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) contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) { if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
if evm.Config.Tracer != nil { 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 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. // by the error checking condition below.
if err == nil { if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas 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) evm.StateDB.SetCode(address, ret)
} else { } else {
err = ErrCodeStoreOutOfGas err = ErrCodeStoreOutOfGas
@ -513,7 +513,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) { if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted { if err != ErrExecutionReverted {
contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeFailedExecution) contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution)
} }
} }

View file

@ -592,7 +592,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// reuse size int for stackvalue // reuse size int for stackvalue
stackvalue := size 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() //TODO: use uint256.Int instead of converting with toBig()
var bigVal = big0 var bigVal = big0
if !value.IsZero() { if !value.IsZero() {
@ -640,7 +640,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
) )
// Apply EIP150 // Apply EIP150
gas -= gas / 64 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 // reuse size int for stackvalue
stackvalue := size stackvalue := size
//TODO: use uint256.Int instead of converting with toBig() //TODO: use uint256.Int instead of converting with toBig()

View file

@ -185,7 +185,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} else if sLen > operation.maxStack { } else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: 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 return nil, ErrOutOfGas
} }
if operation.dynamicGas != nil { if operation.dynamicGas != nil {
@ -211,7 +211,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
var dynamicCost uint64 var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize) dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
cost += dynamicCost // for tracing 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 return nil, ErrOutOfGas
} }
// Do tracing before memory expansion // Do tracing before memory expansion

View file

@ -48,47 +48,47 @@ type EVMLogger interface {
// GasChangeReason is used to indicate the reason for a gas change, useful // GasChangeReason is used to indicate the reason for a gas change, useful
// for tracing and reporting. // 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 type GasChangeReason byte
const ( const (
GasChangeUnspecified GasChangeReason = iota GasChangeUnspecified GasChangeReason = iota
// GasInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call // GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
GasInitialBalance // one such gas change per transaction.
// GasRefunded is the amount of gas that will be refunded to the caller for data returned to the chain GasChangeTxInitialBalance
// PR Review: Is that the right description? Called in core/state_transition.go#StateTransition.refundGas // GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
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 // 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. // GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules
// I think our old name `GasChangeContractCreation/2` is not really accurate. I don't GasChangeCallContractCreation
// 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 // GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules
GasChangeContractCreation2 GasChangeCallContractCreation2
// GasChangeCodeStorage is the amount of gas that will be charged for code storage // GasChangeCallCodeStorage is the amount of gas that will be charged for code storage
GasChangeCodeStorage GasChangeCallCodeStorage
// GasChangeOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was // 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 // performed can be check by `CaptureState` handling
GasChangeOpCode GasChangeCallOpCode
// GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution // GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution
GasChangePrecompiledContract GasChangeCallPrecompiledContract
// GasChangeStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules // GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules
GasChangeStorageColdAccess GasChangeCallStorageColdAccess
// GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if
// 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 // 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 GasChangeCallLeftOverRefunded
// GasChangeFailedExecution is the burning of the remaining gas when the execution failed without a revert // GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert
GasChangeFailedExecution GasChangeCallFailedExecution
) )

View file

@ -169,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
evm.StateDB.AddAddressToAccessList(addr) evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available // Charge the remaining difference here already, to correctly calculate available
// gas for call // gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeStorageColdAccess) { if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas return 0, ErrOutOfGas
} }
} }