mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +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.
|
// making the transaction invalid, rather a DOS protection.
|
||||||
ErrOversizedData = errors.New("oversized data")
|
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
|
// 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
|
// 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
|
// input transaction of non-blob type when a blob transaction from this sender
|
||||||
// remains pending (and vice-versa).
|
// remains pending (and vice-versa).
|
||||||
ErrAlreadyReserved = errors.New("address already reserved")
|
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")
|
ErrZeroGasPrice = errors.New("zero gas price")
|
||||||
|
|
||||||
ErrUnderMinGasPrice = errors.New("under min 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
|
// ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
|
||||||
// another remote transaction.
|
// another remote transaction.
|
||||||
ErrTxPoolOverflow = errors.New("txpool is full")
|
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 (
|
var (
|
||||||
|
|
@ -660,22 +673,8 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
opts := &txpool.ValidationOptionsWithState{
|
opts := &txpool.ValidationOptionsWithState{
|
||||||
State: pool.currentState,
|
State: pool.currentState,
|
||||||
|
|
||||||
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
|
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
|
||||||
UsedAndLeftSlots: func(addr common.Address) (int, int) {
|
UsedAndLeftSlots: nil, // Pool has own mechanism to limit the number of transactions
|
||||||
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
|
|
||||||
},
|
|
||||||
ExistingExpenditure: func(addr common.Address) *big.Int {
|
ExistingExpenditure: func(addr common.Address) *big.Int {
|
||||||
if list := pool.pending[addr]; list != nil {
|
if list := pool.pending[addr]; list != nil {
|
||||||
return list.totalcost
|
return list.totalcost
|
||||||
|
|
@ -690,18 +689,6 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
}
|
}
|
||||||
return nil
|
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,
|
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 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -835,7 +861,7 @@ func (pool *LegacyPool) add(tx *types.Transaction, local bool) (replaced bool, e
|
||||||
pool.priced.Put(dropTx, false)
|
pool.priced.Put(dropTx, false)
|
||||||
}
|
}
|
||||||
log.Trace("Discarding future transaction replacing pending tx", "hash", hash)
|
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)
|
t.Fatalf("failed to add well priced transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Ensure that replacing a pending transaction with a future transaction fails
|
// 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 {
|
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, txpool.ErrFutureReplacePending)
|
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending)
|
||||||
}
|
}
|
||||||
pending, queued = pool.Stats()
|
pending, queued = pool.Stats()
|
||||||
if pending != 2 {
|
if pending != 2 {
|
||||||
|
|
@ -2680,12 +2680,12 @@ func TestSetCodeTransactions(t *testing.T) {
|
||||||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, minGasPrice, keyA)); err != nil {
|
if err := pool.addRemoteSync(pricedTransaction(0, 100000, minGasPrice, keyA)); err != nil {
|
||||||
t.Fatalf("%s: failed to add remote transaction: %v", name, err)
|
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) {
|
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, txpool.ErrAccountLimitExceeded, err)
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||||
}
|
}
|
||||||
// Also check gapped transaction.
|
// Also check gapped transaction.
|
||||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, new(big.Int).Set(minGasPrice), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
|
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, txpool.ErrAccountLimitExceeded, err)
|
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrInflightTxLimitReached, err)
|
||||||
}
|
}
|
||||||
// Replace by fee.
|
// Replace by fee.
|
||||||
if err := pool.addRemoteSync(pricedTransaction(0, 100000, legacyReplacePrice, keyA)); err != nil {
|
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)
|
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, new(big.Int).Set(minGasPrice), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) {
|
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, txpool.ErrAccountLimitExceeded, err)
|
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 {
|
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)
|
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)
|
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 {
|
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)
|
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)
|
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)
|
t.Fatalf("failed to add with remote setcode transaction: %v", err)
|
||||||
}
|
}
|
||||||
// Try to add a transactions in
|
// Try to add a transactions in
|
||||||
if err := pool.addRemoteSync(pricedTransaction(2, 100000, new(big.Int).Set(legacyPrice), keyA)); !errors.Is(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, txpool.ErrAccountLimitExceeded)
|
t.Fatalf("unexpected error %v, expecting %v", err, ErrInflightTxLimitReached)
|
||||||
}
|
}
|
||||||
// Simulate the chain moving
|
// Simulate the chain moving
|
||||||
blockchain.statedb.SetNonce(addrA, 2)
|
blockchain.statedb.SetNonce(addrA, 2)
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ type ValidationOptionsWithState struct {
|
||||||
// nonce gaps will be ignored and permitted.
|
// nonce gaps will be ignored and permitted.
|
||||||
FirstNonceGap func(addr common.Address) uint64
|
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
|
// used and the number still permitted for an account. New transactions will
|
||||||
// be rejected once the number of remaining slots reaches zero.
|
// be rejected once the number of remaining slots reaches zero.
|
||||||
UsedAndLeftSlots func(addr common.Address) (int, int)
|
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.
|
// transaction's cost with the given nonce to check for overdrafts.
|
||||||
ExistingCost func(addr common.Address, nonce uint64) *big.Int
|
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
|
Trc21FeeCapacity map[common.Address]*big.Int
|
||||||
|
|
||||||
PendingNonce func(addr common.Address) uint64
|
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
|
// Transaction takes a new nonce value out of the pool. Ensure it doesn't
|
||||||
// overflow the number of permitted transactions from a single accoun
|
// overflow the number of permitted transactions from a single accoun
|
||||||
// (i.e. max cancellable via out-of-bound transaction).
|
// (i.e. max cancellable via out-of-bound transaction).
|
||||||
if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
|
if opts.UsedAndLeftSlots != nil {
|
||||||
return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
|
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue