core/vm: reverting a bunch of slop from the ai

This commit is contained in:
Marius van der Wijden 2026-03-09 18:43:59 +01:00
parent 22738576fc
commit 88d7fff06a
6 changed files with 54 additions and 247 deletions

View file

@ -36,6 +36,7 @@ type GasPool struct {
// NewGasPool initializes the gasPool with the given amount.
func NewGasPool(amount uint64) *GasPool {
fmt.Printf("DEBUG_POOL NewGasPool: amount=%d\n", amount)
return &GasPool{
remaining: amount,
initial: amount,
@ -45,6 +46,7 @@ func NewGasPool(amount uint64) *GasPool {
// SubGas deducts the given amount from the pool if enough gas is
// available and returns an error otherwise.
func (gp *GasPool) SubGas(amount uint64) error {
fmt.Printf("DEBUG_POOL SubGas: before_remaining=%d amount=%d initial=%d\n", gp.remaining, amount, gp.initial)
if gp.remaining < amount {
return ErrGasLimitReached
}
@ -55,6 +57,8 @@ func (gp *GasPool) SubGas(amount uint64) error {
// ReturnGas adds the refunded gas back to the pool and updates
// the cumulative gas usage accordingly.
func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
fmt.Printf("DEBUG_POOL ReturnGas: before_remaining=%d returned=%d gasUsed=%d initial=%d cumR=%d cumS=%d\n",
gp.remaining, returned, gasUsed, gp.initial, gp.cumulativeRegular, gp.cumulativeState)
if gp.remaining > math.MaxUint64-returned {
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
}
@ -73,38 +77,6 @@ func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
return nil
}
// ReturnGasAmsterdam returns unused gas to the pool using two-dimensional
// gas accounting (EIP-8037). receiptGasUsed is the per-tx gas the user pays
// (tx.gas - gas_left - state_gas_reservoir), used for receipt cumulative tracking.
//
// The block gas increment for this tx is the change in max(sumR, sumS) after
// adding the tx's per-dimension values, NOT max(txR, txS) per-tx. This avoids
// overcounting when the dominant dimension switches between transactions.
func (gp *GasPool) ReturnGasAmsterdam(txGasLimit, txRegular, txState, receiptGasUsed uint64) error {
oldUsed := max(gp.cumulativeRegular, gp.cumulativeState)
gp.cumulativeRegular += txRegular
gp.cumulativeState += txState
gp.cumulativeUsed += receiptGasUsed
newUsed := max(gp.cumulativeRegular, gp.cumulativeState)
blockGasIncrement := newUsed - oldUsed
if blockGasIncrement > txGasLimit {
// State gas via reservoir model can push the dimensional cost above
// the tx gas limit. Consume the excess from the pool.
excess := blockGasIncrement - txGasLimit
if gp.remaining < excess {
return fmt.Errorf("%w: remaining: %d, excess: %d", ErrGasLimitReached, gp.remaining, excess)
}
gp.remaining -= excess
} else {
returned := txGasLimit - blockGasIncrement
if gp.remaining > math.MaxUint64-returned {
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
}
gp.remaining += returned
}
return nil
}
// Gas returns the amount of gas remaining in the pool.
func (gp *GasPool) Gas() uint64 {
return gp.remaining
@ -120,6 +92,8 @@ func (gp *GasPool) CumulativeUsed() uint64 {
// Used returns the amount of consumed gas. For Amsterdam blocks with
// 2D gas accounting (EIP-8037), returns max(sum_regular, sum_state).
func (gp *GasPool) Used() uint64 {
fmt.Printf("DEBUG_POOL Used: initial=%d remaining=%d cumR=%d cumS=%d cumUsed=%d\n",
gp.initial, gp.remaining, gp.cumulativeRegular, gp.cumulativeState, gp.cumulativeUsed)
if gp.cumulativeRegular > 0 || gp.cumulativeState > 0 {
return max(gp.cumulativeRegular, gp.cumulativeState)
}

View file

@ -472,7 +472,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
if err != nil {
return nil, err
}
var execGasStart vm.GasCosts
if rules.IsAmsterdam {
// EIP-8037: total intrinsic must fit within the transaction gas limit.
if msg.GasLimit < gas.Sum() {
@ -482,7 +481,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
executionGas := msg.GasLimit - gas.Sum()
regularGas := min(params.MaxTxGas-gas.RegularGas, executionGas)
st.gasRemaining = vm.GasCosts{RegularGas: regularGas, StateGas: executionGas - regularGas}
execGasStart = st.gasRemaining
} else {
if st.gasRemaining.Underflow(gas) {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining.RegularGas, gas.RegularGas)
@ -572,11 +570,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// gas allowance required to complete execution.
peakGasUsed := st.gasUsed()
var txRegular, txState uint64
if rules.IsAmsterdam {
txRegular, txState = st.blockGasUsed(gas, execGasStart)
}
// Compute refund counter, capped to a refund quotient.
st.gasRemaining.RegularGas += st.calcRefund()
if rules.IsPrague {
@ -591,50 +584,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
if peakGasUsed < floorDataGas {
peakGasUsed = floorDataGas
}
if rules.IsAmsterdam && txRegular < floorDataGas {
txRegular = floorDataGas
}
}
// Compute the receipt gas used (what the user pays) for Amsterdam before
// returning gas. Per EIP-8037:
// tx_gas_used_after_refund = tx.gas - gas_left - state_gas_reservoir - state_gas_refund
// At this point, gas_left already includes the regular refund (added above).
// The state gas refund (e.g., auth to existing accounts) is also returned to the user.
var receiptGasUsed uint64
if rules.IsAmsterdam {
receiptGasUsed = msg.GasLimit - st.gasRemaining.RegularGas - st.gasRemaining.StateGas - st.stateGasRefund
// On successful execution, refund regular gas that was consumed for
// state operations in child calls that subsequently reverted. This gas
// paid for state growth that didn't persist, so the user shouldn't pay.
// On failed execution (exceptional halt), all gas is consumed — no refund.
if vmerr == nil {
receiptGasUsed -= st.gasRemaining.RevertedStateGasSpill
}
}
// Return gas to the user
if rules.IsAmsterdam {
// In Amsterdam, return regular gas + unspent state gas reservoir + state gas refund.
gasReturn := st.gasRemaining.RegularGas + st.gasRemaining.StateGas + st.stateGasRefund
if vmerr == nil {
gasReturn += st.gasRemaining.RevertedStateGasSpill
}
remaining := uint256.NewInt(gasReturn)
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && gasReturn > 0 {
st.evm.Config.Tracer.OnGasChange(gasReturn, 0, tracing.GasChangeTxLeftOverReturned)
}
} else {
st.returnGas()
}
returned := st.returnGas()
fmt.Printf("DEBUG_GAS buyGas_deducted=%d returned=%d gasUsed=%d remR=%d remS=%d initR=%d initS=%d refund=%d stateGasRef=%d SGC=%d\n",
msg.GasLimit, returned, st.gasUsed(), st.gasRemaining.RegularGas, st.gasRemaining.StateGas, st.initialGas.RegularGas, st.initialGas.StateGas, st.state.GetRefund(), st.stateGasRefund, st.gasRemaining.StateGasCharged)
if rules.IsAmsterdam {
err = st.gp.ReturnGasAmsterdam(msg.GasLimit, txRegular, txState, receiptGasUsed)
} else {
err = st.gp.ReturnGas(st.gasRemaining.RegularGas, st.gasUsed())
}
if err != nil {
if err := st.gp.ReturnGas(returned, st.gasUsed()); err != nil {
return nil, err
}
effectiveTip := msg.GasPrice
@ -650,9 +606,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} else {
// For Amsterdam, the fee is based on what the user pays (receipt gas used).
feeGas := st.gasUsed()
if rules.IsAmsterdam {
feeGas = receiptGasUsed
}
fee := new(uint256.Int).SetUint64(feeGas)
fee.Mul(fee, effectiveTipU256)
@ -667,10 +620,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
}
}
usedGas := st.gasUsed()
if rules.IsAmsterdam {
// Per EIP-8037, receipt gas_used = tx_gas_used_after_refund (what the user pays).
usedGas = receiptGasUsed
}
return &ExecutionResult{
UsedGas: usedGas,
MaxUsedGas: peakGasUsed,
@ -768,14 +717,16 @@ func (st *stateTransition) calcRefund() uint64 {
// returnGas returns ETH for remaining gas,
// exchanged at the original rate.
func (st *stateTransition) returnGas() {
remaining := uint256.NewInt(st.gasRemaining.RegularGas)
func (st *stateTransition) returnGas() uint64 {
gas := st.gasRemaining.RegularGas + st.gasRemaining.StateGas
remaining := uint256.NewInt(gas)
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
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 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining.RegularGas, 0, tracing.GasChangeTxLeftOverReturned)
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && gas > 0 {
st.evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeTxLeftOverReturned)
}
return gas
}
// gasUsed returns the amount of gas used up by the state transition.
@ -785,24 +736,6 @@ func (st *stateTransition) gasUsed() uint64 {
(st.gasRemaining.RegularGas + st.gasRemaining.StateGas)
}
// blockGasUsed returns the per-transaction (regular, state) gas used for
// block-level 2D accounting (EIP-8037).
func (st *stateTransition) blockGasUsed(intrinsicGas, execGasStart vm.GasCosts) (uint64, uint64) {
totalExecUsed := (execGasStart.RegularGas + execGasStart.StateGas) -
(st.gasRemaining.RegularGas + st.gasRemaining.StateGas)
execStateUsed := st.gasRemaining.TotalStateGasCharged
// Exclude state gas that was charged from regular gas but then reverted.
// This gas was consumed from the regular pool but was for state operations
// that didn't persist, so it shouldn't count in either dimension for block
// accounting (invisible to the block). This matches nethermind's approach.
execRegularUsed := totalExecUsed - execStateUsed - st.gasRemaining.RevertedStateGasSpill - st.gasRemaining.CollisionConsumedGas
txRegular := intrinsicGas.RegularGas + execRegularUsed
txState := intrinsicGas.StateGas + execStateUsed - st.stateGasRefund
return txRegular, txState
}
// blobGasUsed returns the amount of blob gas used by the message.
func (st *stateTransition) blobGasUsed() uint64 {
return uint64(len(st.msg.BlobHashes) * params.BlobTxBlobGasPerBlob)

View file

@ -145,7 +145,16 @@ func (c *Contract) RefundGas(gas GasCosts, logger *tracing.Hooks, reason tracing
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
logger.OnGasChange(c.Gas.RegularGas, c.Gas.RegularGas+gas.RegularGas, reason)
}
c.Gas.Add(gas)
c.Gas.RegularGas += gas.RegularGas
c.Gas.StateGas = gas.StateGas
/*
if c.Gas.StateGas < gas.StateGasCharged {
// We overcharged StateGas, during reverts we need to add back to regular gas
missing := c.Gas.StateGasCharged - c.Gas.StateGas
c.Gas.RegularGas += missing
}
*/
c.Gas.StateGasCharged += gas.StateGasCharged
}
// Address returns the contracts address

View file

@ -257,8 +257,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
return nil, gas, ErrInsufficientBalance
}
snapshot := evm.StateDB.Snapshot()
savedTotalStateGas := gas.TotalStateGasCharged
savedStateGas := gas.StateGas
stateGas := gas.StateGas
p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) {
@ -323,13 +322,8 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
}
gas.RegularGas = 0
}
// State changes are rolled back on any error, so state gas charges
// from the reverted execution should not count toward block accounting.
// Also restore the state gas reservoir since state creation was undone.
gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert)
// TODO: consider clearing up unused snapshots:
//} else {
// evm.StateDB.DiscardSnapshot(snapshot)
gas.StateGasCharged = 0
gas.StateGas = stateGas
}
return ret, gas, err
}
@ -361,8 +355,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
return nil, gas, ErrInsufficientBalance
}
var snapshot = evm.StateDB.Snapshot()
savedTotalStateGas := gas.TotalStateGasCharged
savedStateGas := gas.StateGas
stateGas := gas.StateGas
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
@ -388,7 +381,8 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
}
gas.RegularGas = 0
}
gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert)
gas.StateGasCharged = 0
gas.StateGas = stateGas
}
return ret, gas, err
}
@ -412,8 +406,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
return nil, gas, ErrDepth
}
var snapshot = evm.StateDB.Snapshot()
savedTotalStateGas := gas.TotalStateGasCharged
savedStateGas := gas.StateGas
stateGas := gas.StateGas
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
@ -440,7 +433,8 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
}
gas.RegularGas = 0
}
gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert)
gas.StateGasCharged = 0
gas.StateGas = stateGas
}
return ret, gas, err
}
@ -467,8 +461,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
// We could change this, but for now it's left for legacy reasons
var snapshot = evm.StateDB.Snapshot()
savedTotalStateGas := gas.TotalStateGasCharged
savedStateGas := gas.StateGas
stateGas := gas.StateGas
// We do an AddBalance of zero here, just in order to trigger a touch.
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
@ -503,7 +496,8 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
}
gas.RegularGas = 0
}
gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert)
gas.StateGasCharged = 0
gas.StateGas = stateGas
}
return ret, gas, err
}
@ -573,8 +567,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasCosts, value *
// It might be possible the contract code is deployed to a pre-existent
// account with non-zero balance.
snapshot := evm.StateDB.Snapshot()
savedTotalStateGas := gas.TotalStateGasCharged
savedStateGas := gas.StateGas
stateGas := gas.StateGas
if !evm.StateDB.Exist(address) {
evm.StateDB.CreateAccount(address)
@ -613,26 +606,13 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasCosts, value *
ret, err = evm.initNewContract(contract, address)
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot)
isRevert := err == ErrExecutionReverted
// Post-Amsterdam code validation errors (ErrMaxCodeSizeExceeded,
// ErrInvalidCode): the init code ran and code storage gas was charged.
// State gas charges must persist for block 2D gas accounting even
// though the state is reverted. Only zero regular gas as penalty.
isCodeValidation := evm.chainRules.IsAmsterdam &&
(errors.Is(err, ErrMaxCodeSizeExceeded) || errors.Is(err, ErrInvalidCode))
if !isRevert {
if isCodeValidation {
contract.Gas.RegularGas = 0
} else {
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
}
}
if !isCodeValidation {
// State changes are rolled back, so state gas charges from the
// reverted execution should not count toward block accounting.
// Also restore the state gas reservoir since state creation was undone.
contract.Gas.RevertStateGas(savedTotalStateGas, savedStateGas, isRevert)
}
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
// State changes are rolled back, so state gas charges from the
// reverted execution should not count toward block accounting.
// Also restore the state gas reservoir since state creation was undone.
contract.Gas.StateGasCharged = 0
contract.Gas.StateGas = stateGas
}
return ret, address, contract.Gas, err
}

View file

@ -6,25 +6,8 @@ type GasCosts struct {
RegularGas uint64
StateGas uint64
// TotalStateGasCharged tracks the cumulative state gas charged during
// execution, including gas that spilled from the reservoir to regular gas.
// This is needed for EIP-8037 block gas accounting where the state gas
// dimension counts ALL state creation charges, not just reservoir consumption.
TotalStateGasCharged uint64
// RevertedStateGasSpill tracks state gas that was charged from regular gas
// (spilled) during execution of a call that subsequently reverted. When a
// call fails, its state changes are undone, but the regular gas was already
// consumed. Block gas accounting must exclude this amount from the regular
// gas dimension since it was for state operations that didn't persist.
// This gas is refunded to the user (invisible to both block and receipt).
RevertedStateGasSpill uint64
// CollisionConsumedGas tracks regular gas consumed on CREATE/CREATE2 address
// collision. On collision, the child's regular gas is consumed (user pays)
// but must be excluded from block regular gas accounting to preserve 2D
// block gas semantics. Unlike RevertedStateGasSpill, this is NOT refunded.
CollisionConsumedGas uint64
// StateGasCharged tracks the cumulative state gas charged during execution.
StateGasCharged uint64
}
func (g GasCosts) Max() uint64 {
@ -55,7 +38,7 @@ func (g GasCosts) Underflow(b GasCosts) bool {
// Sub doesn't check for underflows
func (g *GasCosts) Sub(b GasCosts) {
g.RegularGas -= b.RegularGas
g.TotalStateGasCharged += b.StateGas
g.StateGasCharged += b.StateGas
if b.StateGas > g.StateGas {
diff := b.StateGas - g.StateGas
g.StateGas = 0
@ -69,30 +52,7 @@ func (g *GasCosts) Sub(b GasCosts) {
func (g *GasCosts) Add(b GasCosts) {
g.RegularGas += b.RegularGas
g.StateGas += b.StateGas
g.TotalStateGasCharged += b.TotalStateGasCharged
g.RevertedStateGasSpill += b.RevertedStateGasSpill
g.CollisionConsumedGas += b.CollisionConsumedGas
}
// RevertStateGas handles state gas accounting when a call reverts (EIP-8037).
// It computes how much state gas was charged from regular gas (spill) during the
// call, and either returns it for REVERT errors or tracks it for non-REVERT errors.
func (g *GasCosts) RevertStateGas(savedTotalStateGas, savedStateGas uint64, isRevert bool) {
chargedDuringCall := g.TotalStateGasCharged - savedTotalStateGas
fromReservoir := savedStateGas - g.StateGas
spilledFromRegular := chargedDuringCall - fromReservoir
if isRevert {
// REVERT: return the spilled state gas to regular gas since the caller
// keeps unused gas and state operations were undone.
g.RegularGas += spilledFromRegular
} else {
// Non-REVERT: regular gas is zeroed, but block accounting must exclude
// the spill from the regular gas dimension.
g.RevertedStateGasSpill += spilledFromRegular
}
g.TotalStateGasCharged = savedTotalStateGas
g.StateGas = savedStateGas
g.StateGasCharged += b.StateGasCharged
}
func (g GasCosts) String() string {

View file

@ -17,7 +17,6 @@
package vm
import (
"errors"
"math"
"github.com/ethereum/go-ethereum/common"
@ -674,13 +673,9 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// reuse size int for stackvalue
stackvalue := size
// Pass caller's state gas (reservoir) to child and zero it out to avoid
// double-counting when the unused portion is refunded on return.
stateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation)
res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, GasCosts{RegularGas: gas.RegularGas, StateGas: stateGas}, &value)
res, addr, returnGas, suberr := evm.Create(scope.Contract.Address(), input, GasCosts{RegularGas: gas.RegularGas, StateGas: scope.Contract.Gas.StateGas}, &value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
@ -694,22 +689,6 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
scope.Stack.push(&stackvalue)
// For inner CREATEs that fail code validation (EIP-7954 + EIP-8037):
// the state was reverted so no state growth occurred. Don't propagate
// the code storage state gas to the parent's block gas accounting.
// Restore the parent's state gas reservoir since it was not consumed.
if evm.chainRules.IsAmsterdam && (errors.Is(suberr, ErrMaxCodeSizeExceeded) || errors.Is(suberr, ErrInvalidCode)) {
returnGas.TotalStateGasCharged = 0
returnGas.RevertedStateGasSpill = 0
returnGas.StateGas = stateGas
}
// On address collision, child's regular gas is consumed (not returned).
// Track as CollisionConsumedGas so block 2D accounting is unaffected
// while the user still pays for the consumed gas (not refunded).
if evm.chainRules.IsAmsterdam && errors.Is(suberr, ErrContractAddressCollision) {
returnGas.CollisionConsumedGas += returnGas.RegularGas
returnGas.RegularGas = 0
}
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
if suberr == ErrExecutionReverted {
@ -744,14 +723,10 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
}
// Pass caller's state gas (reservoir) to child and zero it out to avoid
// double-counting when the unused portion is refunded on return.
stateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
scope.Contract.UseGas(GasCosts{RegularGas: gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
// reuse size int for stackvalue
stackvalue := size
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, GasCosts{RegularGas: gas.RegularGas, StateGas: stateGas},
res, addr, returnGas, suberr := evm.Create2(scope.Contract.Address(), input, GasCosts{RegularGas: gas.RegularGas, StateGas: scope.Contract.Gas.StateGas},
&endowment, &salt)
// Push item on the stack based on the returned error.
if suberr != nil {
@ -761,22 +736,6 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
scope.Stack.push(&stackvalue)
// For inner CREATEs that fail code validation (EIP-7954 + EIP-8037):
// the state was reverted so no state growth occurred. Don't propagate
// the code storage state gas to the parent's block gas accounting.
// Restore the parent's state gas reservoir since it was not consumed.
if evm.chainRules.IsAmsterdam && (errors.Is(suberr, ErrMaxCodeSizeExceeded) || errors.Is(suberr, ErrInvalidCode)) {
returnGas.TotalStateGasCharged = 0
returnGas.RevertedStateGasSpill = 0
returnGas.StateGas = stateGas
}
// On address collision, child's regular gas is consumed (not returned).
// Track as CollisionConsumedGas so block 2D accounting is unaffected
// while the user still pays for the consumed gas (not refunded).
if evm.chainRules.IsAmsterdam && errors.Is(suberr, ErrContractAddressCollision) {
returnGas.CollisionConsumedGas += returnGas.RegularGas
returnGas.RegularGas = 0
}
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
if suberr == ErrExecutionReverted {
@ -805,11 +764,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if !value.IsZero() {
gas += params.CallStipend
}
// Pass caller's state gas (reservoir) to callee and zero it out to avoid
// double-counting when the unused portion is refunded on return.
stateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: stateGas}, &value)
ret, returnGas, err := evm.Call(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: scope.Contract.Gas.StateGas}, &value)
if err != nil {
temp.Clear()
@ -843,9 +798,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
gas += params.CallStipend
}
stateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: stateGas}, &value)
ret, returnGas, err := evm.CallCode(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: scope.Contract.Gas.StateGas}, &value)
if err != nil {
temp.Clear()
} else {
@ -875,7 +828,6 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
stateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
ret, returnGas, err := evm.DelegateCall(scope.Contract.Caller(), scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: stateGas}, scope.Contract.value)
if err != nil {
temp.Clear()
@ -906,7 +858,6 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
stateGas := scope.Contract.Gas.StateGas
scope.Contract.Gas.StateGas = 0
ret, returnGas, err := evm.StaticCall(scope.Contract.Address(), toAddr, args, GasCosts{RegularGas: gas, StateGas: stateGas})
if err != nil {
temp.Clear()