core/txpool: all one tx from accounts with in-flight delegation

This commit is contained in:
lightclient 2025-02-07 17:24:16 -07:00
parent ffa02b82e0
commit a15c2973a6
No known key found for this signature in database
GPG key ID: 657913021EF45A6A
3 changed files with 30 additions and 25 deletions

View file

@ -567,8 +567,9 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
if list := pool.queue[addr]; list != nil { if list := pool.queue[addr]; list != nil {
have += list.Len() have += list.Len()
} }
if pool.currentState.GetCodeHash(addr) != types.EmptyCodeHash { if pool.currentState.GetCodeHash(addr) != types.EmptyCodeHash || len(pool.all.auths[addr]) != 0 {
// Allow at most one in-flight tx for delegated accounts. // Allow at most one in-flight tx for delegated accounts or those with
// a pending authorization.
return have, max(0, 1-have) return have, max(0, 1-have)
} }
return have, math.MaxInt return have, math.MaxInt
@ -589,10 +590,6 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
}, },
KnownConflicts: func(from common.Address, auths []common.Address) []common.Address { KnownConflicts: func(from common.Address, auths []common.Address) []common.Address {
var conflicts []common.Address var conflicts []common.Address
// The transaction sender cannot have an in-flight authorization.
if _, ok := pool.all.auths[from]; ok {
conflicts = append(conflicts, from)
}
// Authorities cannot conflict with any pending or queued transactions. // Authorities cannot conflict with any pending or queued transactions.
for _, addr := range auths { for _, addr := range auths {
if list := pool.pending[addr]; list != nil { if list := pool.pending[addr]; list != nil {

View file

@ -2267,24 +2267,24 @@ func TestSetCodeTransactions(t *testing.T) {
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
} }
if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil { if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil {
t.Fatalf("%s: error mismatch: want %v, have %v", txpool.ErrAuthorityReserved, name, err) t.Fatalf("%s: failed to add conflicting delegation: %v", name, err)
} }
}, },
}, },
{ {
name: "reject-tx-from-pooled-delegation", name: "allow-one-tx-from-pooled-delegation",
pending: 1, pending: 2,
run: func(name string) { run: func(name string) {
// Verify C cannot originate another transaction when it has a pooled delegation. // Verify C cannot originate another transaction when it has a pooled delegation.
if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyC}})); err != nil { if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyC}})); err != nil {
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
} }
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAuthorityReserved) { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyC)); err != nil {
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAuthorityReserved, err) t.Fatalf("%s: failed to add with pending delegatio: %v", name, err)
} }
// Also check gapped transaction is rejected. // Also check gapped transaction is rejected.
if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAuthorityReserved) { if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAuthorityReserved, err) t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
} }
}, },
}, },
@ -2341,7 +2341,7 @@ func TestSetCodeTransactions(t *testing.T) {
}, },
{ {
name: "track-multiple-conflicting-delegations", name: "track-multiple-conflicting-delegations",
pending: 2, pending: 3,
run: func(name string) { run: func(name string) {
// Send two setcode txs both with C as an authority. // Send two setcode txs both with C as an authority.
if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyC}})); err != nil { if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyC}})); err != nil {
@ -2354,8 +2354,25 @@ func TestSetCodeTransactions(t *testing.T) {
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil { if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil {
t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) t.Fatalf("%s: failed to replace with remote transaction: %v", name, err)
} }
// Make sure we cannot send from keyC since it is still a pending authority. // Make sure we can only pool one tx from keyC since it is still a
if err, want := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)), txpool.ErrAuthorityReserved; !errors.Is(err, want) { // pending authority.
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err)
}
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), txpool.ErrAccountLimitExceeded; !errors.Is(err, want) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
}
},
},
{
name: "reject-delegation-from-pending-account",
pending: 1,
run: func(name string) {
// Attempt to submit a delegation from an account with a pending tx.
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil {
t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err)
}
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), txpool.ErrAuthorityReserved; !errors.Is(err, want) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
} }
}, },

View file

@ -284,15 +284,6 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved) return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved)
} }
} }
// Verify the every authorization's nonce is not stale. This check is expensive.
for _, auth := range tx.SetCodeAuthorizations() {
if addr, err := auth.Authority(); err == nil {
next := opts.State.GetNonce(addr)
if auth.Nonce < next {
return fmt.Errorf("%w: next nonce %d, auth nonce %d", ErrAuthorityNonceTooLow, next, auth.Nonce)
}
}
}
} }
return nil return nil
} }