From f5d6ea1805aa5a8ecf1227caeefbd384bec6de64 Mon Sep 17 00:00:00 2001 From: alan <652732310@qq.com> Date: Mon, 17 Feb 2025 10:43:07 +0800 Subject: [PATCH 1/8] add cache for authority --- core/types/tx_setcode.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index f2281d4ae7..12f80dca52 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -70,12 +70,13 @@ type SetCodeTx struct { // SetCodeAuthorization is an authorization from an account to deploy code at its address. type SetCodeAuthorization struct { - ChainID uint256.Int `json:"chainId" gencodec:"required"` - Address common.Address `json:"address" gencodec:"required"` - Nonce uint64 `json:"nonce" gencodec:"required"` - V uint8 `json:"yParity" gencodec:"required"` - R uint256.Int `json:"r" gencodec:"required"` - S uint256.Int `json:"s" gencodec:"required"` + ChainID uint256.Int `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"yParity" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` + authority common.Address `json:"-"` } // field type overrides for gencodec @@ -135,6 +136,7 @@ func (a *SetCodeAuthorization) Authority() (common.Address, error) { } var addr common.Address copy(addr[:], crypto.Keccak256(pub[1:])[12:]) + a.authority = addr return addr, nil } From 135d9be3d8dc5b42c498e434269a53ea9034b035 Mon Sep 17 00:00:00 2001 From: alan <652732310@qq.com> Date: Mon, 17 Feb 2025 15:00:08 +0800 Subject: [PATCH 2/8] use atomic.Pointer for authority --- core/types/tx_setcode.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index 12f80dca52..bda0d58669 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -21,6 +21,7 @@ import ( "crypto/ecdsa" "errors" "math/big" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -70,13 +71,13 @@ type SetCodeTx struct { // SetCodeAuthorization is an authorization from an account to deploy code at its address. type SetCodeAuthorization struct { - ChainID uint256.Int `json:"chainId" gencodec:"required"` - Address common.Address `json:"address" gencodec:"required"` - Nonce uint64 `json:"nonce" gencodec:"required"` - V uint8 `json:"yParity" gencodec:"required"` - R uint256.Int `json:"r" gencodec:"required"` - S uint256.Int `json:"s" gencodec:"required"` - authority common.Address `json:"-"` + ChainID uint256.Int `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"yParity" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` + authority atomic.Pointer[common.Address] `json:"-"` } // field type overrides for gencodec @@ -136,7 +137,7 @@ func (a *SetCodeAuthorization) Authority() (common.Address, error) { } var addr common.Address copy(addr[:], crypto.Keccak256(pub[1:])[12:]) - a.authority = addr + a.authority.Store(&addr) return addr, nil } From 95c732cfaca127e23860a3348a59091b6ccda5e7 Mon Sep 17 00:00:00 2001 From: alan <652732310@qq.com> Date: Tue, 18 Feb 2025 10:24:49 +0800 Subject: [PATCH 3/8] pre-derive authorities --- core/state_transition.go | 24 ++++++----- core/types/transaction.go | 31 ++++++++------ core/types/tx_setcode.go | 66 +++++++++++++++++++++++++---- eth/tracers/native/prestate.go | 6 +-- internal/ethapi/transaction_args.go | 1 + tests/state_test_util.go | 1 + 6 files changed, 93 insertions(+), 36 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index bf5ac07636..73231864f9 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -156,6 +156,7 @@ type Message struct { BlobGasFeeCap *big.Int BlobHashes []common.Hash SetCodeAuthorizations []types.SetCodeAuthorization + AuthorityCache *types.AuthorityCache // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. @@ -185,6 +186,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In Data: tx.Data(), AccessList: tx.AccessList(), SetCodeAuthorizations: tx.SetCodeAuthorizations(), + AuthorityCache: tx.AuthorityCache(), SkipNonceChecks: false, SkipTransactionChecks: false, BlobHashes: tx.BlobHashes(), @@ -504,9 +506,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { // Apply EIP-7702 authorizations. if msg.SetCodeAuthorizations != nil { - for _, auth := range msg.SetCodeAuthorizations { + for i, auth := range msg.SetCodeAuthorizations { // Note errors are ignored, we simply skip invalid authorizations here. - st.applyAuthorization(&auth) + authority, err := msg.AuthorityCache.Authority(i) + if err != nil { + continue + } + st.applyAuthorization(&auth, authority) } } @@ -574,7 +580,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { } // validateAuthorization validates an EIP-7702 authorization against the state. -func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) { +func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization, preDerivedAuthority common.Address) (authority common.Address, err error) { // Verify chain ID is null or equal to current chain ID. if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 { return authority, ErrAuthorizationWrongChainID @@ -583,11 +589,9 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio if auth.Nonce+1 < auth.Nonce { return authority, ErrAuthorizationNonceOverflow } - // Validate signature values and recover authority. - authority, err = auth.Authority() - if err != nil { - return authority, fmt.Errorf("%w: %v", ErrAuthorizationInvalidSignature, err) - } + + authority = preDerivedAuthority + // Check the authority account // 1) doesn't have code or has exisiting delegation // 2) matches the auth's nonce @@ -605,8 +609,8 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio } // applyAuthorization applies an EIP-7702 code delegation to the state. -func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization) error { - authority, err := st.validateAuthorization(auth) +func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization, preDerivedAuthority common.Address) error { + authority, err := st.validateAuthorization(auth, preDerivedAuthority) if err != nil { return err } diff --git a/core/types/transaction.go b/core/types/transaction.go index 6af960b8c3..7b65f475e6 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -519,20 +519,25 @@ func (tx *Transaction) SetCodeAuthorities() []common.Address { if !ok { return nil } - var ( - marks = make(map[common.Address]bool) - auths = make([]common.Address, 0, len(setcodetx.AuthList)) - ) - for _, auth := range setcodetx.AuthList { - if addr, err := auth.Authority(); err == nil { - if marks[addr] { - continue - } - marks[addr] = true - auths = append(auths, addr) - } + if authorityCache := setcodetx.authorityCache.Load(); authorityCache != nil { + return authorityCache.authorities } - return auths + cache := ToAuthorityCache(setcodetx.AuthList) + setcodetx.authorityCache.Store(cache) + return cache.authorities +} + +func (tx *Transaction) AuthorityCache() *AuthorityCache { + setcodetx, ok := tx.inner.(*SetCodeTx) + if !ok { + return nil + } + if authorityCache := setcodetx.authorityCache.Load(); authorityCache != nil { + return authorityCache + } + cache := ToAuthorityCache(setcodetx.AuthList) + setcodetx.authorityCache.Store(cache) + return cache } // SetTime sets the decoding time of a transaction. This is used by tests to set diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index bda0d58669..2bf9505fd4 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -65,19 +65,70 @@ type SetCodeTx struct { V *uint256.Int R *uint256.Int S *uint256.Int + + // caches + authorityCache atomic.Pointer[AuthorityCache] +} + +func ToAuthorityCache(authList []SetCodeAuthorization) *AuthorityCache { + var ( + marks = make(map[common.Address]bool) + auths = make([]common.Address, 0, len(authList)) + invalid []invalidAuth // empty since it's expected to be empty most of the time + ) + for i, auth := range authList { + if addr, err := auth.Authority(); err == nil { + if marks[addr] { + continue + } + marks[addr] = true + auths = append(auths, addr) + } else { + invalid = append(invalid, invalidAuth{index: i, error: err}) + } + } + return &AuthorityCache{authorities: auths, invalidAuths: invalid} +} + +type AuthorityCache struct { + authorities []common.Address + invalidAuths []invalidAuth +} + +type invalidAuth struct { + index int + error error +} + +func (ac *AuthorityCache) Authority(i int) (common.Address, error) { + indexToSub := 0 + for _, invalid := range ac.invalidAuths { + if i == invalid.index { + return common.Address{}, invalid.error + } + if i > invalid.index { + indexToSub++ + continue + } + break + } + targetIndex := i - indexToSub + if targetIndex < 0 || targetIndex >= len(ac.authorities) { + return common.Address{}, errors.New("index out of range") + } + return ac.authorities[i-indexToSub], nil } //go:generate go run github.com/fjl/gencodec -type SetCodeAuthorization -field-override authorizationMarshaling -out gen_authorization.go // SetCodeAuthorization is an authorization from an account to deploy code at its address. type SetCodeAuthorization struct { - ChainID uint256.Int `json:"chainId" gencodec:"required"` - Address common.Address `json:"address" gencodec:"required"` - Nonce uint64 `json:"nonce" gencodec:"required"` - V uint8 `json:"yParity" gencodec:"required"` - R uint256.Int `json:"r" gencodec:"required"` - S uint256.Int `json:"s" gencodec:"required"` - authority atomic.Pointer[common.Address] `json:"-"` + ChainID uint256.Int `json:"chainId" gencodec:"required"` + Address common.Address `json:"address" gencodec:"required"` + Nonce uint64 `json:"nonce" gencodec:"required"` + V uint8 `json:"yParity" gencodec:"required"` + R uint256.Int `json:"r" gencodec:"required"` + S uint256.Int `json:"s" gencodec:"required"` } // field type overrides for gencodec @@ -137,7 +188,6 @@ func (a *SetCodeAuthorization) Authority() (common.Address, error) { } var addr common.Address copy(addr[:], crypto.Keccak256(pub[1:])[12:]) - a.authority.Store(&addr) return addr, nil } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 49679d312f..4a4739d335 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -185,11 +185,7 @@ func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction t.lookupAccount(env.Coinbase) // Add accounts with authorizations to the prestate before they get applied. - for _, auth := range tx.SetCodeAuthorizations() { - addr, err := auth.Authority() - if err != nil { - continue - } + for _, addr := range tx.SetCodeAuthorities() { t.lookupAccount(addr) } } diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 23aa8e5947..a3a82101f6 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -490,6 +490,7 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck bool) *c BlobGasFeeCap: (*big.Int)(args.BlobFeeCap), BlobHashes: args.BlobHashes, SetCodeAuthorizations: args.AuthorizationList, + AuthorityCache: types.ToAuthorityCache(args.AuthorizationList), SkipNonceChecks: skipNonceCheck, SkipTransactionChecks: true, } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 1d6cc8db70..6dfee932fa 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -477,6 +477,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess BlobHashes: tx.BlobVersionedHashes, BlobGasFeeCap: tx.BlobGasFeeCap, SetCodeAuthorizations: authList, + AuthorityCache: types.ToAuthorityCache(authList), } return msg, nil } From df0f945f6dfb562dbf31f45241c677222805ae3a Mon Sep 17 00:00:00 2001 From: lightclient Date: Fri, 23 May 2025 12:03:21 -0600 Subject: [PATCH 4/8] core: rework tx auth cache --- core/state_transition.go | 30 ++++++------ core/txpool/legacypool/legacypool.go | 6 +-- core/txpool/legacypool/legacypool_test.go | 2 +- core/types/transaction.go | 47 ++++++++++--------- core/types/tx_setcode.go | 56 +++++------------------ eth/tracers/native/prestate.go | 2 +- 6 files changed, 57 insertions(+), 86 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 73231864f9..1c7a491eba 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -156,7 +156,7 @@ type Message struct { BlobGasFeeCap *big.Int BlobHashes []common.Hash SetCodeAuthorizations []types.SetCodeAuthorization - AuthorityCache *types.AuthorityCache + Authorities []*common.Address // Recovered authority addresses // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. @@ -186,7 +186,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In Data: tx.Data(), AccessList: tx.AccessList(), SetCodeAuthorizations: tx.SetCodeAuthorizations(), - AuthorityCache: tx.AuthorityCache(), + Authorities: tx.SetCodeAuthorities(), SkipNonceChecks: false, SkipTransactionChecks: false, BlobHashes: tx.BlobHashes(), @@ -507,12 +507,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { // Apply EIP-7702 authorizations. if msg.SetCodeAuthorizations != nil { for i, auth := range msg.SetCodeAuthorizations { - // Note errors are ignored, we simply skip invalid authorizations here. - authority, err := msg.AuthorityCache.Authority(i) - if err != nil { + addr := msg.Authorities[i] + if addr == nil { + // Skip invalid authorizations. continue } - st.applyAuthorization(&auth, authority) + st.applyAuthorization(&auth, *addr) } } @@ -580,18 +580,16 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { } // validateAuthorization validates an EIP-7702 authorization against the state. -func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization, preDerivedAuthority common.Address) (authority common.Address, err error) { +func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization, authority common.Address) error { // Verify chain ID is null or equal to current chain ID. if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 { - return authority, ErrAuthorizationWrongChainID + return ErrAuthorizationWrongChainID } // Limit nonce to 2^64-1 per EIP-2681. if auth.Nonce+1 < auth.Nonce { - return authority, ErrAuthorizationNonceOverflow + return ErrAuthorizationNonceOverflow } - authority = preDerivedAuthority - // Check the authority account // 1) doesn't have code or has exisiting delegation // 2) matches the auth's nonce @@ -600,17 +598,17 @@ func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorizatio st.state.AddAddressToAccessList(authority) code := st.state.GetCode(authority) if _, ok := types.ParseDelegation(code); len(code) != 0 && !ok { - return authority, ErrAuthorizationDestinationHasCode + return ErrAuthorizationDestinationHasCode } if have := st.state.GetNonce(authority); have != auth.Nonce { - return authority, ErrAuthorizationNonceMismatch + return ErrAuthorizationNonceMismatch } - return authority, nil + return nil } // applyAuthorization applies an EIP-7702 code delegation to the state. -func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization, preDerivedAuthority common.Address) error { - authority, err := st.validateAuthorization(auth, preDerivedAuthority) +func (st *stateTransition) applyAuthorization(auth *types.SetCodeAuthorization, authority common.Address) error { + err := st.validateAuthorization(auth, authority) if err != nil { return err } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index ceedc74a53..456ce15fa9 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -619,7 +619,7 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error { } // For symmetry, allow at most one in-flight tx for any authority with a // pending transaction. - if auths := tx.SetCodeAuthorities(); len(auths) > 0 { + if auths := tx.UniqueSetCodeAuthorities(); len(auths) > 0 { for _, auth := range auths { var count int if pending := pool.pending[auth]; pending != nil { @@ -1748,7 +1748,7 @@ func (t *lookup) TxsBelowTip(threshold *big.Int) types.Transactions { // addAuthorities tracks the supplied tx in relation to each authority it // specifies. func (t *lookup) addAuthorities(tx *types.Transaction) { - for _, addr := range tx.SetCodeAuthorities() { + for _, addr := range tx.UniqueSetCodeAuthorities() { list, ok := t.auths[addr] if !ok { list = []common.Hash{} @@ -1766,7 +1766,7 @@ func (t *lookup) addAuthorities(tx *types.Transaction) { // authorities. func (t *lookup) removeAuthorities(tx *types.Transaction) { hash := tx.Hash() - for _, addr := range tx.SetCodeAuthorities() { + for _, addr := range tx.UniqueSetCodeAuthorities() { list := t.auths[addr] // Remove tx from tracker. if i := slices.Index(list, hash); i >= 0 { diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index fb994d8208..6fadf9b073 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -273,7 +273,7 @@ func validatePoolInternals(pool *LegacyPool) error { } // Ensure all auths in pool are tracked for _, tx := range pool.all.txs { - for _, addr := range tx.SetCodeAuthorities() { + for _, addr := range tx.UniqueSetCodeAuthorities() { list := pool.all.auths[addr] if i := slices.Index(list, tx.Hash()); i < 0 { return fmt.Errorf("authority not tracked: addr %s, tx %s", addr, tx.Hash()) diff --git a/core/types/transaction.go b/core/types/transaction.go index 7b65f475e6..418eb02755 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -58,9 +58,10 @@ type Transaction struct { time time.Time // Time first seen locally (spam avoidance) // caches - hash atomic.Pointer[common.Hash] - size atomic.Uint64 - from atomic.Pointer[sigCache] + hash atomic.Pointer[common.Hash] + size atomic.Uint64 + from atomic.Pointer[sigCache] + auths atomic.Pointer[authCache] } // NewTx creates a new transaction. @@ -512,32 +513,36 @@ func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization { return setcodetx.AuthList } -// SetCodeAuthorities returns a list of unique authorities from the -// authorization list. -func (tx *Transaction) SetCodeAuthorities() []common.Address { +// SetCodeAuthorities returns a list of authorities from the authorization list. +// Nil is used to represent authorizations that fail derivation. +func (tx *Transaction) SetCodeAuthorities() []*common.Address { setcodetx, ok := tx.inner.(*SetCodeTx) if !ok { return nil } - if authorityCache := setcodetx.authorityCache.Load(); authorityCache != nil { - return authorityCache.authorities + if cache := tx.auths.Load(); cache != nil { + return *cache } - cache := ToAuthorityCache(setcodetx.AuthList) - setcodetx.authorityCache.Store(cache) - return cache.authorities + cache := setcodetx.deriveAuthorities() + tx.auths.Store(&cache) + return cache } -func (tx *Transaction) AuthorityCache() *AuthorityCache { - setcodetx, ok := tx.inner.(*SetCodeTx) - if !ok { - return nil +// UniqueSetCodeAuthorities returns a list of unique authorities from the +// authorization list. +func (tx *Transaction) UniqueSetCodeAuthorities() []common.Address { + var ( + auths = tx.SetCodeAuthorities() + marks = make(map[common.Address]bool) + uniques []common.Address + ) + for _, auth := range auths { + if auth != nil && !marks[*auth] { + marks[*auth] = true + uniques = append(uniques, *auth) + } } - if authorityCache := setcodetx.authorityCache.Load(); authorityCache != nil { - return authorityCache - } - cache := ToAuthorityCache(setcodetx.AuthList) - setcodetx.authorityCache.Store(cache) - return cache + return uniques } // SetTime sets the decoding time of a transaction. This is used by tests to set diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index 2bf9505fd4..86eec46be0 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -67,56 +67,24 @@ type SetCodeTx struct { S *uint256.Int // caches - authorityCache atomic.Pointer[AuthorityCache] + auths atomic.Pointer[authCache] } -func ToAuthorityCache(authList []SetCodeAuthorization) *AuthorityCache { - var ( - marks = make(map[common.Address]bool) - auths = make([]common.Address, 0, len(authList)) - invalid []invalidAuth // empty since it's expected to be empty most of the time - ) - for i, auth := range authList { +type authCache []*common.Address + +// deriveAuthorities returns a list of recovered authorization signers. The +// length is always equal to the number of authorizations. Any authorities that +// fail to recover are set as nil in the list. +func (tx *SetCodeTx) deriveAuthorities() authCache { + auths := make([]*common.Address, 0, len(tx.AuthList)) + for _, auth := range tx.AuthList { if addr, err := auth.Authority(); err == nil { - if marks[addr] { - continue - } - marks[addr] = true - auths = append(auths, addr) + auths = append(auths, &addr) } else { - invalid = append(invalid, invalidAuth{index: i, error: err}) + auths = append(auths, nil) } } - return &AuthorityCache{authorities: auths, invalidAuths: invalid} -} - -type AuthorityCache struct { - authorities []common.Address - invalidAuths []invalidAuth -} - -type invalidAuth struct { - index int - error error -} - -func (ac *AuthorityCache) Authority(i int) (common.Address, error) { - indexToSub := 0 - for _, invalid := range ac.invalidAuths { - if i == invalid.index { - return common.Address{}, invalid.error - } - if i > invalid.index { - indexToSub++ - continue - } - break - } - targetIndex := i - indexToSub - if targetIndex < 0 || targetIndex >= len(ac.authorities) { - return common.Address{}, errors.New("index out of range") - } - return ac.authorities[i-indexToSub], nil + return auths } //go:generate go run github.com/fjl/gencodec -type SetCodeAuthorization -field-override authorizationMarshaling -out gen_authorization.go diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 4a4739d335..80cc37aa04 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -185,7 +185,7 @@ func (t *prestateTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction t.lookupAccount(env.Coinbase) // Add accounts with authorizations to the prestate before they get applied. - for _, addr := range tx.SetCodeAuthorities() { + for _, addr := range tx.UniqueSetCodeAuthorities() { t.lookupAccount(addr) } } From 60bcc82b4cecda3b5abc7391945070a4f9bf98e5 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 29 Oct 2025 19:58:20 -0600 Subject: [PATCH 5/8] core/types: remove unused auth cache on setcode tx --- core/types/tx_setcode.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index 86eec46be0..c1d99a09fb 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -21,7 +21,6 @@ import ( "crypto/ecdsa" "errors" "math/big" - "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -65,9 +64,6 @@ type SetCodeTx struct { V *uint256.Int R *uint256.Int S *uint256.Int - - // caches - auths atomic.Pointer[authCache] } type authCache []*common.Address From d7f29ad2e6df9506642a28500c580bc744ec5ef6 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 29 Oct 2025 19:58:58 -0600 Subject: [PATCH 6/8] core: add comment regarding index sharing between auth structures in Message --- core/state_transition.go | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 1c7a491eba..e1daa9bd91 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -143,20 +143,34 @@ func toWordSize(size uint64) uint64 { // A Message contains the data derived from a single transaction that is relevant to state // processing. type Message struct { - To *common.Address - From common.Address - Nonce uint64 - Value *big.Int - GasLimit uint64 - GasPrice *big.Int - GasFeeCap *big.Int - GasTipCap *big.Int - Data []byte - AccessList types.AccessList - BlobGasFeeCap *big.Int - BlobHashes []common.Hash + To *common.Address + From common.Address + Nonce uint64 + Value *big.Int + GasLimit uint64 + GasPrice *big.Int + GasFeeCap *big.Int + GasTipCap *big.Int + Data []byte + AccessList types.AccessList + BlobGasFeeCap *big.Int + BlobHashes []common.Hash + + // When SetCodeAuthorizations is not nil, it represents the full set of + // auths included in the actual transaction. + // + // To avoid recovering the authority addresses during the state transition, + // they are precomputed or retrieved from the Transaction cache and placed + // into Authorities below. The authority for any given index will match the + // corresponding authorization in SetCodeAuthorizations. Nil is used when the + // authorization is invalid to maintain indexes between the two lists. SetCodeAuthorizations []types.SetCodeAuthorization - Authorities []*common.Address // Recovered authority addresses + + // Authorities is the list of recovered authority addresses for the + // authorization list. Nil is used to represent an invalid authorization. The + // indexes between SetCodeAuthorizations and Authorities match so that the + // each authority matches the corresponding authorization. + Authorities []*common.Address // Recovered authority addresses // When SkipNonceChecks is true, the message nonce is not checked against the // account nonce in state. From cc65a4ad321569b6053b395c31a3c0c113d93257 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 29 Oct 2025 20:11:31 -0600 Subject: [PATCH 7/8] core: panic if authorizations length doesn't match authorities --- core/state_transition.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/state_transition.go b/core/state_transition.go index e1daa9bd91..029c6717d5 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -520,6 +520,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { // Apply EIP-7702 authorizations. if msg.SetCodeAuthorizations != nil { + if len(msg.SetCodeAuthorizations) != len(msg.Authorities) { + // This is an invariant of Message that cannot be invalidated. + panic("Length of authorizations does match authorities") + } for i, auth := range msg.SetCodeAuthorizations { addr := msg.Authorities[i] if addr == nil { From 350978a0a66e53ba735f987bf8d7d8453afbf06a Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 29 Oct 2025 20:20:48 -0600 Subject: [PATCH 8/8] all: expose DeriveAuthorities publicly, use in other Message call sites --- core/types/transaction.go | 2 +- core/types/tx_setcode.go | 10 ++++++---- internal/ethapi/transaction_args.go | 2 +- tests/state_test_util.go | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index 418eb02755..1121179b29 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -523,7 +523,7 @@ func (tx *Transaction) SetCodeAuthorities() []*common.Address { if cache := tx.auths.Load(); cache != nil { return *cache } - cache := setcodetx.deriveAuthorities() + cache := (authCache)(DeriveAuthorities(setcodetx.AuthList)) tx.auths.Store(&cache) return cache } diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index c1d99a09fb..0ed06b72e2 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -66,14 +66,16 @@ type SetCodeTx struct { S *uint256.Int } +// authCache is an internal helper type used in the Transaction object to cache +// authorizations. type authCache []*common.Address -// deriveAuthorities returns a list of recovered authorization signers. The +// DeriveAuthorities returns a list of recovered authorization signers. The // length is always equal to the number of authorizations. Any authorities that // fail to recover are set as nil in the list. -func (tx *SetCodeTx) deriveAuthorities() authCache { - auths := make([]*common.Address, 0, len(tx.AuthList)) - for _, auth := range tx.AuthList { +func DeriveAuthorities(authList []SetCodeAuthorization) []*common.Address { + auths := make([]*common.Address, 0, len(authList)) + for _, auth := range authList { if addr, err := auth.Authority(); err == nil { auths = append(auths, &addr) } else { diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index a3a82101f6..9927438174 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -490,7 +490,7 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck bool) *c BlobGasFeeCap: (*big.Int)(args.BlobFeeCap), BlobHashes: args.BlobHashes, SetCodeAuthorizations: args.AuthorizationList, - AuthorityCache: types.ToAuthorityCache(args.AuthorizationList), + Authorities: types.DeriveAuthorities(args.AuthorizationList), SkipNonceChecks: skipNonceCheck, SkipTransactionChecks: true, } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 6dfee932fa..ef5f4a4a83 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -477,7 +477,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess BlobHashes: tx.BlobVersionedHashes, BlobGasFeeCap: tx.BlobGasFeeCap, SetCodeAuthorizations: authList, - AuthorityCache: types.ToAuthorityCache(authList), + Authorities: types.DeriveAuthorities(authList), } return msg, nil }