mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-24 23:46:17 +00:00
Claude fixes for Glam-6
- AL key gas - 7708 remove burn logs - Refund auths fully - 8037 halt mechanics - 8037 spill mechanics
This commit is contained in:
parent
9c7bd2abe5
commit
3523721bd4
5 changed files with 78 additions and 57 deletions
|
|
@ -122,14 +122,22 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
if accessList != nil {
|
||||
addresses := uint64(len(accessList))
|
||||
storageKeys := uint64(accessList.StorageKeys())
|
||||
if (math.MaxUint64-gas.RegularGas)/params.TxAccessListAddressGas < addresses {
|
||||
// EIP-8038 ties the access-list base costs to the cold-access costs
|
||||
// (TX_ACCESS_LIST_ADDRESS = COLD_ACCOUNT_ACCESS = 3000,
|
||||
// TX_ACCESS_LIST_STORAGE_KEY = COLD_STORAGE_ACCESS = 3000) in Amsterdam.
|
||||
addressGas, storageKeyGas := params.TxAccessListAddressGas, params.TxAccessListStorageKeyGas
|
||||
if rules.IsAmsterdam {
|
||||
addressGas = params.ColdAccountAccessAmsterdam
|
||||
storageKeyGas = params.ColdStorageAccessAmsterdam
|
||||
}
|
||||
if (math.MaxUint64-gas.RegularGas)/addressGas < addresses {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += addresses * params.TxAccessListAddressGas
|
||||
if (math.MaxUint64-gas.RegularGas)/params.TxAccessListStorageKeyGas < storageKeys {
|
||||
gas.RegularGas += addresses * addressGas
|
||||
if (math.MaxUint64-gas.RegularGas)/storageKeyGas < storageKeys {
|
||||
return vm.GasCosts{}, ErrGasUintOverflow
|
||||
}
|
||||
gas.RegularGas += storageKeys * params.TxAccessListStorageKeyGas
|
||||
gas.RegularGas += storageKeys * storageKeyGas
|
||||
|
||||
// EIP-7981: access list data is charged in addition to the base charge.
|
||||
if rules.IsAmsterdam {
|
||||
|
|
@ -817,12 +825,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// EIP-7708: Emit the ETH-burn logs
|
||||
if rules.IsAmsterdam {
|
||||
for _, log := range st.evm.StateDB.LogsForBurnAccounts() {
|
||||
st.evm.StateDB.AddLog(log)
|
||||
}
|
||||
}
|
||||
return &ExecutionResult{
|
||||
UsedGas: gasUsed,
|
||||
MaxUsedGas: peakUsed,
|
||||
|
|
@ -999,12 +1001,14 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio
|
|||
func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.SetCodeAuthorization) error {
|
||||
authority, err := st.validateAuthorization(auth)
|
||||
if err != nil {
|
||||
// EIP-8037 (spec apply_authorization): an invalid authorization is
|
||||
// skipped without any state-gas refund. The per-auth intrinsic state
|
||||
// charge ((NEW_ACCOUNT + AUTH_BASE) * CPSB) was levied for every
|
||||
// authorization in the list regardless of validity, and only a
|
||||
// successfully-applied authorization that avoids creating new state
|
||||
// earns a refund below. Invalid auths therefore pay in full.
|
||||
if rules.IsAmsterdam {
|
||||
// Spec set_delegation: an invalid authorization writes no state, so
|
||||
// the full per-auth intrinsic charge is refilled — NEW_ACCOUNT +
|
||||
// AUTH_BASE to the state reservoir and the worst-case ACCOUNT_WRITE
|
||||
// to the regular refund counter.
|
||||
st.gasRemaining.RefundState((params.AccountCreationSize + params.AuthorizationCreationSize) * st.evm.Context.CostPerStateByte)
|
||||
st.state.AddRefund(params.AccountWriteAmsterdam)
|
||||
}
|
||||
return err
|
||||
}
|
||||
prevDelegation, curDelegated := types.ParseDelegation(st.state.GetCode(authority))
|
||||
|
|
|
|||
|
|
@ -698,9 +698,10 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
cost.StateGas = stateSetGas
|
||||
}
|
||||
if current != newValue && original == newValue && original == (common.Hash{}) {
|
||||
// Slot set then cleared in the same tx: refund the state gas directly
|
||||
// to the reservoir (not the gas_used/5-capped refund counter).
|
||||
contract.Gas.RefundState(stateSetGas)
|
||||
// Slot set then cleared in the same tx: refund the state gas in LIFO
|
||||
// order (regular gas up to the spilled amount, then the reservoir),
|
||||
// not the gas_used/5-capped refund counter.
|
||||
contract.Gas.CreditStateRefund(stateSetGas)
|
||||
}
|
||||
return cost, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ type GasBudget struct {
|
|||
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
|
||||
UsedRegularGas uint64 // gross regular gas consumed in this frame
|
||||
UsedStateGas int64 // signed net state-gas consumed in this frame
|
||||
SpilledStateGas uint64 // state gas that spilled from the reservoir into regular gas in this frame
|
||||
}
|
||||
|
||||
// NewGasBudget initializes a fresh GasBudget for execution / forwarding,
|
||||
|
|
@ -117,6 +118,7 @@ func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
|
|||
spillover := cost.StateGas - g.StateGas
|
||||
g.StateGas = 0
|
||||
g.RegularGas -= spillover
|
||||
g.SpilledStateGas += spillover
|
||||
} else {
|
||||
g.StateGas -= cost.StateGas
|
||||
}
|
||||
|
|
@ -145,9 +147,10 @@ func (g *GasBudget) IsZero() bool {
|
|||
return g.RegularGas == 0 && g.StateGas == 0
|
||||
}
|
||||
|
||||
// RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0).
|
||||
// The reservoir is credited and the signed usage counter is decremented
|
||||
// in lockstep, preserving the per-frame invariant:
|
||||
// RefundState applies an inline state-gas refund directly to the reservoir
|
||||
// (e.g., a SetCode authorization on an already-existing leaf, or a top-level
|
||||
// transaction refund). The reservoir is credited and the signed usage counter
|
||||
// is decremented in lockstep, preserving the per-frame invariant:
|
||||
//
|
||||
// StateGas + UsedStateGas == initialStateGas + spillover_so_far
|
||||
//
|
||||
|
|
@ -157,6 +160,24 @@ func (g *GasBudget) RefundState(s uint64) {
|
|||
g.UsedStateGas -= int64(s)
|
||||
}
|
||||
|
||||
// CreditStateRefund applies an inline state-gas refund in LIFO order, mirroring
|
||||
// the spec's credit_state_gas_refund. State-gas charges draw from the reservoir
|
||||
// first and from regular gas last, so a refill credits the pool charged last
|
||||
// first: regular gas up to the amount previously spilled, then the reservoir.
|
||||
// This restores the exact pools the original charge drew from, so a probe
|
||||
// sub-call that can only observe the reservoir sees a refund only when the
|
||||
// reservoir actually had headroom.
|
||||
//
|
||||
// Used for SSTORE 0->A->0 restorations and NEW_ACCOUNT refunds on failed
|
||||
// CALL/CREATE sub-calls (spec storage.py / system.py credit_state_gas_refund).
|
||||
func (g *GasBudget) CreditStateRefund(s uint64) {
|
||||
fromRegular := min(s, g.SpilledStateGas)
|
||||
g.RegularGas += fromRegular
|
||||
g.SpilledStateGas -= fromRegular
|
||||
g.StateGas += s - fromRegular
|
||||
g.UsedStateGas -= int64(s)
|
||||
}
|
||||
|
||||
// Forward drains `regular` regular gas and the entire state reservoir from
|
||||
// the parent's running budget and returns the initial GasBudget for a child
|
||||
// frame. The parent's UsedRegularGas is bumped by the forwarded amount so
|
||||
|
|
@ -201,48 +222,44 @@ func (g GasBudget) ExitSuccess() GasBudget {
|
|||
return g
|
||||
}
|
||||
|
||||
// ExitRevert produces the leftover for a REVERT exit. Per EIP-8037, all state
|
||||
// gas charged by the reverted frame is refunded to the caller's reservoir:
|
||||
//
|
||||
// leftover.StateGas = StateGas + UsedStateGas
|
||||
//
|
||||
// UsedStateGas is reset since the frame's state changes are discarded.
|
||||
// ExitRevert produces the leftover for a REVERT exit. It mirrors the spec's
|
||||
// refill_frame_state_gas: the frame's state gas is rolled back in LIFO order —
|
||||
// the spilled portion is credited back to regular gas and the remainder to the
|
||||
// reservoir — so the pools the charges drew from are restored. No gas is burned
|
||||
// on a revert.
|
||||
func (g GasBudget) ExitRevert() GasBudget {
|
||||
reservoir := int64(g.StateGas) + g.UsedStateGas
|
||||
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.SpilledStateGas)
|
||||
if reservoir < 0 {
|
||||
// Reservoir should never be negative. By construction it equals
|
||||
// the initial state-gas allocation plus any spillover to regular
|
||||
// gas.
|
||||
// Reservoir should never be negative. By construction it equals the
|
||||
// initial state-gas allocation (spillover is returned to regular gas).
|
||||
reservoir = 0
|
||||
log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas)
|
||||
log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas, "spilled", g.SpilledStateGas)
|
||||
}
|
||||
return GasBudget{
|
||||
RegularGas: g.RegularGas,
|
||||
RegularGas: g.RegularGas + g.SpilledStateGas,
|
||||
StateGas: uint64(reservoir),
|
||||
UsedRegularGas: g.UsedRegularGas,
|
||||
UsedStateGas: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// ExitHalt produces the leftover for an exceptional halt.
|
||||
//
|
||||
// Per the updated EIP-8037, only the regular gas_left is burned (folded into
|
||||
// UsedRegularGas); the entire state-gas reservoir — including any portion that
|
||||
// spilled into the regular pool during execution — is refunded to the caller's
|
||||
// reservoir rather than reclassified as burned regular gas.
|
||||
// ExitHalt produces the leftover for an exceptional halt. It mirrors the spec's
|
||||
// refill_frame_state_gas followed by the gas_left burn: the frame's state gas
|
||||
// is rolled back LIFO (spilled portion to regular gas, remainder to the
|
||||
// reservoir), then ALL remaining regular gas — including the just-refilled
|
||||
// spilled portion — is burned. Only the non-spilled reservoir survives.
|
||||
func (g GasBudget) ExitHalt() GasBudget {
|
||||
reservoir := int64(g.StateGas) + g.UsedStateGas
|
||||
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.SpilledStateGas)
|
||||
if reservoir < 0 {
|
||||
// Reservoir should never be negative. By construction it equals
|
||||
// the initial state-gas allocation plus any spillover to regular
|
||||
// gas.
|
||||
// Reservoir should never be negative. By construction it equals the
|
||||
// initial state-gas allocation (spillover is burned with regular gas).
|
||||
reservoir = 0
|
||||
log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas)
|
||||
log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas, "spilled", g.SpilledStateGas)
|
||||
}
|
||||
return GasBudget{
|
||||
RegularGas: 0,
|
||||
StateGas: uint64(reservoir),
|
||||
UsedRegularGas: g.UsedRegularGas + g.RegularGas,
|
||||
UsedRegularGas: g.UsedRegularGas + g.RegularGas + g.SpilledStateGas,
|
||||
UsedStateGas: 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -681,9 +681,9 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
|
||||
// EIP-8037: no account was created on any failure path, so refund the
|
||||
// account-creation state gas charged before the opcode ran (gasCreateEip8037)
|
||||
// back to the reservoir.
|
||||
// in LIFO order (regular gas up to the spilled amount, then the reservoir).
|
||||
if evm.chainRules.IsAmsterdam && suberr != nil {
|
||||
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
||||
scope.Contract.Gas.CreditStateRefund(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
||||
}
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
|
|
@ -722,9 +722,9 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
|
||||
// EIP-8037: no account was created on any failure path, so refund the
|
||||
// account-creation state gas charged before the opcode ran (gasCreate2Eip8037)
|
||||
// back to the reservoir.
|
||||
// in LIFO order (regular gas up to the spilled amount, then the reservoir).
|
||||
if evm.chainRules.IsAmsterdam && suberr != nil {
|
||||
scope.Contract.Gas.RefundState(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
||||
scope.Contract.Gas.CreditStateRefund(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
||||
}
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
|
|
@ -955,12 +955,11 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
|
|||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
evm.StateDB.AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)
|
||||
}
|
||||
if evm.chainRules.IsAmsterdam && !balance.IsZero() {
|
||||
if this != beneficiary {
|
||||
evm.StateDB.AddLog(types.EthTransferLog(this, beneficiary, balance))
|
||||
} else if newContract {
|
||||
evm.StateDB.AddLog(types.EthBurnLog(this, balance))
|
||||
}
|
||||
// EIP-7708: emit a transfer log when value moves to a distinct beneficiary.
|
||||
// The current spec emits no burn log for a self-beneficiary selfdestruct
|
||||
// (the balance is preserved and silently cleared at tx end).
|
||||
if evm.chainRules.IsAmsterdam && !balance.IsZero() && this != beneficiary {
|
||||
evm.StateDB.AddLog(types.EthTransferLog(this, beneficiary, balance))
|
||||
}
|
||||
|
||||
if tracer := evm.Config.Tracer; tracer != nil {
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ const (
|
|||
|
||||
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
|
||||
MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions
|
||||
MaxCodeSizeAmsterdam = 32768 // Maximum bytecode to permit for a contract post Amsterdam
|
||||
MaxCodeSizeAmsterdam = 65536 // Maximum bytecode to permit for a contract post Amsterdam (EIP-7954: 64 KiB)
|
||||
MaxInitCodeSizeAmsterdam = 2 * MaxCodeSizeAmsterdam // Maximum initcode to permit in a creation transaction and create instructions post Amsterdam
|
||||
|
||||
// Precompiled contract gas prices
|
||||
|
|
|
|||
Loading…
Reference in a new issue