From fca99f23521f1eb06543ac1b1b239559940febb9 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Thu, 21 Aug 2025 21:00:41 +0900 Subject: [PATCH 01/20] WIP: test(mempool): add integration test --- tests/integration/mempool/test_helper.go | 306 +++++++++ .../mempool/test_mempool_integration.go | 283 -------- .../mempool/test_mempool_integration_abci.go | 645 ++++++++++++++++++ 3 files changed, 951 insertions(+), 283 deletions(-) create mode 100644 tests/integration/mempool/test_helper.go create mode 100644 tests/integration/mempool/test_mempool_integration_abci.go diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go new file mode 100644 index 000000000..9a09acbb2 --- /dev/null +++ b/tests/integration/mempool/test_helper.go @@ -0,0 +1,306 @@ +package mempool + +import ( + "fmt" + "math/big" + + abci "github.com/cometbft/cometbft/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/evm/crypto/ethsecp256k1" + "github.com/cosmos/evm/testutil/keyring" + evmtypes "github.com/cosmos/evm/x/vm/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key +func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { + feeDenom := "aatom" + gasLimit := uint64(TxGas) + + // Calculate fee amount from gas price: fee = gas_price * gas_limit + feeAmount := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) + + fmt.Printf("DEBUG: Creating cosmos transaction with gas price: %s aatom/gas, fee: %s %s\n", gasPrice.String(), feeAmount.String(), feeDenom) + + fromAddr := key.AccAddr + toAddr := s.keyring.GetKey(1).AccAddr + amount := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 1000)) + + bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) + + txBuilder := s.network.App.GetTxConfig().NewTxBuilder() + err := txBuilder.SetMsgs(bankMsg) + s.Require().NoError(err) + + txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin(feeDenom, feeAmount.Int64()))) + txBuilder.SetGasLimit(gasLimit) + + // Sign the transaction with proper signing instead of dummy signature + err = s.factory.SignCosmosTx(key.Priv, txBuilder) + s.Require().NoError(err) + + fmt.Printf("DEBUG: Created cosmos transaction successfully\n") + return txBuilder.GetTx() +} + +// createCosmosSendTransaction creates a simple bank send transaction using the first key +func (s *IntegrationTestSuite) createCosmosSendTransaction(gasPrice *big.Int) sdk.Tx { + key := s.keyring.GetKey(0) + return s.createCosmosSendTransactionWithKey(key, gasPrice) +} + +// calculateCosmosGasPrice calculates the gas price for a Cosmos transaction +func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit uint64) *big.Int { + return new(big.Int).Div(big.NewInt(feeAmount), big.NewInt(int64(gasLimit))) //#nosec G115 -- not concern, test +} + +// calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction +// This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee +func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, baseFee *big.Int) *big.Int { + gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) + if baseFee == nil || baseFee.Sign() == 0 { + return gasPrice // No base fee, effective tip equals gas price + } + + if gasPrice.Cmp(baseFee) < 0 { + return big.NewInt(0) // Gas price lower than base fee, effective tip is zero + } + + return new(big.Int).Sub(gasPrice, baseFee) +} + +// createEVMTransaction creates an EVM transaction using the provided key +func (s *IntegrationTestSuite) createEVMTransactionWithKey(key keyring.Key, gasPrice *big.Int) (sdk.Tx, error) { + fmt.Printf("DEBUG: Creating EVM transaction with gas price: %s\n", gasPrice.String()) + + privKey := key.Priv + + // Convert Cosmos address to EVM address + fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) + fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) + + to := common.HexToAddress("0x1234567890123456789012345678901234567890") + ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ + Nonce: 0, + To: &to, + Value: big.NewInt(1000), + Gas: TxGas, + GasPrice: gasPrice, + Data: nil, + }) + + // Convert to ECDSA private key for signing + ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) + if !ok { + return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) + } + + ecdsaPrivKey, err := ethPrivKey.ToECDSA() + if err != nil { + return nil, err + } + + signer := ethtypes.HomesteadSigner{} + signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) + if err != nil { + return nil, err + } + + msgEthTx := &evmtypes.MsgEthereumTx{} + msgEthTx.FromEthereumTx(signedTx) + + txBuilder := s.network.App.GetTxConfig().NewTxBuilder() + err = txBuilder.SetMsgs(msgEthTx) + if err != nil { + return nil, err + } + + fmt.Printf("DEBUG: Created EVM transaction successfully\n") + return txBuilder.GetTx(), nil +} + +// createEVMTransaction creates an EVM transaction using the first key +func (s *IntegrationTestSuite) createEVMTransaction(gasPrice *big.Int) (sdk.Tx, error) { + key := s.keyring.GetKey(0) + return s.createEVMTransactionWithKey(key, gasPrice) +} + +// createEVMContractDeployment creates an EVM transaction for contract deployment +func (s *IntegrationTestSuite) createEVMContractDeployment(key keyring.Key, gasPrice *big.Int, data []byte) (sdk.Tx, error) { + fmt.Printf("DEBUG: Creating EVM contract deployment transaction with gas price: %s\n", gasPrice.String()) + + privKey := key.Priv + + // Convert Cosmos address to EVM address + fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) + fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) + + ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ + Nonce: 0, + To: nil, // nil for contract deployment + Value: big.NewInt(0), + Gas: 100000, + GasPrice: gasPrice, + Data: data, + }) + + // Convert to ECDSA private key for signing + ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) + if !ok { + return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) + } + + ecdsaPrivKey, err := ethPrivKey.ToECDSA() + if err != nil { + return nil, err + } + + signer := ethtypes.HomesteadSigner{} + signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) + if err != nil { + return nil, err + } + + msgEthTx := &evmtypes.MsgEthereumTx{} + msgEthTx.FromEthereumTx(signedTx) + + txBuilder := s.network.App.GetTxConfig().NewTxBuilder() + err = txBuilder.SetMsgs(msgEthTx) + if err != nil { + return nil, err + } + + fmt.Printf("DEBUG: Created EVM contract deployment transaction successfully\n") + return txBuilder.GetTx(), nil +} + +// createEVMValueTransfer creates an EVM transaction for value transfer +func (s *IntegrationTestSuite) createEVMValueTransfer(key keyring.Key, gasPrice *big.Int, value *big.Int, to common.Address) (sdk.Tx, error) { + fmt.Printf("DEBUG: Creating EVM value transfer transaction with gas price: %s\n", gasPrice.String()) + + privKey := key.Priv + + // Convert Cosmos address to EVM address + fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) + fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) + + ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ + Nonce: 0, + To: &to, + Value: value, + Gas: TxGas, + GasPrice: gasPrice, + Data: nil, + }) + + // Convert to ECDSA private key for signing + ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) + if !ok { + return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) + } + + ecdsaPrivKey, err := ethPrivKey.ToECDSA() + if err != nil { + return nil, err + } + + signer := ethtypes.HomesteadSigner{} + signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) + if err != nil { + return nil, err + } + + msgEthTx := &evmtypes.MsgEthereumTx{} + msgEthTx.FromEthereumTx(signedTx) + if err != nil { + return nil, err + } + + txBuilder := s.network.App.GetTxConfig().NewTxBuilder() + err = txBuilder.SetMsgs(msgEthTx) + if err != nil { + return nil, err + } + + fmt.Printf("DEBUG: Created EVM value transfer transaction successfully\n") + return txBuilder.GetTx(), nil +} + +// createEVMTransactionWithNonce creates an EVM transaction with a specific nonce +func (s *IntegrationTestSuite) createEVMTransactionWithNonce(key keyring.Key, gasPrice *big.Int, nonce int) (sdk.Tx, error) { + fmt.Printf("DEBUG: Creating EVM transaction with gas price: %s and nonce: %d\n", gasPrice.String(), nonce) + + privKey := key.Priv + + // Convert Cosmos address to EVM address + fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) + fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) + + to := common.HexToAddress("0x1234567890123456789012345678901234567890") + ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ + Nonce: uint64(nonce), //#nosec G115 -- int overflow is not a concern here + To: &to, + Value: big.NewInt(1000), + Gas: TxGas, + GasPrice: gasPrice, + Data: nil, + }) + + // Convert to ECDSA private key for signing + ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) + if !ok { + return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) + } + + ecdsaPrivKey, err := ethPrivKey.ToECDSA() + if err != nil { + return nil, err + } + + signer := ethtypes.HomesteadSigner{} + signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) + if err != nil { + return nil, err + } + + msgEthTx := &evmtypes.MsgEthereumTx{} + msgEthTx.FromEthereumTx(signedTx) + if err != nil { + return nil, err + } + + txBuilder := s.network.App.GetTxConfig().NewTxBuilder() + err = txBuilder.SetMsgs(msgEthTx) + if err != nil { + return nil, err + } + + fmt.Printf("DEBUG: Created EVM transaction successfully\n") + return txBuilder.GetTx(), nil +} + +func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) ([]*abci.ResponseCheckTx, error) { + result := make([]*abci.ResponseCheckTx, 0) + + for _, tx := range txs { + txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) + if err != nil { + return []*abci.ResponseCheckTx{}, fmt.Errorf("failed to encode cosmos tx: %w", err) + } + + res, err := s.network.App.CheckTx(&abci.RequestCheckTx{ + Tx: txBytes, + Type: abci.CheckTxType_New, + }) + if err != nil { + return []*abci.ResponseCheckTx{}, fmt.Errorf("failed to encode cosmos tx: %w", err) + } + + result = append(result, res) + } + + return result, nil +} diff --git a/tests/integration/mempool/test_mempool_integration.go b/tests/integration/mempool/test_mempool_integration.go index f14671d0c..748edb73d 100644 --- a/tests/integration/mempool/test_mempool_integration.go +++ b/tests/integration/mempool/test_mempool_integration.go @@ -9,15 +9,12 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" - "github.com/cosmos/evm/crypto/ethsecp256k1" "github.com/cosmos/evm/testutil/integration/evm/network" "github.com/cosmos/evm/testutil/keyring" evmtypes "github.com/cosmos/evm/x/vm/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/mempool" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) // Constants @@ -1412,283 +1409,3 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { fmt.Printf("DEBUG: TestNonceGappedEVMTransactions - Completed test case %d/%d: %s\n", i+1, len(testCases), tc.name) } } - -// Helper methods - -// createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key -func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { - feeDenom := "aatom" - gasLimit := uint64(TxGas) - - // Calculate fee amount from gas price: fee = gas_price * gas_limit - feeAmount := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) - - fmt.Printf("DEBUG: Creating cosmos transaction with gas price: %s aatom/gas, fee: %s %s\n", gasPrice.String(), feeAmount.String(), feeDenom) - - fromAddr := key.AccAddr - toAddr := s.keyring.GetKey(1).AccAddr - amount := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 1000)) - - bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err := txBuilder.SetMsgs(bankMsg) - s.Require().NoError(err) - - txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin(feeDenom, feeAmount.Int64()))) - txBuilder.SetGasLimit(gasLimit) - - // Sign the transaction - privKey := key.Priv - // Create a dummy signature for testing - sigData := signing.SingleSignatureData{ - SignMode: signing.SignMode_SIGN_MODE_DIRECT, - Signature: []byte("dummy_signature_for_testing"), - } - sig := signing.SignatureV2{ - PubKey: privKey.PubKey(), - Data: &sigData, - Sequence: 0, - } - err = txBuilder.SetSignatures(sig) - s.Require().NoError(err) - - fmt.Printf("DEBUG: Created cosmos transaction successfully\n") - return txBuilder.GetTx() -} - -// createCosmosSendTransaction creates a simple bank send transaction using the first key -func (s *IntegrationTestSuite) createCosmosSendTransaction(gasPrice *big.Int) sdk.Tx { - key := s.keyring.GetKey(0) - return s.createCosmosSendTransactionWithKey(key, gasPrice) -} - -// calculateCosmosGasPrice calculates the gas price for a Cosmos transaction -func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit uint64) *big.Int { - return new(big.Int).Div(big.NewInt(feeAmount), big.NewInt(int64(gasLimit))) //#nosec G115 -- not concern, test -} - -// calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction -// This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee -func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, baseFee *big.Int) *big.Int { - gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) - if baseFee == nil || baseFee.Sign() == 0 { - return gasPrice // No base fee, effective tip equals gas price - } - - if gasPrice.Cmp(baseFee) < 0 { - return big.NewInt(0) // Gas price lower than base fee, effective tip is zero - } - - return new(big.Int).Sub(gasPrice, baseFee) -} - -// createEVMTransaction creates an EVM transaction using the provided key -func (s *IntegrationTestSuite) createEVMTransactionWithKey(key keyring.Key, gasPrice *big.Int) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM transaction with gas price: %s\n", gasPrice.String()) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - to := common.HexToAddress("0x1234567890123456789012345678901234567890") - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: 0, - To: &to, - Value: big.NewInt(1000), - Gas: TxGas, - GasPrice: gasPrice, - Data: nil, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err - } - - fmt.Printf("DEBUG: Created EVM transaction successfully\n") - return txBuilder.GetTx(), nil -} - -// createEVMTransaction creates an EVM transaction using the first key -func (s *IntegrationTestSuite) createEVMTransaction(gasPrice *big.Int) (sdk.Tx, error) { - key := s.keyring.GetKey(0) - return s.createEVMTransactionWithKey(key, gasPrice) -} - -// createEVMContractDeployment creates an EVM transaction for contract deployment -func (s *IntegrationTestSuite) createEVMContractDeployment(key keyring.Key, gasPrice *big.Int, data []byte) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM contract deployment transaction with gas price: %s\n", gasPrice.String()) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: 0, - To: nil, // nil for contract deployment - Value: big.NewInt(0), - Gas: 100000, - GasPrice: gasPrice, - Data: data, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err - } - - fmt.Printf("DEBUG: Created EVM contract deployment transaction successfully\n") - return txBuilder.GetTx(), nil -} - -// createEVMValueTransfer creates an EVM transaction for value transfer -func (s *IntegrationTestSuite) createEVMValueTransfer(key keyring.Key, gasPrice *big.Int, value *big.Int, to common.Address) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM value transfer transaction with gas price: %s\n", gasPrice.String()) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: 0, - To: &to, - Value: value, - Gas: TxGas, - GasPrice: gasPrice, - Data: nil, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - if err != nil { - return nil, err - } - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err - } - - fmt.Printf("DEBUG: Created EVM value transfer transaction successfully\n") - return txBuilder.GetTx(), nil -} - -// createEVMTransactionWithNonce creates an EVM transaction with a specific nonce -func (s *IntegrationTestSuite) createEVMTransactionWithNonce(key keyring.Key, gasPrice *big.Int, nonce int) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM transaction with gas price: %s and nonce: %d\n", gasPrice.String(), nonce) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - to := common.HexToAddress("0x1234567890123456789012345678901234567890") - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: uint64(nonce), //#nosec G115 -- int overflow is not a concern here - To: &to, - Value: big.NewInt(1000), - Gas: TxGas, - GasPrice: gasPrice, - Data: nil, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - if err != nil { - return nil, err - } - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err - } - - fmt.Printf("DEBUG: Created EVM transaction successfully\n") - return txBuilder.GetTx(), nil -} diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go new file mode 100644 index 000000000..21d91ad13 --- /dev/null +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -0,0 +1,645 @@ +package mempool + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/mempool" + evmtypes "github.com/cosmos/evm/x/vm/types" +) + +// TestTransactionOrdering tests transaction ordering based on fees +func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { + fmt.Printf("DEBUG: Starting TestTransactionOrdering\n") + testCases := []struct { + name string + setupTxs func() []sdk.Tx + verifyFunc func(iterator mempool.Iterator) + }{ + { + name: "mixed EVM and cosmos transaction ordering", + setupTxs: func() []sdk.Tx { + // Create EVM transaction with high gas price + highGasPriceEVMTx, err := s.createEVMTransaction(big.NewInt(5000000000)) + s.Require().NoError(err) + + // Create Cosmos transactions with different fee amounts + highFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(6), big.NewInt(5000000000)) + mediumFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(7), big.NewInt(3000000000)) + lowFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(8), big.NewInt(1000000000)) + + // Insert in non-priority order + return []sdk.Tx{lowFeeCosmosTx, highGasPriceEVMTx, mediumFeeCosmosTx, highFeeCosmosTx} + }, + verifyFunc: func(iterator mempool.Iterator) { + // First transaction should be EVM with highest gas price (5 gaatom = 5000000000 aatom) + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + + ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok) + ethTx := ethMsg.AsTransaction() + s.Require().Equal(big.NewInt(5000000000), ethTx.GasPrice(), "First transaction should be EVM with highest gas price") + + // Second transaction should be Cosmos with high fee (25000 aatom gas price) + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx2 := iterator.Tx() + s.Require().NotNil(tx2) + + // Should be Cosmos transaction with high fee + feeTx := tx2.(sdk.FeeTx) + cosmosGasPrice := s.calculateCosmosGasPrice(feeTx.GetFee().AmountOf("aatom").BigInt().Int64(), feeTx.GetGas()) + s.Require().Equal(big.NewInt(5000000000), cosmosGasPrice, "Second transaction should be Cosmos with 25000 aatom gas price") + }, + }, + { + name: "EVM-only transaction replacement", + setupTxs: func() []sdk.Tx { + // Create first EVM transaction with low fee + lowFeeEVMTx, err := s.createEVMTransaction(big.NewInt(1000000000)) // 1 gaatom + s.Require().NoError(err) + + // Create second EVM transaction with high fee + highFeeEVMTx, err := s.createEVMTransaction(big.NewInt(5000000000)) // 5 gaatom + s.Require().NoError(err) + + // Insert low fee transaction first + return []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} + }, + verifyFunc: func(iterator mempool.Iterator) { + // First transaction should be high fee + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok) + ethTx := ethMsg.AsTransaction() + s.Require().Equal(big.NewInt(5000000000), ethTx.GasPrice()) + iterator = iterator.Next() + s.Require().Nil(iterator) // transaction with same nonce got replaced by higher fee + }, + }, + { + name: "EVM-only transaction replacement", + setupTxs: func() []sdk.Tx { + key := s.keyring.GetKey(0) + // Create first EVM transaction with low fee + lowFeeEVMTx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) // 1 gaatom + s.Require().NoError(err) + + // Create second EVM transaction with high fee + highFeeEVMTx, err := s.createEVMTransactionWithNonce(key, big.NewInt(5000000000), 0) // 5 gaatom + s.Require().NoError(err) + + // Insert low fee transaction first + return []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} + }, + verifyFunc: func(iterator mempool.Iterator) { + // First transaction should be high fee + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok) + ethTx := ethMsg.AsTransaction() + s.Require().Equal(big.NewInt(5000000000), ethTx.GasPrice()) + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx2 := iterator.Tx() + s.Require().NotNil(tx2) + ethMsg, ok = tx2.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok) + ethTx = ethMsg.AsTransaction() + s.Require().Equal(big.NewInt(1000000000), ethTx.GasPrice()) + iterator = iterator.Next() + s.Require().Nil(iterator) + }, + }, + { + name: "cosmos-only transaction replacement", + setupTxs: func() []sdk.Tx { + highFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom + lowFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom + mediumFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom + + // Insert in random order + return []sdk.Tx{mediumFeeTx, lowFeeTx, highFeeTx} + }, + verifyFunc: func(iterator mempool.Iterator) { + // Should get first transaction from cosmos pool + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + // Calculate gas price: fee_amount / gas_limit = 5000000000 / 200000 = 25000 + expectedGasPrice := big.NewInt(5000000000) + feeTx := tx1.(sdk.FeeTx) + actualGasPrice := s.calculateCosmosGasPrice(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas()) + s.Require().Equal(expectedGasPrice, actualGasPrice, "Expected gas price should match fee_amount/gas_limit") + iterator = iterator.Next() + s.Require().Nil(iterator) + }, + }, + { + name: "mixed EVM and Cosmos transactions with equal effective tips", + setupTxs: func() []sdk.Tx { + // Create transactions with equal effective tips (assuming base fee = 0) + // EVM: 1000 aatom/gas effective tip + evmTx, err := s.createEVMTransaction(big.NewInt(1000000000)) // 1 gaatom/gas + s.Require().NoError(err) + + // Cosmos with same effective tip: 1000 * 200000 = 200000000 aatom total fee + cosmosTx := s.createCosmosSendTransaction(big.NewInt(1000000000)) // 1 gaatom/gas effective tip + + // Insert Cosmos first, then EVM + return []sdk.Tx{cosmosTx, evmTx} + }, + verifyFunc: func(iterator mempool.Iterator) { + // Both transactions have equal effective tip, so either could be first + // But EVM should be preferred when effective tips are equal + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + + // Check if first transaction is EVM (preferred when effective tips are equal) + ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok) + ethTx := ethMsg.AsTransaction() + // For EVM, effective tip = gas_price - base_fee (assuming base fee = 0) + effectiveTip := ethTx.GasPrice() // effective_tip = gas_price - 0 + s.Require().Equal(big.NewInt(1000000000), effectiveTip, "First transaction should be EVM with 1 gaatom effective tip") + + // Second transaction should be the other type + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx2 := iterator.Tx() + s.Require().NotNil(tx2) + + feeTx := tx2.(sdk.FeeTx) + effectiveTip = s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 + s.Require().Equal(big.NewInt(1000000000), effectiveTip, "Second transaction should be Cosmos with 1000 aatom effective tip") + }, + }, + { + name: "mixed transactions with EVM having higher effective tip", + setupTxs: func() []sdk.Tx { + // Create EVM transaction with higher gas price + evmTx, err := s.createEVMTransaction(big.NewInt(5000000000)) // 5 gaatom/gas + s.Require().NoError(err) + + // Create Cosmos transaction with lower gas price + cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000000000)) // 2 gaatom/gas + + // Insert Cosmos first, then EVM + return []sdk.Tx{cosmosTx, evmTx} + }, + verifyFunc: func(iterator mempool.Iterator) { + // EVM should be first due to higher effective tip + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + + ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok, "First transaction should be EVM due to higher effective tip") + ethTx := ethMsg.AsTransaction() + effectiveTip := ethTx.GasPrice() // effective_tip = gas_price - 0 + s.Require().Equal(big.NewInt(5000000000), effectiveTip, "First transaction should be EVM with 5000 aatom effective tip") + + // Second transaction should be Cosmos + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx2 := iterator.Tx() + s.Require().NotNil(tx2) + + feeTx := tx2.(sdk.FeeTx) + effectiveTip2 := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 + s.Require().Equal(big.NewInt(2000000000), effectiveTip2, "Second transaction should be Cosmos with 2000 aatom effective tip") + }, + }, + { + name: "mixed transactions with Cosmos having higher effective tip", + setupTxs: func() []sdk.Tx { + // Create EVM transaction with lower gas price + evmTx, err := s.createEVMTransaction(big.NewInt(2000000000)) // 2000 aatom/gas + s.Require().NoError(err) + + // Create Cosmos transaction with higher gas price + cosmosTx := s.createCosmosSendTransaction(big.NewInt(5000000000)) // 5000 aatom/gas + + // Insert EVM first, then Cosmos + return []sdk.Tx{evmTx, cosmosTx} + + }, + verifyFunc: func(iterator mempool.Iterator) { + // Cosmos should be first due to higher effective tip + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + + feeTx := tx1.(sdk.FeeTx) + effectiveTip := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 + s.Require().Equal(big.NewInt(5000000000), effectiveTip, "First transaction should be Cosmos with 5000 aatom effective tip") + + // Second transaction should be EVM + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx2 := iterator.Tx() + s.Require().NotNil(tx2) + + ethMsg, ok := tx2.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok, "Second transaction should be EVM") + ethTx := ethMsg.AsTransaction() + effectiveTip2 := ethTx.GasPrice() // effective_tip = gas_price - 0 + s.Require().Equal(big.NewInt(2000000000), effectiveTip2, "Second transaction should be EVM with 2000 aatom effective tip") + }, + }, + { + name: "mixed transaction ordering with multiple effective tips", + setupTxs: func() []sdk.Tx { + // Create multiple transactions with different gas prices + // EVM: 8000, 4000, 2000 aatom/gas + // Cosmos: 6000, 3000, 1000 aatom/gas + + evmHigh, err := s.createEVMTransaction(big.NewInt(8000000000)) + s.Require().NoError(err) + evmMedium, err := s.createEVMTransactionWithKey(s.keyring.GetKey(1), big.NewInt(4000000000)) + s.Require().NoError(err) + evmLow, err := s.createEVMTransactionWithKey(s.keyring.GetKey(2), big.NewInt(2000000000)) + s.Require().NoError(err) + + cosmosHigh := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(3), big.NewInt(6000000000)) + cosmosMedium := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(4), big.NewInt(3000000000)) + cosmosLow := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(5), big.NewInt(1000000000)) + + return []sdk.Tx{evmHigh, evmMedium, evmLow, cosmosHigh, cosmosMedium, cosmosLow} + }, + verifyFunc: func(iterator mempool.Iterator) { + // Expected order by gas price (highest first): + // 1. EVM 8 gaatom/gas + // 2. Cosmos 6 gaatom/gas + // 3. EVM 4 gaatom/gas + // 4. Cosmos 3 gaatom/gas + // 5. EVM 2 gaatom/gas + // 6. Cosmos 1 gaatom/gas + + // First: EVM 8 + tx1 := iterator.Tx() + s.Require().NotNil(tx1) + ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok, "First transaction should be EVM with highest gas price") + ethTx := ethMsg.AsTransaction() + s.Require().Equal(big.NewInt(8000000000), ethTx.GasPrice(), "First transaction should be EVM with 8000 aatom/gas") + + // Second: Cosmos 6 + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx2 := iterator.Tx() + s.Require().NotNil(tx2) + feeTx2 := tx2.(sdk.FeeTx) + cosmosGasPrice2 := s.calculateCosmosGasPrice(feeTx2.GetFee().AmountOf("aatom").Int64(), feeTx2.GetGas()) + s.Require().Equal(big.NewInt(6000000000), cosmosGasPrice2, "Second transaction should be Cosmos with 6000 aatom/gas") + + // Third: EVM 4 + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx3 := iterator.Tx() + s.Require().NotNil(tx3) + ethMsg3, ok := tx3.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok, "Third transaction should be EVM") + ethTx3 := ethMsg3.AsTransaction() + s.Require().Equal(big.NewInt(4000000000), ethTx3.GasPrice(), "Third transaction should be EVM with 4000 aatom/gas") + + // Fourth: Cosmos 3 + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx4 := iterator.Tx() + s.Require().NotNil(tx4) + feeTx4 := tx4.(sdk.FeeTx) + cosmosGasPrice4 := s.calculateCosmosGasPrice(feeTx4.GetFee().AmountOf("aatom").Int64(), feeTx4.GetGas()) + s.Require().Equal(big.NewInt(3000000000), cosmosGasPrice4, "Fourth transaction should be Cosmos with 3000 aatom/gas") + + // Fifth: EVM 2 + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx5 := iterator.Tx() + s.Require().NotNil(tx5) + ethMsg5, ok := tx5.GetMsgs()[0].(*evmtypes.MsgEthereumTx) + s.Require().True(ok, "Fifth transaction should be EVM") + ethTx5 := ethMsg5.AsTransaction() + s.Require().Equal(big.NewInt(2000000000), ethTx5.GasPrice(), "Fifth transaction should be EVM with 2000 aatom/gas") + + // Sixth: Cosmos 1 + iterator = iterator.Next() + s.Require().NotNil(iterator) + tx6 := iterator.Tx() + s.Require().NotNil(tx6) + feeTx6 := tx6.(sdk.FeeTx) + cosmosGasPrice6 := s.calculateCosmosGasPrice(feeTx6.GetFee().AmountOf("aatom").Int64(), feeTx6.GetGas()) + s.Require().Equal(big.NewInt(1000000000), cosmosGasPrice6, "Sixth transaction should be Cosmos with 1000 aatom/gas") + + // No more transactions + iterator = iterator.Next() + s.Require().Nil(iterator) + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + // Reset test setup to ensure clean state + s.SetupTest() + + txs := tc.setupTxs() + + _, err := s.checkTxs(txs) + s.Require().NoError(err) + + mpool := s.network.App.GetMempool() + iterator := mpool.Select(s.network.GetContext(), nil) + + tc.verifyFunc(iterator) + }) + } +} + +// TestNonceGappedEVMTransactions tests the behavior of nonce-gapped EVM transactions +// and the transition from queued to pending when gaps are filled +func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { + fmt.Printf("DEBUG: Starting TestNonceGappedEVMTransactions\n") + + testCases := []struct { + name string + setupTxs func() ([]sdk.Tx, []int) // Returns transactions and their expected nonces + verifyFunc func(mpool mempool.Mempool) + }{ + { + name: "insert transactions with nonce gaps", + setupTxs: func() ([]sdk.Tx, []int) { + key := s.keyring.GetKey(0) + var txs []sdk.Tx + var nonces []int + + // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) + for i := 0; i <= 6; i += 2 { + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // Only nonce 0 should be pending (the first consecutive transaction) + // nonces 2, 4, 6 should be queued + count := mpool.CountTx() + s.Require().Equal(1, count, "Only nonce 0 should be pending, others should be queued") + }, + }, + { + name: "fill nonce gap and verify pending count increases", + setupTxs: func() ([]sdk.Tx, []int) { + key := s.keyring.GetKey(0) + var txs []sdk.Tx + var nonces []int + + // First, insert transactions with gaps: nonces 0, 2, 4 + for i := 0; i <= 4; i += 2 { + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + + // Then fill the gap by inserting nonce 1 + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, 1) + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // After filling nonce 1, transactions 0, 1, 2 should be pending + // nonce 4 should still be queued + count := mpool.CountTx() + s.Require().Equal(3, count, "After filling gap, nonces 0, 1, 2 should be pending") + }, + }, + { + name: "fill multiple nonce gaps", + setupTxs: func() ([]sdk.Tx, []int) { + key := s.keyring.GetKey(0) + var txs []sdk.Tx + var nonces []int + + // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 + for i := 0; i <= 9; i += 3 { + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + + // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 + for i := 1; i <= 8; i++ { + if i%3 != 0 { // Skip nonces that are already inserted + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + } + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // After filling all gaps, all transactions should be pending + count := mpool.CountTx() + s.Require().Equal(10, count, "After filling all gaps, all 10 transactions should be pending") + }, + }, + { + name: "test different accounts with nonce gaps", + setupTxs: func() ([]sdk.Tx, []int) { + var txs []sdk.Tx + var nonces []int + + // Use different keys for different accounts + key1 := s.keyring.GetKey(0) + key2 := s.keyring.GetKey(1) + + // Account 1: nonces 0, 2 (gap at 1) + for i := 0; i <= 2; i += 2 { + tx, err := s.createEVMTransactionWithNonce(key1, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + + // Account 2: nonces 0, 3 (gaps at 1, 2) + for i := 0; i <= 3; i += 3 { + tx, err := s.createEVMTransactionWithNonce(key2, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // Account 1: nonce 0 pending, nonce 2 queued + // Account 2: nonce 0 pending, nonce 3 queued + // Total: 2 pending transactions + count := mpool.CountTx() + s.Require().Equal(2, count, "Only nonce 0 from each account should be pending") + }, + }, + { + name: "test replacement transactions with higher gas price", + setupTxs: func() ([]sdk.Tx, []int) { + key := s.keyring.GetKey(0) + var txs []sdk.Tx + var nonces []int + + // Insert transaction with nonce 0 and low gas price + tx1, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 0) + s.Require().NoError(err) + txs = append(txs, tx1) + nonces = append(nonces, 0) + + // Insert transaction with nonce 1 + tx2, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + s.Require().NoError(err) + txs = append(txs, tx2) + nonces = append(nonces, 1) + + // Replace nonce 0 transaction with higher gas price + tx3, err := s.createEVMTransactionWithNonce(key, big.NewInt(2000000000), 0) + s.Require().NoError(err) + txs = append(txs, tx3) + nonces = append(nonces, 0) + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // After replacement, both nonces 0 and 1 should be pending + count := mpool.CountTx() + s.Require().Equal(2, count, "After replacement, both transactions should be pending") + }, + }, + { + name: "track count changes when filling nonce gaps", + setupTxs: func() ([]sdk.Tx, []int) { + key := s.keyring.GetKey(0) + var txs []sdk.Tx + var nonces []int + + // Insert transactions with gaps: nonces 0, 3, 6, 9 + for i := 0; i <= 9; i += 3 { + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + + // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 + for i := 1; i <= 8; i++ { + if i%3 != 0 { // Skip nonces that are already inserted + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) + } + } + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // After filling all gaps, all transactions should be pending + count := mpool.CountTx() + s.Require().Equal(10, count, "After filling all gaps, all 10 transactions should be pending") + }, + }, + { + name: "removing places subsequent transactions back into queued", + setupTxs: func() ([]sdk.Tx, []int) { + key := s.keyring.GetKey(0) + var txs []sdk.Tx + var nonces []int + + // Insert transactions with gaps: nonces 0, 1, 3, 4, 6, 7 + for i := 0; i <= 7; i++ { + if i != 1 { // Skip nonce 1 to create a gap + tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) + s.Require().NoError(err) + txs = append(txs, tx) + nonces = append(nonces, i) //#nosec G115 -- int overflow is not a concern here + } + } + + return txs, nonces + }, + verifyFunc: func(mpool mempool.Mempool) { + // Initially: nonces 0 should be pending, nonces 2, 3, 4, 5, 6, 7 should be queued + initialCount := mpool.CountTx() + s.Require().Equal(1, initialCount, "Initially only nonces 0, 1 should be pending") + key := s.keyring.GetKey(0) + + // Fill gap by inserting nonce 1 + tx1, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + s.Require().NoError(err) + err = mpool.Insert(s.network.GetContext(), tx1) + s.Require().NoError(err) + + // After filling gap: all nonce transactions should be in pending + countAfterFilling := mpool.CountTx() + s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") + + // Remove nonce 1 transaction, dropping the rest (except for 0) into queued + tx1, err = s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + s.Require().NoError(err) + err = mpool.Remove(tx1) + s.Require().NoError(err) + + // After removal: only nonce 0 should be pending, the rest get dropped to queued + countAfterRemoval := mpool.CountTx() + s.Require().Equal(1, countAfterRemoval, "After removing nonce 1, only nonce 0 should be pending") + + // Fill gap by inserting nonce 1 + tx1, err = s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + s.Require().NoError(err) + err = mpool.Insert(s.network.GetContext(), tx1) + s.Require().NoError(err) + + // After filling gap: all transactions should be re-promoted and places into pending + countAfterFilling = mpool.CountTx() + s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") + }, + }, + } + + for i, tc := range testCases { + fmt.Printf("DEBUG: TestNonceGappedEVMTransactions - Starting test case %d/%d: %s\n", i+1, len(testCases), tc.name) + s.Run(tc.name, func() { + fmt.Printf("DEBUG: Running test case: %s\n", tc.name) + // Reset test setup to ensure clean state + s.SetupTest() + fmt.Printf("DEBUG: SetupTest completed for: %s\n", tc.name) + + txs, nonces := tc.setupTxs() + mpool := s.network.App.GetMempool() + + // Insert transactions and track count changes + initialCount := mpool.CountTx() + fmt.Printf("DEBUG: Initial mempool count: %d\n", initialCount) + + for i, tx := range txs { + err := mpool.Insert(s.network.GetContext(), tx) + s.Require().NoError(err) + + currentCount := mpool.CountTx() + fmt.Printf("DEBUG: After inserting nonce %d: count = %d\n", nonces[i], currentCount) + } + + tc.verifyFunc(mpool) + fmt.Printf("DEBUG: Completed test case: %s\n", tc.name) + }) + fmt.Printf("DEBUG: TestNonceGappedEVMTransactions - Completed test case %d/%d: %s\n", i+1, len(testCases), tc.name) + } +} From 35918155a864cca74a7dd482a776f15185c239d2 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Fri, 22 Aug 2025 16:03:18 +0900 Subject: [PATCH 02/20] WIP: test(mempool): add integration test --- tests/integration/mempool/test_helper.go | 46 +++++++++++++++++++ .../mempool/test_mempool_integration.go | 3 +- .../mempool/test_mempool_integration_abci.go | 24 +++++----- 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 9a09acbb2..24a90c8d0 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -6,9 +6,12 @@ import ( abci "github.com/cometbft/cometbft/abci/types" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/evm/crypto/ethsecp256k1" + "github.com/cosmos/evm/testutil/integration/base/factory" "github.com/cosmos/evm/testutil/keyring" evmtypes "github.com/cosmos/evm/x/vm/types" "github.com/ethereum/go-ethereum/common" @@ -304,3 +307,46 @@ func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) ([]*abci.ResponseCheckTx, return result, nil } + +// createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key +func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey2(key keyring.Key, gasPrice *big.Int) sdk.Tx { + feeDenom := "aatom" + gasLimit := uint64(TxGas) + + // Calculate fee amount from gas price: fee = gas_price * gas_limit + feeAmount := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) + + fromAddr := key.AccAddr + toAddr := s.keyring.GetKey(1).AccAddr + amount := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 1000)) + + bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) + + txArgs := factory.CosmosTxArgs{ + Msgs: []sdk.Msg{bankMsg}, + Fees: sdk.NewCoins(sdk.NewCoin(feeDenom, sdkmath.NewIntFromBigInt(feeAmount))), + } + tx, err := s.factory.BuildCosmosTx(key.Priv, txArgs) + s.Require().NoError(err) + + fmt.Printf("DEBUG: Created cosmos transaction successfully\n") + return tx +} + +// createEVMTransaction creates an EVM transaction using the provided key +func (s *IntegrationTestSuite) createEVMTransactionWithKey2(key keyring.Key, gasPrice *big.Int) sdk.Tx { + to := common.HexToAddress("0x1234567890123456789012345678901234567890") + + ethTxArgs := evmtypes.EvmTxArgs{ + Nonce: 0, + To: &to, + Amount: big.NewInt(1000), + GasLimit: TxGas, + GasPrice: gasPrice, + Input: nil, + } + tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) + s.Require().NoError(err) + + return tx +} diff --git a/tests/integration/mempool/test_mempool_integration.go b/tests/integration/mempool/test_mempool_integration.go index 748edb73d..8261af068 100644 --- a/tests/integration/mempool/test_mempool_integration.go +++ b/tests/integration/mempool/test_mempool_integration.go @@ -19,7 +19,8 @@ import ( // Constants const ( - TxGas = 21000 + // TxGas = 21000 + TxGas = 100_000 ) // TestMempoolInsert tests transaction insertion into the mempool diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index 21d91ad13..44b784f9f 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -25,9 +25,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { s.Require().NoError(err) // Create Cosmos transactions with different fee amounts - highFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(6), big.NewInt(5000000000)) - mediumFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(7), big.NewInt(3000000000)) - lowFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(8), big.NewInt(1000000000)) + highFeeCosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(6), big.NewInt(5000000000)) + mediumFeeCosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(7), big.NewInt(3000000000)) + lowFeeCosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(8), big.NewInt(1000000000)) // Insert in non-priority order return []sdk.Tx{lowFeeCosmosTx, highGasPriceEVMTx, mediumFeeCosmosTx, highFeeCosmosTx} @@ -118,9 +118,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { { name: "cosmos-only transaction replacement", setupTxs: func() []sdk.Tx { - highFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom - lowFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom - mediumFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom + highFeeTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom + lowFeeTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom + mediumFeeTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom // Insert in random order return []sdk.Tx{mediumFeeTx, lowFeeTx, highFeeTx} @@ -147,7 +147,7 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { s.Require().NoError(err) // Cosmos with same effective tip: 1000 * 200000 = 200000000 aatom total fee - cosmosTx := s.createCosmosSendTransaction(big.NewInt(1000000000)) // 1 gaatom/gas effective tip + cosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas effective tip // Insert Cosmos first, then EVM return []sdk.Tx{cosmosTx, evmTx} @@ -185,7 +185,7 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { s.Require().NoError(err) // Create Cosmos transaction with lower gas price - cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000000000)) // 2 gaatom/gas + cosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom/gas // Insert Cosmos first, then EVM return []sdk.Tx{cosmosTx, evmTx} @@ -220,7 +220,7 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { s.Require().NoError(err) // Create Cosmos transaction with higher gas price - cosmosTx := s.createCosmosSendTransaction(big.NewInt(5000000000)) // 5000 aatom/gas + cosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5000 aatom/gas // Insert EVM first, then Cosmos return []sdk.Tx{evmTx, cosmosTx} @@ -262,9 +262,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { evmLow, err := s.createEVMTransactionWithKey(s.keyring.GetKey(2), big.NewInt(2000000000)) s.Require().NoError(err) - cosmosHigh := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(3), big.NewInt(6000000000)) - cosmosMedium := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(4), big.NewInt(3000000000)) - cosmosLow := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(5), big.NewInt(1000000000)) + cosmosHigh := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(3), big.NewInt(6000000000)) + cosmosMedium := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(4), big.NewInt(3000000000)) + cosmosLow := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(5), big.NewInt(1000000000)) return []sdk.Tx{evmHigh, evmMedium, evmLow, cosmosHigh, cosmosMedium, cosmosLow} }, From ad3185714f012146f180d4a2b1445cd979dfb3fe Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Mon, 25 Aug 2025 20:35:33 +0900 Subject: [PATCH 03/20] test(mempool): refactor integration test --- tests/integration/mempool/test_helper.go | 52 ++- .../mempool/test_mempool_integration_abci.go | 426 ++++++------------ 2 files changed, 176 insertions(+), 302 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 24a90c8d0..2a9dc4a8a 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -1,10 +1,12 @@ package mempool import ( + "encoding/hex" "fmt" "math/big" abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/tmhash" sdkmath "cosmossdk.io/math" @@ -309,12 +311,8 @@ func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) ([]*abci.ResponseCheckTx, } // createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key -func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey2(key keyring.Key, gasPrice *big.Int) sdk.Tx { +func (s *IntegrationTestSuite) createCosmosSendTxWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { feeDenom := "aatom" - gasLimit := uint64(TxGas) - - // Calculate fee amount from gas price: fee = gas_price * gas_limit - feeAmount := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) fromAddr := key.AccAddr toAddr := s.keyring.GetKey(1).AccAddr @@ -322,9 +320,11 @@ func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey2(key keyring.K bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) + gasPriceConverted := sdkmath.NewIntFromBigInt(gasPrice) + txArgs := factory.CosmosTxArgs{ - Msgs: []sdk.Msg{bankMsg}, - Fees: sdk.NewCoins(sdk.NewCoin(feeDenom, sdkmath.NewIntFromBigInt(feeAmount))), + Msgs: []sdk.Msg{bankMsg}, + GasPrice: &gasPriceConverted, } tx, err := s.factory.BuildCosmosTx(key.Priv, txArgs) s.Require().NoError(err) @@ -334,7 +334,7 @@ func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey2(key keyring.K } // createEVMTransaction creates an EVM transaction using the provided key -func (s *IntegrationTestSuite) createEVMTransactionWithKey2(key keyring.Key, gasPrice *big.Int) sdk.Tx { +func (s *IntegrationTestSuite) createEVMTransferWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { to := common.HexToAddress("0x1234567890123456789012345678901234567890") ethTxArgs := evmtypes.EvmTxArgs{ @@ -350,3 +350,39 @@ func (s *IntegrationTestSuite) createEVMTransactionWithKey2(key keyring.Key, gas return tx } + +// createEVMTransaction creates an EVM transaction using the provided key +func (s *IntegrationTestSuite) createEVMTransferWithKeyAndNonce(key keyring.Key, gasPrice *big.Int, nonce uint64) sdk.Tx { + to := common.HexToAddress("0x1234567890123456789012345678901234567890") + + ethTxArgs := evmtypes.EvmTxArgs{ + Nonce: nonce, + To: &to, + Amount: big.NewInt(1000), + GasLimit: TxGas, + GasPrice: gasPrice, + Input: nil, + } + tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) + s.Require().NoError(err) + + return tx +} + +func (s *IntegrationTestSuite) getTxHashes(txs []sdk.Tx) []string { + txHashes := []string{} + for _, tx := range txs { + txHash := s.getTxHash(tx) + txHashes = append(txHashes, txHash) + } + + return txHashes +} + +func (s *IntegrationTestSuite) getTxHash(tx sdk.Tx) string { + txEncoder := s.network.App.GetTxConfig().TxEncoder() + txBytes, err := txEncoder(tx) + s.Require().NoError(err) + + return hex.EncodeToString(tmhash.Sum(txBytes)) +} diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index 44b784f9f..524f5da84 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/mempool" - evmtypes "github.com/cosmos/evm/x/vm/types" ) // TestTransactionOrdering tests transaction ordering based on fees @@ -14,327 +13,173 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { fmt.Printf("DEBUG: Starting TestTransactionOrdering\n") testCases := []struct { name string - setupTxs func() []sdk.Tx - verifyFunc func(iterator mempool.Iterator) + setupTxs func() ([]sdk.Tx, []string) + verifyFunc func(iterator mempool.Iterator, txHashesInOrder []string) + bypass bool // Temporarily bypass test cases that have known issue. }{ { name: "mixed EVM and cosmos transaction ordering", - setupTxs: func() []sdk.Tx { + setupTxs: func() ([]sdk.Tx, []string) { // Create EVM transaction with high gas price - highGasPriceEVMTx, err := s.createEVMTransaction(big.NewInt(5000000000)) - s.Require().NoError(err) + highGasPriceEVMTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // Create Cosmos transactions with different fee amounts - highFeeCosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(6), big.NewInt(5000000000)) - mediumFeeCosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(7), big.NewInt(3000000000)) - lowFeeCosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(8), big.NewInt(1000000000)) + highFeeCosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(6), big.NewInt(5000000000)) + mediumFeeCosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(7), big.NewInt(3000000000)) + lowFeeCosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(8), big.NewInt(2000000000)) - // Insert in non-priority order - return []sdk.Tx{lowFeeCosmosTx, highGasPriceEVMTx, mediumFeeCosmosTx, highFeeCosmosTx} - }, - verifyFunc: func(iterator mempool.Iterator) { - // First transaction should be EVM with highest gas price (5 gaatom = 5000000000 aatom) - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - - ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok) - ethTx := ethMsg.AsTransaction() - s.Require().Equal(big.NewInt(5000000000), ethTx.GasPrice(), "First transaction should be EVM with highest gas price") - - // Second transaction should be Cosmos with high fee (25000 aatom gas price) - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx2 := iterator.Tx() - s.Require().NotNil(tx2) - - // Should be Cosmos transaction with high fee - feeTx := tx2.(sdk.FeeTx) - cosmosGasPrice := s.calculateCosmosGasPrice(feeTx.GetFee().AmountOf("aatom").BigInt().Int64(), feeTx.GetGas()) - s.Require().Equal(big.NewInt(5000000000), cosmosGasPrice, "Second transaction should be Cosmos with 25000 aatom gas price") + // Input txs in order + inputTxs := []sdk.Tx{lowFeeCosmosTx, highGasPriceEVMTx, mediumFeeCosmosTx, highFeeCosmosTx} + + // Expected txs in order + expectedTxs := []sdk.Tx{highGasPriceEVMTx, highFeeCosmosTx, mediumFeeCosmosTx, lowFeeCosmosTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, }, { name: "EVM-only transaction replacement", - setupTxs: func() []sdk.Tx { + setupTxs: func() ([]sdk.Tx, []string) { // Create first EVM transaction with low fee - lowFeeEVMTx, err := s.createEVMTransaction(big.NewInt(1000000000)) // 1 gaatom - s.Require().NoError(err) + lowFeeEVMTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom // Create second EVM transaction with high fee - highFeeEVMTx, err := s.createEVMTransaction(big.NewInt(5000000000)) // 5 gaatom - s.Require().NoError(err) + highFeeEVMTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom - // Insert low fee transaction first - return []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} - }, - verifyFunc: func(iterator mempool.Iterator) { - // First transaction should be high fee - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok) - ethTx := ethMsg.AsTransaction() - s.Require().Equal(big.NewInt(5000000000), ethTx.GasPrice()) - iterator = iterator.Next() - s.Require().Nil(iterator) // transaction with same nonce got replaced by higher fee + // Input txs in order + inputTxs := []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} + + // Expected Txs in order + expectedTxs := []sdk.Tx{highFeeEVMTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, + bypass: true, }, { - name: "EVM-only transaction replacement", - setupTxs: func() []sdk.Tx { + name: "EVM-only transaction ordering", + setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) // Create first EVM transaction with low fee - lowFeeEVMTx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) // 1 gaatom - s.Require().NoError(err) + lowFeeEVMTx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(1)) // 2 gaatom // Create second EVM transaction with high fee - highFeeEVMTx, err := s.createEVMTransactionWithNonce(key, big.NewInt(5000000000), 0) // 5 gaatom - s.Require().NoError(err) + highFeeEVMTx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(5000000000), uint64(0)) // 5 gaatom - // Insert low fee transaction first - return []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} - }, - verifyFunc: func(iterator mempool.Iterator) { - // First transaction should be high fee - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok) - ethTx := ethMsg.AsTransaction() - s.Require().Equal(big.NewInt(5000000000), ethTx.GasPrice()) - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx2 := iterator.Tx() - s.Require().NotNil(tx2) - ethMsg, ok = tx2.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok) - ethTx = ethMsg.AsTransaction() - s.Require().Equal(big.NewInt(1000000000), ethTx.GasPrice()) - iterator = iterator.Next() - s.Require().Nil(iterator) + // Input txs in order + inputTxs := []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} + + // Expected txs in order + expectedTxs := []sdk.Tx{highFeeEVMTx, lowFeeEVMTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, }, { name: "cosmos-only transaction replacement", - setupTxs: func() []sdk.Tx { - highFeeTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom - lowFeeTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom - mediumFeeTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom + setupTxs: func() ([]sdk.Tx, []string) { + highFeeTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom + lowFeeTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom + mediumFeeTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom - // Insert in random order - return []sdk.Tx{mediumFeeTx, lowFeeTx, highFeeTx} - }, - verifyFunc: func(iterator mempool.Iterator) { - // Should get first transaction from cosmos pool - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - // Calculate gas price: fee_amount / gas_limit = 5000000000 / 200000 = 25000 - expectedGasPrice := big.NewInt(5000000000) - feeTx := tx1.(sdk.FeeTx) - actualGasPrice := s.calculateCosmosGasPrice(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas()) - s.Require().Equal(expectedGasPrice, actualGasPrice, "Expected gas price should match fee_amount/gas_limit") - iterator = iterator.Next() - s.Require().Nil(iterator) + // Input txs in order + inputTxs := []sdk.Tx{mediumFeeTx, lowFeeTx, highFeeTx} + + // Expected txs in order + expectedTxs := []sdk.Tx{highFeeTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, + bypass: true, }, { name: "mixed EVM and Cosmos transactions with equal effective tips", - setupTxs: func() []sdk.Tx { + setupTxs: func() ([]sdk.Tx, []string) { // Create transactions with equal effective tips (assuming base fee = 0) // EVM: 1000 aatom/gas effective tip - evmTx, err := s.createEVMTransaction(big.NewInt(1000000000)) // 1 gaatom/gas - s.Require().NoError(err) + evmTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas // Cosmos with same effective tip: 1000 * 200000 = 200000000 aatom total fee - cosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas effective tip + cosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas effective tip - // Insert Cosmos first, then EVM - return []sdk.Tx{cosmosTx, evmTx} - }, - verifyFunc: func(iterator mempool.Iterator) { - // Both transactions have equal effective tip, so either could be first - // But EVM should be preferred when effective tips are equal - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - - // Check if first transaction is EVM (preferred when effective tips are equal) - ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok) - ethTx := ethMsg.AsTransaction() - // For EVM, effective tip = gas_price - base_fee (assuming base fee = 0) - effectiveTip := ethTx.GasPrice() // effective_tip = gas_price - 0 - s.Require().Equal(big.NewInt(1000000000), effectiveTip, "First transaction should be EVM with 1 gaatom effective tip") - - // Second transaction should be the other type - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx2 := iterator.Tx() - s.Require().NotNil(tx2) - - feeTx := tx2.(sdk.FeeTx) - effectiveTip = s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 - s.Require().Equal(big.NewInt(1000000000), effectiveTip, "Second transaction should be Cosmos with 1000 aatom effective tip") + // Input txs in order + inputTxs := []sdk.Tx{cosmosTx, evmTx} + + // Expected txs in order + expectedTxs := []sdk.Tx{evmTx, cosmosTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, + bypass: true, }, { name: "mixed transactions with EVM having higher effective tip", - setupTxs: func() []sdk.Tx { + setupTxs: func() ([]sdk.Tx, []string) { // Create EVM transaction with higher gas price - evmTx, err := s.createEVMTransaction(big.NewInt(5000000000)) // 5 gaatom/gas - s.Require().NoError(err) + evmTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom/gas // Create Cosmos transaction with lower gas price - cosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom/gas + cosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom/gas - // Insert Cosmos first, then EVM - return []sdk.Tx{cosmosTx, evmTx} - }, - verifyFunc: func(iterator mempool.Iterator) { - // EVM should be first due to higher effective tip - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - - ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok, "First transaction should be EVM due to higher effective tip") - ethTx := ethMsg.AsTransaction() - effectiveTip := ethTx.GasPrice() // effective_tip = gas_price - 0 - s.Require().Equal(big.NewInt(5000000000), effectiveTip, "First transaction should be EVM with 5000 aatom effective tip") - - // Second transaction should be Cosmos - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx2 := iterator.Tx() - s.Require().NotNil(tx2) - - feeTx := tx2.(sdk.FeeTx) - effectiveTip2 := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 - s.Require().Equal(big.NewInt(2000000000), effectiveTip2, "Second transaction should be Cosmos with 2000 aatom effective tip") + // Input txs in order + inputTxs := []sdk.Tx{cosmosTx, evmTx} + + // Expected txs in order + expectedTxs := []sdk.Tx{evmTx, cosmosTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, + bypass: true, }, { name: "mixed transactions with Cosmos having higher effective tip", - setupTxs: func() []sdk.Tx { + setupTxs: func() ([]sdk.Tx, []string) { // Create EVM transaction with lower gas price - evmTx, err := s.createEVMTransaction(big.NewInt(2000000000)) // 2000 aatom/gas - s.Require().NoError(err) + evmTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2000 aatom/gas // Create Cosmos transaction with higher gas price - cosmosTx := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5000 aatom/gas + cosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5000 aatom/gas - // Insert EVM first, then Cosmos - return []sdk.Tx{evmTx, cosmosTx} + // Input txs in order + inputTxs := []sdk.Tx{evmTx, cosmosTx} + // Expected txs in order + expectedTxs := []sdk.Tx{cosmosTx, evmTx} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, - verifyFunc: func(iterator mempool.Iterator) { - // Cosmos should be first due to higher effective tip - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - - feeTx := tx1.(sdk.FeeTx) - effectiveTip := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 - s.Require().Equal(big.NewInt(5000000000), effectiveTip, "First transaction should be Cosmos with 5000 aatom effective tip") - - // Second transaction should be EVM - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx2 := iterator.Tx() - s.Require().NotNil(tx2) - - ethMsg, ok := tx2.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok, "Second transaction should be EVM") - ethTx := ethMsg.AsTransaction() - effectiveTip2 := ethTx.GasPrice() // effective_tip = gas_price - 0 - s.Require().Equal(big.NewInt(2000000000), effectiveTip2, "Second transaction should be EVM with 2000 aatom effective tip") - }, + bypass: true, }, { name: "mixed transaction ordering with multiple effective tips", - setupTxs: func() []sdk.Tx { + setupTxs: func() ([]sdk.Tx, []string) { // Create multiple transactions with different gas prices - // EVM: 8000, 4000, 2000 aatom/gas - // Cosmos: 6000, 3000, 1000 aatom/gas + // EVM: 10000, 8000, 6000 aatom/gas + // Cosmos: 9000, 7000, 5000 aatom/gas - evmHigh, err := s.createEVMTransaction(big.NewInt(8000000000)) - s.Require().NoError(err) - evmMedium, err := s.createEVMTransactionWithKey(s.keyring.GetKey(1), big.NewInt(4000000000)) - s.Require().NoError(err) - evmLow, err := s.createEVMTransactionWithKey(s.keyring.GetKey(2), big.NewInt(2000000000)) - s.Require().NoError(err) + evmHigh := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(10000000000)) + evmMedium := s.createEVMTransferWithKey(s.keyring.GetKey(1), big.NewInt(8000000000)) + evmLow := s.createEVMTransferWithKey(s.keyring.GetKey(2), big.NewInt(6000000000)) - cosmosHigh := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(3), big.NewInt(6000000000)) - cosmosMedium := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(4), big.NewInt(3000000000)) - cosmosLow := s.createCosmosSendTransactionWithKey2(s.keyring.GetKey(5), big.NewInt(1000000000)) + cosmosHigh := s.createCosmosSendTxWithKey(s.keyring.GetKey(3), big.NewInt(9000000000)) + cosmosMedium := s.createCosmosSendTxWithKey(s.keyring.GetKey(4), big.NewInt(7000000000)) + cosmosLow := s.createCosmosSendTxWithKey(s.keyring.GetKey(5), big.NewInt(5000000000)) - return []sdk.Tx{evmHigh, evmMedium, evmLow, cosmosHigh, cosmosMedium, cosmosLow} - }, - verifyFunc: func(iterator mempool.Iterator) { - // Expected order by gas price (highest first): - // 1. EVM 8 gaatom/gas - // 2. Cosmos 6 gaatom/gas - // 3. EVM 4 gaatom/gas - // 4. Cosmos 3 gaatom/gas - // 5. EVM 2 gaatom/gas - // 6. Cosmos 1 gaatom/gas - - // First: EVM 8 - tx1 := iterator.Tx() - s.Require().NotNil(tx1) - ethMsg, ok := tx1.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok, "First transaction should be EVM with highest gas price") - ethTx := ethMsg.AsTransaction() - s.Require().Equal(big.NewInt(8000000000), ethTx.GasPrice(), "First transaction should be EVM with 8000 aatom/gas") - - // Second: Cosmos 6 - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx2 := iterator.Tx() - s.Require().NotNil(tx2) - feeTx2 := tx2.(sdk.FeeTx) - cosmosGasPrice2 := s.calculateCosmosGasPrice(feeTx2.GetFee().AmountOf("aatom").Int64(), feeTx2.GetGas()) - s.Require().Equal(big.NewInt(6000000000), cosmosGasPrice2, "Second transaction should be Cosmos with 6000 aatom/gas") - - // Third: EVM 4 - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx3 := iterator.Tx() - s.Require().NotNil(tx3) - ethMsg3, ok := tx3.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok, "Third transaction should be EVM") - ethTx3 := ethMsg3.AsTransaction() - s.Require().Equal(big.NewInt(4000000000), ethTx3.GasPrice(), "Third transaction should be EVM with 4000 aatom/gas") - - // Fourth: Cosmos 3 - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx4 := iterator.Tx() - s.Require().NotNil(tx4) - feeTx4 := tx4.(sdk.FeeTx) - cosmosGasPrice4 := s.calculateCosmosGasPrice(feeTx4.GetFee().AmountOf("aatom").Int64(), feeTx4.GetGas()) - s.Require().Equal(big.NewInt(3000000000), cosmosGasPrice4, "Fourth transaction should be Cosmos with 3000 aatom/gas") - - // Fifth: EVM 2 - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx5 := iterator.Tx() - s.Require().NotNil(tx5) - ethMsg5, ok := tx5.GetMsgs()[0].(*evmtypes.MsgEthereumTx) - s.Require().True(ok, "Fifth transaction should be EVM") - ethTx5 := ethMsg5.AsTransaction() - s.Require().Equal(big.NewInt(2000000000), ethTx5.GasPrice(), "Fifth transaction should be EVM with 2000 aatom/gas") - - // Sixth: Cosmos 1 - iterator = iterator.Next() - s.Require().NotNil(iterator) - tx6 := iterator.Tx() - s.Require().NotNil(tx6) - feeTx6 := tx6.(sdk.FeeTx) - cosmosGasPrice6 := s.calculateCosmosGasPrice(feeTx6.GetFee().AmountOf("aatom").Int64(), feeTx6.GetGas()) - s.Require().Equal(big.NewInt(1000000000), cosmosGasPrice6, "Sixth transaction should be Cosmos with 1000 aatom/gas") - - // No more transactions - iterator = iterator.Next() - s.Require().Nil(iterator) + // Input txs in order + inputTxs := []sdk.Tx{cosmosHigh, cosmosMedium, cosmosLow, evmHigh, evmMedium, evmLow} + + // Expected txs in order + expectedTxs := []sdk.Tx{evmHigh, cosmosHigh, evmMedium, cosmosMedium, evmLow, cosmosLow} + expectedTxHashes := s.getTxHashes(expectedTxs) + + return inputTxs, expectedTxHashes }, }, } @@ -344,7 +189,7 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Reset test setup to ensure clean state s.SetupTest() - txs := tc.setupTxs() + txs, txHashesInOrder := tc.setupTxs() _, err := s.checkTxs(txs) s.Require().NoError(err) @@ -352,7 +197,14 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { mpool := s.network.App.GetMempool() iterator := mpool.Select(s.network.GetContext(), nil) - tc.verifyFunc(iterator) + if !tc.bypass { + for _, txHash := range txHashesInOrder { + actualTxHash := s.getTxHash(iterator.Tx()) + s.Require().Equal(txHash, actualTxHash) + + iterator = iterator.Next() + } + } }) } } @@ -376,8 +228,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) for i := 0; i <= 6; i += 2 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -400,15 +251,13 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // First, insert transactions with gaps: nonces 0, 2, 4 for i := 0; i <= 4; i += 2 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } // Then fill the gap by inserting nonce 1 - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) txs = append(txs, tx) nonces = append(nonces, 1) @@ -430,8 +279,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -439,8 +287,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -466,16 +313,14 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Account 1: nonces 0, 2 (gap at 1) for i := 0; i <= 2; i += 2 { - tx, err := s.createEVMTransactionWithNonce(key1, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key1, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } // Account 2: nonces 0, 3 (gaps at 1, 2) for i := 0; i <= 3; i += 3 { - tx, err := s.createEVMTransactionWithNonce(key2, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key2, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -498,20 +343,17 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { var nonces []int // Insert transaction with nonce 0 and low gas price - tx1, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 0) - s.Require().NoError(err) + tx1 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(0)) txs = append(txs, tx1) nonces = append(nonces, 0) // Insert transaction with nonce 1 - tx2, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) + tx2 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) txs = append(txs, tx2) nonces = append(nonces, 1) // Replace nonce 0 transaction with higher gas price - tx3, err := s.createEVMTransactionWithNonce(key, big.NewInt(2000000000), 0) - s.Require().NoError(err) + tx3 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(0)) txs = append(txs, tx3) nonces = append(nonces, 0) @@ -532,8 +374,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Insert transactions with gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -541,8 +382,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -563,11 +403,10 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { var txs []sdk.Tx var nonces []int - // Insert transactions with gaps: nonces 0, 1, 3, 4, 6, 7 + // Insert transactions with gaps: nonces 0, 2, 3, 4, 5, 6, 7 for i := 0; i <= 7; i++ { if i != 1 { // Skip nonce 1 to create a gap - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) nonces = append(nonces, i) //#nosec G115 -- int overflow is not a concern here } @@ -582,9 +421,8 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { key := s.keyring.GetKey(0) // Fill gap by inserting nonce 1 - tx1, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), tx1) + tx1 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) + err := mpool.Insert(s.network.GetContext(), tx1) s.Require().NoError(err) // After filling gap: all nonce transactions should be in pending @@ -592,7 +430,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") // Remove nonce 1 transaction, dropping the rest (except for 0) into queued - tx1, err = s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + tx1 = s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) s.Require().NoError(err) err = mpool.Remove(tx1) s.Require().NoError(err) @@ -602,7 +440,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { s.Require().Equal(1, countAfterRemoval, "After removing nonce 1, only nonce 0 should be pending") // Fill gap by inserting nonce 1 - tx1, err = s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) + tx1 = s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), tx1) s.Require().NoError(err) From a6568057b2c53e2f156f71d551688d3955ccd7b0 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Mon, 25 Aug 2025 22:04:40 +0900 Subject: [PATCH 04/20] test(mempool): fix integration test --- tests/integration/mempool/test_helper.go | 38 +-- .../mempool/test_mempool_integration_abci.go | 235 ++++++------------ 2 files changed, 102 insertions(+), 171 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 2a9dc4a8a..891ef8c69 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -20,6 +20,11 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" ) +// Constants +const ( + TxGas = 100_000 +) + // createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { feeDenom := "aatom" @@ -287,27 +292,30 @@ func (s *IntegrationTestSuite) createEVMTransactionWithNonce(key keyring.Key, ga return txBuilder.GetTx(), nil } -func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) ([]*abci.ResponseCheckTx, error) { - result := make([]*abci.ResponseCheckTx, 0) - +func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) error { for _, tx := range txs { - txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) - if err != nil { - return []*abci.ResponseCheckTx{}, fmt.Errorf("failed to encode cosmos tx: %w", err) + if err := s.checkTx(tx); err != nil { + return err } + } + return nil +} - res, err := s.network.App.CheckTx(&abci.RequestCheckTx{ - Tx: txBytes, - Type: abci.CheckTxType_New, - }) - if err != nil { - return []*abci.ResponseCheckTx{}, fmt.Errorf("failed to encode cosmos tx: %w", err) - } +func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error { + txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) + if err != nil { + return fmt.Errorf("failed to encode cosmos tx: %w", err) + } - result = append(result, 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 result, nil + return nil } // createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index 524f5da84..ce6a1a820 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -12,10 +12,9 @@ import ( func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { fmt.Printf("DEBUG: Starting TestTransactionOrdering\n") testCases := []struct { - name string - setupTxs func() ([]sdk.Tx, []string) - verifyFunc func(iterator mempool.Iterator, txHashesInOrder []string) - bypass bool // Temporarily bypass test cases that have known issue. + name string + setupTxs func() ([]sdk.Tx, []string) + bypass bool // Temporarily bypass test cases that have known issue. }{ { name: "mixed EVM and cosmos transaction ordering", @@ -33,9 +32,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{highGasPriceEVMTx, highFeeCosmosTx, mediumFeeCosmosTx, lowFeeCosmosTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, }, { @@ -52,9 +51,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected Txs in order expectedTxs := []sdk.Tx{highFeeEVMTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, bypass: true, }, @@ -73,9 +72,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{highFeeEVMTx, lowFeeEVMTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, }, { @@ -90,9 +89,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{highFeeTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, bypass: true, }, @@ -111,9 +110,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{evmTx, cosmosTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, bypass: true, }, @@ -131,9 +130,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{evmTx, cosmosTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, bypass: true, }, @@ -151,9 +150,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{cosmosTx, evmTx} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, bypass: true, }, @@ -177,9 +176,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Expected txs in order expectedTxs := []sdk.Tx{evmHigh, cosmosHigh, evmMedium, cosmosMedium, evmLow, cosmosLow} - expectedTxHashes := s.getTxHashes(expectedTxs) + expTxHashes := s.getTxHashes(expectedTxs) - return inputTxs, expectedTxHashes + return inputTxs, expTxHashes }, }, } @@ -189,21 +188,23 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // Reset test setup to ensure clean state s.SetupTest() - txs, txHashesInOrder := tc.setupTxs() + txs, expTxHashes := tc.setupTxs() - _, err := s.checkTxs(txs) + err := s.checkTxs(txs) s.Require().NoError(err) mpool := s.network.App.GetMempool() iterator := mpool.Select(s.network.GetContext(), nil) - if !tc.bypass { - for _, txHash := range txHashesInOrder { - actualTxHash := s.getTxHash(iterator.Tx()) - s.Require().Equal(txHash, actualTxHash) + if tc.bypass { + return + } - iterator = iterator.Next() - } + for _, txHash := range expTxHashes { + actualTxHash := s.getTxHash(iterator.Tx()) + s.Require().Equal(txHash, actualTxHash) + + iterator = iterator.Next() } }) } @@ -216,24 +217,27 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { testCases := []struct { name string - setupTxs func() ([]sdk.Tx, []int) // Returns transactions and their expected nonces + setupTxs func() ([]sdk.Tx, []string) // Returns transactions and their expected nonces verifyFunc func(mpool mempool.Mempool) + bypass bool }{ { name: "insert transactions with nonce gaps", - setupTxs: func() ([]sdk.Tx, []int) { + setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) var txs []sdk.Tx - var nonces []int // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) for i := 0; i <= 6; i += 2 { - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) + tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(i)) txs = append(txs, tx) - nonces = append(nonces, i) } - return txs, nonces + // Expected txs in order + expectedTxs := txs[:1] + expTxHashes := s.getTxHashes(expectedTxs) + + return txs, expTxHashes }, verifyFunc: func(mpool mempool.Mempool) { // Only nonce 0 should be pending (the first consecutive transaction) @@ -244,24 +248,25 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { }, { name: "fill nonce gap and verify pending count increases", - setupTxs: func() ([]sdk.Tx, []int) { + setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) var txs []sdk.Tx - var nonces []int // First, insert transactions with gaps: nonces 0, 2, 4 for i := 0; i <= 4; i += 2 { tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) - nonces = append(nonces, i) } // Then fill the gap by inserting nonce 1 tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) txs = append(txs, tx) - nonces = append(nonces, 1) - return txs, nonces + // Expected txs in order + expectedTxs := []sdk.Tx{txs[0], txs[3], txs[1]} + expTxHashes := s.getTxHashes(expectedTxs) + + return txs, expTxHashes }, verifyFunc: func(mpool mempool.Mempool) { // After filling nonce 1, transactions 0, 1, 2 should be pending @@ -272,16 +277,15 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { }, { name: "fill multiple nonce gaps", - setupTxs: func() ([]sdk.Tx, []int) { + setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) var txs []sdk.Tx - var nonces []int // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) - nonces = append(nonces, i) + } // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 @@ -289,11 +293,15 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { if i%3 != 0 { // Skip nonces that are already inserted tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) - nonces = append(nonces, i) + } } - return txs, nonces + // Expected txs in order + expectedTxs := []sdk.Tx{txs[0], txs[4], txs[5], txs[1], txs[6], txs[7], txs[2], txs[8], txs[9], txs[3]} + expTxHashes := s.getTxHashes(expectedTxs) + + return txs, expTxHashes }, verifyFunc: func(mpool mempool.Mempool) { // After filling all gaps, all transactions should be pending @@ -303,9 +311,8 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { }, { name: "test different accounts with nonce gaps", - setupTxs: func() ([]sdk.Tx, []int) { + setupTxs: func() ([]sdk.Tx, []string) { var txs []sdk.Tx - var nonces []int // Use different keys for different accounts key1 := s.keyring.GetKey(0) @@ -315,17 +322,19 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { for i := 0; i <= 2; i += 2 { tx := s.createEVMTransferWithKeyAndNonce(key1, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) - nonces = append(nonces, i) } // Account 2: nonces 0, 3 (gaps at 1, 2) for i := 0; i <= 3; i += 3 { tx := s.createEVMTransferWithKeyAndNonce(key2, big.NewInt(1000000000), uint64(i)) txs = append(txs, tx) - nonces = append(nonces, i) } - return txs, nonces + // Expected txs in order + expectedTxs := []sdk.Tx{txs[0], txs[2]} + expTxHashes := s.getTxHashes(expectedTxs) + + return txs, expTxHashes }, verifyFunc: func(mpool mempool.Mempool) { // Account 1: nonce 0 pending, nonce 2 queued @@ -337,147 +346,61 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { }, { name: "test replacement transactions with higher gas price", - setupTxs: func() ([]sdk.Tx, []int) { + setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) var txs []sdk.Tx - var nonces []int // Insert transaction with nonce 0 and low gas price tx1 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(0)) txs = append(txs, tx1) - nonces = append(nonces, 0) // Insert transaction with nonce 1 tx2 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) txs = append(txs, tx2) - nonces = append(nonces, 1) // Replace nonce 0 transaction with higher gas price tx3 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(0)) txs = append(txs, tx3) - nonces = append(nonces, 0) - return txs, nonces + // Expected txs in order + expectedTxs := []sdk.Tx{txs[2], txs[1]} + expTxHashes := s.getTxHashes(expectedTxs) + + return txs, expTxHashes }, verifyFunc: func(mpool mempool.Mempool) { // After replacement, both nonces 0 and 1 should be pending count := mpool.CountTx() s.Require().Equal(2, count, "After replacement, both transactions should be pending") }, - }, - { - name: "track count changes when filling nonce gaps", - setupTxs: func() ([]sdk.Tx, []int) { - key := s.keyring.GetKey(0) - var txs []sdk.Tx - var nonces []int - - // Insert transactions with gaps: nonces 0, 3, 6, 9 - for i := 0; i <= 9; i += 3 { - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) - txs = append(txs, tx) - nonces = append(nonces, i) - } - - // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 - for i := 1; i <= 8; i++ { - if i%3 != 0 { // Skip nonces that are already inserted - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) - txs = append(txs, tx) - nonces = append(nonces, i) - } - } - - return txs, nonces - }, - verifyFunc: func(mpool mempool.Mempool) { - // After filling all gaps, all transactions should be pending - count := mpool.CountTx() - s.Require().Equal(10, count, "After filling all gaps, all 10 transactions should be pending") - }, - }, - { - name: "removing places subsequent transactions back into queued", - setupTxs: func() ([]sdk.Tx, []int) { - key := s.keyring.GetKey(0) - var txs []sdk.Tx - var nonces []int - - // Insert transactions with gaps: nonces 0, 2, 3, 4, 5, 6, 7 - for i := 0; i <= 7; i++ { - if i != 1 { // Skip nonce 1 to create a gap - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) - txs = append(txs, tx) - nonces = append(nonces, i) //#nosec G115 -- int overflow is not a concern here - } - } - - return txs, nonces - }, - verifyFunc: func(mpool mempool.Mempool) { - // Initially: nonces 0 should be pending, nonces 2, 3, 4, 5, 6, 7 should be queued - initialCount := mpool.CountTx() - s.Require().Equal(1, initialCount, "Initially only nonces 0, 1 should be pending") - key := s.keyring.GetKey(0) - - // Fill gap by inserting nonce 1 - tx1 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) - err := mpool.Insert(s.network.GetContext(), tx1) - s.Require().NoError(err) - - // After filling gap: all nonce transactions should be in pending - countAfterFilling := mpool.CountTx() - s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") - - // Remove nonce 1 transaction, dropping the rest (except for 0) into queued - tx1 = s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) - s.Require().NoError(err) - err = mpool.Remove(tx1) - s.Require().NoError(err) - - // After removal: only nonce 0 should be pending, the rest get dropped to queued - countAfterRemoval := mpool.CountTx() - s.Require().Equal(1, countAfterRemoval, "After removing nonce 1, only nonce 0 should be pending") - - // Fill gap by inserting nonce 1 - tx1 = s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) - s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), tx1) - s.Require().NoError(err) - - // After filling gap: all transactions should be re-promoted and places into pending - countAfterFilling = mpool.CountTx() - s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") - }, + bypass: true, }, } - for i, tc := range testCases { - fmt.Printf("DEBUG: TestNonceGappedEVMTransactions - Starting test case %d/%d: %s\n", i+1, len(testCases), tc.name) + for _, tc := range testCases { s.Run(tc.name, func() { - fmt.Printf("DEBUG: Running test case: %s\n", tc.name) - // Reset test setup to ensure clean state s.SetupTest() - fmt.Printf("DEBUG: SetupTest completed for: %s\n", tc.name) - txs, nonces := tc.setupTxs() + txs, expTxHashes := tc.setupTxs() + + err := s.checkTxs(txs) + s.Require().NoError(err) + mpool := s.network.App.GetMempool() + iterator := mpool.Select(s.network.GetContext(), nil) - // Insert transactions and track count changes - initialCount := mpool.CountTx() - fmt.Printf("DEBUG: Initial mempool count: %d\n", initialCount) + if tc.bypass { + return + } - for i, tx := range txs { - err := mpool.Insert(s.network.GetContext(), tx) - s.Require().NoError(err) + for _, txHash := range expTxHashes { + actualTxHash := s.getTxHash(iterator.Tx()) + s.Require().Equal(txHash, actualTxHash) - currentCount := mpool.CountTx() - fmt.Printf("DEBUG: After inserting nonce %d: count = %d\n", nonces[i], currentCount) + iterator = iterator.Next() } tc.verifyFunc(mpool) - fmt.Printf("DEBUG: Completed test case: %s\n", tc.name) }) - fmt.Printf("DEBUG: TestNonceGappedEVMTransactions - Completed test case %d/%d: %s\n", i+1, len(testCases), tc.name) } } From 5a384a7654d94c9eb0a9c239dbc57cdaee647c9d Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Mon, 25 Aug 2025 22:58:55 +0900 Subject: [PATCH 05/20] test(mempool): refactor integration test --- .../tests/integration/mempool/mempool_test.go | 3 +- tests/integration/mempool/test_helper.go | 353 +++--------------- .../mempool/test_mempool_integration.go | 252 ++++--------- .../mempool/test_mempool_integration_abci.go | 66 ++-- 4 files changed, 163 insertions(+), 511 deletions(-) diff --git a/evmd/tests/integration/mempool/mempool_test.go b/evmd/tests/integration/mempool/mempool_test.go index 3714c9afc..8c3830dba 100644 --- a/evmd/tests/integration/mempool/mempool_test.go +++ b/evmd/tests/integration/mempool/mempool_test.go @@ -1,9 +1,10 @@ package mempool import ( - "github.com/cosmos/evm/evmd/tests/integration" "testing" + "github.com/cosmos/evm/evmd/tests/integration" + "github.com/stretchr/testify/suite" "github.com/cosmos/evm/tests/integration/mempool" diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 891ef8c69..14a99779f 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -12,12 +12,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/evm/crypto/ethsecp256k1" "github.com/cosmos/evm/testutil/integration/base/factory" "github.com/cosmos/evm/testutil/keyring" evmtypes "github.com/cosmos/evm/x/vm/types" - "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" ) // Constants @@ -26,14 +23,8 @@ const ( ) // createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key -func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { +func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big.Int) sdk.Tx { feeDenom := "aatom" - gasLimit := uint64(TxGas) - - // Calculate fee amount from gas price: fee = gas_price * gas_limit - feeAmount := new(big.Int).Mul(gasPrice, big.NewInt(int64(gasLimit))) - - fmt.Printf("DEBUG: Creating cosmos transaction with gas price: %s aatom/gas, fee: %s %s\n", gasPrice.String(), feeAmount.String(), feeDenom) fromAddr := key.AccAddr toAddr := s.keyring.GetKey(1).AccAddr @@ -41,257 +32,55 @@ func (s *IntegrationTestSuite) createCosmosSendTransactionWithKey(key keyring.Ke bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err := txBuilder.SetMsgs(bankMsg) - s.Require().NoError(err) - - txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin(feeDenom, feeAmount.Int64()))) - txBuilder.SetGasLimit(gasLimit) + gasPriceConverted := sdkmath.NewIntFromBigInt(gasPrice) - // Sign the transaction with proper signing instead of dummy signature - err = s.factory.SignCosmosTx(key.Priv, txBuilder) + txArgs := factory.CosmosTxArgs{ + Msgs: []sdk.Msg{bankMsg}, + GasPrice: &gasPriceConverted, + } + tx, err := s.factory.BuildCosmosTx(key.Priv, txArgs) s.Require().NoError(err) fmt.Printf("DEBUG: Created cosmos transaction successfully\n") - return txBuilder.GetTx() -} - -// createCosmosSendTransaction creates a simple bank send transaction using the first key -func (s *IntegrationTestSuite) createCosmosSendTransaction(gasPrice *big.Int) sdk.Tx { - key := s.keyring.GetKey(0) - return s.createCosmosSendTransactionWithKey(key, gasPrice) -} - -// calculateCosmosGasPrice calculates the gas price for a Cosmos transaction -func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit uint64) *big.Int { - return new(big.Int).Div(big.NewInt(feeAmount), big.NewInt(int64(gasLimit))) //#nosec G115 -- not concern, test -} - -// calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction -// This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee -func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, baseFee *big.Int) *big.Int { - gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) - if baseFee == nil || baseFee.Sign() == 0 { - return gasPrice // No base fee, effective tip equals gas price - } - - if gasPrice.Cmp(baseFee) < 0 { - return big.NewInt(0) // Gas price lower than base fee, effective tip is zero - } - - return new(big.Int).Sub(gasPrice, baseFee) + return tx } // createEVMTransaction creates an EVM transaction using the provided key -func (s *IntegrationTestSuite) createEVMTransactionWithKey(key keyring.Key, gasPrice *big.Int) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM transaction with gas price: %s\n", gasPrice.String()) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) +func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce uint64, gasPrice *big.Int) sdk.Tx { + // to := common.HexToAddress(TestRecipient) + to := s.keyring.GetKey(1).Addr - to := common.HexToAddress("0x1234567890123456789012345678901234567890") - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: 0, + ethTxArgs := evmtypes.EvmTxArgs{ + Nonce: nonce, To: &to, - Value: big.NewInt(1000), - Gas: TxGas, - GasPrice: gasPrice, - Data: nil, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err - } - - fmt.Printf("DEBUG: Created EVM transaction successfully\n") - return txBuilder.GetTx(), nil -} - -// createEVMTransaction creates an EVM transaction using the first key -func (s *IntegrationTestSuite) createEVMTransaction(gasPrice *big.Int) (sdk.Tx, error) { - key := s.keyring.GetKey(0) - return s.createEVMTransactionWithKey(key, gasPrice) -} - -// createEVMContractDeployment creates an EVM transaction for contract deployment -func (s *IntegrationTestSuite) createEVMContractDeployment(key keyring.Key, gasPrice *big.Int, data []byte) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM contract deployment transaction with gas price: %s\n", gasPrice.String()) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: 0, - To: nil, // nil for contract deployment - Value: big.NewInt(0), - Gas: 100000, + Amount: big.NewInt(1000), + GasLimit: TxGas, GasPrice: gasPrice, - Data: data, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err + Input: nil, } + tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) + s.Require().NoError(err) - fmt.Printf("DEBUG: Created EVM contract deployment transaction successfully\n") - return txBuilder.GetTx(), nil + return tx } -// createEVMValueTransfer creates an EVM transaction for value transfer -func (s *IntegrationTestSuite) createEVMValueTransfer(key keyring.Key, gasPrice *big.Int, value *big.Int, to common.Address) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM value transfer transaction with gas price: %s\n", gasPrice.String()) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ +// 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{ Nonce: 0, - To: &to, - Value: value, - Gas: TxGas, - GasPrice: gasPrice, - Data: nil, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - if err != nil { - return nil, err - } - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err - } - - fmt.Printf("DEBUG: Created EVM value transfer transaction successfully\n") - return txBuilder.GetTx(), nil -} - -// createEVMTransactionWithNonce creates an EVM transaction with a specific nonce -func (s *IntegrationTestSuite) createEVMTransactionWithNonce(key keyring.Key, gasPrice *big.Int, nonce int) (sdk.Tx, error) { - fmt.Printf("DEBUG: Creating EVM transaction with gas price: %s and nonce: %d\n", gasPrice.String(), nonce) - - privKey := key.Priv - - // Convert Cosmos address to EVM address - fromAddr := common.BytesToAddress(key.AccAddr.Bytes()) - fmt.Printf("DEBUG: Using prefunded account: %s\n", fromAddr.Hex()) - - to := common.HexToAddress("0x1234567890123456789012345678901234567890") - ethTx := ethtypes.NewTx(ðtypes.LegacyTx{ - Nonce: uint64(nonce), //#nosec G115 -- int overflow is not a concern here - To: &to, - Value: big.NewInt(1000), - Gas: TxGas, + To: nil, + Amount: nil, + GasLimit: TxGas, GasPrice: gasPrice, - Data: nil, - }) - - // Convert to ECDSA private key for signing - ethPrivKey, ok := privKey.(*ethsecp256k1.PrivKey) - if !ok { - return nil, fmt.Errorf("expected ethsecp256k1.PrivKey, got %T", privKey) - } - - ecdsaPrivKey, err := ethPrivKey.ToECDSA() - if err != nil { - return nil, err - } - - signer := ethtypes.HomesteadSigner{} - signedTx, err := ethtypes.SignTx(ethTx, signer, ecdsaPrivKey) - if err != nil { - return nil, err - } - - msgEthTx := &evmtypes.MsgEthereumTx{} - msgEthTx.FromEthereumTx(signedTx) - if err != nil { - return nil, err - } - - txBuilder := s.network.App.GetTxConfig().NewTxBuilder() - err = txBuilder.SetMsgs(msgEthTx) - if err != nil { - return nil, err + Input: data, } + tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) + s.Require().NoError(err) - fmt.Printf("DEBUG: Created EVM transaction successfully\n") - return txBuilder.GetTx(), nil + return tx } +// 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 { @@ -301,6 +90,7 @@ func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) error { return nil } +// checkTxs call abci CheckTx for a transaction func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error { txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) if err != nil { @@ -318,65 +108,7 @@ func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error { return nil } -// createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key -func (s *IntegrationTestSuite) createCosmosSendTxWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { - feeDenom := "aatom" - - fromAddr := key.AccAddr - toAddr := s.keyring.GetKey(1).AccAddr - amount := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 1000)) - - bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) - - gasPriceConverted := sdkmath.NewIntFromBigInt(gasPrice) - - txArgs := factory.CosmosTxArgs{ - Msgs: []sdk.Msg{bankMsg}, - GasPrice: &gasPriceConverted, - } - tx, err := s.factory.BuildCosmosTx(key.Priv, txArgs) - s.Require().NoError(err) - - fmt.Printf("DEBUG: Created cosmos transaction successfully\n") - return tx -} - -// createEVMTransaction creates an EVM transaction using the provided key -func (s *IntegrationTestSuite) createEVMTransferWithKey(key keyring.Key, gasPrice *big.Int) sdk.Tx { - to := common.HexToAddress("0x1234567890123456789012345678901234567890") - - ethTxArgs := evmtypes.EvmTxArgs{ - Nonce: 0, - To: &to, - Amount: big.NewInt(1000), - GasLimit: TxGas, - GasPrice: gasPrice, - Input: nil, - } - tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) - s.Require().NoError(err) - - return tx -} - -// createEVMTransaction creates an EVM transaction using the provided key -func (s *IntegrationTestSuite) createEVMTransferWithKeyAndNonce(key keyring.Key, gasPrice *big.Int, nonce uint64) sdk.Tx { - to := common.HexToAddress("0x1234567890123456789012345678901234567890") - - ethTxArgs := evmtypes.EvmTxArgs{ - Nonce: nonce, - To: &to, - Amount: big.NewInt(1000), - GasLimit: TxGas, - GasPrice: gasPrice, - Input: nil, - } - tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) - s.Require().NoError(err) - - return tx -} - +// getTxHashes returns transaction hashes for multiple transactions func (s *IntegrationTestSuite) getTxHashes(txs []sdk.Tx) []string { txHashes := []string{} for _, tx := range txs { @@ -387,6 +119,7 @@ func (s *IntegrationTestSuite) getTxHashes(txs []sdk.Tx) []string { return txHashes } +// getTxHash returns transaction hash for a transaction func (s *IntegrationTestSuite) getTxHash(tx sdk.Tx) string { txEncoder := s.network.App.GetTxConfig().TxEncoder() txBytes, err := txEncoder(tx) @@ -394,3 +127,23 @@ func (s *IntegrationTestSuite) getTxHash(tx sdk.Tx) string { return hex.EncodeToString(tmhash.Sum(txBytes)) } + +// calculateCosmosGasPrice calculates the gas price for a Cosmos transaction +func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit uint64) *big.Int { + return new(big.Int).Div(big.NewInt(feeAmount), big.NewInt(int64(gasLimit))) //#nosec G115 -- not concern, test +} + +// calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction +// This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee +func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, baseFee *big.Int) *big.Int { + gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) + if baseFee == nil || baseFee.Sign() == 0 { + return gasPrice // No base fee, effective tip equals gas price + } + + if gasPrice.Cmp(baseFee) < 0 { + return big.NewInt(0) // Gas price lower than base fee, effective tip is zero + } + + return new(big.Int).Sub(gasPrice, baseFee) +} diff --git a/tests/integration/mempool/test_mempool_integration.go b/tests/integration/mempool/test_mempool_integration.go index 8261af068..5f2a27240 100644 --- a/tests/integration/mempool/test_mempool_integration.go +++ b/tests/integration/mempool/test_mempool_integration.go @@ -17,12 +17,6 @@ import ( "github.com/cosmos/cosmos-sdk/types/mempool" ) -// Constants -const ( - // TxGas = 21000 - TxGas = 100_000 -) - // TestMempoolInsert tests transaction insertion into the mempool func (s *IntegrationTestSuite) TestMempoolInsert() { fmt.Printf("DEBUG: Starting TestMempoolInsert\n") @@ -36,7 +30,7 @@ func (s *IntegrationTestSuite) TestMempoolInsert() { { name: "cosmos transaction success", setupTx: func() sdk.Tx { - return s.createCosmosSendTransaction(big.NewInt(1000)) + return s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) }, wantError: false, verifyFunc: func() { @@ -47,9 +41,7 @@ func (s *IntegrationTestSuite) TestMempoolInsert() { { name: "EVM transaction success", setupTx: func() sdk.Tx { - tx, err := s.createEVMTransaction(big.NewInt(1000000000)) - s.Require().NoError(err) - return tx + return s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) }, wantError: false, verifyFunc: func() { @@ -63,10 +55,7 @@ func (s *IntegrationTestSuite) TestMempoolInsert() { key := s.keyring.GetKey(0) data := []byte{0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3} // Simple contract deployment - // Use the contract deployment helper - tx, err := s.createEVMContractDeployment(key, big.NewInt(1000000000), data) - s.Require().NoError(err) - return tx + return s.createEVMContractDeployTx(key, big.NewInt(1000000000), data) }, wantError: false, verifyFunc: func() { @@ -188,7 +177,7 @@ func (s *IntegrationTestSuite) TestMempoolRemove() { { name: "remove cosmos transaction success", setupTx: func() sdk.Tx { - return s.createCosmosSendTransaction(big.NewInt(1000)) + return s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) }, insertFirst: true, wantError: false, @@ -198,17 +187,16 @@ func (s *IntegrationTestSuite) TestMempoolRemove() { }, }, { - name: "remove EVM transaction success", + name: "remove EVM transaction fail", setupTx: func() sdk.Tx { - tx, err := s.createEVMTransaction(big.NewInt(1000000000)) - s.Require().NoError(err) - return tx + return s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) }, insertFirst: true, wantError: false, verifyFunc: func() { mpool := s.network.App.GetMempool() - s.Require().Equal(0, mpool.CountTx()) + // mempool.Remove can only remove invalid evm transaction + s.Require().Equal(1, mpool.CountTx()) }, }, { @@ -226,7 +214,7 @@ func (s *IntegrationTestSuite) TestMempoolRemove() { { name: "remove non-existent transaction", setupTx: func() sdk.Tx { - return s.createCosmosSendTransaction(big.NewInt(1000)) + return s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) }, insertFirst: false, wantError: true, // Remove should error for non-existent transactions @@ -290,7 +278,7 @@ func (s *IntegrationTestSuite) TestMempoolSelect() { { name: "single cosmos transaction", setupTxs: func() { - cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000)) + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) mpool := s.network.App.GetMempool() err := mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) @@ -304,10 +292,10 @@ func (s *IntegrationTestSuite) TestMempoolSelect() { { name: "single EVM transaction", setupTxs: func() { - evmTx, err := s.createEVMTransaction(big.NewInt(1000000000)) - s.Require().NoError(err) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) + mpool := s.network.App.GetMempool() - err = mpool.Insert(s.network.GetContext(), evmTx) + err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -359,7 +347,7 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { { name: "single cosmos transaction iteration", setupTxs: func() { - cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000)) + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) mpool := s.network.App.GetMempool() err := mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) @@ -372,10 +360,9 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { { name: "single EVM transaction iteration", setupTxs: func() { - evmTx, err := s.createEVMTransaction(big.NewInt(1000000000)) - s.Require().NoError(err) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) mpool := s.network.App.GetMempool() - err = mpool.Insert(s.network.GetContext(), evmTx) + err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -396,11 +383,11 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { setupTxs: func() { mpool := s.network.App.GetMempool() - cosmosTx1 := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(1000)) + cosmosTx1 := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) err := mpool.Insert(s.network.GetContext(), cosmosTx1) s.Require().NoError(err) - cosmosTx2 := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(1), big.NewInt(2000)) + cosmosTx2 := s.createCosmosSendTx(s.keyring.GetKey(1), big.NewInt(2000)) err = mpool.Insert(s.network.GetContext(), cosmosTx2) s.Require().NoError(err) }, @@ -421,13 +408,13 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { mpool := s.network.App.GetMempool() // Add EVM transaction - evmTx, err := s.createEVMTransaction(big.NewInt(2000)) - s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), evmTx) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(2000)) + + err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) // Add Cosmos transaction - cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000)) + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) err = mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) }, @@ -469,18 +456,17 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { name: "mixed EVM and cosmos transaction ordering", setupTxs: func() { // Create EVM transaction with high gas price - highGasPriceEVMTx, err := s.createEVMTransaction(big.NewInt(5000000000)) - s.Require().NoError(err) + highGasPriceEVMTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(5000000000)) // Create Cosmos transactions with different fee amounts - highFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(6), big.NewInt(5000000000)) - mediumFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(7), big.NewInt(3000000000)) - lowFeeCosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(8), big.NewInt(1000000000)) + highFeeCosmosTx := s.createCosmosSendTx(s.keyring.GetKey(6), big.NewInt(5000000000)) + mediumFeeCosmosTx := s.createCosmosSendTx(s.keyring.GetKey(7), big.NewInt(3000000000)) + lowFeeCosmosTx := s.createCosmosSendTx(s.keyring.GetKey(8), big.NewInt(1000000000)) mpool := s.network.App.GetMempool() // Insert in non-priority order - err = mpool.Insert(s.network.GetContext(), lowFeeCosmosTx) + err := mpool.Insert(s.network.GetContext(), lowFeeCosmosTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), highGasPriceEVMTx) s.Require().NoError(err) @@ -515,17 +501,15 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { name: "EVM-only transaction replacement", setupTxs: func() { // Create first EVM transaction with low fee - lowFeeEVMTx, err := s.createEVMTransaction(big.NewInt(1000000000)) // 1 gaatom - s.Require().NoError(err) + lowFeeEVMTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) // 1 gaatom // Create second EVM transaction with high fee - highFeeEVMTx, err := s.createEVMTransaction(big.NewInt(5000000000)) // 5 gaatom - s.Require().NoError(err) + highFeeEVMTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(5000000000)) // 5 gaatom mpool := s.network.App.GetMempool() // Insert low fee transaction first - err = mpool.Insert(s.network.GetContext(), lowFeeEVMTx) + err := mpool.Insert(s.network.GetContext(), lowFeeEVMTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), highFeeEVMTx) s.Require().NoError(err) @@ -547,17 +531,15 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { setupTxs: func() { key := s.keyring.GetKey(0) // Create first EVM transaction with low fee - lowFeeEVMTx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) // 1 gaatom - s.Require().NoError(err) + lowFeeEVMTx := s.createEVMValueTransferTx(key, 1, big.NewInt(1000000000)) // 1 gaatom // Create second EVM transaction with high fee - highFeeEVMTx, err := s.createEVMTransactionWithNonce(key, big.NewInt(5000000000), 0) // 5 gaatom - s.Require().NoError(err) + highFeeEVMTx := s.createEVMValueTransferTx(key, 0, big.NewInt(5000000000)) // 5 gaatom mpool := s.network.App.GetMempool() // Insert low fee transaction first - err = mpool.Insert(s.network.GetContext(), lowFeeEVMTx) + err := mpool.Insert(s.network.GetContext(), lowFeeEVMTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), highFeeEVMTx) s.Require().NoError(err) @@ -585,9 +567,9 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { { name: "cosmos-only transaction replacement", setupTxs: func() { - highFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom - lowFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom - mediumFeeTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom + highFeeTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom + lowFeeTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom + mediumFeeTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom mpool := s.network.App.GetMempool() @@ -617,16 +599,15 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { setupTxs: func() { // Create transactions with equal effective tips (assuming base fee = 0) // EVM: 1000 aatom/gas effective tip - evmTx, err := s.createEVMTransaction(big.NewInt(1000000000)) // 1 gaatom/gas - s.Require().NoError(err) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) // 1 gaatom/gas // Cosmos with same effective tip: 1000 * 200000 = 200000000 aatom total fee - cosmosTx := s.createCosmosSendTransaction(big.NewInt(1000000000)) // 1 gaatom/gas effective tip + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas effective tip mpool := s.network.App.GetMempool() // Insert Cosmos first, then EVM - err = mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) @@ -660,16 +641,15 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { name: "mixed transactions with EVM having higher effective tip", setupTxs: func() { // Create EVM transaction with higher gas price - evmTx, err := s.createEVMTransaction(big.NewInt(5000000000)) // 5 gaatom/gas - s.Require().NoError(err) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(5000000000)) // 5 gaatom/gas // Create Cosmos transaction with lower gas price - cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000000000)) // 2 gaatom/gas + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom/gas mpool := s.network.App.GetMempool() // Insert Cosmos first, then EVM - err = mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) @@ -700,16 +680,15 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { name: "mixed transactions with Cosmos having higher effective tip", setupTxs: func() { // Create EVM transaction with lower gas price - evmTx, err := s.createEVMTransaction(big.NewInt(2000000000)) // 2000 aatom/gas - s.Require().NoError(err) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(2000000000)) // 2000 aatom/gas // Create Cosmos transaction with higher gas price - cosmosTx := s.createCosmosSendTransaction(big.NewInt(5000000000)) // 5000 aatom/gas + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5000 aatom/gas mpool := s.network.App.GetMempool() // Insert EVM first, then Cosmos - err = mpool.Insert(s.network.GetContext(), evmTx) + err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) @@ -743,21 +722,18 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { // EVM: 8000, 4000, 2000 aatom/gas // Cosmos: 6000, 3000, 1000 aatom/gas - evmHigh, err := s.createEVMTransaction(big.NewInt(8000000000)) - s.Require().NoError(err) - evmMedium, err := s.createEVMTransactionWithKey(s.keyring.GetKey(1), big.NewInt(4000000000)) - s.Require().NoError(err) - evmLow, err := s.createEVMTransactionWithKey(s.keyring.GetKey(2), big.NewInt(2000000000)) - s.Require().NoError(err) + evmHigh := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(8000000000)) + evmMedium := s.createEVMValueTransferTx(s.keyring.GetKey(1), 0, big.NewInt(4000000000)) + evmLow := s.createEVMValueTransferTx(s.keyring.GetKey(2), 0, big.NewInt(2000000000)) - cosmosHigh := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(3), big.NewInt(6000000000)) - cosmosMedium := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(4), big.NewInt(3000000000)) - cosmosLow := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(5), big.NewInt(1000000000)) + cosmosHigh := s.createCosmosSendTx(s.keyring.GetKey(3), big.NewInt(6000000000)) + cosmosMedium := s.createCosmosSendTx(s.keyring.GetKey(4), big.NewInt(3000000000)) + cosmosLow := s.createCosmosSendTx(s.keyring.GetKey(5), big.NewInt(1000000000)) mpool := s.network.App.GetMempool() // Insert in random order - err = mpool.Insert(s.network.GetContext(), cosmosLow) + err := mpool.Insert(s.network.GetContext(), cosmosLow) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmMedium) s.Require().NoError(err) @@ -877,7 +853,7 @@ func (s *IntegrationTestSuite) TestSelectBy() { { name: "single cosmos transaction - terminates properly", setupTxs: func() { - cosmosTx := s.createCosmosSendTransaction(big.NewInt(2000)) + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) mpool := s.network.App.GetMempool() err := mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) @@ -890,10 +866,9 @@ func (s *IntegrationTestSuite) TestSelectBy() { { name: "single EVM transaction - terminates properly", setupTxs: func() { - evmTx, err := s.createEVMTransaction(big.NewInt(1000000000)) - s.Require().NoError(err) + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) mpool := s.network.App.GetMempool() - err = mpool.Insert(s.network.GetContext(), evmTx) + err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) }, filterFunc: func(tx sdk.Tx) bool { @@ -908,7 +883,7 @@ func (s *IntegrationTestSuite) TestSelectBy() { // Add transactions with different fees for i := 1; i < 6; i++ { // Use different keys for different transactions - cosmosTx := s.createCosmosSendTransactionWithKey(s.keyring.GetKey(i), big.NewInt(int64(i*1000))) // 5000, 4000, 3000, 2000, 1000 + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(i), big.NewInt(int64(i*1000))) // 5000, 4000, 3000, 2000, 1000 err := mpool.Insert(s.network.GetContext(), cosmosTx) s.Require().NoError(err) } @@ -938,9 +913,8 @@ func (s *IntegrationTestSuite) TestSelectBy() { fmt.Printf("DEBUG: Using prefunded account %d: %s\n", keyIndex, fromAddr.Hex()) // Use the helper method with specific nonce - evmTx, err := s.createEVMTransactionWithKey(key, big.NewInt(int64(i)*100000000000)) - s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), evmTx) + evmTx := s.createEVMValueTransferTx(key, 0, big.NewInt(int64(i)*100000000000)) + err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) } }, @@ -1011,7 +985,7 @@ func (s *IntegrationTestSuite) TestMempoolHeightRequirement() { s.Require().Equal(int64(2), nw.GetContext().BlockHeight()) mpool := nw.App.GetMempool() - tx := s.createCosmosSendTransaction(big.NewInt(1000)) + tx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) // Should fail because mempool requires block height >= 2 err = mpool.Insert(nw.GetContext(), tx) @@ -1034,9 +1008,7 @@ func (s *IntegrationTestSuite) TestEVMTransactionComprehensive() { { name: "EVM transaction with high gas price", setupTx: func() sdk.Tx { - tx, err := s.createEVMTransaction(big.NewInt(10000000000)) // 10 gaatom - s.Require().NoError(err) - return tx + return s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(10000000000)) // 10 gaatom }, wantError: false, verifyFunc: func() { @@ -1047,9 +1019,7 @@ func (s *IntegrationTestSuite) TestEVMTransactionComprehensive() { { name: "EVM transaction with low gas price", setupTx: func() sdk.Tx { - tx, err := s.createEVMTransaction(big.NewInt(100000000)) // 0.1 gaatom - s.Require().NoError(err) - return tx + return s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(100000000)) // 0.1 gaatom }, wantError: false, verifyFunc: func() { @@ -1065,9 +1035,7 @@ func (s *IntegrationTestSuite) TestEVMTransactionComprehensive() { data := []byte{0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3} // Simple contract deployment // Use the contract deployment helper - tx, err := s.createEVMContractDeployment(key, big.NewInt(1000000000), data) - s.Require().NoError(err) - return tx + return s.createEVMContractDeployTx(key, big.NewInt(1000000000), data) }, wantError: false, verifyFunc: func() { @@ -1080,11 +1048,9 @@ func (s *IntegrationTestSuite) TestEVMTransactionComprehensive() { setupTx: func() sdk.Tx { // Use key 0 again since this is a separate test (SetupTest resets state) key := s.keyring.GetKey(0) - to := common.HexToAddress("0x1234567890123456789012345678901234567890") // Use the value transfer helper - tx, err := s.createEVMValueTransfer(key, big.NewInt(1000000000), big.NewInt(1000000000000000000), to) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, 0, big.NewInt(1000000000)) return tx }, wantError: false, @@ -1143,8 +1109,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) for i := 0; i <= 6; i += 2 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1167,15 +1132,13 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // First, insert transactions with gaps: nonces 0, 2, 4 for i := 0; i <= 4; i += 2 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } // Then fill the gap by inserting nonce 1 - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, 1, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, 1) @@ -1197,8 +1160,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1206,8 +1168,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1233,16 +1194,14 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Account 1: nonces 0, 2 (gap at 1) for i := 0; i <= 2; i += 2 { - tx, err := s.createEVMTransactionWithNonce(key1, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key1, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } // Account 2: nonces 0, 3 (gaps at 1, 2) for i := 0; i <= 3; i += 3 { - tx, err := s.createEVMTransactionWithNonce(key2, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key2, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1265,20 +1224,17 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { var nonces []int // Insert transaction with nonce 0 and low gas price - tx1, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 0) - s.Require().NoError(err) + tx1 := s.createEVMValueTransferTx(key, 0, big.NewInt(1000000000)) txs = append(txs, tx1) nonces = append(nonces, 0) // Insert transaction with nonce 1 - tx2, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) + tx2 := s.createEVMValueTransferTx(key, 1, big.NewInt(1000000000)) txs = append(txs, tx2) nonces = append(nonces, 1) // Replace nonce 0 transaction with higher gas price - tx3, err := s.createEVMTransactionWithNonce(key, big.NewInt(2000000000), 0) - s.Require().NoError(err) + tx3 := s.createEVMValueTransferTx(key, 0, big.NewInt(2000000000)) txs = append(txs, tx3) nonces = append(nonces, 0) @@ -1299,8 +1255,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Insert transactions with gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1308,8 +1263,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1323,62 +1277,6 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { s.Require().Equal(10, count, "After filling all gaps, all 10 transactions should be pending") }, }, - { - name: "removing places subsequent transactions back into queued", - setupTxs: func() ([]sdk.Tx, []int) { - key := s.keyring.GetKey(0) - var txs []sdk.Tx - var nonces []int - - // Insert transactions with gaps: nonces 0, 1, 3, 4, 6, 7 - for i := 0; i <= 7; i++ { - if i != 1 { // Skip nonce 1 to create a gap - tx, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), i) - s.Require().NoError(err) - txs = append(txs, tx) - nonces = append(nonces, i) //#nosec G115 -- int overflow is not a concern here - } - } - - return txs, nonces - }, - verifyFunc: func(mpool mempool.Mempool) { - // Initially: nonces 0 should be pending, nonces 2, 3, 4, 5, 6, 7 should be queued - initialCount := mpool.CountTx() - s.Require().Equal(1, initialCount, "Initially only nonces 0, 1 should be pending") - key := s.keyring.GetKey(0) - - // Fill gap by inserting nonce 1 - tx1, err := s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), tx1) - s.Require().NoError(err) - - // After filling gap: all nonce transactions should be in pending - countAfterFilling := mpool.CountTx() - s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") - - // Remove nonce 1 transaction, dropping the rest (except for 0) into queued - tx1, err = s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) - err = mpool.Remove(tx1) - s.Require().NoError(err) - - // After removal: only nonce 0 should be pending, the rest get dropped to queued - countAfterRemoval := mpool.CountTx() - s.Require().Equal(1, countAfterRemoval, "After removing nonce 1, only nonce 0 should be pending") - - // Fill gap by inserting nonce 1 - tx1, err = s.createEVMTransactionWithNonce(key, big.NewInt(1000000000), 1) - s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), tx1) - s.Require().NoError(err) - - // After filling gap: all transactions should be re-promoted and places into pending - countAfterFilling = mpool.CountTx() - s.Require().Equal(8, countAfterFilling, "After filling gap, only nonce 0 should be pending due to gap at nonce 1") - }, - }, } for i, tc := range testCases { diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index ce6a1a820..213d0aa8f 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -20,12 +20,12 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { name: "mixed EVM and cosmos transaction ordering", setupTxs: func() ([]sdk.Tx, []string) { // Create EVM transaction with high gas price - highGasPriceEVMTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) + highGasPriceEVMTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(5000000000)) // Create Cosmos transactions with different fee amounts - highFeeCosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(6), big.NewInt(5000000000)) - mediumFeeCosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(7), big.NewInt(3000000000)) - lowFeeCosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(8), big.NewInt(2000000000)) + highFeeCosmosTx := s.createCosmosSendTx(s.keyring.GetKey(6), big.NewInt(5000000000)) + mediumFeeCosmosTx := s.createCosmosSendTx(s.keyring.GetKey(7), big.NewInt(3000000000)) + lowFeeCosmosTx := s.createCosmosSendTx(s.keyring.GetKey(8), big.NewInt(2000000000)) // Input txs in order inputTxs := []sdk.Tx{lowFeeCosmosTx, highGasPriceEVMTx, mediumFeeCosmosTx, highFeeCosmosTx} @@ -41,10 +41,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { name: "EVM-only transaction replacement", setupTxs: func() ([]sdk.Tx, []string) { // Create first EVM transaction with low fee - lowFeeEVMTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom + lowFeeEVMTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(2000000000)) // 2 gaatom // Create second EVM transaction with high fee - highFeeEVMTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom + highFeeEVMTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(5000000000)) // 5 gaatom // Input txs in order inputTxs := []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} @@ -62,10 +62,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) // Create first EVM transaction with low fee - lowFeeEVMTx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(1)) // 2 gaatom + lowFeeEVMTx := s.createEVMValueTransferTx(key, uint64(1), big.NewInt(2000000000)) // 2 gaatom // Create second EVM transaction with high fee - highFeeEVMTx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(5000000000), uint64(0)) // 5 gaatom + highFeeEVMTx := s.createEVMValueTransferTx(key, uint64(0), big.NewInt(5000000000)) // 5 gaatom // Input txs in order inputTxs := []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} @@ -80,9 +80,9 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { { name: "cosmos-only transaction replacement", setupTxs: func() ([]sdk.Tx, []string) { - highFeeTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom - lowFeeTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom - mediumFeeTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom + highFeeTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom + lowFeeTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom + mediumFeeTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(3000000000)) // 3 gaatom // Input txs in order inputTxs := []sdk.Tx{mediumFeeTx, lowFeeTx, highFeeTx} @@ -100,10 +100,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { setupTxs: func() ([]sdk.Tx, []string) { // Create transactions with equal effective tips (assuming base fee = 0) // EVM: 1000 aatom/gas effective tip - evmTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(1000000000)) // 1 gaatom/gas // Cosmos with same effective tip: 1000 * 200000 = 200000000 aatom total fee - cosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas effective tip + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000000000)) // 1 gaatom/gas effective tip // Input txs in order inputTxs := []sdk.Tx{cosmosTx, evmTx} @@ -120,10 +120,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { name: "mixed transactions with EVM having higher effective tip", setupTxs: func() ([]sdk.Tx, []string) { // Create EVM transaction with higher gas price - evmTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5 gaatom/gas + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(5000000000)) // 5 gaatom/gas // Create Cosmos transaction with lower gas price - cosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom/gas + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2 gaatom/gas // Input txs in order inputTxs := []sdk.Tx{cosmosTx, evmTx} @@ -140,10 +140,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { name: "mixed transactions with Cosmos having higher effective tip", setupTxs: func() ([]sdk.Tx, []string) { // Create EVM transaction with lower gas price - evmTx := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(2000000000)) // 2000 aatom/gas + evmTx := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(2000000000)) // 2000 aatom/gas // Create Cosmos transaction with higher gas price - cosmosTx := s.createCosmosSendTxWithKey(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5000 aatom/gas + cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(5000000000)) // 5000 aatom/gas // Input txs in order inputTxs := []sdk.Tx{evmTx, cosmosTx} @@ -163,13 +163,13 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { // EVM: 10000, 8000, 6000 aatom/gas // Cosmos: 9000, 7000, 5000 aatom/gas - evmHigh := s.createEVMTransferWithKey(s.keyring.GetKey(0), big.NewInt(10000000000)) - evmMedium := s.createEVMTransferWithKey(s.keyring.GetKey(1), big.NewInt(8000000000)) - evmLow := s.createEVMTransferWithKey(s.keyring.GetKey(2), big.NewInt(6000000000)) + evmHigh := s.createEVMValueTransferTx(s.keyring.GetKey(0), 0, big.NewInt(10000000000)) + evmMedium := s.createEVMValueTransferTx(s.keyring.GetKey(1), 0, big.NewInt(8000000000)) + evmLow := s.createEVMValueTransferTx(s.keyring.GetKey(2), 0, big.NewInt(6000000000)) - cosmosHigh := s.createCosmosSendTxWithKey(s.keyring.GetKey(3), big.NewInt(9000000000)) - cosmosMedium := s.createCosmosSendTxWithKey(s.keyring.GetKey(4), big.NewInt(7000000000)) - cosmosLow := s.createCosmosSendTxWithKey(s.keyring.GetKey(5), big.NewInt(5000000000)) + cosmosHigh := s.createCosmosSendTx(s.keyring.GetKey(3), big.NewInt(9000000000)) + cosmosMedium := s.createCosmosSendTx(s.keyring.GetKey(4), big.NewInt(7000000000)) + cosmosLow := s.createCosmosSendTx(s.keyring.GetKey(5), big.NewInt(5000000000)) // Input txs in order inputTxs := []sdk.Tx{cosmosHigh, cosmosMedium, cosmosLow, evmHigh, evmMedium, evmLow} @@ -229,7 +229,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) for i := 0; i <= 6; i += 2 { - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(i)) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(2000000000)) txs = append(txs, tx) } @@ -254,12 +254,12 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // First, insert transactions with gaps: nonces 0, 2, 4 for i := 0; i <= 4; i += 2 { - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) } // Then fill the gap by inserting nonce 1 - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) + tx := s.createEVMValueTransferTx(key, uint64(1), big.NewInt(1000000000)) txs = append(txs, tx) // Expected txs in order @@ -283,7 +283,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) } @@ -291,7 +291,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(i)) + tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) } @@ -320,13 +320,13 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { // Account 1: nonces 0, 2 (gap at 1) for i := 0; i <= 2; i += 2 { - tx := s.createEVMTransferWithKeyAndNonce(key1, big.NewInt(1000000000), uint64(i)) + tx := s.createEVMValueTransferTx(key1, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) } // Account 2: nonces 0, 3 (gaps at 1, 2) for i := 0; i <= 3; i += 3 { - tx := s.createEVMTransferWithKeyAndNonce(key2, big.NewInt(1000000000), uint64(i)) + tx := s.createEVMValueTransferTx(key2, uint64(i), big.NewInt(1000000000)) txs = append(txs, tx) } @@ -351,15 +351,15 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { var txs []sdk.Tx // Insert transaction with nonce 0 and low gas price - tx1 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(0)) + tx1 := s.createEVMValueTransferTx(key, uint64(0), big.NewInt(1000000000)) txs = append(txs, tx1) // Insert transaction with nonce 1 - tx2 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(1000000000), uint64(1)) + tx2 := s.createEVMValueTransferTx(key, uint64(1), big.NewInt(1000000000)) txs = append(txs, tx2) // Replace nonce 0 transaction with higher gas price - tx3 := s.createEVMTransferWithKeyAndNonce(key, big.NewInt(2000000000), uint64(0)) + tx3 := s.createEVMValueTransferTx(key, uint64(0), big.NewInt(2000000000)) txs = append(txs, tx3) // Expected txs in order From d74f5adc80fca2c8db49375b963bf2e6a45b63dc Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Mon, 25 Aug 2025 23:15:52 +0900 Subject: [PATCH 06/20] chore: modify comments --- tests/integration/mempool/test_helper.go | 1 - tests/integration/mempool/test_mempool_integration_abci.go | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 14a99779f..17e47e646 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -47,7 +47,6 @@ func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big // createEVMTransaction creates an EVM transaction using the provided key func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce uint64, gasPrice *big.Int) sdk.Tx { - // to := common.HexToAddress(TestRecipient) to := s.keyring.GetKey(1).Addr ethTxArgs := evmtypes.EvmTxArgs{ diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index 213d0aa8f..28d464dd7 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -14,7 +14,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { testCases := []struct { name string setupTxs func() ([]sdk.Tx, []string) - bypass bool // Temporarily bypass test cases that have known issue. + // TODO: remove bypass option after anteHandler is fixed. + // Current anteHandler rejects valid high-gas transaction to replace low-gas transaction + // So, all replacement test cases fail. + bypass bool }{ { name: "mixed EVM and cosmos transaction ordering", From abbe07106dc6796c8159486db1aca21859245823 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 26 Aug 2025 14:39:13 +0900 Subject: [PATCH 07/20] test(mempool): add PrepareProposal check to integration test --- .../mempool/test_mempool_integration_abci.go | 62 ++++++++++++++++--- testutil/integration/evm/network/abci.go | 21 +++++++ 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index 28d464dd7..b1774e878 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -1,16 +1,20 @@ package mempool import ( + "encoding/hex" "fmt" "math/big" + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/tmhash" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/mempool" ) -// TestTransactionOrdering tests transaction ordering based on fees -func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { - fmt.Printf("DEBUG: Starting TestTransactionOrdering\n") +// TestTransactionOrderingWithABCIMethodCalls tests transaction ordering based on fees +func (s *IntegrationTestSuite) TestTransactionOrderingWithABCIMethodCalls() { + fmt.Printf("DEBUG: Starting TestTransactionOrderingWithABCIMethodCalls\n") testCases := []struct { name string setupTxs func() ([]sdk.Tx, []string) @@ -193,30 +197,50 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithCheckTx() { txs, expTxHashes := tc.setupTxs() + // Call CheckTx for transactions err := s.checkTxs(txs) s.Require().NoError(err) - mpool := s.network.App.GetMempool() - iterator := mpool.Select(s.network.GetContext(), nil) + // Call FinalizeBlock to make finalizeState before calling PrepareProposal + _, err = s.network.FinalizeBlock() + s.Require().NoError(err) + + // Call PrepareProposal to selcet transactions from mempool and make proposal + prepareProposalRes, err := s.network.App.PrepareProposal(&abci.RequestPrepareProposal{ + MaxTxBytes: 1_000_000, + Height: 1, + }) + s.Require().NoError(err) if tc.bypass { return } + // Check whether expected transactions are included and returned as pending state in mempool + mpool := s.network.App.GetMempool() + iterator := mpool.Select(s.network.GetContext(), nil) for _, txHash := range expTxHashes { actualTxHash := s.getTxHash(iterator.Tx()) s.Require().Equal(txHash, actualTxHash) iterator = iterator.Next() } + + // Check whether expected transactions are selcted by PrepareProposal + txHashes := make([]string, 0) + for _, txBytes := range prepareProposalRes.Txs { + txHash := hex.EncodeToString(tmhash.Sum(txBytes)) + txHashes = append(txHashes, txHash) + } + s.Require().Equal(txHashes, expTxHashes) }) } } -// TestNonceGappedEVMTransactions tests the behavior of nonce-gapped EVM transactions +// TestNonceGappedEVMTransactionsWithABCIMethodCalls tests the behavior of nonce-gapped EVM transactions // and the transition from queued to pending when gaps are filled -func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { - fmt.Printf("DEBUG: Starting TestNonceGappedEVMTransactions\n") +func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls() { + fmt.Printf("DEBUG: Starting TestNonceGappedEVMTransactionsWithABCIMethodCalls\n") testCases := []struct { name string @@ -386,9 +410,21 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { txs, expTxHashes := tc.setupTxs() + // Call CheckTx for transactions err := s.checkTxs(txs) s.Require().NoError(err) + // Call FinalizeBlock to make finalizeState before calling PrepareProposal + _, err = s.network.FinalizeBlock() + s.Require().NoError(err) + + // Call PrepareProposal to selcet transactions from mempool and make proposal + prepareProposalRes, err := s.network.App.PrepareProposal(&abci.RequestPrepareProposal{ + MaxTxBytes: 1_000_000, + Height: 1, + }) + s.Require().NoError(err) + mpool := s.network.App.GetMempool() iterator := mpool.Select(s.network.GetContext(), nil) @@ -396,14 +432,22 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithCheckTx() { return } + // Check whether expected transactions are included and returned as pending state in mempool for _, txHash := range expTxHashes { actualTxHash := s.getTxHash(iterator.Tx()) s.Require().Equal(txHash, actualTxHash) iterator = iterator.Next() } - tc.verifyFunc(mpool) + + // Check whether expected transactions are selcted by PrepareProposal + txHashes := make([]string, 0) + for _, txBytes := range prepareProposalRes.Txs { + txHash := hex.EncodeToString(tmhash.Sum(txBytes)) + txHashes = append(txHashes, txHash) + } + s.Require().Equal(txHashes, expTxHashes) }) } } diff --git a/testutil/integration/evm/network/abci.go b/testutil/integration/evm/network/abci.go index fc5bf9c54..a1bdb7fa3 100644 --- a/testutil/integration/evm/network/abci.go +++ b/testutil/integration/evm/network/abci.go @@ -30,6 +30,27 @@ func (n *IntegrationNetwork) NextBlockWithTxs(txBytes ...[]byte) (*abcitypes.Res return n.finalizeBlockAndCommit(time.Second, txBytes...) } +// FinalizeBlock is a helper function that runs FinalizeBlock logic +// without Commit and initializing context. +func (n *IntegrationNetwork) FinalizeBlock() (*abcitypes.ResponseFinalizeBlock, error) { + header := n.ctx.BlockHeader() + // Update block header and BeginBlock + header.Height++ + header.AppHash = n.app.LastCommitID().Hash + // Calculate new block time after duration + newBlockTime := header.Time.Add(time.Second) + header.Time = newBlockTime + + // FinalizeBlock to run endBlock, deliverTx & beginBlock logic + req := buildFinalizeBlockReq(header, n.valSet.Validators) + + res, err := n.app.FinalizeBlock(req) + if err != nil { + return nil, err + } + return res, nil +} + // finalizeBlockAndCommit is a private helper function that runs the FinalizeBlock logic // with the provided txBytes, updates the context and // commits the changes to have a block time after the given duration. From 603c8908a89077d7e85e2a2ea891815d1cfcf443 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 26 Aug 2025 14:42:09 +0900 Subject: [PATCH 08/20] chore: fix lint --- tests/integration/mempool/test_helper.go | 8 +++++-- .../mempool/test_mempool_integration.go | 16 ++++++------- .../mempool/test_mempool_integration_abci.go | 24 +++++++++---------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 17e47e646..ae92a6678 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -46,11 +46,15 @@ func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big } // createEVMTransaction creates an EVM transaction using the provided key -func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce uint64, gasPrice *big.Int) sdk.Tx { +func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce int, gasPrice *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: nonce, + Nonce: uint64(nonce), To: &to, Amount: big.NewInt(1000), GasLimit: TxGas, diff --git a/tests/integration/mempool/test_mempool_integration.go b/tests/integration/mempool/test_mempool_integration.go index 5f2a27240..80f839823 100644 --- a/tests/integration/mempool/test_mempool_integration.go +++ b/tests/integration/mempool/test_mempool_integration.go @@ -1109,7 +1109,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) for i := 0; i <= 6; i += 2 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1132,7 +1132,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // First, insert transactions with gaps: nonces 0, 2, 4 for i := 0; i <= 4; i += 2 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1160,7 +1160,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1168,7 +1168,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1194,14 +1194,14 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Account 1: nonces 0, 2 (gap at 1) for i := 0; i <= 2; i += 2 { - tx := s.createEVMValueTransferTx(key1, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key1, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } // Account 2: nonces 0, 3 (gaps at 1, 2) for i := 0; i <= 3; i += 3 { - tx := s.createEVMValueTransferTx(key2, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key2, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1255,7 +1255,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Insert transactions with gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } @@ -1263,7 +1263,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactions() { // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) nonces = append(nonces, i) } diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index b1774e878..f639f22bd 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -69,10 +69,10 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithABCIMethodCalls() { setupTxs: func() ([]sdk.Tx, []string) { key := s.keyring.GetKey(0) // Create first EVM transaction with low fee - lowFeeEVMTx := s.createEVMValueTransferTx(key, uint64(1), big.NewInt(2000000000)) // 2 gaatom + lowFeeEVMTx := s.createEVMValueTransferTx(key, 1, big.NewInt(2000000000)) // 2 gaatom // Create second EVM transaction with high fee - highFeeEVMTx := s.createEVMValueTransferTx(key, uint64(0), big.NewInt(5000000000)) // 5 gaatom + highFeeEVMTx := s.createEVMValueTransferTx(key, 0, big.NewInt(5000000000)) // 5 gaatom // Input txs in order inputTxs := []sdk.Tx{lowFeeEVMTx, highFeeEVMTx} @@ -256,7 +256,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls // Insert transactions with gaps: nonces 0, 2, 4, 6 (missing 1, 3, 5) for i := 0; i <= 6; i += 2 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(2000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(2000000000)) txs = append(txs, tx) } @@ -281,12 +281,12 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls // First, insert transactions with gaps: nonces 0, 2, 4 for i := 0; i <= 4; i += 2 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) } // Then fill the gap by inserting nonce 1 - tx := s.createEVMValueTransferTx(key, uint64(1), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, 1, big.NewInt(1000000000)) txs = append(txs, tx) // Expected txs in order @@ -310,7 +310,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls // Insert transactions with multiple gaps: nonces 0, 3, 6, 9 for i := 0; i <= 9; i += 3 { - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) } @@ -318,7 +318,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls // Fill gaps by inserting nonces 1, 2, 4, 5, 7, 8 for i := 1; i <= 8; i++ { if i%3 != 0 { // Skip nonces that are already inserted - tx := s.createEVMValueTransferTx(key, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key, i, big.NewInt(1000000000)) txs = append(txs, tx) } @@ -347,13 +347,13 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls // Account 1: nonces 0, 2 (gap at 1) for i := 0; i <= 2; i += 2 { - tx := s.createEVMValueTransferTx(key1, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key1, i, big.NewInt(1000000000)) txs = append(txs, tx) } // Account 2: nonces 0, 3 (gaps at 1, 2) for i := 0; i <= 3; i += 3 { - tx := s.createEVMValueTransferTx(key2, uint64(i), big.NewInt(1000000000)) + tx := s.createEVMValueTransferTx(key2, i, big.NewInt(1000000000)) txs = append(txs, tx) } @@ -378,15 +378,15 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls var txs []sdk.Tx // Insert transaction with nonce 0 and low gas price - tx1 := s.createEVMValueTransferTx(key, uint64(0), big.NewInt(1000000000)) + tx1 := s.createEVMValueTransferTx(key, 0, big.NewInt(1000000000)) txs = append(txs, tx1) // Insert transaction with nonce 1 - tx2 := s.createEVMValueTransferTx(key, uint64(1), big.NewInt(1000000000)) + tx2 := s.createEVMValueTransferTx(key, 1, big.NewInt(1000000000)) txs = append(txs, tx2) // Replace nonce 0 transaction with higher gas price - tx3 := s.createEVMValueTransferTx(key, uint64(0), big.NewInt(2000000000)) + tx3 := s.createEVMValueTransferTx(key, 0, big.NewInt(2000000000)) txs = append(txs, tx3) // Expected txs in order From bc522ebf47bf7c418eac0f46185bc8460a4a0ded Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 26 Aug 2025 14:45:05 +0900 Subject: [PATCH 09/20] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89951c5f2..b2e68a59e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [\#467](https://github.com/cosmos/evm/pull/467) Replace GlobalEVMMempool by passing to JSONRPC on initiate. - [\#352](https://github.com/cosmos/evm/pull/352) Remove the creation of a Geth EVM instance, stateDB during the AnteHandler balance check. - [\#496](https://github.com/cosmos/evm/pull/496) Simplify mempool instantiation by using configs instead of objects. +- [\#512](https://github.com/cosmos/evm/pull/512) Add integration test for apside mempool ### FEATURES From dbe047bb742b8a88a05b8ed834efdbaafbb0be45 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 26 Aug 2025 14:53:37 +0900 Subject: [PATCH 10/20] test(mempool): modify testcode --- tests/integration/mempool/test_mempool_integration_abci.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/mempool/test_mempool_integration_abci.go b/tests/integration/mempool/test_mempool_integration_abci.go index f639f22bd..5e76c5689 100644 --- a/tests/integration/mempool/test_mempool_integration_abci.go +++ b/tests/integration/mempool/test_mempool_integration_abci.go @@ -232,7 +232,7 @@ func (s *IntegrationTestSuite) TestTransactionOrderingWithABCIMethodCalls() { txHash := hex.EncodeToString(tmhash.Sum(txBytes)) txHashes = append(txHashes, txHash) } - s.Require().Equal(txHashes, expTxHashes) + s.Require().Equal(expTxHashes, txHashes) }) } } @@ -447,7 +447,7 @@ func (s *IntegrationTestSuite) TestNonceGappedEVMTransactionsWithABCIMethodCalls txHash := hex.EncodeToString(tmhash.Sum(txBytes)) txHashes = append(txHashes, txHash) } - s.Require().Equal(txHashes, expTxHashes) + s.Require().Equal(expTxHashes, txHashes) }) } } From 29fd29fc263e8740bb9b3337c18a9f161ae07eae Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 26 Aug 2025 15:10:49 +0900 Subject: [PATCH 11/20] chore: fix lint --- tests/integration/mempool/test_helper.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index ae92a6678..3934bf0fa 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -5,11 +5,11 @@ import ( "fmt" "math/big" + sdkmath "cosmossdk.io/math" + abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/crypto/tmhash" - sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/evm/testutil/integration/base/factory" From 142fbcad922e19be0b25530db2f6b5b9bf880808 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 26 Aug 2025 15:21:33 +0900 Subject: [PATCH 12/20] chore: fix lint --- tests/integration/mempool/test_helper.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 3934bf0fa..3c74c50b1 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -5,16 +5,17 @@ import ( "fmt" "math/big" - sdkmath "cosmossdk.io/math" - abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/crypto/tmhash" - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/evm/testutil/integration/base/factory" "github.com/cosmos/evm/testutil/keyring" evmtypes "github.com/cosmos/evm/x/vm/types" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) // Constants From 5d2942414b17bf94dc01f2c20e6ae03f069ce90d Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Wed, 27 Aug 2025 21:28:50 +0900 Subject: [PATCH 13/20] fix(ante): remove bond denom from allowed fee denom --- ante/cosmos/min_gas_price.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ante/cosmos/min_gas_price.go b/ante/cosmos/min_gas_price.go index 8303fdfcf..193b0f016 100644 --- a/ante/cosmos/min_gas_price.go +++ b/ante/cosmos/min_gas_price.go @@ -42,11 +42,8 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate feeCoins := feeTx.GetFee() evmDenom := evmtypes.GetEVMCoinDenom() - // only allow user to pass in aatom and stake native token as transaction fees - // allow use stake native tokens for fees is just for unit tests to pass - // - // TODO: is the handling of stake necessary here? Why not adjust the tests to contain the correct denom? - validFees := len(feeCoins) == 0 || (len(feeCoins) == 1 && slices.Contains([]string{evmDenom, sdk.DefaultBondDenom}, feeCoins.GetDenomByIndex(0))) + // only allow user to pass in evm coin as transaction fee + validFees := len(feeCoins) == 0 || (len(feeCoins) == 1 && slices.Contains([]string{evmDenom}, feeCoins.GetDenomByIndex(0))) if !validFees && !simulate { return ctx, fmt.Errorf("expected only native token %s for fee, but got %s", evmDenom, feeCoins.String()) } From 66811c58c794ea08c28cbaa9ae09da5972a2eac4 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Wed, 27 Aug 2025 21:29:24 +0900 Subject: [PATCH 14/20] fix(mempool): calculation of priority of cosmos tx --- mempool/iterator.go | 41 ++++++++++++++++++++++++++++++++++++++--- mempool/mempool.go | 14 ++++---------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/mempool/iterator.go b/mempool/iterator.go index ec1875951..50c8bb979 100644 --- a/mempool/iterator.go +++ b/mempool/iterator.go @@ -1,6 +1,7 @@ package mempool import ( + "math" "math/big" ethtypes "github.com/ethereum/go-ethereum/core/types" @@ -8,14 +9,16 @@ import ( "github.com/cosmos/evm/mempool/miner" "github.com/cosmos/evm/mempool/txpool" + cosmosevmtypes "github.com/cosmos/evm/types" msgtypes "github.com/cosmos/evm/x/vm/types" "cosmossdk.io/log" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/mempool" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" ) var _ mempool.Iterator = &EVMMempoolIterator{} @@ -255,7 +258,29 @@ func (i *EVMMempoolIterator) extractCosmosEffectiveTip(tx sdk.Tx) *uint256.Int { return nil // Transaction doesn't implement FeeTx interface } - var bondDenomFeeAmount math.Int + // default to `MaxInt64` when there's no extension option. + maxPriorityPrice := sdkmath.LegacyNewDec(math.MaxInt64) + + if hasExtOptsTx, ok := feeTx.(authante.HasExtensionOptionsTx); ok { + for _, opt := range hasExtOptsTx.GetExtensionOptions() { + if extOpt, ok := opt.GetCachedValue().(*cosmosevmtypes.ExtensionOptionDynamicFeeTx); ok { + maxPriorityPrice = extOpt.MaxPriorityPrice + if maxPriorityPrice.IsNil() { + maxPriorityPrice = sdkmath.LegacyZeroDec() + } + break + } + } + } + + // Convert gasTipCap (LegacyDec) to *uint256.Int for comparison + gasTipCap, overflow := uint256.FromBig(maxPriorityPrice.BigInt()) + if overflow { + i.logger.Debug("overflowed on gas tip cap calculation") + return nil + } + + var bondDenomFeeAmount sdkmath.Int fees := feeTx.GetFee() for _, coin := range fees { if coin.Denom == i.bondDenom { @@ -264,8 +289,14 @@ func (i *EVMMempoolIterator) extractCosmosEffectiveTip(tx sdk.Tx) *uint256.Int { } } + gas := sdkmath.NewIntFromUint64(feeTx.GetGas()) + if gas.IsZero() { + i.logger.Debug("Cosmos transaction has zero gas") + return nil + } + // Calculate gas price: fee_amount / gas_limit - gasPrice, overflow := uint256.FromBig(bondDenomFeeAmount.Quo(math.NewIntFromUint64(feeTx.GetGas())).BigInt()) + gasPrice, overflow := uint256.FromBig(bondDenomFeeAmount.Quo(gas).BigInt()) if overflow { i.logger.Debug("overflowed on gas price calculation") return nil @@ -287,6 +318,10 @@ func (i *EVMMempoolIterator) extractCosmosEffectiveTip(tx sdk.Tx) *uint256.Int { } effectiveTip := new(uint256.Int).Sub(gasPrice, baseFee) + if effectiveTip.Cmp(gasTipCap) > 0 { + effectiveTip = gasTipCap + } + i.logger.Debug("calculated effective tip", "gas_price", gasPrice.String(), "base_fee", baseFee.String(), "effective_tip", effectiveTip.String()) return effectiveTip } diff --git a/mempool/mempool.go b/mempool/mempool.go index 409292fba..e2d121487 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -146,18 +146,12 @@ func NewExperimentalEVMMempool(getCtxCallback func(height int64, prove bool) (sd defaultConfig := sdkmempool.PriorityNonceMempoolConfig[math.Int]{} defaultConfig.TxPriority = sdkmempool.TxPriority[math.Int]{ GetTxPriority: func(goCtx context.Context, tx sdk.Tx) math.Int { - cosmosTxFee, ok := tx.(sdk.FeeTx) - if !ok { + ctx := sdk.UnwrapSDKContext(goCtx) + priority := ctx.Priority() + if priority < 0 { return math.ZeroInt() } - found, coin := cosmosTxFee.GetFee().Find(bondDenom) - if !found { - return math.ZeroInt() - } - - gasPrice := coin.Amount.Quo(math.NewIntFromUint64(cosmosTxFee.GetGas())) - - return gasPrice + return math.NewIntFromUint64(uint64(priority)) }, Compare: func(a, b math.Int) int { return a.BigInt().Cmp(b.BigInt()) From 9449d73ae4afdd9215bb2dba89a5fbafccc6ce13 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Wed, 27 Aug 2025 21:29:44 +0900 Subject: [PATCH 15/20] test(mempool): fix mempool integration test --- .../mempool/test_mempool_integration.go | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tests/integration/mempool/test_mempool_integration.go b/tests/integration/mempool/test_mempool_integration.go index 80f839823..8e9011c3c 100644 --- a/tests/integration/mempool/test_mempool_integration.go +++ b/tests/integration/mempool/test_mempool_integration.go @@ -280,7 +280,12 @@ func (s *IntegrationTestSuite) TestMempoolSelect() { setupTxs: func() { cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) mpool := s.network.App.GetMempool() - err := mpool.Insert(s.network.GetContext(), cosmosTx) + + // NOTE: In normal tx flow, ctx.priority is set by anteHandler. + // However, in this test code, tx is directly inserted into mempool bypassing anteHandler. + // If ctx.priority were not set by anteHandler, ordering of cosmos txs can be non-deterministic. + // So we should set ctx.pritority only for testing. + err := mpool.Insert(s.network.GetContext().WithPriority(2000), cosmosTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -349,7 +354,7 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { setupTxs: func() { cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) mpool := s.network.App.GetMempool() - err := mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext().WithPriority(2000), cosmosTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -384,11 +389,11 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { mpool := s.network.App.GetMempool() cosmosTx1 := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) - err := mpool.Insert(s.network.GetContext(), cosmosTx1) + err := mpool.Insert(s.network.GetContext().WithPriority(1000), cosmosTx1) s.Require().NoError(err) cosmosTx2 := s.createCosmosSendTx(s.keyring.GetKey(1), big.NewInt(2000)) - err = mpool.Insert(s.network.GetContext(), cosmosTx2) + err = mpool.Insert(s.network.GetContext().WithPriority(2000), cosmosTx2) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -415,7 +420,7 @@ func (s *IntegrationTestSuite) TestMempoolIterator() { // Add Cosmos transaction cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) - err = mpool.Insert(s.network.GetContext(), cosmosTx) + err = mpool.Insert(s.network.GetContext().WithPriority(2000), cosmosTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -466,13 +471,13 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { mpool := s.network.App.GetMempool() // Insert in non-priority order - err := mpool.Insert(s.network.GetContext(), lowFeeCosmosTx) + err := mpool.Insert(s.network.GetContext().WithPriority(1000000000), lowFeeCosmosTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), highGasPriceEVMTx) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), mediumFeeCosmosTx) + err = mpool.Insert(s.network.GetContext().WithPriority(3000000000), mediumFeeCosmosTx) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), highFeeCosmosTx) + err = mpool.Insert(s.network.GetContext().WithPriority(5000000000), highFeeCosmosTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -574,11 +579,11 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { mpool := s.network.App.GetMempool() // Insert in random order - err := mpool.Insert(s.network.GetContext(), mediumFeeTx) + err := mpool.Insert(s.network.GetContext().WithPriority(1000000000), mediumFeeTx) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), lowFeeTx) + err = mpool.Insert(s.network.GetContext().WithPriority(3000000000), lowFeeTx) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), highFeeTx) + err = mpool.Insert(s.network.GetContext().WithPriority(5000000000), highFeeTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -607,7 +612,7 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { mpool := s.network.App.GetMempool() // Insert Cosmos first, then EVM - err := mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext().WithPriority(1000000000), cosmosTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) @@ -649,7 +654,7 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { mpool := s.network.App.GetMempool() // Insert Cosmos first, then EVM - err := mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext().WithPriority(2000000000), cosmosTx) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) @@ -690,7 +695,7 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { // Insert EVM first, then Cosmos err := mpool.Insert(s.network.GetContext(), evmTx) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), cosmosTx) + err = mpool.Insert(s.network.GetContext().WithPriority(5000000000), cosmosTx) s.Require().NoError(err) }, verifyFunc: func(iterator mempool.Iterator) { @@ -733,15 +738,15 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { mpool := s.network.App.GetMempool() // Insert in random order - err := mpool.Insert(s.network.GetContext(), cosmosLow) + err := mpool.Insert(s.network.GetContext().WithPriority(1000000000), cosmosLow) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmMedium) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), cosmosHigh) + err = mpool.Insert(s.network.GetContext().WithPriority(6000000000), cosmosHigh) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmLow) s.Require().NoError(err) - err = mpool.Insert(s.network.GetContext(), cosmosMedium) + err = mpool.Insert(s.network.GetContext().WithPriority(3000000000), cosmosMedium) s.Require().NoError(err) err = mpool.Insert(s.network.GetContext(), evmHigh) s.Require().NoError(err) @@ -855,7 +860,7 @@ func (s *IntegrationTestSuite) TestSelectBy() { setupTxs: func() { cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(2000)) mpool := s.network.App.GetMempool() - err := mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext().WithPriority(2000), cosmosTx) s.Require().NoError(err) }, filterFunc: func(tx sdk.Tx) bool { @@ -884,7 +889,7 @@ func (s *IntegrationTestSuite) TestSelectBy() { // Add transactions with different fees for i := 1; i < 6; i++ { // Use different keys for different transactions cosmosTx := s.createCosmosSendTx(s.keyring.GetKey(i), big.NewInt(int64(i*1000))) // 5000, 4000, 3000, 2000, 1000 - err := mpool.Insert(s.network.GetContext(), cosmosTx) + err := mpool.Insert(s.network.GetContext().WithPriority(int64(i*1000)), cosmosTx) s.Require().NoError(err) } }, @@ -988,7 +993,7 @@ func (s *IntegrationTestSuite) TestMempoolHeightRequirement() { tx := s.createCosmosSendTx(s.keyring.GetKey(0), big.NewInt(1000)) // Should fail because mempool requires block height >= 2 - err = mpool.Insert(nw.GetContext(), tx) + err = mpool.Insert(nw.GetContext().WithPriority(1000), tx) // The mempool might not enforce height requirements in this context // Just check that the operation completes (either success or error) s.Require().True(err == nil || err != nil) From 8c251603197d356862e828ab946e2caebac9b2c5 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Fri, 29 Aug 2025 19:01:21 +0900 Subject: [PATCH 16/20] add unit test for cosmos tx feeCoins validation --- ante/cosmos/min_gas_price.go | 6 +- ante/cosmos/min_gas_price_test.go | 94 +++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 ante/cosmos/min_gas_price_test.go diff --git a/ante/cosmos/min_gas_price.go b/ante/cosmos/min_gas_price.go index 193b0f016..44b81ff82 100644 --- a/ante/cosmos/min_gas_price.go +++ b/ante/cosmos/min_gas_price.go @@ -43,7 +43,7 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate evmDenom := evmtypes.GetEVMCoinDenom() // only allow user to pass in evm coin as transaction fee - validFees := len(feeCoins) == 0 || (len(feeCoins) == 1 && slices.Contains([]string{evmDenom}, feeCoins.GetDenomByIndex(0))) + validFees := IsValidFeeCoins(feeCoins, []string{evmDenom}) if !validFees && !simulate { return ctx, fmt.Errorf("expected only native token %s for fee, but got %s", evmDenom, feeCoins.String()) } @@ -91,3 +91,7 @@ func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate return next(ctx, tx, simulate) } + +func IsValidFeeCoins(feeCoins sdk.Coins, allowedDenoms []string) bool { + return len(feeCoins) == 0 || (len(feeCoins) == 1 && slices.Contains(allowedDenoms, feeCoins.GetDenomByIndex(0))) +} diff --git a/ante/cosmos/min_gas_price_test.go b/ante/cosmos/min_gas_price_test.go new file mode 100644 index 000000000..d7ec18894 --- /dev/null +++ b/ante/cosmos/min_gas_price_test.go @@ -0,0 +1,94 @@ +package cosmos_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/evm/ante/cosmos" + "github.com/stretchr/testify/require" +) + +func TestIsValidFeeCoins(t *testing.T) { + tests := []struct { + name string + feeCoins sdk.Coins + allowedDenoms []string + expectedResult bool + }{ + { + name: "empty fee coins should be valid", + feeCoins: sdk.Coins{}, + allowedDenoms: []string{"uatom"}, + expectedResult: true, + }, + { + name: "nil fee coins should be valid", + feeCoins: nil, + allowedDenoms: []string{"uatom"}, + expectedResult: true, + }, + { + name: "single allowed denom should be valid", + feeCoins: sdk.NewCoins(sdk.NewCoin("uatom", sdkmath.NewInt(1000))), + allowedDenoms: []string{"uatom"}, + expectedResult: true, + }, + { + name: "single allowed denom from multiple allowed should be valid", + feeCoins: sdk.NewCoins(sdk.NewCoin("uatom", sdkmath.NewInt(1000))), + allowedDenoms: []string{"uatom", "stake", "wei"}, + expectedResult: true, + }, + { + name: "single disallowed denom should be invalid", + feeCoins: sdk.NewCoins(sdk.NewCoin("forbidden", sdkmath.NewInt(1000))), + allowedDenoms: []string{"uatom"}, + expectedResult: false, + }, + { + name: "single disallowed denom with empty allowed list should be invalid", + feeCoins: sdk.NewCoins(sdk.NewCoin("uatom", sdkmath.NewInt(1000))), + allowedDenoms: []string{}, + expectedResult: false, + }, + { + name: "multiple fee coins should be invalid", + feeCoins: sdk.NewCoins(sdk.NewCoin("uatom", sdkmath.NewInt(1000)), sdk.NewCoin("stake", sdkmath.NewInt(500))), + allowedDenoms: []string{"uatom", "stake"}, + expectedResult: false, + }, + { + name: "multiple fee coins with one allowed should be invalid", + feeCoins: sdk.NewCoins(sdk.NewCoin("uatom", sdkmath.NewInt(1000)), sdk.NewCoin("forbidden", sdkmath.NewInt(500))), + allowedDenoms: []string{"uatom"}, + expectedResult: false, + }, + { + name: "empty allowed denoms with empty fee coins should be valid", + feeCoins: sdk.Coins{}, + allowedDenoms: []string{}, + expectedResult: true, + }, + { + name: "case sensitive denom matching", + feeCoins: sdk.NewCoins(sdk.NewCoin("UATOM", sdkmath.NewInt(1000))), + allowedDenoms: []string{"uatom"}, + expectedResult: false, + }, + { + name: "zero amount allowed denom should be valid", + feeCoins: sdk.NewCoins(sdk.NewCoin("uatom", sdkmath.NewInt(0))), + allowedDenoms: []string{"uatom"}, + expectedResult: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := cosmos.IsValidFeeCoins(tt.feeCoins, tt.allowedDenoms) + require.Equal(t, tt.expectedResult, result, "IsValidFeeCoins returned unexpected result") + }) + } +} From 155b2b9618c7fb9794e812cdef9ce16cd261addf Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Mon, 1 Sep 2025 12:17:45 +0900 Subject: [PATCH 17/20] chore: fix lint --- ante/cosmos/min_gas_price_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ante/cosmos/min_gas_price_test.go b/ante/cosmos/min_gas_price_test.go index d7ec18894..51b6f473c 100644 --- a/ante/cosmos/min_gas_price_test.go +++ b/ante/cosmos/min_gas_price_test.go @@ -3,11 +3,13 @@ package cosmos_test import ( "testing" + "github.com/stretchr/testify/require" + + "github.com/cosmos/evm/ante/cosmos" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/evm/ante/cosmos" - "github.com/stretchr/testify/require" ) func TestIsValidFeeCoins(t *testing.T) { From db33377a96ea97af3e75457166a29f9269cbf8f3 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Mon, 1 Sep 2025 12:18:09 +0900 Subject: [PATCH 18/20] chore: modify test helper function and comments --- tests/integration/mempool/test_helper.go | 20 +++++++++++++------ .../mempool/test_mempool_integration.go | 6 +++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go index 3c74c50b1..d32f15e87 100644 --- a/tests/integration/mempool/test_helper.go +++ b/tests/integration/mempool/test_helper.go @@ -3,6 +3,7 @@ package mempool import ( "encoding/hex" "fmt" + "math" "math/big" abci "github.com/cometbft/cometbft/abci/types" @@ -23,7 +24,9 @@ const ( TxGas = 100_000 ) -// createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key +var MaxGasTipCap = big.NewInt(math.MaxInt64) + +// createCosmosSendTx creates a simple bank send transaction with the provided key and gasPrice func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big.Int) sdk.Tx { feeDenom := "aatom" @@ -46,7 +49,7 @@ func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big return tx } -// createEVMTransaction creates an EVM transaction using the provided key +// createEVMValueTransferTx creates an EVM value transfer transaction using the provided key and gasPrice func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce int, gasPrice *big.Int) sdk.Tx { to := s.keyring.GetKey(1).Addr @@ -68,7 +71,7 @@ func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce i return tx } -// createEVMContractDeployTx creates an EVM transaction for contract deployment +// createEVMContractDeployTx creates an EVM contract deploy transaction using the provided key and gasPrice func (s *IntegrationTestSuite) createEVMContractDeployTx(key keyring.Key, gasPrice *big.Int, data []byte) sdk.Tx { ethTxArgs := evmtypes.EvmTxArgs{ Nonce: 0, @@ -94,7 +97,7 @@ func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) error { return nil } -// checkTxs call abci CheckTx for a transaction +// checkTx calls abci CheckTx for a transaction func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error { txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) if err != nil { @@ -139,7 +142,7 @@ func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit // calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction // This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee -func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, baseFee *big.Int) *big.Int { +func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, gasTipCap, baseFee *big.Int) *big.Int { gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) if baseFee == nil || baseFee.Sign() == 0 { return gasPrice // No base fee, effective tip equals gas price @@ -149,5 +152,10 @@ func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasL return big.NewInt(0) // Gas price lower than base fee, effective tip is zero } - return new(big.Int).Sub(gasPrice, baseFee) + effectiveTip := new(big.Int).Sub(gasPrice, baseFee) + if effectiveTip.Cmp(gasTipCap) > 0 { + effectiveTip = gasTipCap + } + + return effectiveTip } diff --git a/tests/integration/mempool/test_mempool_integration.go b/tests/integration/mempool/test_mempool_integration.go index 8e9011c3c..361b93c38 100644 --- a/tests/integration/mempool/test_mempool_integration.go +++ b/tests/integration/mempool/test_mempool_integration.go @@ -638,7 +638,7 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { s.Require().NotNil(tx2) feeTx := tx2.(sdk.FeeTx) - effectiveTip = s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 + effectiveTip = s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), MaxGasTipCap, big.NewInt(0)) // base fee = 0 s.Require().Equal(big.NewInt(1000000000), effectiveTip, "Second transaction should be Cosmos with 1000 aatom effective tip") }, }, @@ -677,7 +677,7 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { s.Require().NotNil(tx2) feeTx := tx2.(sdk.FeeTx) - effectiveTip2 := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 + effectiveTip2 := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), MaxGasTipCap, big.NewInt(0)) // base fee = 0 s.Require().Equal(big.NewInt(2000000000), effectiveTip2, "Second transaction should be Cosmos with 2000 aatom effective tip") }, }, @@ -704,7 +704,7 @@ func (s *IntegrationTestSuite) TestTransactionOrdering() { s.Require().NotNil(tx1) feeTx := tx1.(sdk.FeeTx) - effectiveTip := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), big.NewInt(0)) // base fee = 0 + effectiveTip := s.calculateCosmosEffectiveTip(feeTx.GetFee().AmountOf("aatom").Int64(), feeTx.GetGas(), MaxGasTipCap, big.NewInt(0)) // base fee = 0 s.Require().Equal(big.NewInt(5000000000), effectiveTip, "First transaction should be Cosmos with 5000 aatom effective tip") // Second transaction should be EVM From ca3cdb2569f34d505c29107b028a10beaf3b29ef Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 9 Sep 2025 20:03:22 +0900 Subject: [PATCH 19/20] chore: resolve merge conflict --- tests/integration/mempool/test_helper.go | 161 ---------------------- tests/integration/mempool/test_helpers.go | 21 ++- 2 files changed, 15 insertions(+), 167 deletions(-) delete mode 100644 tests/integration/mempool/test_helper.go diff --git a/tests/integration/mempool/test_helper.go b/tests/integration/mempool/test_helper.go deleted file mode 100644 index d32f15e87..000000000 --- a/tests/integration/mempool/test_helper.go +++ /dev/null @@ -1,161 +0,0 @@ -package mempool - -import ( - "encoding/hex" - "fmt" - "math" - "math/big" - - abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/crypto/tmhash" - - "github.com/cosmos/evm/testutil/integration/base/factory" - "github.com/cosmos/evm/testutil/keyring" - evmtypes "github.com/cosmos/evm/x/vm/types" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -// Constants -const ( - TxGas = 100_000 -) - -var MaxGasTipCap = big.NewInt(math.MaxInt64) - -// createCosmosSendTx creates a simple bank send transaction with the provided key and gasPrice -func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big.Int) sdk.Tx { - feeDenom := "aatom" - - fromAddr := key.AccAddr - toAddr := s.keyring.GetKey(1).AccAddr - amount := sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 1000)) - - bankMsg := banktypes.NewMsgSend(fromAddr, toAddr, amount) - - gasPriceConverted := sdkmath.NewIntFromBigInt(gasPrice) - - txArgs := factory.CosmosTxArgs{ - Msgs: []sdk.Msg{bankMsg}, - GasPrice: &gasPriceConverted, - } - tx, err := s.factory.BuildCosmosTx(key.Priv, txArgs) - s.Require().NoError(err) - - fmt.Printf("DEBUG: Created cosmos transaction successfully\n") - return tx -} - -// createEVMValueTransferTx creates an EVM value transfer transaction using the provided key and gasPrice -func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce int, gasPrice *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, - GasPrice: gasPrice, - Input: nil, - } - tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) - s.Require().NoError(err) - - return tx -} - -// createEVMContractDeployTx creates an EVM contract deploy transaction using the provided key and gasPrice -func (s *IntegrationTestSuite) createEVMContractDeployTx(key keyring.Key, gasPrice *big.Int, data []byte) sdk.Tx { - ethTxArgs := evmtypes.EvmTxArgs{ - Nonce: 0, - To: nil, - Amount: nil, - GasLimit: TxGas, - GasPrice: gasPrice, - Input: data, - } - tx, err := s.factory.GenerateSignedEthTx(key.Priv, ethTxArgs) - s.Require().NoError(err) - - return tx -} - -// 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 { - return err - } - } - return nil -} - -// checkTx calls abci CheckTx for a transaction -func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error { - txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) - if err != nil { - return fmt.Errorf("failed to encode cosmos tx: %w", err) - } - - _, 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 -} - -// getTxHashes returns transaction hashes for multiple transactions -func (s *IntegrationTestSuite) getTxHashes(txs []sdk.Tx) []string { - txHashes := []string{} - for _, tx := range txs { - txHash := s.getTxHash(tx) - txHashes = append(txHashes, txHash) - } - - return txHashes -} - -// getTxHash returns transaction hash for a transaction -func (s *IntegrationTestSuite) getTxHash(tx sdk.Tx) string { - txEncoder := s.network.App.GetTxConfig().TxEncoder() - txBytes, err := txEncoder(tx) - s.Require().NoError(err) - - return hex.EncodeToString(tmhash.Sum(txBytes)) -} - -// calculateCosmosGasPrice calculates the gas price for a Cosmos transaction -func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit uint64) *big.Int { - return new(big.Int).Div(big.NewInt(feeAmount), big.NewInt(int64(gasLimit))) //#nosec G115 -- not concern, test -} - -// calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction -// This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee -func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, gasTipCap, baseFee *big.Int) *big.Int { - gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) - if baseFee == nil || baseFee.Sign() == 0 { - return gasPrice // No base fee, effective tip equals gas price - } - - if gasPrice.Cmp(baseFee) < 0 { - return big.NewInt(0) // Gas price lower than base fee, effective tip is zero - } - - effectiveTip := new(big.Int).Sub(gasPrice, baseFee) - if effectiveTip.Cmp(gasTipCap) > 0 { - effectiveTip = gasTipCap - } - - return effectiveTip -} diff --git a/tests/integration/mempool/test_helpers.go b/tests/integration/mempool/test_helpers.go index 69246265a..d32f15e87 100644 --- a/tests/integration/mempool/test_helpers.go +++ b/tests/integration/mempool/test_helpers.go @@ -3,6 +3,7 @@ package mempool import ( "encoding/hex" "fmt" + "math" "math/big" abci "github.com/cometbft/cometbft/abci/types" @@ -23,7 +24,9 @@ const ( TxGas = 100_000 ) -// createCosmosSendTransactionWithKey creates a simple bank send transaction with the specified key +var MaxGasTipCap = big.NewInt(math.MaxInt64) + +// createCosmosSendTx creates a simple bank send transaction with the provided key and gasPrice func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big.Int) sdk.Tx { feeDenom := "aatom" @@ -42,10 +45,11 @@ func (s *IntegrationTestSuite) createCosmosSendTx(key keyring.Key, gasPrice *big tx, err := s.factory.BuildCosmosTx(key.Priv, txArgs) s.Require().NoError(err) + fmt.Printf("DEBUG: Created cosmos transaction successfully\n") return tx } -// createEVMTransaction creates an EVM transaction using the provided key +// createEVMValueTransferTx creates an EVM value transfer transaction using the provided key and gasPrice func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce int, gasPrice *big.Int) sdk.Tx { to := s.keyring.GetKey(1).Addr @@ -67,7 +71,7 @@ func (s *IntegrationTestSuite) createEVMValueTransferTx(key keyring.Key, nonce i return tx } -// createEVMContractDeployTx creates an EVM transaction for contract deployment +// createEVMContractDeployTx creates an EVM contract deploy transaction using the provided key and gasPrice func (s *IntegrationTestSuite) createEVMContractDeployTx(key keyring.Key, gasPrice *big.Int, data []byte) sdk.Tx { ethTxArgs := evmtypes.EvmTxArgs{ Nonce: 0, @@ -93,7 +97,7 @@ func (s *IntegrationTestSuite) checkTxs(txs []sdk.Tx) error { return nil } -// checkTxs call abci CheckTx for a transaction +// checkTx calls abci CheckTx for a transaction func (s *IntegrationTestSuite) checkTx(tx sdk.Tx) error { txBytes, err := s.network.App.GetTxConfig().TxEncoder()(tx) if err != nil { @@ -138,7 +142,7 @@ func (s *IntegrationTestSuite) calculateCosmosGasPrice(feeAmount int64, gasLimit // calculateCosmosEffectiveTip calculates the effective tip for a Cosmos transaction // This aligns with EVM transaction prioritization: effective_tip = gas_price - base_fee -func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, baseFee *big.Int) *big.Int { +func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasLimit uint64, gasTipCap, baseFee *big.Int) *big.Int { gasPrice := s.calculateCosmosGasPrice(feeAmount, gasLimit) if baseFee == nil || baseFee.Sign() == 0 { return gasPrice // No base fee, effective tip equals gas price @@ -148,5 +152,10 @@ func (s *IntegrationTestSuite) calculateCosmosEffectiveTip(feeAmount int64, gasL return big.NewInt(0) // Gas price lower than base fee, effective tip is zero } - return new(big.Int).Sub(gasPrice, baseFee) + effectiveTip := new(big.Int).Sub(gasPrice, baseFee) + if effectiveTip.Cmp(gasTipCap) > 0 { + effectiveTip = gasTipCap + } + + return effectiveTip } From ac9cd726cb027f1ce4ed9e3687d55f03c9657b83 Mon Sep 17 00:00:00 2001 From: Kyuhyeon Choi Date: Tue, 9 Sep 2025 20:04:31 +0900 Subject: [PATCH 20/20] chore: update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c94336a07..e66e3a366 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ - [\#442](https://github.com/cosmos/evm/pull/442) Prevent nil pointer by checking error in gov precompile FromResponse. - [\#387](https://github.com/cosmos/evm/pull/387) (Experimental) EVM-compatible appside mempool - [\#476](https://github.com/cosmos/evm/pull/476) Add revert error e2e tests for contract and precompile calls +- [\#534](https://github.com/cosmos/evm/pull/534) Align cosmos tx priority with priority setup of anteHandler ### FEATURES