mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +00:00
core: get rid of auth delegation state refund
This commit is contained in:
parent
8c28920c36
commit
46256649a3
3 changed files with 57 additions and 55 deletions
|
|
@ -665,9 +665,10 @@ 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 indicator charge applies when the delegation is set
|
||||||
// later one writes zero net bytes; the earlier indicator charge is refilled.
|
// and is never credited back when a later auth clears it in the same
|
||||||
func TestAuthClearSameTxDoubleRefill(t *testing.T) {
|
// transaction.
|
||||||
|
func TestAuthClearSameTxNoRefill(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 +677,28 @@ 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 (indicator charge kept on clear)", gp.cumulativeState, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0->a->0->b in one tx: the indicator charge applies at most once per
|
||||||
|
// authority — re-installing a delegation after an intra-tx clear is free.
|
||||||
|
func TestAuthSetClearSetChargedOnce(t *testing.T) {
|
||||||
|
set, _ := signAuth(t, authKeyA, delegate8037, 0)
|
||||||
|
clr, _ := signAuth(t, authKeyA, common.Address{}, 1)
|
||||||
|
set2, authority := signAuth(t, authKeyA, common.HexToAddress("0xde1e8a7f"), 2)
|
||||||
|
sdb := mkState(senderAlloc(nil))
|
||||||
|
_, gp, err := applyMsg(t, sdb, setCodeTx(0, senderAddr, []types.SetCodeAuthorization{set, clr, set2}))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// The final delegation is installed and the indicator was paid exactly once.
|
||||||
|
if _, delegated := types.ParseDelegation(sdb.GetCode(authority)); !delegated {
|
||||||
|
t.Fatal("final delegation not installed")
|
||||||
|
}
|
||||||
|
if want := authWorstState; gp.cumulativeState != want {
|
||||||
|
t.Fatalf("state gas = %d, want %d (leaf + indicator exactly once)", gp.cumulativeState, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]*authTracking{}); 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]*authTracking{}); 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]*authTracking{}); 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]*authTracking{}); 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,11 +115,11 @@ 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{}
|
authorities := map[common.Address]*authTracking{}
|
||||||
if err := st.applyAuthorization(rules8037, &a0, delegates); err != nil {
|
if err := st.applyAuthorization(rules8037, &a0, authorities); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := st.applyAuthorization(rules8037, &a1, delegates); err != nil {
|
if err := st.applyAuthorization(rules8037, &a1, authorities); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
|
if want := params.AccountWriteAmsterdam; st.gasRemaining.UsedRegularGas != want {
|
||||||
|
|
@ -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]*authTracking{}); 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 {
|
||||||
|
|
|
||||||
|
|
@ -930,27 +930,11 @@ func (st *stateTransition) chargeCallRecipientEIP2780(value *uint256.Int) bool {
|
||||||
// settleGas finalizes the per-tx gas accounting after EVM execution:
|
// settleGas finalizes the per-tx gas accounting after EVM execution:
|
||||||
//
|
//
|
||||||
// - Snapshots the EIP-8037 block-level 2D figures (tx_regular_gas,
|
// - Snapshots the EIP-8037 block-level 2D figures (tx_regular_gas,
|
||||||
// tx_state_gas) before any refund or floor:
|
// tx_state_gas) before any refund.
|
||||||
//
|
|
||||||
// tx_gas_used_before_refund = tx.gas - gas_left - state_gas_reservoir
|
|
||||||
// tx_state_gas = state_gas_used
|
|
||||||
// tx_regular_gas = tx_gas_used_before_refund - tx_state_gas
|
|
||||||
//
|
|
||||||
// - Computes the receipt scalar tx_gas_used by applying the EIP-3529
|
// - Computes the receipt scalar tx_gas_used by applying the EIP-3529
|
||||||
// refund (capped at tx_gas_used_before_refund/5) and the EIP-7623
|
// refund and the EIP-7623 calldata floor.
|
||||||
// calldata floor:
|
|
||||||
//
|
|
||||||
// tx_gas_used = max(tx_gas_used_before_refund - tx_gas_refund, calldata_floor)
|
|
||||||
//
|
|
||||||
// - Charges the block gas pool (2D under Amsterdam, scalar pre-Amsterdam).
|
// - Charges the block gas pool (2D under Amsterdam, scalar pre-Amsterdam).
|
||||||
//
|
|
||||||
// - Refunds the leftover gas to the sender as ETH.
|
// - Refunds the leftover gas to the sender as ETH.
|
||||||
//
|
|
||||||
// Returns the receipt-level tx_gas_used and the pre-refund peak (consumed
|
|
||||||
// by gas-estimation callers via ExecutionResult.MaxUsedGas). UsedStateGas
|
|
||||||
// should never become negative in the top-most frame, since state-gas
|
|
||||||
// refunds occur only when state creation is reverted within the same
|
|
||||||
// transaction and clearing pre-existing state is never refunded.
|
|
||||||
func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (gasUsed, peakUsed uint64, err error) {
|
func (st *stateTransition) settleGas(rules params.Rules, floorDataGas uint64) (gasUsed, peakUsed uint64, err error) {
|
||||||
if st.gasRemaining.UsedStateGas < 0 {
|
if st.gasRemaining.UsedStateGas < 0 {
|
||||||
return 0, 0, fmt.Errorf("negative topmost frame state gas usage, %d", st.gasRemaining.UsedStateGas)
|
return 0, 0, fmt.Errorf("negative topmost frame state gas usage, %d", st.gasRemaining.UsedStateGas)
|
||||||
|
|
@ -1043,8 +1027,15 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio
|
||||||
return authority, nil
|
return authority, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// authTracking tracks the charges already paid for an authority by earlier
|
||||||
|
// authorizations in the same transaction.
|
||||||
|
type authTracking struct {
|
||||||
|
written bool // first-write ACCOUNT_WRITE surcharge paid
|
||||||
|
authBaseCovered bool // indicator exists at tx start, or paid earlier
|
||||||
|
}
|
||||||
|
|
||||||
// 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, authorities map[common.Address]*authTracking) error {
|
||||||
authority, err := st.validateAuthorization(auth)
|
authority, err := st.validateAuthorization(auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -1060,12 +1051,11 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
||||||
// The authority's cold access was already charged unconditionally at the
|
// The authority's cold access was already charged unconditionally at the
|
||||||
// intrinsic phase, so only state-dependent costs remain here.
|
// intrinsic phase, so only state-dependent costs remain here.
|
||||||
var cost vm.GasCosts
|
var cost vm.GasCosts
|
||||||
authBase := params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte
|
|
||||||
|
|
||||||
preDelegated, seen := delegates[authority]
|
track := authorities[authority]
|
||||||
if !seen {
|
if track == nil {
|
||||||
preDelegated = curDelegated
|
track = &authTracking{authBaseCovered: curDelegated}
|
||||||
delegates[authority] = preDelegated
|
authorities[authority] = track
|
||||||
}
|
}
|
||||||
// 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
|
||||||
|
|
@ -1084,28 +1074,19 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
||||||
// phase. A zero-value transaction pays no TX_VALUE_COST, so a
|
// phase. A zero-value transaction pays no TX_VALUE_COST, so a
|
||||||
// write to tx.to here is still the first paid write.
|
// write to tx.to here is still the first paid write.
|
||||||
hasValue := st.msg.Value != nil && !st.msg.Value.IsZero()
|
hasValue := st.msg.Value != nil && !st.msg.Value.IsZero()
|
||||||
if !seen && authority != st.msg.From && (authority != st.to() || !hasValue) {
|
if !track.written && authority != st.msg.From && (authority != st.to() || !hasValue) {
|
||||||
cost.RegularGas += params.AccountWriteAmsterdam
|
cost.RegularGas += params.AccountWriteAmsterdam
|
||||||
|
track.written = true
|
||||||
}
|
}
|
||||||
// Durable state growth of the new account
|
// Durable state growth of the new account
|
||||||
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
|
// Charge the net-new indicator bytes at most once per authority;
|
||||||
// slot adds net-new state bytes. Overwriting an occupied slot, or one
|
// clearing within the same transaction refunds nothing.
|
||||||
// occupied at transaction start, writes no new bytes.
|
if auth.Address != (common.Address{}) && !track.authBaseCovered {
|
||||||
if auth.Address != (common.Address{}) && !curDelegated && !preDelegated {
|
cost.StateGas += params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte
|
||||||
cost.StateGas += authBase
|
track.authBaseCovered = true
|
||||||
}
|
|
||||||
// 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
|
||||||
|
|
@ -1132,9 +1113,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)
|
authorities := make(map[common.Address]*authTracking)
|
||||||
for _, auth := range auths {
|
for _, auth := range auths {
|
||||||
if err := st.applyAuthorization(rules, &auth, preDelegated); err == ErrOutOfGasRuntime {
|
if err := st.applyAuthorization(rules, &auth, authorities); err == ErrOutOfGasRuntime {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue