package types import ( "bytes" "math/big" "testing" "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ethermint/crypto/ethsecp256k1" "github.com/cosmos/ethermint/tests" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" ) type MsgsTestSuite struct { suite.Suite signer keyring.Signer from ethcmn.Address to ethcmn.Address chainID *big.Int } func TestMsgsTestSuite(t *testing.T) { suite.Run(t, new(MsgsTestSuite)) } func (suite *MsgsTestSuite) SetupTest() { privFrom, err := ethsecp256k1.GenerateKey() suite.Require().NoError(err) privTo, err := ethsecp256k1.GenerateKey() suite.Require().NoError(err) suite.signer = tests.NewSigner(privFrom) suite.from = crypto.PubkeyToAddress(privFrom.ToECDSA().PublicKey) suite.to = crypto.PubkeyToAddress(privTo.ToECDSA().PublicKey) suite.chainID = big.NewInt(1) } func (suite *MsgsTestSuite) TestMsgEthereumTx_Constructor() { msg := NewMsgEthereumTx(nil, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) suite.Require().Equal(msg.Data.To, suite.to.Hex()) suite.Require().Equal(msg.Route(), RouterKey) suite.Require().Equal(msg.Type(), TypeMsgEthereumTx) suite.Require().NotNil(msg.To()) suite.Require().Equal(msg.GetMsgs(), []sdk.Msg{msg}) suite.Require().Panics(func() { msg.GetSigners() }) suite.Require().Panics(func() { msg.GetSignBytes() }) msg = NewMsgEthereumTxContract(nil, 0, nil, 100000, nil, []byte("test"), nil) suite.Require().NotNil(msg) suite.Require().Empty(msg.Data.To) suite.Require().Nil(msg.To()) } func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() { testCases := []struct { msg string to *ethcmn.Address amount *big.Int gasPrice *big.Int expectPass bool }{ {msg: "pass with recipient", to: &suite.to, amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true}, {msg: "pass contract", to: nil, amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true}, {msg: "invalid recipient", to: ðcmn.Address{}, amount: big.NewInt(-1), gasPrice: big.NewInt(1000), expectPass: false}, // NOTE: these can't be effectively tested because the SetBytes function from big.Int only sets // the absolute value {msg: "negative amount", to: &suite.to, amount: big.NewInt(-1), gasPrice: big.NewInt(1000), expectPass: true}, {msg: "negative gas price", to: &suite.to, amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: true}, {msg: "zero gas price", to: &suite.to, amount: big.NewInt(100), gasPrice: big.NewInt(0), expectPass: true}, } for i, tc := range testCases { msg := NewMsgEthereumTx(suite.chainID, 0, tc.to, tc.amount, 0, tc.gasPrice, nil, nil) err := msg.ValidateBasic() if tc.expectPass { suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg) } else { suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg) } } } func (suite *MsgsTestSuite) TestMsgEthereumTx_EncodeRLP() { expMsg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) raw, err := rlp.EncodeToBytes(&expMsg) suite.Require().NoError(err) msg := &MsgEthereumTx{} err = rlp.Decode(bytes.NewReader(raw), &msg) suite.Require().NoError(err) suite.Require().Equal(expMsg.Data, msg.Data) } func (suite *MsgsTestSuite) TestMsgEthereumTx_RLPSignBytes() { msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) suite.NotPanics(func() { _ = msg.RLPSignBytes(suite.chainID) }) } func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() { msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) testCases := []struct { msg string ethSigner ethtypes.Signer malleate func() expectPass bool }{ { "pass - EIP2930 signer", ethtypes.NewEIP2930Signer(suite.chainID), func() { msg.From = suite.from.Hex() }, true, }, // TODO: support legacy txs { "not supported - EIP155 signer", ethtypes.NewEIP155Signer(suite.chainID), func() { msg.From = suite.from.Hex() }, false, }, { "not supported - Homestead signer", ethtypes.HomesteadSigner{}, func() { msg.From = suite.from.Hex() }, false, }, { "not supported - Frontier signer", ethtypes.FrontierSigner{}, func() { msg.From = suite.from.Hex() }, false, }, { "no from address ", ethtypes.NewEIP2930Signer(suite.chainID), func() { msg.From = "" }, false, }, { "from address ≠ signer address", ethtypes.NewEIP2930Signer(suite.chainID), func() { msg.From = suite.to.Hex() }, false, }, } for i, tc := range testCases { tc.malleate() err := msg.Sign(tc.ethSigner, suite.signer) if tc.expectPass { suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg) tx := msg.AsTransaction() signer := ethtypes.NewEIP2930Signer(suite.chainID) sender, err := ethtypes.Sender(signer, tx) suite.Require().NoError(err, tc.msg) suite.Require().Equal(msg.From, sender.Hex(), tc.msg) } else { suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg) } } }