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) } }