@@ -298,8 +298,9 @@ func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transac
298298// minimums will need to be done only starting at the swapped in/out nonce
299299// and leading up to the first no-change.
300300type BlobPool struct {
301- config Config // Pool configuration
302- reserve txpool.AddressReserver // Address reserver to ensure exclusivity across subpools
301+ config Config // Pool configuration
302+ reserver * txpool.Reserver // Address reserver to ensure exclusivity across subpools
303+ hasPendingAuth func (common.Address ) bool // Determine whether the specified address has a pending 7702-auth
303304
304305 store billy.Database // Persistent data store for the tx metadata and blobs
305306 stored uint64 // Useful data size of all transactions on disk
@@ -329,13 +330,14 @@ type BlobPool struct {
329330
330331// New creates a new blob transaction pool to gather, sort and filter inbound
331332// blob transactions from the network.
332- func New (config Config , chain BlockChain ) * BlobPool {
333+ func New (config Config , chain BlockChain , hasPendingAuth func (common. Address ) bool ) * BlobPool {
333334 // Sanitize the input to ensure no vulnerable gas prices are set
334335 config = (& config ).sanitize ()
335336
336337 // Create the transaction pool with its initial settings
337338 return & BlobPool {
338339 config : config ,
340+ hasPendingAuth : hasPendingAuth ,
339341 signer : types .LatestSigner (chain .Config ()),
340342 chain : chain ,
341343 lookup : newLookup (),
@@ -353,8 +355,8 @@ func (p *BlobPool) Filter(tx *types.Transaction) bool {
353355// Init sets the gas price needed to keep a transaction in the pool and the chain
354356// head to allow balance / nonce checks. The transaction journal will be loaded
355357// from disk and filtered based on the provided starting settings.
356- func (p * BlobPool ) Init (gasTip uint64 , head * types.Header , reserve txpool.AddressReserver ) error {
357- p .reserve = reserve
358+ func (p * BlobPool ) Init (gasTip uint64 , head * types.Header , reserver * txpool.Reserver ) error {
359+ p .reserver = reserver
358360
359361 var (
360362 queuedir string
@@ -499,7 +501,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
499501 return err
500502 }
501503 if _ , ok := p .index [sender ]; ! ok {
502- if err := p .reserve (sender , true ); err != nil {
504+ if err := p .reserver . Hold (sender ); err != nil {
503505 return err
504506 }
505507 p .index [sender ] = []* blobTxMeta {}
@@ -554,7 +556,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
554556 if inclusions != nil { // only during reorgs will the heap be initialized
555557 heap .Remove (p .evict , p .evict .index [addr ])
556558 }
557- p .reserve (addr , false )
559+ p .reserver . Release (addr )
558560
559561 if gapped {
560562 log .Warn ("Dropping dangling blob transactions" , "from" , addr , "missing" , next , "drop" , nonces , "ids" , ids )
@@ -707,7 +709,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
707709 if inclusions != nil { // only during reorgs will the heap be initialized
708710 heap .Remove (p .evict , p .evict .index [addr ])
709711 }
710- p .reserve (addr , false )
712+ p .reserver . Release (addr )
711713 } else {
712714 p .index [addr ] = txs
713715 }
@@ -1006,7 +1008,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error {
10061008 // Update the indices and metrics
10071009 meta := newBlobTxMeta (id , tx .Size (), p .store .Size (id ), tx )
10081010 if _ , ok := p .index [addr ]; ! ok {
1009- if err := p .reserve (addr , true ); err != nil {
1011+ if err := p .reserver . Hold (addr ); err != nil {
10101012 log .Warn ("Failed to reserve account for blob pool" , "tx" , tx .Hash (), "from" , addr , "err" , err )
10111013 return err
10121014 }
@@ -1066,7 +1068,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
10661068 delete (p .spent , addr )
10671069
10681070 heap .Remove (p .evict , p .evict .index [addr ])
1069- p .reserve (addr , false )
1071+ p .reserver . Release (addr )
10701072 }
10711073 // Clear out the transactions from the data store
10721074 log .Warn ("Dropping underpriced blob transaction" , "from" , addr , "rejected" , tx .nonce , "tip" , tx .execTipCap , "want" , tip , "drop" , nonces , "ids" , ids )
@@ -1101,6 +1103,39 @@ func (p *BlobPool) ValidateTxBasics(tx *types.Transaction) error {
11011103 return txpool .ValidateTransaction (tx , p .head , p .signer , opts )
11021104}
11031105
1106+ // checkDelegationLimit determines if the tx sender is delegated or has a
1107+ // pending delegation, and if so, ensures they have at most one in-flight
1108+ // **executable** transaction, e.g. disallow stacked and gapped transactions
1109+ // from the account.
1110+ func (p * BlobPool ) checkDelegationLimit (tx * types.Transaction ) error {
1111+ from , _ := types .Sender (p .signer , tx ) // validated
1112+
1113+ // Short circuit if the sender has neither delegation nor pending delegation.
1114+ if p .state .GetCodeHash (from ) == types .EmptyCodeHash {
1115+ // Because there is no exclusive lock held between different subpools
1116+ // when processing transactions, a blob transaction may be accepted
1117+ // while other SetCode transactions with pending authorities from the
1118+ // same address are also accepted simultaneously.
1119+ //
1120+ // This scenario is considered acceptable, as the rule primarily ensures
1121+ // that attackers cannot easily and endlessly stack blob transactions
1122+ // with a delegated or pending delegated sender.
1123+ if p .hasPendingAuth == nil || ! p .hasPendingAuth (from ) {
1124+ return nil
1125+ }
1126+ }
1127+ // Allow a single in-flight pending transaction.
1128+ pending := p .index [from ]
1129+ if len (pending ) == 0 {
1130+ return nil
1131+ }
1132+ // If account already has a pending transaction, allow replacement only.
1133+ if len (pending ) == 1 && pending [0 ].nonce == tx .Nonce () {
1134+ return nil
1135+ }
1136+ return txpool .ErrInflightTxLimitReached
1137+ }
1138+
11041139// validateTx checks whether a transaction is valid according to the consensus
11051140// rules and adheres to some heuristic limits of the local node (price and size).
11061141func (p * BlobPool ) validateTx (tx * types.Transaction ) error {
@@ -1141,6 +1176,9 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
11411176 if err := txpool .ValidateTransactionWithState (tx , p .signer , stateOpts ); err != nil {
11421177 return err
11431178 }
1179+ if err := p .checkDelegationLimit (tx ); err != nil {
1180+ return err
1181+ }
11441182 // If the transaction replaces an existing one, ensure that price bumps are
11451183 // adhered to.
11461184 var (
@@ -1369,7 +1407,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
13691407 // only by this subpool until all transactions are evicted
13701408 from , _ := types .Sender (p .signer , tx ) // already validated above
13711409 if _ , ok := p .index [from ]; ! ok {
1372- if err := p .reserve (from , true ); err != nil {
1410+ if err := p .reserver . Hold (from ); err != nil {
13731411 addNonExclusiveMeter .Mark (1 )
13741412 return err
13751413 }
@@ -1381,7 +1419,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
13811419 // by a return statement before running deferred methods. Take care with
13821420 // removing or subscoping err as it will break this clause.
13831421 if err != nil {
1384- p .reserve (from , false )
1422+ p .reserver . Release (from )
13851423 }
13861424 }()
13871425 }
@@ -1513,7 +1551,7 @@ func (p *BlobPool) drop() {
15131551 if last {
15141552 delete (p .index , from )
15151553 delete (p .spent , from )
1516- p .reserve (from , false )
1554+ p .reserver . Release (from )
15171555 } else {
15181556 txs [len (txs )- 1 ] = nil
15191557 txs = txs [:len (txs )- 1 ]
@@ -1789,7 +1827,7 @@ func (p *BlobPool) Clear() {
17891827 // can't happen until Clear releases the reservation lock. Clear cannot
17901828 // acquire the subpool lock until the transaction addition is completed.
17911829 for acct := range p .index {
1792- p .reserve (acct , false )
1830+ p .reserver . Release (acct )
17931831 }
17941832 p .lookup = newLookup ()
17951833 p .index = make (map [common.Address ][]* blobTxMeta )
0 commit comments