Skip to content
Merged
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
- [\#495](https://github.com/cosmos/evm/pull/495) Allow immediate SIGINT interrupt when mempool is not empty
- [\#416](https://github.com/cosmos/evm/pull/416) Fix regression in CometBlockResultByNumber when height is 0 to use the latest block. This fixes eth_getFilterLogs RPC.
- [\#545](https://github.com/cosmos/evm/pull/545) Check if mempool is not nil before accepting nonce gap error tx.
- [\#585](https://github.com/cosmos/evm/pull/585) Use zero constructor to avoid nil pointer panic when BaseFee is 0d
- [\#585](https://github.com/cosmos/evm/pull/585) Use zero constructor to avoid nil pointer panic when BaseFee is 0d
- [\#591](https://github.com/cosmos/evm/pull/591) CheckTxHandler should handle "invalid nonce" tx

### IMPROVEMENTS

Expand Down
16 changes: 8 additions & 8 deletions ante/evm/09_increment_sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ func IncrementNonce(
accountNonce := account.GetSequence()
// we merged the accountNonce verification to accountNonce increment, so when tx includes multiple messages
// with same sender, they'll be accepted.
if txNonce != accountNonce {
if txNonce > accountNonce {
return errorsmod.Wrapf(
mempool.ErrNonceGap,
"tx nonce: %d, account accountNonce: %d", txNonce, accountNonce,
)
}
if txNonce > accountNonce {
return errorsmod.Wrapf(
errortypes.ErrInvalidSequence,
mempool.ErrNonceGap,
"tx nonce: %d, account accountNonce: %d", txNonce, accountNonce,
)
}
if txNonce < accountNonce {
return errorsmod.Wrapf(
mempool.ErrNonceLow,
"invalid nonce; got %d, expected %d", txNonce, accountNonce,
)
}
Expand Down
2 changes: 1 addition & 1 deletion mempool/check_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func NewCheckTxHandler(mempool *ExperimentalEVMMempool) types.CheckTxHandler {
gInfo, result, anteEvents, err := runTx(request.Tx, nil)
if err != nil {
// detect if there is a nonce gap error (only returned for EVM transactions)
if errors.Is(err, ErrNonceGap) {
if errors.Is(err, ErrNonceGap) || errors.Is(err, ErrNonceLow) {
// send it to the mempool for further triage
err := mempool.InsertInvalidNonce(request.Tx)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions mempool/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ var (
ErrExpectedOneError = errors.New("expected 1 error")
ErrNotEVMTransaction = errors.New("transaction is not an EVM transaction")
ErrNonceGap = errors.New("tx nonce is higher than account nonce")
ErrNonceLow = errors.New("tx nonce is lower than account nonce")
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
testkeyring "github.com/cosmos/evm/testutil/keyring"

sdktypes "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
)

func (s *EvmUnitAnteTestSuite) TestIncrementSequence() {
Expand Down Expand Up @@ -38,8 +37,8 @@ func (s *EvmUnitAnteTestSuite) TestIncrementSequence() {
},
},
{
name: "fail: invalid sequence",
expectedError: errors.ErrInvalidSequence,
name: "fail: nonce is low",
expectedError: mempool.ErrNonceLow,
malleate: func(acct sdktypes.AccountI) uint64 {
err := acct.SetSequence(acct.GetSequence() + 1)
s.Require().NoError(err)
Expand Down
81 changes: 75 additions & 6 deletions tests/integration/mempool/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import (
"encoding/hex"
"fmt"
"math/big"
"time"

abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/crypto/tmhash"

evmmempool "github.com/cosmos/evm/mempool"
"github.com/cosmos/evm/testutil/integration/base/factory"
"github.com/cosmos/evm/testutil/keyring"
evmtypes "github.com/cosmos/evm/x/vm/types"
Expand Down Expand Up @@ -67,6 +69,29 @@ func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce i
return tx
}

// createEVMTransaction creates an EVM transaction using the provided key
func (s *IntegrationTestSuite) createEVMValueTransferDynamicFeeTx(key keyring.Key, nonce int, gasFeeCap, gasTipCap *big.Int) sdk.Tx {
to := s.keyring.GetKey(1).Addr

if nonce < 0 {
s.Require().NoError(fmt.Errorf("nonce must be non-negative"))
}

ethTxArgs := evmtypes.EvmTxArgs{
Nonce: uint64(nonce),
To: &to,
Amount: big.NewInt(1000),
GasLimit: TxGas,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
Input: nil,
}
tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs)
s.Require().NoError(err)

return tx
}

// createEVMContractDeployTx creates an EVM transaction for contract deployment
func (s *IntegrationTestSuite) createEVMContractDeployTx(key keyring.Key, gasPrice *big.Int, data []byte) sdk.Tx {
ethTxArgs := evmtypes.EvmTxArgs{
Expand All @@ -86,29 +111,48 @@ func (s *IntegrationTestSuite) createEVMContractDeployTx(key keyring.Key, gasPri
// checkTxs call abci CheckTx for multipile transactions
func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) error {
for _, tx := range txs {
if err := s.checkTx(tx); err != nil {
if res, err := s.checkTx(tx); err != nil {
if err != nil {
return fmt.Errorf("failed to execute CheckTx for tx: %s", s.getTxHash(tx))
}
if res.Code != abci.CodeTypeOK {
return fmt.Errorf("tx (%s) failed to pass CheckTx with log: %s", s.getTxHash(tx), res.Log)
}
return err
}
}
return nil
}

// checkTxs call abci CheckTx for a transaction
func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error {
func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) (*abci.ResponseCheckTx, error) {
txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx)
if err != nil {
return fmt.Errorf("failed to encode cosmos tx: %w", err)
return nil, fmt.Errorf("failed to encode cosmos tx: %w", err)
}

_, err = s.network.App.CheckTx(&abci.RequestCheckTx{
res, err := s.network.App.CheckTx(&abci.RequestCheckTx{
Tx: txBytes,
Type: abci.CheckTxType_New,
})
if err != nil {
return fmt.Errorf("failed to encode cosmos tx: %w", err)
return nil, fmt.Errorf("failed to execute CheckTx: %w", err)
}

return nil
return res, nil
}

func (s *IntegrationTestSuite) getTxBytes(txs []sdk.Tx) ([][]byte, error) {
txEncoder := s.network.App.GetTxConfig().TxEncoder()
txBytes := make([][]byte, 0)
for _, tx := range txs {
bz, err := txEncoder(tx)
if err != nil {
return nil, fmt.Errorf("failed to encode tx: %w", err)
}
txBytes = append(txBytes, bz)
}
return txBytes, nil
}

// getTxHashes returns transaction hashes for multiple transactions
Expand Down Expand Up @@ -150,3 +194,28 @@ func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasL

return new(big.Int).Sub(gasPrice, baseFee)
}

// notifyNewBlockToMempool triggers the natural block notification mechanism used in production.
// This sends a ChainHeadEvent that causes the mempool to update its state and remove committed transactions.
// The event subscription mechanism naturally calls Reset() which triggers the transaction cleanup process.
func (s *IntegrationTestSuite) notifyNewBlockToMempool() {
// Get the EVM mempool from the app
evmMempool := s.network.App.GetMempool()

// Access the underlying blockchain interface from the EVM mempool
if evmMempoolCast, ok := evmMempool.(*evmmempool.ExperimentalEVMMempool); ok {
blockchain := evmMempoolCast.GetBlockchain()

// Trigger a new block notification
// This sends a ChainHeadEvent that the mempool subscribes to.
// The TxPool's event loop receives this and calls Reset() for each subpool,
// which naturally removes committed transactions via demoteUnexecutables().
blockchain.NotifyNewBlock()

// The ChainHeadEvent is processed asynchronously, so we need to wait a bit
// for the event to be processed and the reset to complete.
// In integration tests, this might need a small delay to ensure the event
// is processed before we check the mempool state.
time.Sleep(100 * time.Millisecond)
}
}
Loading
Loading