core. last 3 fixes (claude) will pull them out later

This commit is contained in:
MariusVanDerWijden 2026-07-02 11:10:20 +02:00
parent 64d77794a7
commit 8f16e534f8
No known key found for this signature in database
3 changed files with 37 additions and 6 deletions

View file

@ -737,7 +737,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// If the contract creation failed, or the destination was pre-existing,
// refund the account-creation state gas pre-charged in IntrinsicGas.
if rules.IsAmsterdam && !creation {
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
st.gasRemaining.RefundStateToReservoir(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
}
} else {
// Increment the nonce for the next transaction.
@ -753,6 +753,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
// performing the resolution and warming.
if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok {
st.state.AddAddressToAccessList(addr)
// The spec resolves the delegated code in process_message_call,
// before the top-frame recipient charges: the delegated account is
// read (and recorded in the block access list) even if the cold
// access charge below runs out of gas.
if rules.IsAmsterdam {
st.state.GetCode(addr)
}
}
// EIP-2780: charge the transaction's top-level recipient costs. If the
// budget cannot cover the charge, the top frame halts out of gas.
@ -966,7 +973,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
authority, err := st.validateAuthorization(auth)
if err != nil {
if rules.IsAmsterdam {
st.gasRemaining.RefundState((params.AccountCreationSize + params.AuthorizationCreationSize) * st.evm.Context.CostPerStateByte)
st.gasRemaining.RefundStateToReservoir((params.AccountCreationSize + params.AuthorizationCreationSize) * st.evm.Context.CostPerStateByte)
st.state.AddRefund(params.AccountWriteAmsterdam)
}
return err
@ -979,7 +986,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
}
} else {
if st.state.Exist(authority) {
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
st.gasRemaining.RefundStateToReservoir(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
st.state.AddRefund(params.AccountWriteAmsterdam)
}
authBase := params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte
@ -991,17 +998,17 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
}
if auth.Address == (common.Address{}) {
// Clearing writes no indicator, refill this auth's state charge.
st.gasRemaining.RefundState(authBase)
st.gasRemaining.RefundStateToReservoir(authBase)
// The indicator was created by an earlier auth within the same
// transaction, refill the state charge as it's no longer justified.
if curDelegated && !preDelegated {
st.gasRemaining.RefundState(authBase)
st.gasRemaining.RefundStateToReservoir(authBase)
}
} else if curDelegated || preDelegated {
// The 23-byte slot is already occupied, overwriting it writes no
// new bytes, refill the state charge.
st.gasRemaining.RefundState(authBase)
st.gasRemaining.RefundStateToReservoir(authBase)
}
}

View file

@ -196,6 +196,22 @@ func (g *GasBudget) RefundState(s uint64) {
g.UsedStateGas -= int64(s)
}
// RefundStateToReservoir credits a state-gas refund directly to the
// reservoir, without repaying spilled regular gas first.
//
// Per the spec's set_delegation, authorization refunds (and the post-create
// new-account refund) are added to message.state_gas_reservoir directly, in
// contrast to the LIFO inline refunds handled by RefundState. The usage
// counter is decremented by the full amount, matching the spec's
// tx_state_gas = intrinsic_state + state_gas_used - state_refund and
// preserving the per-frame invariant:
//
// StateGas + UsedStateGas == initialStateGas + Spilled
func (g *GasBudget) RefundStateToReservoir(s uint64) {
g.StateGas += s
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

View file

@ -415,6 +415,14 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
if !contract.chargeRegular(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
return GasCosts{}, ErrOutOfGas
}
// The spec checks the delegation cost together with the memory
// expansion and transfer costs before reading the delegated account.
// Since intrinsicCost is only checked (not charged) above, re-check it
// against the remaining gas so the delegated address is not recorded
// in the block access list when the combined costs are unaffordable.
if contract.Gas.RegularGas < intrinsicCost {
return GasCosts{}, ErrOutOfGas
}
// The delegated address has passed its gas check; record it in the
// block access list now, before the call's sender-balance and
// call-stack-depth checks.