Skip to content

Commit 8609a19

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 <[email protected]>
1 parent 4b22205 commit 8609a19

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,22 +51,9 @@ 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
6157
// remains pending (and vice-versa).
6258
ErrAlreadyReserved = errors.New("address already reserved")
63-
64-
// ErrAuthorityReserved is returned if a transaction has an authorization
65-
// signed by an address which already has in-flight transactions known to the
66-
// pool.
67-
ErrAuthorityReserved = errors.New("authority already reserved")
68-
69-
// ErrAuthorityNonce is returned if a transaction has an authorization with
70-
// a nonce that is not currently valid for the authority.
71-
ErrAuthorityNonceTooLow = errors.New("authority nonce too low")
7259
)

core/txpool/legacypool/legacypool.go

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

6679
var (
@@ -572,22 +585,8 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
572585
opts := &txpool.ValidationOptionsWithState{
573586
State: pool.currentState,
574587

575-
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
576-
UsedAndLeftSlots: func(addr common.Address) (int, int) {
577-
var have int
578-
if list := pool.pending[addr]; list != nil {
579-
have += list.Len()
580-
}
581-
if list := pool.queue[addr]; list != nil {
582-
have += list.Len()
583-
}
584-
if pool.currentState.GetCodeHash(addr) != types.EmptyCodeHash || len(pool.all.auths[addr]) != 0 {
585-
// Allow at most one in-flight tx for delegated accounts or those with
586-
// a pending authorization.
587-
return have, max(0, 1-have)
588-
}
589-
return have, math.MaxInt
590-
},
588+
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
589+
UsedAndLeftSlots: nil, // Pool has own mechanism to limit the number of transactions
591590
ExistingExpenditure: func(addr common.Address) *big.Int {
592591
if list := pool.pending[addr]; list != nil {
593592
return list.totalcost.ToBig()
@@ -602,22 +601,49 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
602601
}
603602
return nil
604603
},
605-
KnownConflicts: func(from common.Address, auths []common.Address) []common.Address {
606-
var conflicts []common.Address
607-
// Authorities cannot conflict with any pending or queued transactions.
608-
for _, addr := range auths {
609-
if list := pool.pending[addr]; list != nil {
610-
conflicts = append(conflicts, addr)
611-
} else if list := pool.queue[addr]; list != nil {
612-
conflicts = append(conflicts, addr)
613-
}
614-
}
615-
return conflicts
616-
},
617604
}
618605
if err := txpool.ValidateTransactionWithState(tx, pool.signer, opts); err != nil {
619606
return err
620607
}
608+
return pool.validateAuth(tx)
609+
}
610+
611+
// validateAuth verifies that the transaction complies with code authorization
612+
// restrictions brought by SetCode transaction type.
613+
func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
614+
from, _ := types.Sender(pool.signer, tx) // validated
615+
616+
// Allow at most one in-flight tx for delegated accounts or those with a
617+
// pending authorization.
618+
if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 {
619+
var (
620+
count int
621+
exists bool
622+
)
623+
pending := pool.pending[from]
624+
if pending != nil {
625+
count += pending.Len()
626+
exists = pending.Contains(tx.Nonce())
627+
}
628+
queue := pool.queue[from]
629+
if queue != nil {
630+
count += queue.Len()
631+
exists = exists || queue.Contains(tx.Nonce())
632+
}
633+
// Replace the existing in-flight transaction for delegated accounts
634+
// are still supported
635+
if count >= 1 && !exists {
636+
return ErrInflightTxLimitReached
637+
}
638+
}
639+
// Authorities cannot conflict with any pending or queued transactions.
640+
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
641+
for _, auth := range auths {
642+
if pool.pending[auth] != nil || pool.queue[auth] != nil {
643+
return ErrAuthorityReserved
644+
}
645+
}
646+
}
621647
return nil
622648
}
623649

@@ -709,7 +735,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) {
709735
pool.priced.Put(dropTx)
710736
}
711737
log.Trace("Discarding future transaction replacing pending tx", "hash", hash)
712-
return false, txpool.ErrFutureReplacePending
738+
return false, ErrFutureReplacePending
713739
}
714740
}
715741

core/txpool/legacypool/legacypool_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,8 +1645,8 @@ func TestUnderpricing(t *testing.T) {
16451645
t.Fatalf("failed to add well priced transaction: %v", err)
16461646
}
16471647
// Ensure that replacing a pending transaction with a future transaction fails
1648-
if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != txpool.ErrFutureReplacePending {
1649-
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, txpool.ErrFutureReplacePending)
1648+
if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); err != ErrFutureReplacePending {
1649+
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending)
16501650
}
16511651
pending, queued = pool.Stats()
16521652
if pending != 4 {
@@ -2248,12 +2248,12 @@ func TestSetCodeTransactions(t *testing.T) {
22482248
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil {
22492249
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
22502250
}
2251-
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2252-
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
2251+
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
2252+
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
22532253
}
22542254
// Also check gapped transaction.
2255-
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2256-
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
2255+
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
2256+
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
22572257
}
22582258
// Replace by fee.
22592259
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil {
@@ -2287,8 +2287,8 @@ func TestSetCodeTransactions(t *testing.T) {
22872287
t.Fatalf("%s: failed to add with pending delegatio: %v", name, err)
22882288
}
22892289
// Also check gapped transaction is rejected.
2290-
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2291-
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
2290+
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, ErrInflightTxLimitReached) {
2291+
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
22922292
}
22932293
},
22942294
},
@@ -2363,7 +2363,7 @@ func TestSetCodeTransactions(t *testing.T) {
23632363
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
23642364
t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err)
23652365
}
2366-
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), txpool.ErrAccountLimitExceeded; !errors.Is(err, want) {
2366+
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), ErrInflightTxLimitReached; !errors.Is(err, want) {
23672367
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
23682368
}
23692369
},
@@ -2376,7 +2376,7 @@ func TestSetCodeTransactions(t *testing.T) {
23762376
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
23772377
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
23782378
}
2379-
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), txpool.ErrAuthorityReserved; !errors.Is(err, want) {
2379+
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), ErrAuthorityReserved; !errors.Is(err, want) {
23802380
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
23812381
}
23822382
},
@@ -2440,8 +2440,8 @@ func TestSetCodeTransactionsReorg(t *testing.T) {
24402440
t.Fatalf("failed to add with remote setcode transaction: %v", err)
24412441
}
24422442
// Try to add a transactions in
2443-
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
2444-
t.Fatalf("unexpected error %v, expecting %v", err, txpool.ErrAccountLimitExceeded)
2443+
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1000), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
2444+
t.Fatalf("unexpected error %v, expecting %v", err, ErrInflightTxLimitReached)
24452445
}
24462446
// Simulate the chain moving
24472447
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
@@ -206,7 +206,7 @@ type ValidationOptionsWithState struct {
206206
// nonce gaps will be ignored and permitted.
207207
FirstNonceGap func(addr common.Address) uint64
208208

209-
// UsedAndLeftSlots is a mandatory callback to retrieve the number of tx slots
209+
// UsedAndLeftSlots is an optional callback to retrieve the number of tx slots
210210
// used and the number still permitted for an account. New transactions will
211211
// be rejected once the number of remaining slots reaches zero.
212212
UsedAndLeftSlots func(addr common.Address) (int, int)
@@ -218,11 +218,6 @@ type ValidationOptionsWithState struct {
218218
// ExistingCost is a mandatory callback to retrieve an already pooled
219219
// transaction's cost with the given nonce to check for overdrafts.
220220
ExistingCost func(addr common.Address, nonce uint64) *big.Int
221-
222-
// KnownConflicts is an optional callback which iterates over the list of
223-
// addresses and returns all addresses known to the pool with in-flight
224-
// transactions.
225-
KnownConflicts func(sender common.Address, authorizers []common.Address) []common.Address
226221
}
227222

228223
// ValidateTransactionWithState is a helper method to check whether a transaction
@@ -273,15 +268,9 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
273268
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
274269
// overflow the number of permitted transactions from a single account
275270
// (i.e. max cancellable via out-of-bound transaction).
276-
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
277-
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
278-
}
279-
280-
// Verify no authorizations will invalidate existing transactions known to
281-
// the pool.
282-
if opts.KnownConflicts != nil {
283-
if conflicts := opts.KnownConflicts(from, tx.SetCodeAuthorities()); len(conflicts) > 0 {
284-
return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved)
271+
if opts.UsedAndLeftSlots != nil {
272+
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
273+
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
285274
}
286275
}
287276
}

0 commit comments

Comments
 (0)