mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
parent
1f3faa5184
commit
96a9c89aa6
4 changed files with 71 additions and 65 deletions
|
|
@ -55,21 +55,12 @@ var (
|
|||
// making the transaction invalid, rather a DOS protection.
|
||||
ErrOversizedData = errors.New("oversized data")
|
||||
|
||||
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
||||
// transaction. Future transactions should only be able to replace other future transactions.
|
||||
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
||||
|
||||
// ErrAlreadyReserved is returned if the sender address has a pending transaction
|
||||
// in a different subpool. For example, this error is returned in response to any
|
||||
// input transaction of non-blob type when a blob transaction from this sender
|
||||
// remains pending (and vice-versa).
|
||||
ErrAlreadyReserved = errors.New("address already reserved")
|
||||
|
||||
// ErrAuthorityReserved is returned if a transaction has an authorization
|
||||
// signed by an address which already has in-flight transactions known to the
|
||||
// pool.
|
||||
ErrAuthorityReserved = errors.New("authority already reserved")
|
||||
|
||||
ErrZeroGasPrice = errors.New("zero gas price")
|
||||
|
||||
ErrUnderMinGasPrice = errors.New("under min gas price")
|
||||
|
|
|
|||
|
|
@ -67,6 +67,19 @@ var (
|
|||
// ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
|
||||
// another remote transaction.
|
||||
ErrTxPoolOverflow = errors.New("txpool is full")
|
||||
|
||||
// ErrInflightTxLimitReached is returned when the maximum number of in-flight
|
||||
// transactions is reached for specific accounts.
|
||||
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
|
||||
|
||||
// ErrAuthorityReserved is returned if a transaction has an authorization
|
||||
// signed by an address which already has in-flight transactions known to the
|
||||
// pool.
|
||||
ErrAuthorityReserved = errors.New("authority already reserved")
|
||||
|
||||
// ErrFutureReplacePending is returned if a future transaction replaces a pending
|
||||
// one. Future transactions should only be able to replace other future transactions.
|
||||
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -660,22 +673,8 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
opts := &txpool.ValidationOptionsWithState{
|
||||
State: pool.currentState,
|
||||
|
||||
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
|
||||
UsedAndLeftSlots: func(addr common.Address) (int, int) {
|
||||
var have int
|
||||
if list := pool.pending[addr]; list != nil {
|
||||
have += list.Len()
|
||||
}
|
||||
if list := pool.queue[addr]; list != nil {
|
||||
have += list.Len()
|
||||
}
|
||||
if pool.currentState.GetCodeHash(addr) != types.EmptyCodeHash || len(pool.all.auths[addr]) != 0 {
|
||||
// Allow at most one in-flight tx for delegated accounts or those with
|
||||
// a pending authorization.
|
||||
return have, max(0, 1-have)
|
||||
}
|
||||
return have, math.MaxInt
|
||||
},
|
||||
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
|
||||
UsedAndLeftSlots: nil, // Pool has own mechanism to limit the number of transactions
|
||||
ExistingExpenditure: func(addr common.Address) *big.Int {
|
||||
if list := pool.pending[addr]; list != nil {
|
||||
return list.totalcost
|
||||
|
|
@ -690,18 +689,6 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
}
|
||||
return nil
|
||||
},
|
||||
KnownConflicts: func(from common.Address, auths []common.Address) []common.Address {
|
||||
var conflicts []common.Address
|
||||
// Authorities cannot conflict with any pending or queued transactions.
|
||||
for _, addr := range auths {
|
||||
if list := pool.pending[addr]; list != nil {
|
||||
conflicts = append(conflicts, addr)
|
||||
} else if list := pool.queue[addr]; list != nil {
|
||||
conflicts = append(conflicts, addr)
|
||||
}
|
||||
}
|
||||
return conflicts
|
||||
},
|
||||
|
||||
Trc21FeeCapacity: pool.trc21FeeCapacity,
|
||||
|
||||
|
|
@ -733,6 +720,45 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
|||
return core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
}
|
||||
|
||||
return pool.validateAuth(tx)
|
||||
}
|
||||
|
||||
// validateAuth verifies that the transaction complies with code authorization
|
||||
// restrictions brought by SetCode transaction type.
|
||||
func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
|
||||
from, _ := types.Sender(pool.signer, tx) // validated
|
||||
|
||||
// Allow at most one in-flight tx for delegated accounts or those with a
|
||||
// pending authorization.
|
||||
if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 {
|
||||
var (
|
||||
count int
|
||||
exists bool
|
||||
)
|
||||
pending := pool.pending[from]
|
||||
if pending != nil {
|
||||
count += pending.Len()
|
||||
exists = pending.Contains(tx.Nonce())
|
||||
}
|
||||
queue := pool.queue[from]
|
||||
if queue != nil {
|
||||
count += queue.Len()
|
||||
exists = exists || queue.Contains(tx.Nonce())
|
||||
}
|
||||
// Replacing the existing in-flight transaction for delegated accounts
|
||||
// is still supported.
|
||||
if count >= 1 && !exists {
|
||||
return ErrInflightTxLimitReached
|
||||
}
|
||||
}
|
||||
// Authorities cannot conflict with any pending or queued transactions.
|
||||
if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
|
||||
for _, auth := range auths {
|
||||
if pool.pending[auth] != nil || pool.queue[auth] != nil {
|
||||
return ErrAuthorityReserved
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -835,7 +861,7 @@ func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, e
|
|||
pool.priced.Put(dropTx, false)
|
||||
}
|
||||
log.Trace("Discarding future transaction replacing pending tx", "hash", hash)
|
||||
return false, txpool.ErrFutureReplacePending
|
||||
return false, ErrFutureReplacePending
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1905,8 +1905,8 @@ func TestUnderpricing(t *testing.T) {
|
|||
t.Fatalf("failed to add well priced transaction: %v", err)
|
||||
}
|
||||
// Ensure that replacing a pending transaction with a future transaction fails
|
||||
if err := pool.addRemote(pricedTransaction(5, 100000, big.NewInt(1500000000), keys[1])); err != txpool.ErrFutureReplacePending {
|
||||
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, txpool.ErrFutureReplacePending)
|
||||
if err := pool.addRemote(pricedTransaction(5, 100000, big.NewInt(1500000000), keys[1])); err != ErrFutureReplacePending {
|
||||
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending)
|
||||
}
|
||||
pending, queued = pool.Stats()
|
||||
if pending != 2 {
|
||||
|
|
@ -2680,12 +2680,12 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, minGasPrice, keyA)); err != nil {
|
||||
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
|
||||
}
|
||||
if err := pool.addRemoteSync(pricedTransaction(1, 100000, new(big.Int).Set(minGasPrice), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
|
||||
if err := pool.addRemoteSync(pricedTransaction(1, 100000, new(big.Int).Set(minGasPrice), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||
}
|
||||
// Also check gapped transaction.
|
||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, new(big.Int).Set(minGasPrice), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
|
||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, new(big.Int).Set(minGasPrice), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||
}
|
||||
// Replace by fee.
|
||||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, legacyReplacePrice, keyA)); err != nil {
|
||||
|
|
@ -2719,8 +2719,8 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
t.Fatalf("%s: failed to add with pending delegatio: %v", name, err)
|
||||
}
|
||||
// Also check gapped transaction is rejected.
|
||||
if err := pool.addRemoteSync(pricedTransaction(1, 100000, new(big.Int).Set(minGasPrice), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err)
|
||||
if err := pool.addRemoteSync(pricedTransaction(1, 100000, new(big.Int).Set(minGasPrice), keyC)); !errors.Is(err, ErrInflightTxLimitReached) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -2795,7 +2795,7 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, new(big.Int).Set(minGasPrice), 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, new(big.Int).Set(minGasPrice), keyC)), txpool.ErrAccountLimitExceeded; !errors.Is(err, want) {
|
||||
if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, new(big.Int).Set(minGasPrice), keyC)), ErrInflightTxLimitReached; !errors.Is(err, want) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
|
||||
}
|
||||
},
|
||||
|
|
@ -2808,7 +2808,7 @@ func TestSetCodeTransactions(t *testing.T) {
|
|||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, new(big.Int).Set(minGasPrice), 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) {
|
||||
if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), ErrAuthorityReserved; !errors.Is(err, want) {
|
||||
t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err)
|
||||
}
|
||||
},
|
||||
|
|
@ -2877,8 +2877,8 @@ func TestSetCodeTransactionsReorg(t *testing.T) {
|
|||
t.Fatalf("failed to add with remote setcode transaction: %v", err)
|
||||
}
|
||||
// Try to add a transactions in
|
||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, new(big.Int).Set(legacyPrice), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
|
||||
t.Fatalf("unexpected error %v, expecting %v", err, txpool.ErrAccountLimitExceeded)
|
||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, new(big.Int).Set(legacyPrice), keyA)); !errors.Is(err, ErrInflightTxLimitReached) {
|
||||
t.Fatalf("unexpected error %v, expecting %v", err, ErrInflightTxLimitReached)
|
||||
}
|
||||
// Simulate the chain moving
|
||||
blockchain.statedb.SetNonce(addrA, 2)
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ type ValidationOptionsWithState struct {
|
|||
// nonce gaps will be ignored and permitted.
|
||||
FirstNonceGap func(addr common.Address) uint64
|
||||
|
||||
// UsedAndLeftSlots is a mandatory callback to retrieve the number of tx slots
|
||||
// UsedAndLeftSlots is an optional callback to retrieve the number of tx slots
|
||||
// used and the number still permitted for an account. New transactions will
|
||||
// be rejected once the number of remaining slots reaches zero.
|
||||
UsedAndLeftSlots func(addr common.Address) (int, int)
|
||||
|
|
@ -154,11 +154,6 @@ type ValidationOptionsWithState struct {
|
|||
// transaction's cost with the given nonce to check for overdrafts.
|
||||
ExistingCost func(addr common.Address, nonce uint64) *big.Int
|
||||
|
||||
// KnownConflicts is an optional callback which iterates over the list of
|
||||
// addresses and returns all addresses known to the pool with in-flight
|
||||
// transactions.
|
||||
KnownConflicts func(sender common.Address, authorizers []common.Address) []common.Address
|
||||
|
||||
Trc21FeeCapacity map[common.Address]*big.Int
|
||||
|
||||
PendingNonce func(addr common.Address) uint64
|
||||
|
|
@ -232,15 +227,9 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
|
|||
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
|
||||
// overflow the number of permitted transactions from a single accoun
|
||||
// (i.e. max cancellable via out-of-bound transaction).
|
||||
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
|
||||
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
||||
}
|
||||
|
||||
// Verify no authorizations will invalidate existing transactions known to
|
||||
// the pool.
|
||||
if opts.KnownConflicts != nil {
|
||||
if conflicts := opts.KnownConflicts(from, tx.SetCodeAuthorities()); len(conflicts) > 0 {
|
||||
return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved)
|
||||
if opts.UsedAndLeftSlots != nil {
|
||||
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
|
||||
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue