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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions rpc/backend/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

cmtrpctypes "github.com/cometbft/cometbft/rpc/core/types"

"github.com/cosmos/evm/rpc/backend/eth"
rpctypes "github.com/cosmos/evm/rpc/types"
cosmosevmtypes "github.com/cosmos/evm/types"
evmtypes "github.com/cosmos/evm/x/vm/types"
Expand Down Expand Up @@ -262,6 +263,7 @@ func (b *Backend) BlockNumberFromCometByHash(blockHash common.Hash) (*big.Int, e
// EthMsgsFromCometBlock returns all real MsgEthereumTxs from a
// CometBFT block. It also ensures consistency over the correct txs indexes
// across RPC endpoints
// This function now supports both new EVM and legacy Evmos message types
func (b *Backend) EthMsgsFromCometBlock(
resBlock *cmtrpctypes.ResultBlock,
blockRes *cmtrpctypes.ResultBlockResults,
Expand All @@ -288,12 +290,22 @@ func (b *Backend) EthMsgsFromCometBlock(
}

for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
// Try to adapt the message to our unified interface
ethMsg := eth.TryAdaptEthTxMsg(msg)
if ethMsg == nil {
// Not an Ethereum transaction message
continue
}

result = append(result, ethMsg)
// Convert to new EVM format for return
// Use the chain ID from the backend
convertedMsg, err := eth.ConvertToNewEVMMsgWithChainID(ethMsg, b.EvmChainID)
if err != nil {
b.Logger.Debug("failed to convert ethereum tx", "height", block.Height, "error", err.Error())
continue
}

result = append(result, convertedMsg)
}
}

Expand Down
37 changes: 37 additions & 0 deletions rpc/backend/eth/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package eth

import (
"fmt"

legacytypes "github.com/cosmos/evm/rpc/types/legacy"
evmtypes "github.com/cosmos/evm/x/vm/types"

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

// AdaptEthTxMsg converts an sdk.Msg to our unified TxMsg interface.
// This function tries to match the message to known MsgEthereumTx types
// and returns an appropriate adapter. This makes it easy to add support
// for new message types in the future by adding new case statements.
func AdaptEthTxMsg(msg sdk.Msg) (TxMsg, error) {
switch m := msg.(type) {
case *evmtypes.MsgEthereumTx:
// New EVM type
return &NewEVMEthTxMsg{MsgEthereumTx: m}, nil
case *legacytypes.MsgEthereumTx:
// Legacy Evmos type
return &LegacyEvmosEthTxMsg{MsgEthereumTx: m}, nil
default:
return nil, fmt.Errorf("unsupported message type: %T", msg)
}
}

// TryAdaptEthTxMsg attempts to adapt a message to TxMsg.
// Returns nil if the message is not an Ethereum transaction type.
func TryAdaptEthTxMsg(msg sdk.Msg) TxMsg {
adapted, err := AdaptEthTxMsg(msg)
if err != nil {
return nil
}
return adapted
}
62 changes: 62 additions & 0 deletions rpc/backend/eth/evm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package eth

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"

evmtypes "github.com/cosmos/evm/x/vm/types"
)

var _ TxMsg = NewEVMEthTxMsg{}

// NewEVMEthTxMsg creates an adapter for the new EVM MsgEthereumTx
type NewEVMEthTxMsg struct {
*evmtypes.MsgEthereumTx
}

// AsTransaction returns the underlying Ethereum transaction for the new EVM type
func (m NewEVMEthTxMsg) AsTransaction() *ethtypes.Transaction {
return m.MsgEthereumTx.AsTransaction()
}

// Hash returns the transaction hash for the new EVM type
func (m NewEVMEthTxMsg) Hash() common.Hash {
return m.MsgEthereumTx.Hash()
}

// GetSenderLegacy returns the sender address for the new EVM type
func (m NewEVMEthTxMsg) GetSenderLegacy(signer ethtypes.Signer) (common.Address, error) {
return m.MsgEthereumTx.GetSenderLegacy(signer)
}

// GetGas returns the gas limit for the new EVM type
func (m NewEVMEthTxMsg) GetGas() uint64 {
return m.MsgEthereumTx.GetGas()
}

// ConvertToNewEVMMsg converts any TxMsg to the new EVM MsgEthereumTx format.
// This is useful when we need to work with code that specifically requires
// the new format. For legacy messages, this creates a new message with
// the converted data.
func ConvertToNewEVMMsg(msg TxMsg, signer ethtypes.Signer) (*evmtypes.MsgEthereumTx, error) {
// If it's already the new format, return as-is
if newMsg, ok := msg.(*NewEVMEthTxMsg); ok {
return newMsg.MsgEthereumTx, nil
}

// For legacy messages, create a new message
tx := msg.AsTransaction()
sender, err := msg.GetSenderLegacy(signer)
if err != nil {
return nil, fmt.Errorf("failed to get sender: %w", err)
}

newMsg := &evmtypes.MsgEthereumTx{
From: sender.Bytes(),
}
newMsg.FromEthereumTx(tx)

return newMsg, nil
}
73 changes: 73 additions & 0 deletions rpc/backend/eth/evmos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package eth

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"

legacytypes "github.com/cosmos/evm/rpc/types/legacy"
evmtypes "github.com/cosmos/evm/x/vm/types"
)

var _ TxMsg = LegacyEvmosEthTxMsg{}

// LegacyEvmosEthTxMsg creates an adapter for the old Evmos MsgEthereumTx
type LegacyEvmosEthTxMsg struct {
*legacytypes.MsgEthereumTx
}

// AsTransaction returns the underlying Ethereum transaction for the legacy Evmos type
func (m LegacyEvmosEthTxMsg) AsTransaction() *ethtypes.Transaction {
return m.MsgEthereumTx.AsTransaction()
}

// Hash returns the transaction hash for the legacy Evmos type
func (m LegacyEvmosEthTxMsg) Hash() common.Hash {
// The old type stores hash as hex string, convert it back
if m.MsgEthereumTx.Hash != "" {
return common.HexToHash(m.MsgEthereumTx.Hash)
}
// Fallback: compute from transaction
return m.AsTransaction().Hash()
}

// GetSenderLegacy returns the sender address for the legacy Evmos type
func (m LegacyEvmosEthTxMsg) GetSenderLegacy(signer ethtypes.Signer) (common.Address, error) {
// If From field is set, use it
if m.From != "" {
return common.HexToAddress(m.From), nil
}
// Otherwise recover from signature
tx := m.AsTransaction()
sender, err := signer.Sender(tx)
if err != nil {
return common.Address{}, err
}
// Cache the sender for future use
m.From = sender.Hex()
return sender, nil
}

// GetGas returns the gas limit for the legacy Evmos type
func (m LegacyEvmosEthTxMsg) GetGas() uint64 {
return m.MsgEthereumTx.GetGas()
}

// ConvertToNewEVMMsgWithChainID is like ConvertToNewEVMMsg but uses chainID to create signer
func ConvertToNewEVMMsgWithChainID(msg TxMsg, chainID *big.Int) (*evmtypes.MsgEthereumTx, error) {
// If it's already the new format, return as-is
if newMsg, ok := msg.(*NewEVMEthTxMsg); ok {
return newMsg.MsgEthereumTx, nil
}

tx := msg.AsTransaction()
var signer ethtypes.Signer
if tx.Protected() {
signer = ethtypes.LatestSignerForChainID(tx.ChainId())
} else {
signer = ethtypes.FrontierSigner{}
}

return ConvertToNewEVMMsg(msg, signer)
}
20 changes: 20 additions & 0 deletions rpc/backend/eth/msg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package eth

import (
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)

// TxMsg is a unified interface for different versions of MsgEthereumTx
// This interface abstracts the differences between the old Evmos format
// and the new EVM format, making it easy to add support for future versions.
type TxMsg interface {
// AsTransaction returns the underlying Ethereum transaction
AsTransaction() *ethtypes.Transaction
// Hash returns the transaction hash
Hash() common.Hash
// GetSenderLegacy returns the sender address, recovering from signature if needed
GetSenderLegacy(signer ethtypes.Signer) (common.Address, error)
// GetGas returns the gas limit
GetGas() uint64
}
51 changes: 40 additions & 11 deletions rpc/backend/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
tmrpcclient "github.com/cometbft/cometbft/rpc/client"
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"

"github.com/cosmos/evm/rpc/backend/eth"
rpctypes "github.com/cosmos/evm/rpc/types"
evmtypes "github.com/cosmos/evm/x/vm/types"

Expand Down Expand Up @@ -57,12 +58,19 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *rpctypes.TraceConfi
continue
}
for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
// Try to adapt the message to our unified interface
ethMsg := eth.TryAdaptEthTxMsg(msg)
if ethMsg == nil {
continue
}

predecessors = append(predecessors, ethMsg)
// Convert to new format for predecessors
convertedMsg, err := eth.ConvertToNewEVMMsgWithChainID(ethMsg, b.EvmChainID)
if err != nil {
b.Logger.Debug("failed to convert ethereum tx", "height", blk.Block.Height, "error", err.Error())
continue
}
predecessors = append(predecessors, convertedMsg)
}
}

Expand All @@ -75,19 +83,33 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *rpctypes.TraceConfi
// add predecessor messages in current cosmos tx
index := int(transaction.MsgIndex) // #nosec G115
for i := 0; i < index; i++ {
ethMsg, ok := tx.GetMsgs()[i].(*evmtypes.MsgEthereumTx)
if !ok {
ethMsg := eth.TryAdaptEthTxMsg(tx.GetMsgs()[i])
if ethMsg == nil {
continue
}
// Convert to new format for predecessors
convertedMsg, err := eth.ConvertToNewEVMMsgWithChainID(ethMsg, b.EvmChainID)
if err != nil {
b.Logger.Debug("failed to convert ethereum tx", "error", err.Error())
continue
}
predecessors = append(predecessors, ethMsg)
predecessors = append(predecessors, convertedMsg)
}

ethMessage, ok := tx.GetMsgs()[transaction.MsgIndex].(*evmtypes.MsgEthereumTx)
if !ok {
// Adapt the target message
adaptedMsg, err := eth.AdaptEthTxMsg(tx.GetMsgs()[transaction.MsgIndex])
if err != nil {
b.Logger.Debug("invalid transaction type", "type", fmt.Sprintf("%T", tx))
return nil, fmt.Errorf("invalid transaction type %T", tx)
}

// Convert to new format
ethMessage, err := eth.ConvertToNewEVMMsgWithChainID(adaptedMsg, b.EvmChainID)
if err != nil {
b.Logger.Debug("failed to convert ethereum tx", "error", err.Error())
return nil, fmt.Errorf("failed to convert ethereum tx: %w", err)
}

nc, ok := b.ClientCtx.Client.(tmrpcclient.NetworkClient)
if !ok {
return nil, errors.New("invalid rpc client")
Expand Down Expand Up @@ -179,12 +201,19 @@ func (b *Backend) TraceBlock(height rpctypes.BlockNumber,
}

for _, msg := range decodedTx.GetMsgs() {
ethMessage, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
// Try to adapt the message to our unified interface
ethMsg := eth.TryAdaptEthTxMsg(msg)
if ethMsg == nil {
// Just considers Ethereum transactions
continue
}
txsMessages = append(txsMessages, ethMessage)
// Convert to new format
convertedMsg, err := eth.ConvertToNewEVMMsgWithChainID(ethMsg, b.EvmChainID)
if err != nil {
b.Logger.Debug("failed to convert ethereum tx", "error", err.Error())
continue
}
txsMessages = append(txsMessages, convertedMsg)
}
}

Expand Down
Loading
Loading