Skip to content

Commit 205786e

Browse files
rjl493456442lightclient
authored andcommitted
core/txpool: move setcode tx validation into legacyPool (ethereum#31209)
In this PR, several improvements have been made: Authorization-related validations have been moved to legacyPool. Previously, these checks were part of the standard validation procedure, which applies common validations across different pools. Since these checks are specific to SetCode transactions, relocating them to legacyPool is a more reasonable choice. Additionally, authorization conflict checks are now performed regardless of whether the transaction is a replacement or not. --------- Co-authored-by: lightclient <lightclient@protonmail.com>
1 parent f890194 commit 205786e

File tree

4 files changed

+71
-69
lines changed

4 files changed

+71
-69
lines changed

core/txpool/errors.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ var (
5151
// making the transaction invalid, rather a DOS protection.
5252
ErrOversizedData = errors.New("oversized data")
5353

54-
// ErrFutureReplacePending is returned if a future transaction replaces a pending
55-
// one. Future transactions should only be able to replace other future transactions.
56-
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
57-
5854
// ErrAlreadyReserved is returned if the sender address has a pending transaction
5955
// in a different subpool. For example, this error is returned in response to any
6056
// input transaction of non-blob type when a blob transaction from this sender
@@ -63,13 +59,4 @@ var (
6359

6460
// ErrInBlackList is returned if the transaction send by banned address
6561
ErrInBlackList = errors.New("sender or to in black list")
66-
67-
// ErrAuthorityReserved is returned if a transaction has an authorization
68-
// signed by an address which already has in-flight transactions known to the
69-
// pool.
70-
ErrAuthorityReserved = errors.New("authority already reserved")
71-
72-
// ErrAuthorityNonce is returned if a transaction has an authorization with
73-
// a nonce that is not currently valid for the authority.
74-
ErrAuthorityNonceTooLow = errors.New("authority nonce too low")
7562
)

core/txpool/legacypool/legacypool.go

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ var (
6565
// ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
6666
// another remote transaction.
6767
ErrTxPoolOverflow = errors.New("txpool is full")
68+
69+
// ErrInflightTxLimitReached is returned when the maximum number of in-flight
70+
// transactions is reached for specific accounts.
71+
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
72+
73+
// ErrAuthorityReserved is returned if a transaction has an authorization
74+
// signed by an address which already has in-flight transactions known to the
75+
// pool.
76+
ErrAuthorityReserved = errors.New("authority already reserved")
77+
78+
// ErrFutureReplacePending is returned if a future transaction replaces a pending
79+
// one. Future transactions should only be able to replace other future transactions.
80+
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
6881
)
6982

7083
var (
@@ -657,22 +670,8 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
657670
opts := &txpool.ValidationOptionsWithState{
658671
State: pool.currentState,
659672

660-
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
661-
UsedAndLeftSlots: func(addr common.Address) (int, int) {
662-
var have int
663-
if list := pool.pending[addr]; list != nil {
664-
have += list.Len()
665-
}
666-
if list := pool.queue[addr]; list != nil {
667-
have += list.Len()
668-
}
669-
if pool.currentState.GetCodeHash(addr) != types.EmptyCodeHash || len(pool.all.auths[addr]) != 0 {
670-
// Allow at most one in-flight tx for delegated accounts or those with
671-
// a pending authorization.
672-
return have, max(0, 1-have)
673-
}
674-
return have, math.MaxInt
675-
},
673+
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
674+
UsedAndLeftSlots: nil, // Pool has own mechanism to limit the number of transactions
676675
ExistingExpenditure: func(addr common.Address) *big.Int {
677676
if list := pool.pending[addr]; list != nil {
678677
return list.totalcost.ToBig()
@@ -687,22 +686,49 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
687686
}
688687
return nil
689688
},
690-
KnownConflicts: func(from common.Address, auths []common.Address) []common.Address {
691-
var conflicts []common.Address
692-
// Authorities cannot conflict with any pending or queued transactions.
693-
for _, addr := range auths {
694-
if list := pool.pending[addr]; list != nil {
695-
conflicts = append(conflicts, addr)
696-
} else if list := pool.queue[addr]; list != nil {
697-
conflicts = append(conflicts, addr)
698-
}
699-
}
700-
return conflicts
701-
},
702689
}
703690
if err := txpool.ValidateTransactionWithState(tx, pool.signer, opts); err != nil {
704691
return err
705692
}
693+
return pool.validateAuth(tx)
694+
}
695+
696+
// validateAuth verifies that the transaction complies with code authorization
697+
// restrictions brought by SetCode transaction type.
698+
func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
699+
from, _ := types.Sender(pool.signer, tx) // validated
700+
701+
// Allow at most one in-flight tx for delegated accounts or those with a
702+
// pending authorization.
703+
if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 {
704+
var (
705+
count int
706+
exists bool
707+
)
708+
pending := pool.pending[from]
709+
if pending != nil {
710+
count += pending.Len()
711+
exists = pending.Contains(tx.Nonce())
712+
}
713+
queue := pool.queue[from]
714+
if queue != nil {
715+
count += queue.Len()
716+
exists = exists || queue.Contains(tx.Nonce())
717+
}
718+
// Replace the existing in-flight transaction for delegated accounts
719+
// are still supported
720+
if count >= 1 && !exists {
721+
return ErrInflightTxLimitReached
722+
}
723+
}
724+
// Authorities cannot conflict with any pending or queued transactions.
725+
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
726+
for _, auth := range auths {
727+
if pool.pending[auth] != nil || pool.queue[auth] != nil {
728+
return ErrAuthorityReserved
729+
}
730+
}
731+
}
706732
return nil
707733
}
708734

@@ -794,7 +820,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) {
794820
pool.priced.Put(dropTx)
795821
}
796822
log.Trace("Discarding future transaction replacing pending tx", "hash", hash)
797-
return false, txpool.ErrFutureReplacePending
823+
return false, ErrFutureReplacePending
798824
}
799825
}
800826

core/txpool/legacypool/legacypool_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,8 +1647,8 @@ func TestUnderpricing(t *testing.T) {
16471647
t.Fatalf("failed to add well priced transaction: %v", err)
16481648
}
16491649
// Ensure that replacing a pending transaction with a future transaction fails
1650-
if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != txpool.ErrFutureReplacePending {
1651-
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, txpool.ErrFutureReplacePending)
1650+
if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != ErrFutureReplacePending {
1651+
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending)
16521652
}
16531653
pending, queued = pool.Stats()
16541654
if pending != 4 {
@@ -2349,12 +2349,12 @@ func TestSetCodeTransactions(t *testing.T) {
23492349
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil {
23502350
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
23512351
}
2352-
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2353-
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
2352+
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
2353+
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
23542354
}
23552355
// Also check gapped transaction.
2356-
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2357-
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
2356+
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
2357+
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
23582358
}
23592359
// Replace by fee.
23602360
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil {
@@ -2388,8 +2388,8 @@ func TestSetCodeTransactions(t *testing.T) {
23882388
t.Fatalf("%s: failed to add with pending delegatio: %v", name, err)
23892389
}
23902390
// Also check gapped transaction is rejected.
2391-
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2392-
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
2391+
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, ErrInflightTxLimitReached) {
2392+
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
23932393
}
23942394
},
23952395
},
@@ -2464,7 +2464,7 @@ func TestSetCodeTransactions(t *testing.T) {
24642464
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
24652465
t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err)
24662466
}
2467-
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), txpool.ErrAccountLimitExceeded; !errors.Is(err, want) {
2467+
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), ErrInflightTxLimitReached; !errors.Is(err, want) {
24682468
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
24692469
}
24702470
},
@@ -2477,7 +2477,7 @@ func TestSetCodeTransactions(t *testing.T) {
24772477
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
24782478
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
24792479
}
2480-
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), txpool.ErrAuthorityReserved; !errors.Is(err, want) {
2480+
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), ErrAuthorityReserved; !errors.Is(err, want) {
24812481
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
24822482
}
24832483
},
@@ -2541,8 +2541,8 @@ func TestSetCodeTransactionsReorg(t *testing.T) {
25412541
t.Fatalf("failed to add with remote setcode transaction: %v", err)
25422542
}
25432543
// Try to add a transactions in
2544-
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2545-
t.Fatalf("unexpected error %v, expecting %v", err, txpool.ErrAccountLimitExceeded)
2544+
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
2545+
t.Fatalf("unexpected error %v, expecting %v", err, ErrInflightTxLimitReached)
25462546
}
25472547
// Simulate the chain moving
25482548
blockchain.statedb.SetNonce(addrA, 2, tracing.NonceChangeAuthorization)

core/txpool/validation.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ type ValidationOptionsWithState struct {
213213
// nonce gaps will be ignored and permitted.
214214
FirstNonceGap func(addr common.Address) uint64
215215

216-
// UsedAndLeftSlots is a mandatory callback to retrieve the number of tx slots
216+
// UsedAndLeftSlots is an optional callback to retrieve the number of tx slots
217217
// used and the number still permitted for an account. New transactions will
218218
// be rejected once the number of remaining slots reaches zero.
219219
UsedAndLeftSlots func(addr common.Address) (int, int)
@@ -225,11 +225,6 @@ type ValidationOptionsWithState struct {
225225
// ExistingCost is a mandatory callback to retrieve an already pooled
226226
// transaction's cost with the given nonce to check for overdrafts.
227227
ExistingCost func(addr common.Address, nonce uint64) *big.Int
228-
229-
// KnownConflicts is an optional callback which iterates over the list of
230-
// addresses and returns all addresses known to the pool with in-flight
231-
// transactions.
232-
KnownConflicts func(sender common.Address, authorizers []common.Address) []common.Address
233228
}
234229

235230
// ValidateTransactionWithState is a helper method to check whether a transaction
@@ -280,15 +275,9 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
280275
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
281276
// overflow the number of permitted transactions from a single account
282277
// (i.e. max cancellable via out-of-bound transaction).
283-
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
284-
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
285-
}
286-
287-
// Verify no authorizations will invalidate existing transactions known to
288-
// the pool.
289-
if opts.KnownConflicts != nil {
290-
if conflicts := opts.KnownConflicts(from, tx.SetCodeAuthorities()); len(conflicts) > 0 {
291-
return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved)
278+
if opts.UsedAndLeftSlots != nil {
279+
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
280+
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
292281
}
293282
}
294283
}

0 commit comments

Comments
 (0)