Skip to content
Open
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
01cba0d
wip: set up local journal
vladjdk Sep 22, 2025
424565e
fix conflicts
almk-dev Oct 21, 2025
00b07d0
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Oct 22, 2025
bcff45c
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Oct 23, 2025
a720a8d
add rejection handling and new tests
almk-dev Oct 24, 2025
e5cd6f5
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Oct 24, 2025
8433cc5
add journal file opening step
almk-dev Oct 27, 2025
0ecd5ee
add local tx configuration to mempool params
almk-dev Oct 27, 2025
d1cbd4f
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Oct 27, 2025
f83e7ee
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Oct 28, 2025
14d219e
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Oct 31, 2025
87694af
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 4, 2025
bf7b0d1
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 4, 2025
b310ae3
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 4, 2025
75efc7d
use sdk legacy
almk-dev Nov 4, 2025
05cff08
consilidate error checking func
almk-dev Nov 5, 2025
bffcec6
check both resp log and err
almk-dev Nov 5, 2025
efdbf2d
fix string match case
almk-dev Nov 5, 2025
7864f9d
remove dup check
almk-dev Nov 5, 2025
fc2aace
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 5, 2025
b138e61
expect error for last tx
almk-dev Nov 5, 2025
ac05230
filter by nonce idx
almk-dev Nov 5, 2025
825117a
rev
almk-dev Nov 5, 2025
dc1c1fb
Merge branch 'vlad/integrate-txtracker' into abdul/align-temp-reject
almk-dev Nov 5, 2025
ef631ab
expect error for all except
almk-dev Nov 5, 2025
240b021
use nonce gap
almk-dev Nov 5, 2025
f1224f2
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 5, 2025
1ced857
Merge branch 'vlad/integrate-txtracker' into abdul/align-temp-reject
almk-dev Nov 5, 2025
deafb4e
rev
almk-dev Nov 5, 2025
6c4a6b2
rm nonce gap
almk-dev Nov 5, 2025
0cf0816
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 7, 2025
687a411
Merge branch 'vlad/integrate-txtracker' into abdul/align-temp-reject
almk-dev Nov 7, 2025
26f02d3
add back nonce gap case
almk-dev Nov 7, 2025
25efd23
fix wrong arg
almk-dev Nov 7, 2025
a68e02d
add panic
almk-dev Nov 7, 2025
7a3dc86
edit return
almk-dev Nov 7, 2025
307fb07
use contains instead of match
almk-dev Nov 7, 2025
17cea3d
fix system ordering test
almk-dev Nov 7, 2025
ad6683f
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 12, 2025
c52c94f
Merge branch 'vlad/integrate-txtracker' into abdul/align-temp-reject
almk-dev Nov 12, 2025
3d8f2ae
Merge pull request #802 from cosmos/abdul/align-temp-reject
almk-dev Nov 12, 2025
f0fdd77
fix orphaned rlp file creation
almk-dev Nov 12, 2025
ae7f9db
fix path creation before writing
almk-dev Nov 12, 2025
fc7c2f3
remove print
almk-dev Nov 12, 2025
c431e8f
dedupe mempool config
almk-dev Nov 12, 2025
e885db3
fix journal name validation
almk-dev Nov 12, 2025
11c35d1
fix data race
almk-dev Nov 12, 2025
0d1d4cb
remove parseAddress helper
almk-dev Nov 12, 2025
67a175e
gci lint
almk-dev Nov 12, 2025
4311ae4
lint
almk-dev Nov 12, 2025
7953ddc
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 12, 2025
82624f0
mirror geth logic
almk-dev Nov 12, 2025
be63fbb
add back sanitized value to match geth
almk-dev Nov 12, 2025
706e9b9
uncomment temp err check
almk-dev Nov 12, 2025
f03440a
account for temp rejections in system tests
almk-dev Nov 12, 2025
a406eb1
account for temp rejections in system tests in legacy case
almk-dev Nov 12, 2025
6b7cdd3
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 13, 2025
6d48eec
move rotate to within recheck lock
almk-dev Nov 13, 2025
83b68da
handle gh error checking complaint
almk-dev Nov 13, 2025
421c515
remove redundant sanitization for rejournal time
almk-dev Nov 13, 2025
4d8e357
negate sanitize check
almk-dev Nov 13, 2025
e3b2ce2
update tests to only use resubmit returns
almk-dev Nov 13, 2025
47d4174
gofumpt
almk-dev Nov 13, 2025
fc35a1b
add tests
almk-dev Nov 13, 2025
75a0b3d
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 13, 2025
bc9b918
add runners
almk-dev Nov 13, 2025
534f369
fix replacement
almk-dev Nov 13, 2025
e2df42e
fix typo
almk-dev Nov 14, 2025
9a2b4c6
add mutex
almk-dev Nov 14, 2025
9c2e436
Merge branch 'main' into vlad/integrate-txtracker
almk-dev Nov 14, 2025
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@

### FEATURES

- [\#646](https://github.com/cosmos/evm/pull/646) Add TxTracker support for tracking priority transactions and handling temporary rejections
- [\#665](https://github.com/cosmos/evm/pull/665) Add EvmCodec address codec implementation
- [\#346](https://github.com/cosmos/evm/pull/346) Add eth_createAccessList method and implementation
- [\#337](https://github.com/cosmos/evm/pull/337) Support state overrides in eth_call.
Expand Down
66 changes: 53 additions & 13 deletions mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"

ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
Expand All @@ -14,6 +17,7 @@ import (
"github.com/cosmos/evm/mempool/miner"
"github.com/cosmos/evm/mempool/txpool"
"github.com/cosmos/evm/mempool/txpool/legacypool"
"github.com/cosmos/evm/mempool/txpool/locals"
"github.com/cosmos/evm/rpc/stream"
evmtypes "github.com/cosmos/evm/x/vm/types"

Expand Down Expand Up @@ -45,9 +49,10 @@ type (
vmKeeper VMKeeperI

/** Mempools **/
txPool *txpool.TxPool
legacyTxPool *legacypool.LegacyPool
cosmosPool sdkmempool.ExtMempool
txPool *txpool.TxPool
legacyTxPool *legacypool.LegacyPool
localTxTracker *locals.TxTracker
cosmosPool sdkmempool.ExtMempool

/** Utils **/
logger log.Logger
Expand Down Expand Up @@ -146,6 +151,27 @@ func NewExperimentalEVMMempool(
panic("tx pool should contain only legacypool")
}

var localTxTracker *locals.TxTracker

if !legacyConfig.NoLocals {
rejournal := legacyConfig.Rejournal
if rejournal < time.Second {
logger.Debug("Sanitizing invalid txpool journal time", "provided", rejournal, "updated", time.Second)
rejournal = time.Second
}
// Ensure journal directory exists before starting the tracker
if dir := filepath.Dir(legacyConfig.Journal); dir != "." {
if err := os.MkdirAll(dir, 0755); err != nil {
logger.Error("failed to create journal directory", "error", err)
}
}
localTxTracker = locals.New(legacyConfig.Journal, rejournal, blockchain.Config(), txPool)
err := localTxTracker.Start()
if err != nil {
return nil
}
}

// TODO: move this logic to evmd.createMempoolConfig and set the max tx there
// Create Cosmos Mempool from configuration
cosmosPoolConfig := config.CosmosPoolConfig
Expand Down Expand Up @@ -180,23 +206,33 @@ func NewExperimentalEVMMempool(
cosmosPool = sdkmempool.NewPriorityMempool(*cosmosPoolConfig)

evmMempool := &ExperimentalEVMMempool{
vmKeeper: vmKeeper,
txPool: txPool,
legacyTxPool: txPool.Subpools[0].(*legacypool.LegacyPool),
cosmosPool: cosmosPool,
logger: logger,
txConfig: txConfig,
blockchain: blockchain,
blockGasLimit: config.BlockGasLimit,
minTip: config.MinTip,
anteHandler: config.AnteHandler,
vmKeeper: vmKeeper,
txPool: txPool,
legacyTxPool: txPool.Subpools[0].(*legacypool.LegacyPool),
localTxTracker: localTxTracker,
cosmosPool: cosmosPool,
logger: logger,
txConfig: txConfig,
blockchain: blockchain,
blockGasLimit: config.BlockGasLimit,
minTip: config.MinTip,
anteHandler: config.AnteHandler,
}

vmKeeper.SetEvmMempool(evmMempool)

return evmMempool
}

// TrackLocalTxs tracks transactions as local priority via TxTracker.
// No-op if local tracking is not initialized.
func (m *ExperimentalEVMMempool) TrackLocalTxs(txs []*ethtypes.Transaction) {
if m == nil || m.localTxTracker == nil || len(txs) == 0 {
return
}
m.localTxTracker.TrackAll(txs)
}

// GetBlockchain returns the blockchain interface used for chain head event notifications.
// This is primarily used to notify the mempool when new blocks are finalized.
func (m *ExperimentalEVMMempool) GetBlockchain() *Blockchain {
Expand Down Expand Up @@ -429,6 +465,10 @@ func (m *ExperimentalEVMMempool) Close() error {
errs = append(errs, fmt.Errorf("failed to close txpool: %w", err))
}

if err := m.localTxTracker.Stop(); err != nil {
errs = append(errs, fmt.Errorf("failed to close localTxTracker: %w", err))
}

return errors.Join(errs...)
}

Expand Down
111 changes: 49 additions & 62 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 @@ -135,43 +116,49 @@ type BlockChain interface {
StateAt(root common.Hash) (vm.StateDB, error)
}

// Config are the configuration parameters of the transaction pool.
// Config defines the configuration for the EVM mempool transaction pool.
type Config struct {
Locals []common.Address // Addresses that should be treated by default as local
NoLocals bool // Whether local transaction handling should be disabled
Journal string // Journal of local transactions to survive node restarts
Rejournal time.Duration // Time interval to regenerate the local transaction journal

PriceLimit uint64 // Minimum gas price to enforce for acceptance into the pool
PriceBump uint64 // Minimum price bump percentage to replace an already existing transaction (nonce)

AccountSlots uint64 // Number of executable transaction slots guaranteed per account
GlobalSlots uint64 // Maximum number of executable transaction slots for all accounts
AccountQueue uint64 // Maximum number of non-executable transaction slots permitted per account
GlobalQueue uint64 // Maximum number of non-executable transaction slots for all accounts

Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
}

// DefaultConfig contains the default configurations for the transaction pool.
// PriceLimit is the minimum gas price to enforce for acceptance into the pool
PriceLimit uint64 `mapstructure:"price-limit"`
// PriceBump is the minimum price bump percentage to replace an already existing transaction (nonce)
PriceBump uint64 `mapstructure:"price-bump"`
// AccountSlots is the number of executable transaction slots guaranteed per account
AccountSlots uint64 `mapstructure:"account-slots"`
// GlobalSlots is the maximum number of executable transaction slots for all accounts
GlobalSlots uint64 `mapstructure:"global-slots"`
// AccountQueue is the maximum number of non-executable transaction slots permitted per account
AccountQueue uint64 `mapstructure:"account-queue"`
// GlobalQueue is the maximum number of non-executable transaction slots for all accounts
GlobalQueue uint64 `mapstructure:"global-queue"`
// Lifetime is the maximum amount of time non-executable transaction are queued
Lifetime time.Duration `mapstructure:"lifetime"`
// Locals is the set of addresses that should be treated by default as local
Locals []string `mapstructure:"locals"`
// NoLocals disables local transaction handling, exempting local accounts from pricing and acceptance
NoLocals bool `mapstructure:"no-locals"`
// Journal is the path to the local transaction journal file
Journal string `mapstructure:"journal"`
// Rejournal is the time interval to regenerate the local transaction journal
Rejournal time.Duration `mapstructure:"rejournal"`
}

// DefaultConfig returns the default mempool configuration
var DefaultConfig = Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,

PriceLimit: 1,
PriceBump: 10,

AccountSlots: 16,
GlobalSlots: 4096 + 1024, // urgent + floating queue capacity with 4:1 ratio
AccountQueue: 64,
GlobalQueue: 1024,

Lifetime: 3 * time.Hour,
}

// sanitize checks the provided user configurations and changes anything that's
// unreasonable or unworkable.
func (config *Config) sanitize() Config {
PriceLimit: 1, // Minimum gas price of 1 wei
PriceBump: 10, // 10% price bump to replace transaction
AccountSlots: 16, // 16 executable transaction slots per account
GlobalSlots: 5120, // 4096 + 1024 = 5120 global executable slots
AccountQueue: 64, // 64 non-executable transaction slots per account
GlobalQueue: 1024, // 1024 global non-executable slots
Lifetime: 3 * time.Hour, // 3 hour lifetime for queued transactions
Locals: []string{}, // No local addresses by default
NoLocals: false, // Local transaction handling enabled by default
Journal: "transactions.rlp", // Default journal filename
Rejournal: time.Hour, // Regenerate journal every hour
}

// Sanitize checks the provided user configurations and changes anything that's unreasonable or unworkable.
func (config *Config) Sanitize() Config {
conf := *config
if conf.PriceLimit < 1 {
log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultConfig.PriceLimit)
Expand Down Expand Up @@ -266,7 +253,7 @@ type txpoolResetRequest struct {
// transactions from the network.
func New(config Config, chain BlockChain) *LegacyPool {
// Sanitize the input to ensure no vulnerable gas prices are set
config = (&config).sanitize()
config = config.Sanitize()

// Create the transaction pool with its initial settings
pool := &LegacyPool{
Expand Down Expand Up @@ -623,7 +610,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 +641,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 +652,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 +717,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 +728,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 +747,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
37 changes: 37 additions & 0 deletions mempool/txpool/locals/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package locals

import (
"errors"
"strings"

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

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 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
}

return false
}
Loading
Loading