missing fixes

This commit is contained in:
Marius van der Wijden 2026-07-10 13:58:36 +02:00
parent a4f9402459
commit 2d40820732
3 changed files with 42 additions and 31 deletions

View file

@ -665,9 +665,11 @@ func TestAuthClearRefillBase(t *testing.T) {
} }
} }
// 0->a->0 in one tx: the indicator created by an earlier auth and cleared by a // 0->a->0 in one tx: the AUTH_BASE state charge for the indicator set by an
// later one writes zero net bytes; the earlier indicator charge is refilled. // earlier auth is levied once and is never credited back, even when a later
func TestAuthClearSameTxDoubleRefill(t *testing.T) { // auth clears the indicator in the same transaction. The account leaf charge
// plus the kept AUTH_BASE charge remain.
func TestAuthClearSameTxKeepsCharge(t *testing.T) {
set, authority := signAuth(t, authKeyA, delegate8037, 0) set, authority := signAuth(t, authKeyA, delegate8037, 0)
clr, _ := signAuth(t, authKeyA, common.Address{}, 1) clr, _ := signAuth(t, authKeyA, common.Address{}, 1)
sdb := mkState(senderAlloc(nil)) sdb := mkState(senderAlloc(nil))
@ -676,8 +678,8 @@ func TestAuthClearSameTxDoubleRefill(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_ = authority _ = authority
if want := newAccountState; gp.cumulativeState != want { if want := authWorstState; gp.cumulativeState != want {
t.Fatalf("state gas = %d, want %d (net-zero delegation)", gp.cumulativeState, want) t.Fatalf("state gas = %d, want %d (account leaf + kept AUTH_BASE)", gp.cumulativeState, want)
} }
} }

View file

@ -44,7 +44,7 @@ func newAuthTestTransition(sdb *state.StateDB) *stateTransition {
func TestAuthRuntimeChargeNetNew(t *testing.T) { func TestAuthRuntimeChargeNetNew(t *testing.T) {
auth, _ := signAuth(t, authKeyA, delegate8037, 0) auth, _ := signAuth(t, authKeyA, delegate8037, 0)
st := newAuthTestTransition(mkState(senderAlloc(nil))) st := newAuthTestTransition(mkState(senderAlloc(nil)))
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]bool{}); err != nil { if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authDelegationState{}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want { if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
@ -62,7 +62,7 @@ func TestAuthRuntimeChargeNetNew(t *testing.T) {
func TestAuthRuntimeChargeExistingAccount(t *testing.T) { func TestAuthRuntimeChargeExistingAccount(t *testing.T) {
auth, authority := signAuth(t, authKeyA, delegate8037, 0) auth, authority := signAuth(t, authKeyA, delegate8037, 0)
st := newAuthTestTransition(mkState(senderAlloc(types.GenesisAlloc{authority: {Balance: big.NewInt(1)}}))) st := newAuthTestTransition(mkState(senderAlloc(types.GenesisAlloc{authority: {Balance: big.NewInt(1)}})))
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]bool{}); err != nil { if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authDelegationState{}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want { if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
@ -81,7 +81,7 @@ func TestAuthRuntimeChargeWarmAuthority(t *testing.T) {
auth, authority := signAuth(t, authKeyA, delegate8037, 0) auth, authority := signAuth(t, authKeyA, delegate8037, 0)
st := newAuthTestTransition(mkState(senderAlloc(types.GenesisAlloc{authority: {Balance: big.NewInt(1)}}))) st := newAuthTestTransition(mkState(senderAlloc(types.GenesisAlloc{authority: {Balance: big.NewInt(1)}})))
st.state.AddAddressToAccessList(authority) st.state.AddAddressToAccessList(authority)
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]bool{}); err != nil { if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authDelegationState{}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want { if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
@ -99,7 +99,7 @@ func TestAuthRuntimeInvalidNoCharge(t *testing.T) {
ChainID: *uint256.NewInt(999), Address: delegate8037, Nonce: 0, // wrong chain id ChainID: *uint256.NewInt(999), Address: delegate8037, Nonce: 0, // wrong chain id
}) })
st := newAuthTestTransition(mkState(senderAlloc(nil))) st := newAuthTestTransition(mkState(senderAlloc(nil)))
if err := st.applyAuthorization(rules8037, &bad, map[common.Address]bool{}); err == nil { if err := st.applyAuthorization(rules8037, &bad, map[common.Address]*authDelegationState{}); err == nil {
t.Fatal("expected invalid-authorization error") t.Fatal("expected invalid-authorization error")
} }
if st.gasRemaining.UsedRegularGas != 0 || st.gasRemaining.UsedStateGas != 0 { if st.gasRemaining.UsedRegularGas != 0 || st.gasRemaining.UsedStateGas != 0 {
@ -115,7 +115,7 @@ func TestAuthRuntimeDuplicateAuthorityOnce(t *testing.T) {
a0, _ := signAuth(t, authKeyA, delegate8037, 0) a0, _ := signAuth(t, authKeyA, delegate8037, 0)
a1, _ := signAuth(t, authKeyA, delegate8037, 1) a1, _ := signAuth(t, authKeyA, delegate8037, 1)
st := newAuthTestTransition(mkState(senderAlloc(nil))) st := newAuthTestTransition(mkState(senderAlloc(nil)))
delegates := map[common.Address]bool{} delegates := map[common.Address]*authDelegationState{}
if err := st.applyAuthorization(rules8037, &a0, delegates); err != nil { if err := st.applyAuthorization(rules8037, &a0, delegates); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -136,7 +136,7 @@ func TestAuthRuntimeOutOfGas(t *testing.T) {
auth, authority := signAuth(t, authKeyA, delegate8037, 0) auth, authority := signAuth(t, authKeyA, delegate8037, 0)
st := newAuthTestTransition(mkState(senderAlloc(nil))) st := newAuthTestTransition(mkState(senderAlloc(nil)))
st.gasRemaining = vm.NewGasBudget(10_000, 0) // covers neither leaf nor indicator st.gasRemaining = vm.NewGasBudget(10_000, 0) // covers neither leaf nor indicator
if err := st.applyAuthorization(rules8037, &auth, map[common.Address]bool{}); err != ErrOutOfGasRuntime { if err := st.applyAuthorization(rules8037, &auth, map[common.Address]*authDelegationState{}); err != ErrOutOfGasRuntime {
t.Fatalf("err = %v, want ErrOutOfGasRuntime", err) t.Fatalf("err = %v, want ErrOutOfGasRuntime", err)
} }
if st.state.GetNonce(authority) != 0 || len(st.state.GetCode(authority)) != 0 { if st.state.GetNonce(authority) != 0 || len(st.state.GetCode(authority)) != 0 {

View file

@ -991,6 +991,7 @@ func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (g
// Settle down the final gas consumption in the block-level pool // Settle down the final gas consumption in the block-level pool
if rules.IsAmsterdam { if rules.IsAmsterdam {
txRegularGas = max(txRegularGas, floorDataGas)
if err = st.gp.ChargeGasAmsterdam(txRegularGas, txStateGas, gasUsed); err != nil { if err = st.gp.ChargeGasAmsterdam(txRegularGas, txStateGas, gasUsed); err != nil {
return 0, 0, err return 0, 0, err
} }
@ -1043,8 +1044,17 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio
return authority, nil return authority, nil
} }
// authDelegationState tracks, per authority within a single transaction, the
// delegation state observed at transaction start and whether a (non-null)
// delegation has already been set for it earlier in the transaction. It mirrors
// the spec's `delegated_before_tx` flag and `delegation_set_for` set.
type authDelegationState struct {
preDelegated bool // authority carried a delegation at transaction start
setInTx bool // a non-null delegation was set for it earlier in this tx
}
// applyAuthorization applies an EIP-7702 code delegation to the state. // applyAuthorization applies an EIP-7702 code delegation to the state.
func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.SetCodeAuthorization, delegates map[common.Address]bool) error { func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.SetCodeAuthorization, delegates map[common.Address]*authDelegationState) error {
authority, err := st.validateAuthorization(auth) authority, err := st.validateAuthorization(auth)
if err != nil { if err != nil {
return err return err
@ -1062,11 +1072,12 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
var cost vm.GasCosts var cost vm.GasCosts
authBase := params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte authBase := params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte
preDelegated, seen := delegates[authority] track, seen := delegates[authority]
if !seen { if !seen {
preDelegated = curDelegated track = &authDelegationState{preDelegated: curDelegated}
delegates[authority] = preDelegated delegates[authority] = track
} }
preDelegated := track.preDelegated
// Every valid authorization writes the authority account: the // Every valid authorization writes the authority account: the
// nonce bump, and possibly the delegation indicator. The first // nonce bump, and possibly the delegation indicator. The first
// write to an account within the transaction carries the // write to an account within the transaction carries the
@ -1091,25 +1102,23 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
if st.state.Empty(authority) { if st.state.Empty(authority) {
cost.StateGas += params.AccountCreationSize * st.evm.Context.CostPerStateByte cost.StateGas += params.AccountCreationSize * st.evm.Context.CostPerStateByte
} }
// Writing the 23-byte delegation indicator into a previously empty // Writing the 23-byte delegation indicator adds net-new state bytes when
// slot adds net-new state bytes. Overwriting an occupied slot, or one // the authority held no delegation at the start of the transaction and
// occupied at transaction start, writes no new bytes. // none has been set for it earlier in the transaction. This AUTH_BASE
if auth.Address != (common.Address{}) && !curDelegated && !preDelegated { // state charge is levied at most once per authority and is never
// credited back: a delegation set and then cleared within the same
// transaction keeps its charge.
if auth.Address != (common.Address{}) && !preDelegated && !track.setInTx {
cost.StateGas += authBase cost.StateGas += authBase
} }
// Clearing an indicator that was created by an earlier authorization
// within the same transaction writes zero net bytes; refill the
// earlier state charge as it is no longer justified.
//
// Note that the refund and the charges above can never apply to the
// same authorization. The ordering of the refund and the charge is
// therefore irrelevant.
if auth.Address == (common.Address{}) && curDelegated && !preDelegated {
st.gasRemaining.RefundState(authBase)
}
if !st.chargeRuntimeGas(cost) { if !st.chargeRuntimeGas(cost) {
return ErrOutOfGasRuntime return ErrOutOfGasRuntime
} }
// Record that a non-null delegation has been set for this authority in
// this transaction, so a later set does not charge AUTH_BASE again.
if auth.Address != (common.Address{}) {
track.setInTx = true
}
} }
// Update nonce and account code. // Update nonce and account code.
st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization) st.state.SetNonce(authority, auth.Nonce+1, tracing.NonceChangeAuthorization)
@ -1132,9 +1141,9 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
// It reports whether the transaction budget covered all runtime authorization // It reports whether the transaction budget covered all runtime authorization
// charges. // charges.
func (st *stateTransition) applyAuthorizations(rules params.Rules, auths []types.SetCodeAuthorization) bool { func (st *stateTransition) applyAuthorizations(rules params.Rules, auths []types.SetCodeAuthorization) bool {
preDelegated := make(map[common.Address]bool) delegates := make(map[common.Address]*authDelegationState)
for _, auth := range auths { for _, auth := range auths {
if err := st.applyAuthorization(rules, &auth, preDelegated); err == ErrOutOfGasRuntime { if err := st.applyAuthorization(rules, &auth, delegates); err == ErrOutOfGasRuntime {
return false return false
} }
} }