mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-15 12:36:48 +00:00
core: introduce GasChangeHook v2 (#34946)
This PR introduces OnGasChangeV2 tracing hook, as the pre-requisite for landing EIP-8037. --------- Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com>
This commit is contained in:
parent
21c5a287f9
commit
0494cdce23
10 changed files with 164 additions and 63 deletions
|
|
@ -420,8 +420,10 @@ func (st *stateTransition) buyGas() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
|
if st.evm.Config.Tracer.HasGasHook() {
|
||||||
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, tracing.GasChangeTxInitialBalance)
|
empty := vm.GasBudget{}
|
||||||
|
initial := vm.NewGasBudget(st.msg.GasLimit)
|
||||||
|
st.evm.Config.Tracer.EmitGasChange(empty.AsTracing(), initial.AsTracing(), tracing.GasChangeTxInitialBalance)
|
||||||
}
|
}
|
||||||
st.gasRemaining = vm.NewGasBudget(st.msg.GasLimit)
|
st.gasRemaining = vm.NewGasBudget(st.msg.GasLimit)
|
||||||
st.initialBudget = st.gasRemaining.Copy()
|
st.initialBudget = st.gasRemaining.Copy()
|
||||||
|
|
@ -566,8 +568,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
if !sufficient {
|
if !sufficient {
|
||||||
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
|
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, cost.RegularGas)
|
||||||
}
|
}
|
||||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
if st.evm.Config.Tracer.HasGasHook() {
|
||||||
t.OnGasChange(prior, st.gasRemaining.RegularGas, tracing.GasChangeTxIntrinsicGas)
|
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxIntrinsicGas)
|
||||||
}
|
}
|
||||||
// Gas limit suffices for the floor data cost (EIP-7623)
|
// Gas limit suffices for the floor data cost (EIP-7623)
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
|
|
@ -651,8 +653,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||||
if used := st.gasUsed(); used < floorDataGas {
|
if used := st.gasUsed(); used < floorDataGas {
|
||||||
prior, _ := st.gasRemaining.Charge(vm.GasCosts{RegularGas: floorDataGas - used})
|
prior, _ := st.gasRemaining.Charge(vm.GasCosts{RegularGas: floorDataGas - used})
|
||||||
if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil {
|
if st.evm.Config.Tracer.HasGasHook() {
|
||||||
t.OnGasChange(prior, st.gasRemaining.RegularGas, tracing.GasChangeTxDataFloor)
|
st.evm.Config.Tracer.EmitGasChange(prior.AsTracing(), st.gasRemaining.AsTracing(), tracing.GasChangeTxDataFloor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if peakGasUsed < floorDataGas {
|
if peakGasUsed < floorDataGas {
|
||||||
|
|
@ -780,8 +782,11 @@ func (st *stateTransition) calcRefund() vm.GasBudget {
|
||||||
if refund > st.state.GetRefund() {
|
if refund > st.state.GetRefund() {
|
||||||
refund = st.state.GetRefund()
|
refund = st.state.GetRefund()
|
||||||
}
|
}
|
||||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && refund > 0 {
|
if refund > 0 && st.evm.Config.Tracer.HasGasHook() {
|
||||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, st.gasRemaining.RegularGas+refund, tracing.GasChangeTxRefunds)
|
after := st.gasRemaining
|
||||||
|
after.RegularGas += refund
|
||||||
|
|
||||||
|
st.evm.Config.Tracer.EmitGasChange(st.gasRemaining.AsTracing(), after.AsTracing(), tracing.GasChangeTxRefunds)
|
||||||
}
|
}
|
||||||
return vm.NewGasBudget(refund)
|
return vm.NewGasBudget(refund)
|
||||||
}
|
}
|
||||||
|
|
@ -793,8 +798,10 @@ func (st *stateTransition) returnGas() {
|
||||||
remaining.Mul(remaining, st.msg.GasPrice)
|
remaining.Mul(remaining, st.msg.GasPrice)
|
||||||
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
|
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
|
||||||
|
|
||||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining.RegularGas > 0 {
|
if st.gasRemaining.RegularGas > 0 && st.evm.Config.Tracer.HasGasHook() {
|
||||||
st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, 0, tracing.GasChangeTxLeftOverReturned)
|
after := st.gasRemaining
|
||||||
|
after.RegularGas = 0
|
||||||
|
st.evm.Config.Tracer.EmitGasChange(st.gasRemaining.AsTracing(), after.AsTracing(), tracing.GasChangeTxLeftOverReturned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -164,10 +164,36 @@ type (
|
||||||
// FaultHook is invoked when an error occurs during the execution of an opcode.
|
// FaultHook is invoked when an error occurs during the execution of an opcode.
|
||||||
FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error)
|
FaultHook = func(pc uint64, op byte, gas, cost uint64, scope OpContext, depth int, err error)
|
||||||
|
|
||||||
// GasChangeHook is invoked when the gas changes.
|
// GasChangeHook reports changes to the regular execution gas. Tracers
|
||||||
|
// that don't need visibility into the state-access gas dimension
|
||||||
|
// introduced by EIP-8037 (Amsterdam) can implement only this hook; it
|
||||||
|
// will continue to fire across the Amsterdam fork unchanged.
|
||||||
|
//
|
||||||
|
// If both this hook and GasChangeHookV2 are implemented on the same
|
||||||
|
// tracer, only V2 will be invoked. Implement exactly one to avoid
|
||||||
|
// double-counting.
|
||||||
GasChangeHook = func(old, new uint64, reason GasChangeReason)
|
GasChangeHook = func(old, new uint64, reason GasChangeReason)
|
||||||
|
|
||||||
// TODO(sina, rjl), please add GasChangeV2Hook by landing the multi-dimensional gas
|
// GasChangeHookV2 is invoked when any gas dimension changes. It is the
|
||||||
|
// multi-dimensional successor to GasChangeHook, exposing the state-access
|
||||||
|
// gas dimension introduced by EIP-8037 (Amsterdam) alongside the regular
|
||||||
|
// dimension.
|
||||||
|
//
|
||||||
|
// Compatibility:
|
||||||
|
// - Post-Amsterdam: fires for changes to either the regular or the
|
||||||
|
// state-access dimension. The non-changing dimension is passed through
|
||||||
|
// unchanged in both `old` and `new` so consumers always observe the
|
||||||
|
// complete gas vector.
|
||||||
|
// - Pre-Amsterdam: no state-access gas events occur, so the State field
|
||||||
|
// of both `old` and `new` is always zero. Tracers that register only
|
||||||
|
// V2 still receive every regular-gas change as Gas{State: 0} and
|
||||||
|
// behave identically to a V1 tracer; there is no pre-Amsterdam event
|
||||||
|
// a V2-only tracer misses.
|
||||||
|
//
|
||||||
|
// V1 and V2 coexist: when both are registered on a tracer, only V2 is
|
||||||
|
// invoked. Tracers SHOULD register at most one of the two to avoid
|
||||||
|
// double-counting.
|
||||||
|
GasChangeHookV2 = func(old, new Gas, reason GasChangeReason)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- Chain events -
|
- Chain events -
|
||||||
|
|
@ -257,6 +283,7 @@ type Hooks struct {
|
||||||
OnOpcode OpcodeHook
|
OnOpcode OpcodeHook
|
||||||
OnFault FaultHook
|
OnFault FaultHook
|
||||||
OnGasChange GasChangeHook
|
OnGasChange GasChangeHook
|
||||||
|
OnGasChangeV2 GasChangeHookV2
|
||||||
// Chain events
|
// Chain events
|
||||||
OnBlockchainInit BlockchainInitHook
|
OnBlockchainInit BlockchainInitHook
|
||||||
OnClose CloseHook
|
OnClose CloseHook
|
||||||
|
|
@ -280,6 +307,35 @@ type Hooks struct {
|
||||||
OnBlockHashRead BlockHashReadHook
|
OnBlockHashRead BlockHashReadHook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasGasHook reports whether any gas-change hook is registered. Call sites
|
||||||
|
// should use this to short-circuit before constructing the Gas / GasBudget
|
||||||
|
// arguments to EmitGasChange when tracing is off — the dispatch is otherwise
|
||||||
|
// always paid the cost of evaluating those args.
|
||||||
|
func (h *Hooks) HasGasHook() bool {
|
||||||
|
return h != nil && (h.OnGasChangeV2 != nil || h.OnGasChange != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmitGasChange dispatches a gas change event to the registered hooks. If the
|
||||||
|
// multi-dimensional OnGasChangeV2 hook is set it is invoked with the full Gas
|
||||||
|
// vectors; otherwise the single-dimensional OnGasChange hook is invoked with
|
||||||
|
// the regular-gas dimension only. The call is a no-op when the receiver is
|
||||||
|
// nil, when neither hook is registered, or when the reason is GasChangeIgnored.
|
||||||
|
//
|
||||||
|
// Call sites SHOULD use this helper instead of invoking the hooks directly so
|
||||||
|
// that both variants stay consistent across the Amsterdam fork boundary.
|
||||||
|
func (h *Hooks) EmitGasChange(old, new Gas, reason GasChangeReason) {
|
||||||
|
if h == nil || reason == GasChangeIgnored {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.OnGasChangeV2 != nil {
|
||||||
|
h.OnGasChangeV2(old, new, reason)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.OnGasChange != nil {
|
||||||
|
h.OnGasChange(old.Regular, new.Regular, reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BalanceChangeReason is used to indicate the reason for a balance change, useful
|
// BalanceChangeReason is used to indicate the reason for a balance change, useful
|
||||||
// for tracing and reporting.
|
// for tracing and reporting.
|
||||||
type BalanceChangeReason byte
|
type BalanceChangeReason byte
|
||||||
|
|
@ -335,6 +391,19 @@ const (
|
||||||
BalanceChangeRevert BalanceChangeReason = 15
|
BalanceChangeRevert BalanceChangeReason = 15
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Gas represents a multi-dimensional gas budget introduced by EIP-8037.
|
||||||
|
// It carries the regular execution gas and the state-access gas, which are
|
||||||
|
// metered independently from the Amsterdam fork onwards.
|
||||||
|
//
|
||||||
|
// Before Amsterdam, gas metering is single-dimensional and only the Regular
|
||||||
|
// field is meaningful; State is always zero. The struct is shaped so that
|
||||||
|
// pre-Amsterdam call sites can populate it as Gas{Regular: g} without loss
|
||||||
|
// of fidelity relative to the legacy single-uint64 hook.
|
||||||
|
type Gas struct {
|
||||||
|
Regular uint64 // Regular is the budget for ordinary execution gas.
|
||||||
|
State uint64 // State is the budget dedicated to state-access gas (zero pre-Amsterdam).
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,8 @@ func (c *Contract) UseGas(cost GasCosts, logger *tracing.Hooks, reason tracing.G
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
||||||
logger.OnGasChange(prior, c.Gas.RegularGas, reason)
|
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -143,8 +143,8 @@ func (c *Contract) RefundGas(refund GasBudget, logger *tracing.Hooks, reason tra
|
||||||
if !changed {
|
if !changed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
||||||
logger.OnGasChange(prior, c.Gas.RegularGas, reason)
|
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -269,8 +269,8 @@ func RunPrecompiledContract(stateDB StateDB, p PrecompiledContract, address comm
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return nil, gas, ErrOutOfGas
|
return nil, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if logger != nil && logger.OnGasChange != nil {
|
if logger.HasGasHook() {
|
||||||
logger.OnGasChange(prior, gas.RegularGas, tracing.GasChangeCallPrecompiledContract)
|
logger.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeCallPrecompiledContract)
|
||||||
}
|
}
|
||||||
// Touch the precompile for block-level accessList recording once Amsterdam
|
// Touch the precompile for block-level accessList recording once Amsterdam
|
||||||
// fork is activated.
|
// fork is activated.
|
||||||
|
|
|
||||||
|
|
@ -317,8 +317,8 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
|
|
@ -371,8 +371,8 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
|
|
@ -415,8 +415,8 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
|
|
@ -470,8 +470,8 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
|
|
@ -509,8 +509,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return nil, common.Address{}, gas, ErrOutOfGas
|
return nil, common.Address{}, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(prior, gas.RegularGas, tracing.GasChangeWitnessContractCollisionCheck)
|
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -528,8 +528,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
if evm.StateDB.GetNonce(address) != 0 ||
|
if evm.StateDB.GetNonce(address) != 0 ||
|
||||||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||||
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
|
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), tracing.Gas{}, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return nil, common.Address{}, gas, ErrContractAddressCollision
|
return nil, common.Address{}, gas, ErrContractAddressCollision
|
||||||
|
|
@ -558,8 +558,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
return nil, common.Address{}, gas, ErrOutOfGas
|
return nil, common.Address{}, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
|
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(prior, gas.RegularGas, tracing.GasChangeWitnessContractInit)
|
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractInit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
||||||
|
|
@ -673,15 +673,17 @@ func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to comm
|
||||||
if tracer.OnEnter != nil {
|
if tracer.OnEnter != nil {
|
||||||
tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
|
tracer.OnEnter(depth, byte(typ), from, to, input, startGas, value)
|
||||||
}
|
}
|
||||||
if tracer.OnGasChange != nil {
|
if tracer.HasGasHook() {
|
||||||
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
|
initial := NewGasBudget(startGas)
|
||||||
|
tracer.EmitGasChange(tracing.Gas{}, initial.AsTracing(), tracing.GasChangeCallInitialBalance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) {
|
func (evm *EVM) captureEnd(depth int, startGas uint64, leftOverGas uint64, ret []byte, err error) {
|
||||||
tracer := evm.Config.Tracer
|
tracer := evm.Config.Tracer
|
||||||
if leftOverGas != 0 && tracer.OnGasChange != nil {
|
if leftOverGas != 0 && tracer.HasGasHook() {
|
||||||
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
|
leftover := NewGasBudget(leftOverGas)
|
||||||
|
tracer.EmitGasChange(leftover.AsTracing(), tracing.Gas{}, tracing.GasChangeCallLeftOverReturned)
|
||||||
}
|
}
|
||||||
var reverted bool
|
var reverted bool
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
|
)
|
||||||
|
|
||||||
// GasCosts denotes a vector of gas costs in the
|
// GasCosts denotes a vector of gas costs in the
|
||||||
// multidimensional metering paradigm. It represents the cost
|
// multidimensional metering paradigm. It represents the cost
|
||||||
|
|
@ -77,21 +81,26 @@ func (g GasBudget) CanAfford(cost GasCosts) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Charge deducts the given gas cost from the budget. It returns the
|
// Charge deducts the given gas cost from the budget. It returns the
|
||||||
// pre-charge gas value and false if the budget does not have sufficient
|
// pre-charge budget and false if the budget does not have sufficient
|
||||||
// gas to cover the cost.
|
// gas to cover the cost.
|
||||||
func (g *GasBudget) Charge(cost GasCosts) (uint64, bool) {
|
func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
|
||||||
prior := g.RegularGas
|
prior := *g
|
||||||
if prior < cost.RegularGas {
|
if g.RegularGas < cost.RegularGas {
|
||||||
return prior, false
|
return prior, false
|
||||||
}
|
}
|
||||||
g.RegularGas -= cost.RegularGas
|
g.RegularGas -= cost.RegularGas
|
||||||
return prior, true
|
return prior, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refund adds the given gas budget back. It returns the pre-refund gas
|
// Refund adds the given gas budget back. It returns the pre-refund budget
|
||||||
// value and whether the budget was actually changed.
|
// and whether the budget was actually changed.
|
||||||
func (g *GasBudget) Refund(other GasBudget) (uint64, bool) {
|
func (g *GasBudget) Refund(other GasBudget) (GasBudget, bool) {
|
||||||
prior := g.RegularGas
|
prior := *g
|
||||||
g.RegularGas += other.RegularGas
|
g.RegularGas += other.RegularGas
|
||||||
return prior, g.RegularGas != prior
|
return prior, g.RegularGas != prior.RegularGas
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsTracing converts the GasBudget into the tracing-facing Gas vector.
|
||||||
|
func (g GasBudget) AsTracing() tracing.Gas {
|
||||||
|
return tracing.Gas{Regular: g.RegularGas, State: g.StateGas}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,8 +234,12 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte
|
||||||
|
|
||||||
// Do tracing before potential memory expansion
|
// Do tracing before potential memory expansion
|
||||||
if debug {
|
if debug {
|
||||||
if evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
|
evm.Config.Tracer.EmitGasChange(
|
||||||
|
tracing.Gas{Regular: gasCopy, State: contract.Gas.StateGas},
|
||||||
|
tracing.Gas{Regular: gasCopy - cost, State: contract.Gas.StateGas},
|
||||||
|
tracing.GasChangeCallOpCode,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer.OnOpcode != nil {
|
if evm.Config.Tracer.OnOpcode != nil {
|
||||||
evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, evm.returnData, evm.depth, VMErrorFromErr(err))
|
evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, cost, callContext, evm.returnData, evm.depth, VMErrorFromErr(err))
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
|
||||||
OnOpcode: t.OnOpcode,
|
OnOpcode: t.OnOpcode,
|
||||||
OnFault: t.OnFault,
|
OnFault: t.OnFault,
|
||||||
OnGasChange: t.OnGasChange,
|
OnGasChange: t.OnGasChange,
|
||||||
|
OnGasChangeV2: t.OnGasChangeV2,
|
||||||
OnBlockchainInit: t.OnBlockchainInit,
|
OnBlockchainInit: t.OnBlockchainInit,
|
||||||
OnBlockStart: t.OnBlockStart,
|
OnBlockStart: t.OnBlockStart,
|
||||||
OnBlockEnd: t.OnBlockEnd,
|
OnBlockEnd: t.OnBlockEnd,
|
||||||
|
|
@ -113,3 +114,6 @@ func (t *noop) OnBlockHashRead(number uint64, hash common.Hash) {}
|
||||||
|
|
||||||
func (t *noop) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
|
func (t *noop) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *noop) OnGasChangeV2(old, new tracing.Gas, reason tracing.GasChangeReason) {
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,11 @@ func newMuxTracerFromConfig(ctx *tracers.Context, cfg json.RawMessage, chainConf
|
||||||
// the aggregated JSON result returned by GetResult.
|
// the aggregated JSON result returned by GetResult.
|
||||||
//
|
//
|
||||||
// For hooks that have both a V1 and V2 form (OnCodeChange / OnCodeChangeV2,
|
// For hooks that have both a V1 and V2 form (OnCodeChange / OnCodeChangeV2,
|
||||||
// OnNonceChange / OnNonceChangeV2, OnSystemCallStart / OnSystemCallStartV2),
|
// OnNonceChange / OnNonceChangeV2, OnGasChange / OnGasChangeV2,
|
||||||
// the mux exposes only the V2 variant upward. The fanout then prefers each
|
// OnSystemCallStart / OnSystemCallStartV2), the mux exposes only the V2
|
||||||
// child's V2 hook and falls back to V1 if only V1 is set, mirroring the
|
// variant upward. The fanout then prefers each child's V2 hook and falls
|
||||||
// precedence already used in core/state_processor.go.
|
// back to V1 if only V1 is set, mirroring the precedence already used in
|
||||||
|
// core/state_processor.go.
|
||||||
func NewMuxTracer(names []string, objects []*tracers.Tracer) (*tracers.Tracer, error) {
|
func NewMuxTracer(names []string, objects []*tracers.Tracer) (*tracers.Tracer, error) {
|
||||||
t := &muxTracer{names: names, tracers: objects}
|
t := &muxTracer{names: names, tracers: objects}
|
||||||
return &tracers.Tracer{
|
return &tracers.Tracer{
|
||||||
|
|
@ -79,7 +80,7 @@ func NewMuxTracer(names []string, objects []*tracers.Tracer) (*tracers.Tracer, e
|
||||||
OnExit: t.OnExit,
|
OnExit: t.OnExit,
|
||||||
OnOpcode: t.OnOpcode,
|
OnOpcode: t.OnOpcode,
|
||||||
OnFault: t.OnFault,
|
OnFault: t.OnFault,
|
||||||
OnGasChange: t.OnGasChange,
|
OnGasChangeV2: t.OnGasChangeV2,
|
||||||
OnBalanceChange: t.OnBalanceChange,
|
OnBalanceChange: t.OnBalanceChange,
|
||||||
OnNonceChangeV2: t.OnNonceChangeV2,
|
OnNonceChangeV2: t.OnNonceChangeV2,
|
||||||
OnCodeChangeV2: t.OnCodeChangeV2,
|
OnCodeChangeV2: t.OnCodeChangeV2,
|
||||||
|
|
@ -109,10 +110,12 @@ func (t *muxTracer) OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {
|
func (t *muxTracer) OnGasChangeV2(old, new tracing.Gas, reason tracing.GasChangeReason) {
|
||||||
for _, t := range t.tracers {
|
for _, t := range t.tracers {
|
||||||
if t.OnGasChange != nil {
|
if t.OnGasChangeV2 != nil {
|
||||||
t.OnGasChange(old, new, reason)
|
t.OnGasChangeV2(old, new, reason)
|
||||||
|
} else if t.OnGasChange != nil {
|
||||||
|
t.OnGasChange(old.Regular, new.Regular, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ func newNoopTracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *param
|
||||||
OnOpcode: t.OnOpcode,
|
OnOpcode: t.OnOpcode,
|
||||||
OnFault: t.OnFault,
|
OnFault: t.OnFault,
|
||||||
OnGasChange: t.OnGasChange,
|
OnGasChange: t.OnGasChange,
|
||||||
|
OnGasChangeV2: t.OnGasChangeV2,
|
||||||
OnBalanceChange: t.OnBalanceChange,
|
OnBalanceChange: t.OnBalanceChange,
|
||||||
OnNonceChange: t.OnNonceChange,
|
OnNonceChange: t.OnNonceChange,
|
||||||
OnCodeChange: t.OnCodeChange,
|
OnCodeChange: t.OnCodeChange,
|
||||||
|
|
@ -66,6 +67,8 @@ func (t *noopTracer) OnFault(pc uint64, op byte, gas, cost uint64, _ tracing.OpC
|
||||||
|
|
||||||
func (t *noopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
|
func (t *noopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
|
||||||
|
|
||||||
|
func (t *noopTracer) OnGasChangeV2(old, new tracing.Gas, reason tracing.GasChangeReason) {}
|
||||||
|
|
||||||
func (t *noopTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (t *noopTracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue