package types import ( "fmt" "math/big" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth/ante" "github.com/tharsis/ethermint/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" ) var ( _ sdk.Msg = &MsgEthereumTx{} _ sdk.Tx = &MsgEthereumTx{} _ ante.GasTx = &MsgEthereumTx{} ) // message type and route constants const ( // TypeMsgEthereumTx defines the type string of an Ethereum transaction TypeMsgEthereumTx = "ethereum_tx" ) // NewMsgEthereumTx returns a reference to a new Ethereum transaction message. func NewMsgEthereumTx( 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) } // NewMsgEthereumTxContract returns a reference to a new Ethereum transaction // message designated for contract creation. func NewMsgEthereumTxContract( chainID *big.Int, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList, ) *MsgEthereumTx { return newMsgEthereumTx(chainID, nonce, nil, amount, gasLimit, gasPrice, input, accesses) } func newMsgEthereumTx( chainID *big.Int, nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, input []byte, accesses *ethtypes.AccessList, ) *MsgEthereumTx { 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) { msg.Data = &TxData{ Nonce: tx.Nonce(), Input: tx.Data(), GasLimit: tx.Gas(), } 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() } // Route returns the route value of an MsgEthereumTx. func (msg MsgEthereumTx) Route() string { return RouterKey } // Type returns the type value of an MsgEthereumTx. 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 { if msg.From != "" { if err := types.ValidateAddress(msg.From); err != nil { return sdkerrors.Wrap(err, "invalid from address") } } return msg.Data.Validate() } // To returns the recipient address of the transaction. It returns nil if the // transaction is a contract creation. func (msg MsgEthereumTx) To() *common.Address { return msg.Data.to() } // GetMsgs returns a single MsgEthereumTx as an sdk.Msg. func (msg *MsgEthereumTx) GetMsgs() []sdk.Msg { return []sdk.Msg{msg} } // GetSigners returns the expected signers for an Ethereum transaction message. // For such a message, there should exist only a single 'signer'. // // NOTE: This method panics if 'Sign' hasn't been called first. func (msg MsgEthereumTx) GetSigners() []sdk.AccAddress { v, r, s := msg.RawSignatureValues() if msg.From == "" || v == nil || r == nil || s == nil { panic("must use 'Sign' to get the signer address") } signer := sdk.AccAddress(common.HexToAddress(msg.From).Bytes()) return []sdk.AccAddress{signer} } // GetSignBytes returns the Amino bytes of an Ethereum transaction message used // for signing. // // NOTE: This method cannot be used as a chain ID is needed to create valid bytes // to sign over. Use 'RLPSignBytes' instead. func (msg MsgEthereumTx) GetSignBytes() []byte { panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign") } // Sign calculates a secp256k1 ECDSA signature and signs the transaction. It // takes a keyring signer and the chainID to sign an Ethereum transaction according to // EIP155 standard. // This method mutates the transaction as it populates the V, R, S // fields of the Transaction's Signature. // The function will fail if the sender address is not defined for the msg or if // the sender is not registered on the keyring func (msg *MsgEthereumTx) Sign(ethSigner ethtypes.Signer, keyringSigner keyring.Signer) error { from := msg.GetFrom() if from.Empty() { return fmt.Errorf("sender address not defined for message") } tx := msg.AsTransaction() txHash := ethSigner.Hash(tx) sig, _, err := keyringSigner.SignByAddress(from, txHash.Bytes()) if err != nil { return err } tx, err = tx.WithSignature(ethSigner, sig) if err != nil { return err } msg.FromEthereumTx(tx) return nil } // GetGas implements the GasTx interface. It returns the GasLimit of the transaction. func (msg MsgEthereumTx) GetGas() uint64 { return msg.Data.gas() } // Fee returns gasprice * gaslimit. func (msg MsgEthereumTx) Fee() *big.Int { 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 msg.Data.chainID() } // Cost returns amount + gasprice * gaslimit. func (msg MsgEthereumTx) Cost() *big.Int { total := msg.Fee() if msg.Data.amount() != nil { 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 msg.Data.rawSignatureValues() } // GetFrom loads the ethereum sender address from the sigcache and returns an // sdk.AccAddress from its bytes func (msg *MsgEthereumTx) GetFrom() sdk.AccAddress { if msg.From == "" { return nil } 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 func (msg MsgEthereumTx) AsMessage(signer ethtypes.Signer) (core.Message, error) { return msg.AsTransaction().AsMessage(signer) }