Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions ante/evm/04_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ func ValidateMsg(
txData evmtypes.TxData,
from sdktypes.AccAddress,
) error {
if from != nil {
Copy link
Contributor Author

@mmsqe mmsqe Jun 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Vvaradinov We need signer recovery and extractor to make PriorityMempool work

return errorsmod.Wrapf(errortypes.ErrInvalidRequest, "invalid from address; expected nil; got: %q", from.String())
}

return checkDisabledCreateCall(
txData,
&evmParams.AccessControl,
Expand Down
12 changes: 2 additions & 10 deletions ante/evm/05_signature_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,8 @@ func SignatureVerification(
}
}

sender, err := signer.Sender(ethTx)
if err != nil {
return errorsmod.Wrapf(
errortypes.ErrorInvalidSigner,
"couldn't retrieve sender address from the ethereum transaction: %s",
err.Error(),
)
if err := msg.VerifySender(ethCfg.ChainID); err != nil {
return errorsmod.Wrapf(errortypes.ErrorInvalidSigner, "signature verification failed: %s", err.Error())
}

// set up the sender to the transaction field if not already
msg.From = sender.Hex()
return nil
}
2 changes: 1 addition & 1 deletion ante/evm/mono_decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne
}

// 7. can transfer
coreMsg, err := ethMsg.AsMessage(decUtils.Signer, decUtils.BaseFee)
coreMsg, err := ethMsg.AsMessage(decUtils.BaseFee)
if err != nil {
return ctx, errorsmod.Wrapf(
err,
Expand Down
437 changes: 258 additions & 179 deletions api/cosmos/evm/vm/v1/tx.pulsar.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion encoding/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestTxEncoding(t *testing.T) {
Input: []byte{},
}
msg := evmtypes.NewTx(&ethTxParams)
msg.From = addr.Hex()
msg.From = addr.Bytes()

ethSigner := ethtypes.LatestSignerForChainID(big.NewInt(1))
err := msg.Sign(ethSigner, signer)
Expand Down
20 changes: 15 additions & 5 deletions evmd/cmd/evmd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ import (
"github.com/cosmos/cosmos-sdk/client/pruning"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/snapshot"
"github.com/cosmos/cosmos-sdk/server"
sdkserver "github.com/cosmos/cosmos-sdk/server"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"
"github.com/cosmos/cosmos-sdk/types/mempool"
sdktestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
Expand Down Expand Up @@ -315,10 +316,19 @@ func newApp(

// Set up the required mempool and ABCI proposal handlers for Cosmos EVM
baseappOptions = append(baseappOptions, func(app *baseapp.BaseApp) {
mempool := sdkmempool.NoOpMempool{}
app.SetMempool(mempool)

handler := baseapp.NewDefaultProposalHandler(mempool, app)
var mpool mempool.Mempool
if maxTxs := cast.ToInt(appOpts.Get(server.FlagMempoolMaxTxs)); maxTxs >= 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm testing this with a Foundry script that uses transaction batching on this branch, and it seems like this is still failing to process out-of-order nonces.

Getting these errors:

- server returned an error response: error code -32000: invalid nonce; got 24, expected 23: invalid sequence: invalid sequence

Afaik, this PR is supposed to somewhat mitigate that. Is that not the case, or am I doing something wrong here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind share the script? but we use different senders for priority test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything that batch submits transactions using Forge would successfully replicate. Example: https://gist.github.com/vladjdk/cef7503d2dfe4c44eb3dbb1183947ec8

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, since need specify nonce from same sender to avoid out of order in priority mempool, I could try sender nonce mempool later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PriorityNonceMempool don't handle out of order nonce, it only handles priority.

Copy link
Member

@vladjdk vladjdk Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yihuang based on the ADR it should order by nonce:

The follow rules are strictly observed while iterating the mempool to select transactions:

  1. For a given sender their txs must be selected in nonce order.
  2. A transaction with a higher priority is always selected before a transaction with a lower priority except when to do so would violate sender-nonce order.

Based on a quick look of the code, it looks like it orders by sender and nonce as a secondary key:
https://github.com/cosmos/cosmos-sdk/blob/main/types/mempool/priority_nonce.go#L241-L276.

@mmsqe sounds good. Priority nonce, however, should function the same as a sender nonce with an added priority dimension.

Copy link
Contributor

@yihuang yihuang Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vladjdk

the problem is check-tx state, if the txs comes in the order of nonces, the check-tx logic can run on the same check-tx state. but if out of order, the check-tx logic will fail and they won't get into mempool in the first place.

// Setup Mempool and Proposal Handlers
mpool = mempool.NewPriorityMempool(mempool.PriorityNonceMempoolConfig[int64]{
TxPriority: mempool.NewDefaultTxPriority(),
SignerExtractor: evmd.NewEthSignerExtractionAdapter(mempool.NewDefaultSignerExtractionAdapter()),
MaxTx: maxTxs,
})
} else {
mpool = mempool.NoOpMempool{}
}
app.SetMempool(mpool)
handler := baseapp.NewDefaultProposalHandler(mpool, app)
app.SetPrepareProposal(handler.PrepareProposalHandler())
app.SetProcessProposal(handler.ProcessProposalHandler())
})
Expand Down
43 changes: 43 additions & 0 deletions evmd/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package evmd

import (
sdk "github.com/cosmos/cosmos-sdk/types"
mempool "github.com/cosmos/cosmos-sdk/types/mempool"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
evmtypes "github.com/cosmos/evm/x/vm/types"
)

var _ mempool.SignerExtractionAdapter = EthSignerExtractionAdapter{}

// EthSignerExtractionAdapter is the default implementation of SignerExtractionAdapter. It extracts the signers
// from a cosmos-sdk tx via GetSignaturesV2.
type EthSignerExtractionAdapter struct {
fallback mempool.SignerExtractionAdapter
}

// NewEthSignerExtractionAdapter constructs a new EthSignerExtractionAdapter instance
func NewEthSignerExtractionAdapter(fallback mempool.SignerExtractionAdapter) EthSignerExtractionAdapter {
return EthSignerExtractionAdapter{fallback}
}

// GetSigners implements the Adapter interface
// NOTE: only the first item is used by the mempool
func (s EthSignerExtractionAdapter) GetSigners(tx sdk.Tx) ([]mempool.SignerData, error) {
if txWithExtensions, ok := tx.(authante.HasExtensionOptionsTx); ok {
opts := txWithExtensions.GetExtensionOptions()
if len(opts) > 0 && opts[0].GetTypeUrl() == "/cosmos.evm.vm.v1.ExtensionOptionsEthereumTx" {
for _, msg := range tx.GetMsgs() {
if ethMsg, ok := msg.(*evmtypes.MsgEthereumTx); ok {
return []mempool.SignerData{
mempool.NewSignerData(
ethMsg.GetFrom(),
ethMsg.AsTransaction().Nonce(),
),
}, nil
}
}
}
}

return s.fallback.GetSigners(tx)
}
73 changes: 73 additions & 0 deletions evmd/signer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package evmd_test

import (
"math/big"
"testing"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
mempool "github.com/cosmos/cosmos-sdk/types/mempool"
"github.com/cosmos/evm/evmd"
"github.com/cosmos/evm/x/vm/types"
"github.com/stretchr/testify/require"
protov2 "google.golang.org/protobuf/proto"
)

type mockFallback struct {
called bool
}

func (m *mockFallback) GetSigners(tx sdk.Tx) ([]mempool.SignerData, error) {
m.called = true
return []mempool.SignerData{mempool.NewSignerData(sdk.AccAddress("fallback"), 1)}, nil
}

type mockHasExtOptions struct {
msg sdk.Msg
}

func (m *mockHasExtOptions) GetMsgs() []sdk.Msg { return []sdk.Msg{m.msg} }
func (m *mockHasExtOptions) GetMsgsV2() ([]protov2.Message, error) {
return []protov2.Message{}, nil
}
func (m *mockHasExtOptions) GetExtensionOptions() []*codectypes.Any {
return []*codectypes.Any{
{
TypeUrl: "/cosmos.evm.vm.v1.ExtensionOptionsEthereumTx",
Value: []byte{},
},
}
}
func (m *mockHasExtOptions) GetNonCriticalExtensionOptions() []*codectypes.Any { return nil }

func TestGetSigners(t *testing.T) {
ethAddr := sdk.AccAddress("ethsigner")
evmTx := &types.EvmTxArgs{
ChainID: big.NewInt(100),
Nonce: 1,
Amount: big.NewInt(10),
GasLimit: 100000,
GasPrice: big.NewInt(150),
GasFeeCap: big.NewInt(200),
}
ethMsg := types.NewTx(evmTx)
ethMsg.From = ethAddr.Bytes()
txWithEth := &mockHasExtOptions{
msg: ethMsg,
}
fallback := &mockFallback{}
adapter := evmd.NewEthSignerExtractionAdapter(fallback)
signers, err := adapter.GetSigners(txWithEth)
require.NoError(t, err)
require.Equal(t, []mempool.SignerData{{Signer: signers[0].Signer, Sequence: signers[0].Sequence}}, signers)
require.False(t, fallback.called)

fallback = &mockFallback{}
txWithEth = &mockHasExtOptions{}
adapter = evmd.NewEthSignerExtractionAdapter(fallback)
signers, err = adapter.GetSigners(txWithEth)
require.NoError(t, err)
fallbackSigners, _ := new(mockFallback).GetSigners(txWithEth)
require.Equal(t, fallbackSigners, signers)
require.True(t, fallback.called)
}
2 changes: 1 addition & 1 deletion precompiles/testutil/contracts/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func Call(ctx sdk.Context, app evm.EvmApp, args CallArgs) (res abci.ExecTxResult
Input: input,
Accesses: args.AccessList,
})
msg.From = addr.Hex()
msg.From = addr.Bytes()

res, err = integration.DeliverEthTx(app, args.PrivKey, msg)
if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions proto/cosmos/evm/vm/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ message MsgEthereumTx {
// hash of the transaction in hex format
string hash = 3
[ (gogoproto.moretags) = "rlp:\"-\"", (amino.dont_omitempty) = true ];
// from is the ethereum signer address in hex format. This address value is
// checked against the address derived from the signature (V, R, S) using the
string deprecated_from = 4 [ deprecated = true ];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For V1, we're going to be using new protos, with the assumption that the first version is a fresh start. Let's just keep using this field and remove the deprecated tag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this again, and the audited version of the code will still have the string version of from. Looks like we're going to still have to support this/deprecate it since this PR won't yet be in effect for the audited release. Apologies for my mistake here!

// from is the bytes of ethereum signer address. This address value is checked
// against the address derived from the signature (V, R, S) using the
// secp256k1 elliptic curve
string from = 4;
bytes from = 5;
}

// LegacyTx is the transaction data of regular Ethereum transactions.
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ func (b *Backend) formatTxReceipt(ethMsg *evmtypes.MsgEthereumTx, blockMsgs []*e
return nil, err
}

from, err := ethMsg.GetSender(chainID.ToInt())
from, err := ethMsg.GetSenderLegacy(chainID.ToInt())
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/call_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (b *Backend) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) {
}

ethereumTx := &evmtypes.MsgEthereumTx{}
if err := ethereumTx.FromEthereumTx(tx); err != nil {
if err := ethereumTx.FromSignedEthereumTx(tx, b.EvmChainID); err != nil {
b.Logger.Error("transaction converting failed", "error", err.Error())
return common.Hash{}, err
}
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/tx_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (b *Backend) GetTransactionReceipt(hash common.Hash) (map[string]interface{
return nil, err
}

from, err := ethMsg.GetSender(chainID.ToInt())
from, err := ethMsg.GetSenderLegacy(chainID.ToInt())
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion rpc/backend/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (b *Backend) getAccountNonce(accAddr common.Address, pending bool, height i
break
}

sender, err := ethMsg.GetSender(b.EvmChainID)
sender, err := ethMsg.GetSenderLegacy(b.EvmChainID)
if err != nil {
continue
}
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/ante/test_bench_evm_ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func RunBenchmarkEthGasConsumeDecorator(b *testing.B, create network.CreateEvmAp
addr := testutiltx.GenerateAddress()
args.Accesses = &ethtypes.AccessList{{Address: addr, StorageKeys: nil}}
tx := evmtypes.NewTx(args)
tx.From = addr.Hex()
tx.From = addr.Bytes()

cacheCtx, _ := ctx.CacheContext()
// Create new stateDB for each test case from the cached context
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/ante/test_evm_ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ func (s *EvmAnteTestSuite) TestAnteHandler() {
tx, err := s.GetTxFactory().GenerateSignedEthTx(privKey, ethContractCreationTxParams)
s.Require().NoError(err)
msg := tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
msg.From = addr.Hex()
msg.From = addr.Bytes()
return tx
}, true, false, false,
},
Expand Down Expand Up @@ -1248,7 +1248,7 @@ func (s *EvmAnteTestSuite) TestEthSigVerificationDecorator() {
GasPrice: big.NewInt(1),
}
signedTx := evmtypes.NewTx(ethContractCreationTxParams)
signedTx.From = addr.Hex()
signedTx.From = addr.Bytes()
err := signedTx.Sign(ethSigner, utiltx.NewSigner(privKey))
s.Require().NoError(err)

Expand All @@ -1259,7 +1259,7 @@ func (s *EvmAnteTestSuite) TestEthSigVerificationDecorator() {
GasPrice: big.NewInt(1),
}
unprotectedTx := evmtypes.NewTx(unprotectedEthTxParams)
unprotectedTx.From = addr.Hex()
unprotectedTx.From = addr.Bytes()
err = unprotectedTx.Sign(types.HomesteadSigner{}, utiltx.NewSigner(privKey))
s.Require().NoError(err)

Expand Down
9 changes: 2 additions & 7 deletions tests/integration/ante/test_evm_unit_07_can_transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"fmt"
"math/big"

gethtypes "github.com/ethereum/go-ethereum/core/types"

"github.com/cosmos/evm/ante/evm"
testconstants "github.com/cosmos/evm/testutil/constants"
"github.com/cosmos/evm/testutil/integration/evm/factory"
Expand Down Expand Up @@ -74,22 +72,19 @@ func (s *EvmUnitAnteTestSuite) TestCanTransfer() {
s.Run(fmt.Sprintf("%v_%v_%v", evmtypes.GetTxTypeName(s.EthTxType), s.ChainID, tc.name), func() {
baseFeeResp, err := grpcHandler.GetEvmBaseFee()
s.Require().NoError(err)
ethCfg := unitNetwork.GetEVMChainConfig()
evmParams, err := grpcHandler.GetEvmParams()
s.Require().NoError(err)
ctx := unitNetwork.GetContext()
signer := gethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()), uint64(ctx.BlockTime().Unix())) //#nosec G115 -- int overflow is not a concern here
txArgs, err := txFactory.GenerateDefaultTxTypeArgs(senderKey.Addr, s.EthTxType)
s.Require().NoError(err)
txArgs.Amount = big.NewInt(100)

tc.malleate(&txArgs)

msg := evmtypes.NewTx(&txArgs)
msg.From = senderKey.Addr.String()
msg.From = senderKey.Addr.Bytes()
signMsg, err := txFactory.SignMsgEthereumTx(senderKey.Priv, *msg)
s.Require().NoError(err)
coreMsg, err := signMsg.AsMessage(signer, baseFeeResp.BaseFee.BigInt())
coreMsg, err := signMsg.AsMessage(baseFeeResp.BaseFee.BigInt())
s.Require().NoError(err)

// Function under test
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/indexer/test_kv_indexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestKVIndexer(t *testing.T, create network.CreateEvmApp, options ...network
GasLimit: 21000,
}
tx := types.NewTx(&ethTxParams)
tx.From = from.Hex()
tx.From = from.Bytes()
require.NoError(t, tx.Sign(ethSigner, signer))
txHash := tx.AsTransaction().Hash()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,7 @@ func (s *PrecompileTestSuite) TestRun() {
cfg, err := s.network.App.GetEVMKeeper().EVMConfig(ctx, proposerAddress)
s.Require().NoError(err, "failed to instantiate EVM config")

ethChainID := s.network.GetEIP155ChainID()
signer := gethtypes.LatestSignerForChainID(ethChainID)
msg, err := signedMsg.AsMessage(signer, baseFee)
msg, err := signedMsg.AsMessage(baseFee)
s.Require().NoError(err, "failed to instantiate Ethereum message")

// Instantiate EVM
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/rpc/backend/test_backend_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (s *TestSuite) buildEthereumTx() (*evmtypes.MsgEthereumTx, []byte) {
msgEthereumTx := evmtypes.NewTx(&ethTxParams)

// A valid msg should have empty `From`
msgEthereumTx.From = s.from.Hex()
msgEthereumTx.From = s.from.Bytes()

txBuilder := s.backend.ClientCtx.TxConfig.NewTxBuilder()
err := txBuilder.SetMsgs(msgEthereumTx)
Expand All @@ -142,7 +142,7 @@ func (s *TestSuite) buildEthereumTxWithChainID(eip155ChainID *big.Int) *evmtypes
msgEthereumTx := evmtypes.NewTx(&ethTxParams)

// A valid msg should have empty `From`
msgEthereumTx.From = s.from.Hex()
msgEthereumTx.From = s.from.Bytes()

txBuilder := s.backend.ClientCtx.TxConfig.NewTxBuilder()
err := txBuilder.SetMsgs(msgEthereumTx)
Expand Down Expand Up @@ -209,7 +209,7 @@ func (s *TestSuite) signAndEncodeEthTx(msgEthereumTx *evmtypes.MsgEthereumTx) []
signer := utiltx.NewSigner(priv)

ethSigner := ethtypes.LatestSigner(s.backend.ChainConfig())
msgEthereumTx.From = from.String()
msgEthereumTx.From = from.Bytes()
err := msgEthereumTx.Sign(ethSigner, signer)
s.Require().NoError(err)

Expand Down
4 changes: 3 additions & 1 deletion tests/integration/rpc/backend/test_call_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func (s *TestSuite) TestSendRawTransaction() {
func() []byte {
from, priv := utiltx.NewAddrKey()
signer := utiltx.NewSigner(priv)
invalidEvmChainIDTx.From = from.String()
invalidEvmChainIDTx.From = from.Bytes()
err := invalidEvmChainIDTx.Sign(ethSigner, signer)
s.Require().NoError(err)
bytes, _ := rlp.EncodeToBytes(invalidEvmChainIDTx.AsTransaction())
Expand All @@ -348,6 +348,8 @@ func (s *TestSuite) TestSendRawTransaction() {
"fail - unprotected tx",
func() {
s.backend.AllowUnprotectedTxs = false
queryClient := s.backend.QueryClient.QueryClient.(*mocks.EVMQueryClient)
RegisterParamsWithoutHeaderError(queryClient, 1)
},
func() []byte {
bytes, _ := rlp.EncodeToBytes(emptyEvmChainIDTx.AsTransaction())
Expand Down
Loading