@@ -298,8 +298,9 @@ func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transac
298
298
// minimums will need to be done only starting at the swapped in/out nonce
299
299
// and leading up to the first no-change.
300
300
type 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
303
304
304
305
store billy.Database // Persistent data store for the tx metadata and blobs
305
306
stored uint64 // Useful data size of all transactions on disk
@@ -329,13 +330,14 @@ type BlobPool struct {
329
330
330
331
// New creates a new blob transaction pool to gather, sort and filter inbound
331
332
// 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 {
333
334
// Sanitize the input to ensure no vulnerable gas prices are set
334
335
config = (& config ).sanitize ()
335
336
336
337
// Create the transaction pool with its initial settings
337
338
return & BlobPool {
338
339
config : config ,
340
+ hasPendingAuth : hasPendingAuth ,
339
341
signer : types .LatestSigner (chain .Config ()),
340
342
chain : chain ,
341
343
lookup : newLookup (),
@@ -353,8 +355,8 @@ func (p *BlobPool) Filter(tx *types.Transaction) bool {
353
355
// Init sets the gas price needed to keep a transaction in the pool and the chain
354
356
// head to allow balance / nonce checks. The transaction journal will be loaded
355
357
// 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
358
360
359
361
var (
360
362
queuedir string
@@ -499,7 +501,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
499
501
return err
500
502
}
501
503
if _ , ok := p .index [sender ]; ! ok {
502
- if err := p .reserve (sender , true ); err != nil {
504
+ if err := p .reserver . Hold (sender ); err != nil {
503
505
return err
504
506
}
505
507
p .index [sender ] = []* blobTxMeta {}
@@ -554,7 +556,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
554
556
if inclusions != nil { // only during reorgs will the heap be initialized
555
557
heap .Remove (p .evict , p .evict .index [addr ])
556
558
}
557
- p .reserve (addr , false )
559
+ p .reserver . Release (addr )
558
560
559
561
if gapped {
560
562
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
707
709
if inclusions != nil { // only during reorgs will the heap be initialized
708
710
heap .Remove (p .evict , p .evict .index [addr ])
709
711
}
710
- p .reserve (addr , false )
712
+ p .reserver . Release (addr )
711
713
} else {
712
714
p .index [addr ] = txs
713
715
}
@@ -1006,7 +1008,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error {
1006
1008
// Update the indices and metrics
1007
1009
meta := newBlobTxMeta (id , tx .Size (), p .store .Size (id ), tx )
1008
1010
if _ , ok := p .index [addr ]; ! ok {
1009
- if err := p .reserve (addr , true ); err != nil {
1011
+ if err := p .reserver . Hold (addr ); err != nil {
1010
1012
log .Warn ("Failed to reserve account for blob pool" , "tx" , tx .Hash (), "from" , addr , "err" , err )
1011
1013
return err
1012
1014
}
@@ -1066,7 +1068,7 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
1066
1068
delete (p .spent , addr )
1067
1069
1068
1070
heap .Remove (p .evict , p .evict .index [addr ])
1069
- p .reserve (addr , false )
1071
+ p .reserver . Release (addr )
1070
1072
}
1071
1073
// Clear out the transactions from the data store
1072
1074
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 {
1101
1103
return txpool .ValidateTransaction (tx , p .head , p .signer , opts )
1102
1104
}
1103
1105
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
+
1104
1139
// validateTx checks whether a transaction is valid according to the consensus
1105
1140
// rules and adheres to some heuristic limits of the local node (price and size).
1106
1141
func (p * BlobPool ) validateTx (tx * types.Transaction ) error {
@@ -1141,6 +1176,9 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
1141
1176
if err := txpool .ValidateTransactionWithState (tx , p .signer , stateOpts ); err != nil {
1142
1177
return err
1143
1178
}
1179
+ if err := p .checkDelegationLimit (tx ); err != nil {
1180
+ return err
1181
+ }
1144
1182
// If the transaction replaces an existing one, ensure that price bumps are
1145
1183
// adhered to.
1146
1184
var (
@@ -1369,7 +1407,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
1369
1407
// only by this subpool until all transactions are evicted
1370
1408
from , _ := types .Sender (p .signer , tx ) // already validated above
1371
1409
if _ , ok := p .index [from ]; ! ok {
1372
- if err := p .reserve (from , true ); err != nil {
1410
+ if err := p .reserver . Hold (from ); err != nil {
1373
1411
addNonExclusiveMeter .Mark (1 )
1374
1412
return err
1375
1413
}
@@ -1381,7 +1419,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
1381
1419
// by a return statement before running deferred methods. Take care with
1382
1420
// removing or subscoping err as it will break this clause.
1383
1421
if err != nil {
1384
- p .reserve (from , false )
1422
+ p .reserver . Release (from )
1385
1423
}
1386
1424
}()
1387
1425
}
@@ -1513,7 +1551,7 @@ func (p *BlobPool) drop() {
1513
1551
if last {
1514
1552
delete (p .index , from )
1515
1553
delete (p .spent , from )
1516
- p .reserve (from , false )
1554
+ p .reserver . Release (from )
1517
1555
} else {
1518
1556
txs [len (txs )- 1 ] = nil
1519
1557
txs = txs [:len (txs )- 1 ]
@@ -1789,7 +1827,7 @@ func (p *BlobPool) Clear() {
1789
1827
// can't happen until Clear releases the reservation lock. Clear cannot
1790
1828
// acquire the subpool lock until the transaction addition is completed.
1791
1829
for acct := range p .index {
1792
- p .reserve (acct , false )
1830
+ p .reserver . Release (acct )
1793
1831
}
1794
1832
p .lookup = newLookup ()
1795
1833
p .index = make (map [common.Address ][]* blobTxMeta )
0 commit comments