mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-03-21 02:18:07 +00:00
core/vm: reverting a bunch of slop from the ai
This commit is contained in:
parent
22738576fc
commit
88d7fff06a
6 changed files with 54 additions and 247 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue