Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

evm: support legacy tx #109

Merged
merged 7 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
10 changes: 4 additions & 6 deletions app/ante/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,14 +331,15 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
}

ethCfg := config.EthereumConfig(ctd.evmKeeper.ChainID())
signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))

for _, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", msg)
}

coreMsg, err := msgEthTx.AsMessage()
coreMsg, err := msgEthTx.AsMessage(signer)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ctx, err
}
Expand Down Expand Up @@ -409,12 +410,9 @@ func (ald AccessListDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", msg)
}

coreMsg, err := msgEthTx.AsMessage()
if err != nil {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "tx cannot be expressed as core.Message: %s", err.Error())
}
sender := common.BytesToAddress(msgEthTx.GetFrom())

ald.evmKeeper.PrepareAccessList(coreMsg.From(), coreMsg.To(), vm.ActivePrecompiles(rules), coreMsg.AccessList())
ald.evmKeeper.PrepareAccessList(sender, msgEthTx.To(), vm.ActivePrecompiles(rules), *msgEthTx.Data.Accesses.ToEthAccessList())
}

// set the original gas meter
Expand Down
6 changes: 2 additions & 4 deletions app/ante/eth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ func (suite AnteTestSuite) TestCanTransferDecorator() {

addr, _ := newTestAddrKey()

tx := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil)
tx2 := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil)
tx := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, &ethtypes.AccessList{})
tx2 := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, &ethtypes.AccessList{})

tx.From = addr.Hex()

Expand Down Expand Up @@ -376,7 +376,6 @@ func (suite AnteTestSuite) TestAccessListDecorator() {

tx := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil)
tx2 := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, al)
tx3 := evmtypes.NewMsgEthereumTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil)

tx.From = addr.Hex()
tx2.From = addr.Hex()
Expand All @@ -388,7 +387,6 @@ func (suite AnteTestSuite) TestAccessListDecorator() {
expPass bool
}{
{"invalid transaction type", &invalidTx{}, func() {}, false},
{"AsMessage failed", tx3, func() {}, false},
{
"success - no access list",
tx,
Expand Down
171 changes: 37 additions & 134 deletions x/evm/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/cosmos/ethermint/types"

ethcmn "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
Expand All @@ -29,7 +29,7 @@ const (

// NewMsgEthereumTx returns a reference to a new Ethereum transaction message.
func NewMsgEthereumTx(
chainID *big.Int, nonce uint64, to *ethcmn.Address, amount *big.Int,
chainID *big.Int, nonce uint64, to *common.Address, amount *big.Int,
gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList,
) *MsgEthereumTx {
return newMsgEthereumTx(chainID, nonce, to, amount, gasLimit, gasPrice, input, accesses)
Expand All @@ -45,71 +45,38 @@ func NewMsgEthereumTxContract(
}

func newMsgEthereumTx(
chainID *big.Int, nonce uint64, to *ethcmn.Address, amount *big.Int,
chainID *big.Int, nonce uint64, to *common.Address, amount *big.Int,
gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList,
) *MsgEthereumTx {
if len(input) > 0 {
input = ethcmn.CopyBytes(input)
}

var toHex string
if to != nil {
toHex = to.Hex()
}

var chainIDBz []byte
if chainID != nil {
chainIDBz = chainID.Bytes()
}

txData := &TxData{
ChainID: chainIDBz,
Nonce: nonce,
To: toHex,
Input: input,
GasLimit: gasLimit,
Amount: []byte{},
GasPrice: []byte{},
Accesses: NewAccessList(accesses),
V: []byte{},
R: []byte{},
S: []byte{},
}

if amount != nil {
txData.Amount = amount.Bytes()
}
if gasPrice != nil {
txData.GasPrice = gasPrice.Bytes()
}

txData := newTxData(chainID, nonce, to, amount, gasLimit, gasPrice, input, accesses)
return &MsgEthereumTx{Data: txData}
}

// fromEthereumTx populates the message fields from the given ethereum transaction
func (msg *MsgEthereumTx) FromEthereumTx(tx *ethtypes.Transaction) {
to := ""
if tx.To() != nil {
to = tx.To().Hex()
}

al := tx.AccessList()
v, r, s := tx.RawSignatureValues()

msg.Data = &TxData{
ChainID: tx.ChainId().Bytes(),
Nonce: tx.Nonce(),
Input: tx.Data(),
GasLimit: tx.Gas(),
To: to,
Amount: tx.Value().Bytes(),
GasPrice: tx.GasPrice().Bytes(),
Accesses: NewAccessList(&al),
V: v.Bytes(),
R: r.Bytes(),
S: s.Bytes(),
}

v, r, s := tx.RawSignatureValues()
if tx.To() != nil {
msg.Data.To = tx.To().Hex()
}
if tx.Value() != nil {
msg.Data.Amount = tx.Value().Bytes()
}
if tx.GasPrice() != nil {
msg.Data.GasPrice = tx.GasPrice().Bytes()
}
if tx.AccessList() != nil {
al := tx.AccessList()
msg.Data.Accesses = NewAccessList(&al)
}

msg.Data.setSignatureValues(tx.ChainId(), v, r, s)

msg.Size_ = float64(tx.Size())
msg.Hash = tx.Hash().Hex()
}
Expand All @@ -123,13 +90,13 @@ func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx }
// ValidateBasic implements the sdk.Msg interface. It performs basic validation
// checks of a Transaction. If returns an error if validation fails.
func (msg MsgEthereumTx) ValidateBasic() error {
gasPrice := new(big.Int).SetBytes(msg.Data.GasPrice)
gasPrice := msg.Data.gasPrice()
if gasPrice.Sign() == -1 {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
return sdkerrors.Wrapf(ErrInvalidGasPrice, "gas price cannot be negative %s", gasPrice)
}

// Amount can be 0
amount := new(big.Int).SetBytes(msg.Data.Amount)
amount := msg.Data.amount()
if amount.Sign() == -1 {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
return sdkerrors.Wrapf(ErrInvalidAmount, "amount cannot be negative %s", amount)
}
Expand All @@ -151,13 +118,8 @@ func (msg MsgEthereumTx) ValidateBasic() error {

// To returns the recipient address of the transaction. It returns nil if the
// transaction is a contract creation.
func (msg MsgEthereumTx) To() *ethcmn.Address {
if msg.Data.To == "" {
return nil
}

recipient := ethcmn.HexToAddress(msg.Data.To)
return &recipient
func (msg MsgEthereumTx) To() *common.Address {
return msg.Data.to()
}

// GetMsgs returns a single MsgEthereumTx as an sdk.Msg.
Expand All @@ -173,10 +135,10 @@ func (msg MsgEthereumTx) GetSigners() []sdk.AccAddress {
v, r, s := msg.RawSignatureValues()

if msg.From == "" || v == nil || r == nil || s == nil {
panic("must use 'Sign' with a chain ID to get the signer")
panic("must use 'Sign' to get the signer address")
}

signer := sdk.AccAddress(ethcmn.HexToAddress(msg.From).Bytes())
signer := sdk.AccAddress(common.HexToAddress(msg.From).Bytes())
return []sdk.AccAddress{signer}
}

Expand Down Expand Up @@ -221,34 +183,32 @@ func (msg *MsgEthereumTx) Sign(ethSigner ethtypes.Signer, keyringSigner keyring.

// GetGas implements the GasTx interface. It returns the GasLimit of the transaction.
func (msg MsgEthereumTx) GetGas() uint64 {
return msg.Data.GasLimit
return msg.Data.gas()
}

// Fee returns gasprice * gaslimit.
func (msg MsgEthereumTx) Fee() *big.Int {
gasPrice := new(big.Int).SetBytes(msg.Data.GasPrice)
gasLimit := new(big.Int).SetUint64(msg.Data.GasLimit)
gasPrice := msg.Data.gasPrice()
gasLimit := new(big.Int).SetUint64(msg.Data.gas())
return new(big.Int).Mul(gasPrice, gasLimit)
}

// ChainID returns which chain id this transaction was signed for (if at all)
func (msg *MsgEthereumTx) ChainID() *big.Int {
return new(big.Int).SetBytes(msg.Data.ChainID)
return msg.Data.chainID()
}

// Cost returns amount + gasprice * gaslimit.
func (msg MsgEthereumTx) Cost() *big.Int {
total := msg.Fee()
total.Add(total, new(big.Int).SetBytes(msg.Data.Amount))
total.Add(total, msg.Data.amount())
return total
}

// RawSignatureValues returns the V, R, S signature values of the transaction.
// The return values should not be modified by the caller.
func (msg MsgEthereumTx) RawSignatureValues() (v, r, s *big.Int) {
return new(big.Int).SetBytes(msg.Data.V),
new(big.Int).SetBytes(msg.Data.R),
new(big.Int).SetBytes(msg.Data.S)
return msg.Data.rawSignatureValues()
}

// GetFrom loads the ethereum sender address from the sigcache and returns an
Expand All @@ -258,72 +218,15 @@ func (msg *MsgEthereumTx) GetFrom() sdk.AccAddress {
return nil
}

return ethcmn.HexToAddress(msg.From).Bytes()
return common.HexToAddress(msg.From).Bytes()
}

// AsTransaction creates an Ethereum Transaction type from the msg fields
func (msg MsgEthereumTx) AsTransaction() *ethtypes.Transaction {
return ethtypes.NewTx(msg.Data.AsEthereumData())
}

// AsMessage creates an Ethereum core.Message from the msg fields. This method
// fails if the sender address is not defined
func (msg MsgEthereumTx) AsMessage() (core.Message, error) {
if msg.From == "" {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "'from' address cannot be empty")
}

from := ethcmn.HexToAddress(msg.From)

var to *ethcmn.Address
if msg.Data.To != "" {
toAddr := ethcmn.HexToAddress(msg.Data.To)
to = &toAddr
}

var accessList ethtypes.AccessList
if msg.Data.Accesses != nil {
accessList = *msg.Data.Accesses.ToEthAccessList()
}

return ethtypes.NewMessage(
from,
to,
msg.Data.Nonce,
new(big.Int).SetBytes(msg.Data.Amount),
msg.Data.GasLimit,
new(big.Int).SetBytes(msg.Data.GasPrice),
msg.Data.Input,
accessList,
true,
), nil
}

// AsEthereumData returns an AccessListTx transaction data from the proto-formatted
// TxData defined on the Cosmos EVM.
func (data *TxData) AsEthereumData() ethtypes.TxData {
var to *ethcmn.Address
if data.To != "" {
toAddr := ethcmn.HexToAddress(data.To)
to = &toAddr
}

var accessList ethtypes.AccessList
if data.Accesses != nil {
accessList = *data.Accesses.ToEthAccessList()
}

return &ethtypes.AccessListTx{
ChainID: new(big.Int).SetBytes(data.ChainID),
Nonce: data.Nonce,
GasPrice: new(big.Int).SetBytes(data.GasPrice),
Gas: data.GasLimit,
To: to,
Value: new(big.Int).SetBytes(data.Amount),
Data: data.Input,
AccessList: accessList,
V: new(big.Int).SetBytes(data.V),
R: new(big.Int).SetBytes(data.R),
S: new(big.Int).SetBytes(data.S),
}
// AsMessage creates an Ethereum core.Message from the msg fields
func (msg MsgEthereumTx) AsMessage(signer ethtypes.Signer) (core.Message, error) {
return msg.AsTransaction().AsMessage(signer)
}
Loading