@@ -33,6 +33,7 @@ import (
33
33
"github.com/scroll-tech/go-ethereum/core/rawdb"
34
34
"github.com/scroll-tech/go-ethereum/core/state"
35
35
"github.com/scroll-tech/go-ethereum/core/types"
36
+ "github.com/scroll-tech/go-ethereum/crypto/codehash"
36
37
"github.com/scroll-tech/go-ethereum/ethdb"
37
38
"github.com/scroll-tech/go-ethereum/event"
38
39
"github.com/scroll-tech/go-ethereum/log"
@@ -263,6 +264,20 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
263
264
// The pool separates processable transactions (which can be applied to the
264
265
// current state) and future transactions. Transactions move between those
265
266
// 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.
266
281
type TxPool struct {
267
282
config TxPoolConfig
268
283
chainconfig * params.ChainConfig
@@ -789,41 +804,41 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
789
804
if pool .currentState .GetBalance (from ).Cmp (tx .Cost ()) < 0 {
790
805
return ErrInsufficientFunds
791
806
}
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
817
811
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 ()
821
813
}
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 )
822
841
}
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 )
827
842
}
828
843
if tx .Type () == types .SetCodeTxType {
829
844
if len (tx .SetCodeAuthorizations ()) == 0 {
@@ -2018,15 +2033,15 @@ type txLookup struct {
2018
2033
locals map [common.Hash ]* types.Transaction
2019
2034
remotes map [common.Hash ]* types.Transaction
2020
2035
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
2022
2037
}
2023
2038
2024
2039
// newTxLookup returns a new txLookup structure.
2025
2040
func newTxLookup () * txLookup {
2026
2041
return & txLookup {
2027
2042
locals : make (map [common.Hash ]* types.Transaction ),
2028
2043
remotes : make (map [common.Hash ]* types.Transaction ),
2029
- auths : make (map [common.Address ][]* types. Transaction ),
2044
+ auths : make (map [common.Address ][]common. Hash ),
2030
2045
}
2031
2046
}
2032
2047
@@ -2133,6 +2148,7 @@ func (t *txLookup) Remove(hash common.Hash) {
2133
2148
t .lock .Lock ()
2134
2149
defer t .lock .Unlock ()
2135
2150
2151
+ t .removeAuthorities (hash )
2136
2152
tx , ok := t .locals [hash ]
2137
2153
if ! ok {
2138
2154
tx , ok = t .remotes [hash ]
@@ -2143,7 +2159,6 @@ func (t *txLookup) Remove(hash common.Hash) {
2143
2159
}
2144
2160
t .slots -= numSlots (tx )
2145
2161
slotsGauge .Update (int64 (t .slots ))
2146
- t .removeAuthorities (tx )
2147
2162
2148
2163
delete (t .locals , hash )
2149
2164
delete (t .remotes , hash )
@@ -2181,30 +2196,30 @@ func (t *txLookup) RemotesBelowTip(threshold *big.Int) types.Transactions {
2181
2196
// addAuthorities tracks the supplied tx in relation to each authority it
2182
2197
// specifies.
2183
2198
func (t * txLookup ) addAuthorities (tx * types.Transaction ) {
2184
- for _ , addr := range tx .Authorities () {
2199
+ for _ , addr := range tx .SetCodeAuthorities () {
2185
2200
list , ok := t .auths [addr ]
2186
2201
if ! ok {
2187
- list = []* types. Transaction {}
2202
+ list = []common. Hash {}
2188
2203
}
2189
- if slices .Contains (list , tx ) {
2204
+ if slices .Contains (list , tx . Hash () ) {
2190
2205
// Don't add duplicates.
2191
2206
continue
2192
2207
}
2193
- list = append (list , tx )
2208
+ list = append (list , tx . Hash () )
2194
2209
t .auths [addr ] = list
2195
2210
}
2196
2211
}
2197
2212
2198
2213
// removeAuthorities stops tracking the supplied tx in relation to its
2199
2214
// 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 {
2203
2217
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 {
2205
2220
list = append (list [:i ], list [i + 1 :]... )
2206
2221
} else {
2207
- log .Error ("Authority with untracked tx" , "addr" , addr , "hash" , tx . Hash () )
2222
+ log .Error ("Authority with untracked tx" , "addr" , addr , "hash" , hash )
2208
2223
}
2209
2224
if len (list ) == 0 {
2210
2225
// If list is newly empty, delete it entirely.
0 commit comments