Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 7 additions & 26 deletions mempool/txpool/legacypool/legacypool.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package legacypool

import (
"errors"
"maps"
"math/big"
"slices"
Expand All @@ -31,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
Expand Down Expand Up @@ -58,25 +58,6 @@ const (
txMaxSize = 4 * txSlotSize // 128KB
)

var (
// ErrTxPoolOverflow is returned if the transaction pool is full and can't accept
// another remote transaction.
ErrTxPoolOverflow = errors.New("txpool is full")

// ErrOutOfOrderTxFromDelegated is returned when the transaction with gapped
// nonce received from the accounts with delegation or pending delegation.
ErrOutOfOrderTxFromDelegated = errors.New("gapped-nonce tx from 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 (
evictionInterval = time.Minute // Time interval to check for evictable transactions
statsReportInterval = 8 * time.Second // Time interval to report transaction pool stats
Expand Down Expand Up @@ -623,7 +604,7 @@ func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error {
if pending == nil {
// Transaction with gapped nonce is not supported for delegated accounts
if pool.pendingNonces.get(from) != tx.Nonce() {
return ErrOutOfOrderTxFromDelegated
return legacypool.ErrOutOfOrderTxFromDelegated
}
return nil
}
Expand Down Expand Up @@ -654,7 +635,7 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
count += queue.Len()
}
if count > 1 {
return ErrAuthorityReserved
return legacypool.ErrAuthorityReserved
}
// Because there is no exclusive lock held between different subpools
// when processing transactions, the SetCode transaction may be accepted
Expand All @@ -665,7 +646,7 @@ func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
// that attackers cannot easily stack a SetCode transaction when the sender
// is reserved by other pools.
if pool.reserver.Has(auth) {
return ErrAuthorityReserved
return legacypool.ErrAuthorityReserved
}
}
}
Expand Down Expand Up @@ -730,7 +711,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) {
// replacements to 25% of the slots
if pool.changesSinceReorg > int(pool.config.GlobalSlots/4) {
throttleTxMeter.Mark(1)
return false, ErrTxPoolOverflow
return false, legacypool.ErrTxPoolOverflow
}

// New transaction is better than our worse ones, make room for it.
Expand All @@ -741,7 +722,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) {
if !success {
log.Trace("Discarding overflown transaction", "hash", hash)
overflowedTxMeter.Mark(1)
return false, ErrTxPoolOverflow
return false, legacypool.ErrTxPoolOverflow
}

// If the new transaction is a future transaction it should never churn pending transactions
Expand All @@ -760,7 +741,7 @@ func (pool *LegacyPool) add(tx *types.Transaction) (replaced bool, err error) {
pool.priced.Put(dropTx)
}
log.Trace("Discarding future transaction replacing pending tx", "hash", hash)
return false, ErrFutureReplacePending
return false, legacypool.ErrFutureReplacePending
}
}

Expand Down
13 changes: 7 additions & 6 deletions mempool/txpool/legacypool/legacypool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -1696,8 +1697,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.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); !errors.Is(err, ErrFutureReplacePending) {
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, ErrFutureReplacePending)
if err := pool.addRemoteSync(pricedTransaction(5, 100000, big.NewInt(6), keys[1])); !errors.Is(err, legacypool.ErrFutureReplacePending) {
t.Fatalf("adding future replace transaction error mismatch: have %v, want %v", err, legacypool.ErrFutureReplacePending)
}
pending, queued = pool.Stats()
if pending != 4 {
Expand Down Expand Up @@ -2297,8 +2298,8 @@ func TestSetCodeTransactions(t *testing.T) {
statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)})

// Send gapped transaction, it should be rejected.
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, ErrOutOfOrderTxFromDelegated) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrOutOfOrderTxFromDelegated, err)
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, legacypool.ErrOutOfOrderTxFromDelegated) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, legacypool.ErrOutOfOrderTxFromDelegated, err)
}
// Send transactions. First is accepted, second is rejected.
if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil {
Expand Down Expand Up @@ -2377,8 +2378,8 @@ func TestSetCodeTransactions(t *testing.T) {
t.Fatalf("%s: failed to add with pending delegation: %v", name, err)
}
// Delegation rejected since two txs are already in-flight.
if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); !errors.Is(err, ErrAuthorityReserved) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, ErrAuthorityReserved, err)
if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyB}})); !errors.Is(err, legacypool.ErrAuthorityReserved) {
t.Fatalf("%s: error mismatch: want %v, have %v", name, legacypool.ErrAuthorityReserved, err)
}
},
},
Expand Down
41 changes: 24 additions & 17 deletions mempool/txpool/locals/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,36 @@ package locals

import (
"errors"
"strings"

"github.com/cosmos/evm/mempool/txpool"
"github.com/cosmos/evm/mempool/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
)

// IsTemporaryReject determines whether the given error indicates a temporary
// reason to reject a transaction from being included in the txpool. The result
// may change if the txpool's state changes later.
var (
// ErrNonceGap is returned if the tx nonce is higher than the account nonce.
// This is a duplicate of mempool.ErrNonceGap to avoid import cycle.
ErrNonceGap = errors.New("tx nonce is higher than account nonce")
)

// IsTemporaryReject determines whether the given error indicates a temporary reason to reject a
// transaction from being included in the txpool. The result may change if the txpool's state changes later.
// We use strings.Contains instead of errors.Is because we are passing in rawLog errors.
func IsTemporaryReject(err error) bool {
if err == nil {
return false
}

switch {
case errors.Is(err, legacypool.ErrOutOfOrderTxFromDelegated):
return true
case errors.Is(err, txpool.ErrInflightTxLimitReached):
case strings.Contains(err.Error(), legacypool.ErrOutOfOrderTxFromDelegated.Error()),
strings.Contains(err.Error(), txpool.ErrInflightTxLimitReached.Error()),
strings.Contains(err.Error(), legacypool.ErrAuthorityReserved.Error()),
strings.Contains(err.Error(), txpool.ErrUnderpriced.Error()),
strings.Contains(err.Error(), legacypool.ErrTxPoolOverflow.Error()),
strings.Contains(err.Error(), legacypool.ErrFutureReplacePending.Error()),
strings.Contains(err.Error(), ErrNonceGap.Error()):
return true
case errors.Is(err, legacypool.ErrAuthorityReserved):
return true
case errors.Is(err, txpool.ErrUnderpriced):
return true
case errors.Is(err, legacypool.ErrTxPoolOverflow):
return true
case errors.Is(err, legacypool.ErrFutureReplacePending):
return true
default:
return false
}

return false
}
3 changes: 2 additions & 1 deletion mempool/txpool/locals/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"

"github.com/cosmos/evm/mempool/txpool"
"github.com/cosmos/evm/mempool/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
)

func TestIsTemporaryReject_PositiveCases(t *testing.T) {
Expand All @@ -19,6 +19,7 @@ func TestIsTemporaryReject_PositiveCases(t *testing.T) {
{name: "underpriced", err: txpool.ErrUnderpriced},
{name: "txpool overflow", err: legacypool.ErrTxPoolOverflow},
{name: "future replace pending", err: legacypool.ErrFutureReplacePending},
{name: "tx nonce is higher than account nonce", err: ErrNonceGap},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
Expand Down
Loading
Loading