core: apply fixes for 8037

This commit is contained in:
Marius van der Wijden 2026-04-22 19:24:44 +02:00 committed by MariusVanDerWijden
parent fa45d1a38f
commit a91cb20ae9
4 changed files with 45 additions and 28 deletions

View file

@ -609,14 +609,16 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// On outer level tx failure, no state is written.
if rules.IsAmsterdam && vmerr != nil {
st.gasRemaining.StateGas += execGasUsed.StateGas
if execGasUsed.StateGas > 0 {
st.gasRemaining.StateGas += uint64(execGasUsed.StateGas)
}
execGasUsed.StateGas = 0
}
// Refund costs for selfdestructed accounts and slots.
if rules.IsAmsterdam && vmerr == nil {
cpsb := st.evm.Context.CostPerGasByte
stateGasUsed := execGasUsed.StateGas + cost.StateGas
stateGasUsed := int64(cost.StateGas) + execGasUsed.StateGas
var sdRefund uint64
for _, addr := range st.state.SameTxSelfDestructs() {
r := params.AccountCreationSize * cpsb
@ -624,15 +626,22 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
r += uint64(st.state.GetCodeSize(addr)) * cpsb
sdRefund += r
}
if sdRefund > stateGasUsed {
sdRefund = stateGasUsed
if stateGasUsed < 0 {
sdRefund = 0
} else if sdRefund > uint64(stateGasUsed) {
sdRefund = uint64(stateGasUsed)
}
if sdRefund > 0 {
st.gasRemaining.StateGas += sdRefund
if execGasUsed.StateGas >= sdRefund {
execGasUsed.StateGas -= sdRefund
if execGasUsed.StateGas >= int64(sdRefund) {
execGasUsed.StateGas -= int64(sdRefund)
} else {
extra := sdRefund - execGasUsed.StateGas
var extra uint64
if execGasUsed.StateGas > 0 {
extra = sdRefund - uint64(execGasUsed.StateGas)
} else {
extra = sdRefund
}
execGasUsed.StateGas = 0
if cost.StateGas >= extra {
cost.StateGas -= extra
@ -673,7 +682,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// EIP-8037: 2D gas accounting for Amsterdam.
// tx_regular = intrinsic_regular + exec_regular_gas_used
// tx_state = intrinsic_state (adjusted) + exec_state_gas_used
txState := cost.StateGas + execGasUsed.StateGas
// execGasUsed.StateGas may be negative when an SSTORE 0→x→0 refund
// exceeded the intrinsic-charged state gas
txState := cost.StateGas
if execGasUsed.StateGas > 0 {
txState += uint64(execGasUsed.StateGas)
}
txRegular := cost.RegularGas + execGasUsed.RegularGas
txRegular = max(txRegular, floorDataGas)
if err := st.gp.ReturnGasAmsterdam(txRegular, txState, st.gasUsed()); err != nil {

View file

@ -139,13 +139,21 @@ func (c *Contract) UseGas(cost GasCosts, logger *tracing.Hooks, reason tracing.G
return true
}
// RefundGas refunds gas to the contract. gasUsed carries the child frame's
// accumulated gas usage metrics (EIP-8037), incorporated on both success and error.
// RefundGas refunds gas to the contract.
func (c *Contract) RefundGas(err error, initialRegularGasUsed uint64, gas GasBudget, gasUsed GasUsed, logger *tracing.Hooks, reason tracing.GasChangeReason) {
// If the preceding call errored, return the state gas
// to the parent call
// If the preceding call errored, return the child's state-gas reservoir
// and any net state-gas consumption to the parent. A negative
// gasUsed.StateGas (an unabsorbed SSTORE 0→x→0 refund) is an inflation
// to undo: the matching state mutation was reverted with the child, so
// the refund must not leak back to the parent.
if err != nil {
gas.StateGas += gasUsed.StateGas
if gasUsed.StateGas >= 0 {
gas.StateGas += uint64(gasUsed.StateGas)
} else if undo := uint64(-gasUsed.StateGas); gas.StateGas >= undo {
gas.StateGas -= undo
} else {
gas.StateGas = 0
}
gasUsed.StateGas = 0
}
if gas.RegularGas == 0 && gas.StateGas == 0 && gasUsed.StateGas == 0 && gasUsed.RegularGas == 0 {
@ -164,11 +172,7 @@ func (c *Contract) RefundGas(err error, initialRegularGasUsed uint64, gas GasBud
func (c *Contract) RefundCreateStateGas(refund uint64) {
if refund > 0 {
c.Gas.StateGas += refund
if c.GasUsed.StateGas >= refund {
c.GasUsed.StateGas -= refund
} else {
c.GasUsed.StateGas = 0
}
c.GasUsed.StateGas -= int64(refund)
}
}

View file

@ -702,11 +702,7 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
// refund counter, which is capped at gas_used/5.
stateRefund := params.StorageCreationSize * evm.Context.CostPerGasByte
contract.Gas.StateGas += stateRefund
if contract.GasUsed.StateGas >= stateRefund {
contract.GasUsed.StateGas -= stateRefund
} else {
contract.GasUsed.StateGas = 0
}
contract.GasUsed.StateGas -= int64(stateRefund)
// Regular portion of the refund still goes through the refund counter.
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)

View file

@ -18,11 +18,16 @@ package vm
import "fmt"
type GasUsed = GasCosts
// GasUsed is the per-frame accumulator for gas consumption.
// StateGas is signed because of 0 -> X -> 0 SSTORE refunds.
type GasUsed struct {
RegularGas uint64
StateGas int64
}
func (g *GasUsed) Add(costs GasCosts) {
g.RegularGas += costs.RegularGas
g.StateGas += costs.StateGas
g.StateGas += int64(costs.StateGas)
}
// GasCosts denotes a vector of gas costs in the
@ -66,11 +71,9 @@ func (g GasBudget) Used(initial GasBudget) uint64 {
return (initial.RegularGas + initial.StateGas) - (g.RegularGas + g.StateGas)
}
// Exhaust sets all remaining gas to zero, preserving the initial amount
// for usage tracking.
// Exhaust burns the remaining regular gas on exceptional halt.
func (g *GasBudget) Exhaust() {
g.RegularGas = 0
g.StateGas = 0
}
func (g *GasBudget) Copy() GasBudget {