Skip to content

Commit 9ee4b89

Browse files
authored
Merge branch 'ethereum:master' into gethintegration
2 parents 695fcf6 + 5cc9137 commit 9ee4b89

File tree

14 files changed

+309
-218
lines changed

14 files changed

+309
-218
lines changed

core/txpool/blobpool/blobpool.go

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
300300
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
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).
11061141
func (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)

core/txpool/blobpool/blobpool_test.go

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"math/big"
2727
"os"
2828
"path/filepath"
29-
"sync"
3029
"testing"
3130

3231
"github.com/ethereum/go-ethereum/common"
@@ -168,33 +167,6 @@ func (bc *testBlockChain) StateAt(common.Hash) (*state.StateDB, error) {
168167
return bc.statedb, nil
169168
}
170169

171-
// makeAddressReserver is a utility method to sanity check that accounts are
172-
// properly reserved by the blobpool (no duplicate reserves or unreserves).
173-
func makeAddressReserver() txpool.AddressReserver {
174-
var (
175-
reserved = make(map[common.Address]struct{})
176-
lock sync.Mutex
177-
)
178-
return func(addr common.Address, reserve bool) error {
179-
lock.Lock()
180-
defer lock.Unlock()
181-
182-
_, exists := reserved[addr]
183-
if reserve {
184-
if exists {
185-
panic("already reserved")
186-
}
187-
reserved[addr] = struct{}{}
188-
return nil
189-
}
190-
if !exists {
191-
panic("not reserved")
192-
}
193-
delete(reserved, addr)
194-
return nil
195-
}
196-
}
197-
198170
// makeTx is a utility method to construct a random blob transaction and sign it
199171
// with a valid key, only setting the interesting fields from the perspective of
200172
// the blob pool.
@@ -433,6 +405,10 @@ func verifyBlobRetrievals(t *testing.T, pool *BlobPool) {
433405
}
434406
}
435407

408+
func newReserver() *txpool.Reserver {
409+
return txpool.NewReservationTracker().NewHandle(42)
410+
}
411+
436412
// Tests that transactions can be loaded from disk on startup and that they are
437413
// correctly discarded if invalid.
438414
//
@@ -699,8 +675,8 @@ func TestOpenDrops(t *testing.T) {
699675
blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice),
700676
statedb: statedb,
701677
}
702-
pool := New(Config{Datadir: storage}, chain)
703-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
678+
pool := New(Config{Datadir: storage}, chain, nil)
679+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
704680
t.Fatalf("failed to create blob pool: %v", err)
705681
}
706682
defer pool.Close()
@@ -817,8 +793,8 @@ func TestOpenIndex(t *testing.T) {
817793
blobfee: uint256.NewInt(params.BlobTxMinBlobGasprice),
818794
statedb: statedb,
819795
}
820-
pool := New(Config{Datadir: storage}, chain)
821-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
796+
pool := New(Config{Datadir: storage}, chain, nil)
797+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
822798
t.Fatalf("failed to create blob pool: %v", err)
823799
}
824800
defer pool.Close()
@@ -918,8 +894,8 @@ func TestOpenHeap(t *testing.T) {
918894
blobfee: uint256.NewInt(105),
919895
statedb: statedb,
920896
}
921-
pool := New(Config{Datadir: storage}, chain)
922-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
897+
pool := New(Config{Datadir: storage}, chain, nil)
898+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
923899
t.Fatalf("failed to create blob pool: %v", err)
924900
}
925901
defer pool.Close()
@@ -997,8 +973,8 @@ func TestOpenCap(t *testing.T) {
997973
blobfee: uint256.NewInt(105),
998974
statedb: statedb,
999975
}
1000-
pool := New(Config{Datadir: storage, Datacap: datacap}, chain)
1001-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
976+
pool := New(Config{Datadir: storage, Datacap: datacap}, chain, nil)
977+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
1002978
t.Fatalf("failed to create blob pool: %v", err)
1003979
}
1004980
// Verify that enough transactions have been dropped to get the pool's size
@@ -1098,8 +1074,8 @@ func TestChangingSlotterSize(t *testing.T) {
10981074
blobfee: uint256.NewInt(105),
10991075
statedb: statedb,
11001076
}
1101-
pool := New(Config{Datadir: storage}, chain)
1102-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
1077+
pool := New(Config{Datadir: storage}, chain, nil)
1078+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
11031079
t.Fatalf("failed to create blob pool: %v", err)
11041080
}
11051081

@@ -1541,8 +1517,8 @@ func TestAdd(t *testing.T) {
15411517
blobfee: uint256.NewInt(105),
15421518
statedb: statedb,
15431519
}
1544-
pool := New(Config{Datadir: storage}, chain)
1545-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
1520+
pool := New(Config{Datadir: storage}, chain, nil)
1521+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
15461522
t.Fatalf("test %d: failed to create blob pool: %v", i, err)
15471523
}
15481524
verifyPoolInternals(t, pool)
@@ -1638,10 +1614,10 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) {
16381614
blobfee: uint256.NewInt(blobfee),
16391615
statedb: statedb,
16401616
}
1641-
pool = New(Config{Datadir: ""}, chain)
1617+
pool = New(Config{Datadir: ""}, chain, nil)
16421618
)
16431619

1644-
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
1620+
if err := pool.Init(1, chain.CurrentBlock(), newReserver()); err != nil {
16451621
b.Fatalf("failed to create blob pool: %v", err)
16461622
}
16471623
// Make the pool not use disk (just drop everything). This test never reads

core/txpool/errors.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,8 @@ var (
5656
// input transaction of non-blob type when a blob transaction from this sender
5757
// remains pending (and vice-versa).
5858
ErrAlreadyReserved = errors.New("address already reserved")
59+
60+
// ErrInflightTxLimitReached is returned when the maximum number of in-flight
61+
// transactions is reached for specific accounts.
62+
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
5963
)

0 commit comments

Comments
 (0)