@@ -33,6 +33,7 @@ import (
3333 "github.com/scroll-tech/go-ethereum/core/rawdb"
3434 "github.com/scroll-tech/go-ethereum/core/state"
3535 "github.com/scroll-tech/go-ethereum/core/types"
36+ "github.com/scroll-tech/go-ethereum/crypto/codehash"
3637 "github.com/scroll-tech/go-ethereum/ethdb"
3738 "github.com/scroll-tech/go-ethereum/event"
3839 "github.com/scroll-tech/go-ethereum/log"
@@ -263,6 +264,20 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
263264// The pool separates processable transactions (which can be applied to the
264265// current state) and future transactions. Transactions move between those
265266// two states over time as they are received and processed.
267+ //
268+ // In addition to tracking transactions, the pool also tracks a set of pending SetCode
269+ // authorizations (EIP7702). This helps minimize number of transactions that can be
270+ // trivially churned in the pool. As a standard rule, any account with a deployed
271+ // delegation or an in-flight authorization to deploy a delegation will only be allowed a
272+ // single transaction slot instead of the standard number. This is due to the possibility
273+ // of the account being sweeped by an unrelated account.
274+ //
275+ // Because SetCode transactions can have many authorizations included, we avoid explicitly
276+ // checking their validity to save the state lookup. So long as the encompassing
277+ // transaction is valid, the authorization will be accepted and tracked by the pool. In
278+ // case the pool is tracking a pending / queued transaction from a specific account, it
279+ // will reject new transactions with delegations from that account with standard in-flight
280+ // transactions.
266281type TxPool struct {
267282 config TxPoolConfig
268283 chainconfig * params.ChainConfig
@@ -789,41 +804,41 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
789804 if pool .currentState .GetBalance (from ).Cmp (tx .Cost ()) < 0 {
790805 return ErrInsufficientFunds
791806 }
792- usedAndLeftSlots := func (addr common.Address ) (int , int ) {
793- var have int
794- if list := pool .pending [addr ]; list != nil {
795- have += list .Len ()
796- }
797- if list := pool .queue [addr ]; list != nil {
798- have += list .Len ()
799- }
800- if pool .currentState .GetCode (addr ) != nil {
801- // Allow at most one in-flight tx for delegated accounts.
802- return have , max (0 , 1 - have )
803- }
804- return have , math .MaxInt
805- }
806- if used , left := usedAndLeftSlots (from ); left <= 0 {
807- return fmt .Errorf ("%w: pooled %d txs" , ErrAccountLimitExceeded , used )
808- }
809- knownConflicts := func (from common.Address , auths []common.Address ) []common.Address {
810- var conflicts []common.Address
811- // The transaction sender cannot have an in-flight authorization.
812- if _ , ok := pool .all .auths [from ]; ok {
813- conflicts = append (conflicts , from )
814- }
815- // Authorities cannot conflict with any pending or queued transactions.
816- for _ , addr := range auths {
807+ list := pool .pending [from ]
808+ if list == nil || ! list .Overlaps (tx ) {
809+ usedAndLeftSlots := func (addr common.Address ) (int , int ) {
810+ var have int
817811 if list := pool .pending [addr ]; list != nil {
818- conflicts = append (conflicts , addr )
819- } else if list := pool .queue [addr ]; list != nil {
820- conflicts = append (conflicts , addr )
812+ have += list .Len ()
821813 }
814+ if list := pool .queue [addr ]; list != nil {
815+ have += list .Len ()
816+ }
817+ if pool .currentState .GetKeccakCodeHash (addr ) != codehash .EmptyKeccakCodeHash || len (pool .all .auths [addr ]) != 0 {
818+ // Allow at most one in-flight tx for delegated accounts or those with
819+ // a pending authorization.
820+ return have , max (0 , 1 - have )
821+ }
822+ return have , math .MaxInt
823+ }
824+ if used , left := usedAndLeftSlots (from ); left <= 0 {
825+ return fmt .Errorf ("%w: pooled %d txs" , ErrAccountLimitExceeded , used )
826+ }
827+ knownConflicts := func (auths []common.Address ) []common.Address {
828+ var conflicts []common.Address
829+ // Authorities cannot conflict with any pending or queued transactions.
830+ for _ , addr := range auths {
831+ if list := pool .pending [addr ]; list != nil {
832+ conflicts = append (conflicts , addr )
833+ } else if list := pool .queue [addr ]; list != nil {
834+ conflicts = append (conflicts , addr )
835+ }
836+ }
837+ return conflicts
838+ }
839+ if conflicts := knownConflicts (tx .SetCodeAuthorities ()); len (conflicts ) > 0 {
840+ return fmt .Errorf ("%w: authorization conflicts with other known tx" , ErrAuthorityReserved )
822841 }
823- return conflicts
824- }
825- if conflicts := knownConflicts (from , tx .Authorities ()); len (conflicts ) > 0 {
826- return fmt .Errorf ("%w: authorization conflicts with other known tx" , ErrAuthorityReserved )
827842 }
828843 if tx .Type () == types .SetCodeTxType {
829844 if len (tx .SetCodeAuthorizations ()) == 0 {
@@ -2018,15 +2033,15 @@ type txLookup struct {
20182033 locals map [common.Hash ]* types.Transaction
20192034 remotes map [common.Hash ]* types.Transaction
20202035
2021- auths map [common.Address ][]* types. Transaction // All accounts with a pooled authorization
2036+ auths map [common.Address ][]common. Hash // All accounts with a pooled authorization
20222037}
20232038
20242039// newTxLookup returns a new txLookup structure.
20252040func newTxLookup () * txLookup {
20262041 return & txLookup {
20272042 locals : make (map [common.Hash ]* types.Transaction ),
20282043 remotes : make (map [common.Hash ]* types.Transaction ),
2029- auths : make (map [common.Address ][]* types. Transaction ),
2044+ auths : make (map [common.Address ][]common. Hash ),
20302045 }
20312046}
20322047
@@ -2133,6 +2148,7 @@ func (t *txLookup) Remove(hash common.Hash) {
21332148 t .lock .Lock ()
21342149 defer t .lock .Unlock ()
21352150
2151+ t .removeAuthorities (hash )
21362152 tx , ok := t .locals [hash ]
21372153 if ! ok {
21382154 tx , ok = t .remotes [hash ]
@@ -2143,7 +2159,6 @@ func (t *txLookup) Remove(hash common.Hash) {
21432159 }
21442160 t .slots -= numSlots (tx )
21452161 slotsGauge .Update (int64 (t .slots ))
2146- t .removeAuthorities (tx )
21472162
21482163 delete (t .locals , hash )
21492164 delete (t .remotes , hash )
@@ -2181,30 +2196,30 @@ func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
21812196// addAuthorities tracks the supplied tx in relation to each authority it
21822197// specifies.
21832198func (t * txLookup ) addAuthorities (tx * types.Transaction ) {
2184- for _ , addr := range tx .Authorities () {
2199+ for _ , addr := range tx .SetCodeAuthorities () {
21852200 list , ok := t .auths [addr ]
21862201 if ! ok {
2187- list = []* types. Transaction {}
2202+ list = []common. Hash {}
21882203 }
2189- if slices .Contains (list , tx ) {
2204+ if slices .Contains (list , tx . Hash () ) {
21902205 // Don't add duplicates.
21912206 continue
21922207 }
2193- list = append (list , tx )
2208+ list = append (list , tx . Hash () )
21942209 t .auths [addr ] = list
21952210 }
21962211}
21972212
21982213// removeAuthorities stops tracking the supplied tx in relation to its
21992214// authorities.
2200- func (t * txLookup ) removeAuthorities (tx * types.Transaction ) {
2201- for _ , addr := range tx .Authorities () {
2202- // Remove tx from tracker.
2215+ func (t * txLookup ) removeAuthorities (hash common.Hash ) {
2216+ for addr := range t .auths {
22032217 list := t .auths [addr ]
2204- if i := slices .Index (list , tx ); i >= 0 {
2218+ // Remove tx from tracker.
2219+ if i := slices .Index (list , hash ); i >= 0 {
22052220 list = append (list [:i ], list [i + 1 :]... )
22062221 } else {
2207- log .Error ("Authority with untracked tx" , "addr" , addr , "hash" , tx . Hash () )
2222+ log .Error ("Authority with untracked tx" , "addr" , addr , "hash" , hash )
22082223 }
22092224 if len (list ) == 0 {
22102225 // If list is newly empty, delete it entirely.
0 commit comments