forked from cerc-io/laconicd-deprecated
rpc, evm: secure tx signing (#20)
* rpc, evm: secure signing * evm, ante: test signer * tests
This commit is contained in:
+36
-38
@@ -1,7 +1,6 @@
|
||||
package evm_test
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"strings"
|
||||
@@ -14,13 +13,14 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/ethermint/app"
|
||||
"github.com/cosmos/ethermint/crypto/ethsecp256k1"
|
||||
"github.com/cosmos/ethermint/tests"
|
||||
ethermint "github.com/cosmos/ethermint/types"
|
||||
"github.com/cosmos/ethermint/x/evm"
|
||||
"github.com/cosmos/ethermint/x/evm/types"
|
||||
@@ -37,9 +37,9 @@ type EvmTestSuite struct {
|
||||
codec codec.BinaryMarshaler
|
||||
chainID *big.Int
|
||||
|
||||
privKey *ethsecp256k1.PrivKey
|
||||
from ethcmn.Address
|
||||
to sdk.AccAddress
|
||||
signer keyring.Signer
|
||||
from ethcmn.Address
|
||||
to sdk.AccAddress
|
||||
}
|
||||
|
||||
func (suite *EvmTestSuite) SetupTest() {
|
||||
@@ -49,16 +49,17 @@ func (suite *EvmTestSuite) SetupTest() {
|
||||
suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-888", Time: time.Now().UTC()})
|
||||
suite.handler = evm.NewHandler(suite.app.EvmKeeper)
|
||||
suite.codec = suite.app.AppCodec()
|
||||
suite.chainID = big.NewInt(888)
|
||||
suite.chainID = suite.chainID
|
||||
|
||||
privKey, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.to = sdk.AccAddress(privKey.PubKey().Address())
|
||||
|
||||
suite.privKey, err = ethsecp256k1.GenerateKey()
|
||||
privKey, err = ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.signer = tests.NewSigner(privKey)
|
||||
suite.from = ethcmn.BytesToAddress(privKey.PubKey().Address().Bytes())
|
||||
|
||||
}
|
||||
@@ -80,14 +81,16 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
||||
"passed",
|
||||
func() {
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
||||
tx = types.NewMsgEthereumTx(suite.chainID, 0, &suite.from, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
||||
to := ethcmn.BytesToAddress(suite.to)
|
||||
tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
|
||||
tx.From = suite.from.String()
|
||||
|
||||
// parse context chain ID to big.Int
|
||||
chainID, err := ethermint.ParseChainID(suite.ctx.ChainID())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// sign transaction
|
||||
err = tx.Sign(chainID, suite.privKey.ToECDSA())
|
||||
err = tx.Sign(chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
true,
|
||||
@@ -102,7 +105,7 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// sign transaction
|
||||
err = tx.Sign(chainID, suite.privKey.ToECDSA())
|
||||
err = tx.Sign(chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
},
|
||||
false,
|
||||
@@ -173,12 +176,11 @@ func (suite *EvmTestSuite) TestHandlerLogs() {
|
||||
gasLimit := uint64(100000)
|
||||
gasPrice := big.NewInt(1000000)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
err = tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
|
||||
err := tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := suite.handler(suite.ctx, tx)
|
||||
@@ -204,13 +206,12 @@ func (suite *EvmTestSuite) TestQueryTxLogs() {
|
||||
gasLimit := uint64(100000)
|
||||
gasPrice := big.NewInt(1000000)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
// send contract deployment transaction with an event in the constructor
|
||||
bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029")
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
err = tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
|
||||
err := tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := suite.handler(suite.ctx, tx)
|
||||
@@ -291,12 +292,11 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
|
||||
gasLimit := uint64(100000000)
|
||||
gasPrice := big.NewInt(10000)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032")
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
|
||||
err := tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := suite.handler(suite.ctx, tx)
|
||||
@@ -313,7 +313,9 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
|
||||
storeAddr := "0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424"
|
||||
bytecode = common.FromHex(storeAddr)
|
||||
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
|
||||
err = tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err = suite.handler(suite.ctx, tx)
|
||||
@@ -325,7 +327,8 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
|
||||
// query - getOwner
|
||||
bytecode = common.FromHex("0x893d20e8")
|
||||
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
err = tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err = suite.handler(suite.ctx, tx)
|
||||
@@ -342,15 +345,12 @@ func (suite *EvmTestSuite) TestSendTransaction() {
|
||||
gasLimit := uint64(21000)
|
||||
gasPrice := big.NewInt(0x55ae82600)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
pub := priv.ToECDSA().Public().(*ecdsa.PublicKey)
|
||||
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, ethcrypto.PubkeyToAddress(*pub), big.NewInt(100))
|
||||
suite.app.EvmKeeper.SetBalance(suite.ctx, suite.from, big.NewInt(100))
|
||||
|
||||
// send simple value transfer with gasLimit=21000
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
|
||||
err = tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
err := tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := suite.handler(suite.ctx, tx)
|
||||
@@ -418,12 +418,11 @@ func (suite *EvmTestSuite) TestOutOfGasWhenDeployContract() {
|
||||
suite.ctx = suite.ctx.WithGasMeter(sdk.NewGasMeter(gasLimit))
|
||||
gasPrice := big.NewInt(10000)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032")
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
|
||||
err := tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||
@@ -447,13 +446,12 @@ func (suite *EvmTestSuite) TestErrorWhenDeployContract() {
|
||||
gasLimit := uint64(1000000)
|
||||
gasPrice := big.NewInt(10000)
|
||||
|
||||
priv, err := ethsecp256k1.GenerateKey()
|
||||
suite.Require().NoError(err, "failed to create key")
|
||||
|
||||
bytecode := common.FromHex("0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424")
|
||||
|
||||
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
|
||||
tx.Sign(big.NewInt(888), priv.ToECDSA())
|
||||
tx.From = suite.from.String()
|
||||
|
||||
err := tx.Sign(suite.chainID, suite.signer)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
|
||||
|
||||
+28
-22
@@ -1,18 +1,18 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keyring"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -150,13 +150,17 @@ func (msg MsgEthereumTx) GetSignBytes() []byte {
|
||||
// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a
|
||||
// given chainID used for signing.
|
||||
func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||
if msg.Data.ChainID != nil {
|
||||
chainID = new(big.Int).SetBytes(msg.Data.ChainID)
|
||||
}
|
||||
|
||||
var accessList *ethtypes.AccessList
|
||||
if msg.Data.Accesses != nil {
|
||||
accessList = msg.Data.Accesses.ToEthAccessList()
|
||||
}
|
||||
|
||||
return rlpHash([]interface{}{
|
||||
new(big.Int).SetBytes(msg.Data.ChainID),
|
||||
chainID,
|
||||
msg.Data.Nonce,
|
||||
new(big.Int).SetBytes(msg.Data.GasPrice),
|
||||
msg.Data.GasLimit,
|
||||
@@ -167,19 +171,6 @@ func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
|
||||
})
|
||||
}
|
||||
|
||||
// RLPSignHomesteadBytes returns the RLP hash of an Ethereum transaction message with a
|
||||
// a Homestead layout without chainID.
|
||||
func (msg MsgEthereumTx) RLPSignHomesteadBytes() ethcmn.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
msg.Data.Nonce,
|
||||
msg.Data.GasPrice,
|
||||
msg.Data.GasLimit,
|
||||
msg.To(),
|
||||
msg.Data.Amount,
|
||||
msg.Data.Input,
|
||||
})
|
||||
}
|
||||
|
||||
// EncodeRLP implements the rlp.Encoder interface.
|
||||
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, &msg.Data)
|
||||
@@ -202,19 +193,31 @@ func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error {
|
||||
}
|
||||
|
||||
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
|
||||
// takes a private key and chainID to sign an Ethereum transaction according to
|
||||
// EIP155 standard. It mutates the transaction as it populates the V, R, S
|
||||
// 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.
|
||||
func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) error {
|
||||
// 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(chainID *big.Int, signer keyring.Signer) error {
|
||||
from := msg.GetFrom()
|
||||
if from == nil {
|
||||
return fmt.Errorf("sender address not defined for message")
|
||||
}
|
||||
|
||||
txHash := msg.RLPSignBytes(chainID)
|
||||
|
||||
sig, err := ethcrypto.Sign(txHash[:], priv)
|
||||
sig, _, err := signer.SignByAddress(from, txHash[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sig) != 65 {
|
||||
return fmt.Errorf("wrong size for signature: got %d, want 65", len(sig))
|
||||
if len(sig) != crypto.SignatureLength {
|
||||
return fmt.Errorf(
|
||||
"wrong size for signature: got %d, want %d",
|
||||
len(sig),
|
||||
crypto.SignatureLength,
|
||||
)
|
||||
}
|
||||
|
||||
r := new(big.Int).SetBytes(sig[:32])
|
||||
@@ -279,10 +282,13 @@ func (msg *MsgEthereumTx) GetFrom() sdk.AccAddress {
|
||||
return ethcmn.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")
|
||||
|
||||
+103
-62
@@ -2,38 +2,66 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestMsgEthereumTx(t *testing.T) {
|
||||
addr := GenerateEthAddress()
|
||||
type MsgsTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
msg := NewMsgEthereumTx(nil, 0, &addr, nil, 100000, nil, []byte("test"), nil)
|
||||
require.NotNil(t, msg)
|
||||
require.EqualValues(t, msg.Data.To, addr.Bytes())
|
||||
require.Equal(t, msg.Route(), RouterKey)
|
||||
require.Equal(t, msg.Type(), TypeMsgEthereumTx)
|
||||
require.NotNil(t, msg.To())
|
||||
require.Equal(t, msg.GetMsgs(), []sdk.Msg{msg})
|
||||
require.Panics(t, func() { msg.GetSigners() })
|
||||
require.Panics(t, func() { msg.GetSignBytes() })
|
||||
|
||||
msg = NewMsgEthereumTxContract(nil, 0, nil, 100000, nil, []byte("test"), nil)
|
||||
require.NotNil(t, msg)
|
||||
require.Nil(t, msg.Data.To)
|
||||
require.Nil(t, msg.To())
|
||||
signer keyring.Signer
|
||||
from ethcmn.Address
|
||||
to ethcmn.Address
|
||||
chainID *big.Int
|
||||
}
|
||||
|
||||
func TestMsgEthereumTxValidation(t *testing.T) {
|
||||
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.Bytes())
|
||||
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().Nil(msg.Data.To)
|
||||
suite.Require().Nil(msg.To())
|
||||
}
|
||||
|
||||
func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() {
|
||||
testCases := []struct {
|
||||
msg string
|
||||
amount *big.Int
|
||||
@@ -41,68 +69,81 @@ func TestMsgEthereumTxValidation(t *testing.T) {
|
||||
expectPass bool
|
||||
}{
|
||||
{msg: "pass", amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true},
|
||||
{msg: "invalid amount", amount: big.NewInt(-1), gasPrice: big.NewInt(100000), expectPass: false},
|
||||
{msg: "invalid gas price", amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: false},
|
||||
{msg: "invalid gas price", amount: big.NewInt(100), gasPrice: big.NewInt(0), expectPass: false},
|
||||
// NOTE: these can't be effectively tested because the SetBytes function from big.Int only sets
|
||||
// the absolute value
|
||||
{msg: "negative amount", amount: big.NewInt(-1), gasPrice: big.NewInt(1000), expectPass: true},
|
||||
{msg: "negative gas price", amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: true},
|
||||
{msg: "zero gas price", amount: big.NewInt(100), gasPrice: big.NewInt(0), expectPass: true},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
msg := NewMsgEthereumTx(nil, 0, nil, tc.amount, 0, tc.gasPrice, nil, nil)
|
||||
msg := NewMsgEthereumTx(suite.chainID, 0, nil, tc.amount, 0, tc.gasPrice, nil, nil)
|
||||
err := msg.ValidateBasic()
|
||||
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "valid test %d failed: %s", i, tc.msg)
|
||||
suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg)
|
||||
} else {
|
||||
require.NotNil(t, msg.ValidateBasic(), "invalid test %d passed: %s", i, tc.msg)
|
||||
suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgEthereumTxRLPSignBytes(t *testing.T) {
|
||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||
chainID := big.NewInt(3)
|
||||
|
||||
msg := NewMsgEthereumTx(chainID, 0, &addr, nil, 100000, nil, []byte("test"), nil)
|
||||
hash := msg.RLPSignBytes(chainID)
|
||||
require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash))
|
||||
}
|
||||
|
||||
func TestMsgEthereumTxRLPEncode(t *testing.T) {
|
||||
addr := ethcmn.BytesToAddress([]byte("test_address"))
|
||||
expMsg := NewMsgEthereumTx(big.NewInt(1), 0, &addr, nil, 100000, nil, []byte("test"), nil)
|
||||
func (suite *MsgsTestSuite) TestMsgEthereumTx_EncodeRLP() {
|
||||
expMsg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||
|
||||
raw, err := rlp.EncodeToBytes(&expMsg)
|
||||
require.NoError(t, err)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
msg := &MsgEthereumTx{}
|
||||
err = rlp.Decode(bytes.NewReader(raw), &msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expMsg.Data, msg.Data)
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(expMsg.Data, msg.Data)
|
||||
}
|
||||
|
||||
// func TestMsgEthereumTxSig(t *testing.T) {
|
||||
// chainID := big.NewInt(3)
|
||||
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) })
|
||||
}
|
||||
|
||||
// priv1, _ := ethsecp256k1.GenerateKey()
|
||||
// priv2, _ := ethsecp256k1.GenerateKey()
|
||||
// addr1 := ethcmn.BytesToAddress(priv1.PubKey().Address().Bytes())
|
||||
// addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes())
|
||||
func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() {
|
||||
msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil)
|
||||
|
||||
// // require valid signature passes validation
|
||||
// msg := NewMsgEthereumTx(nil, 0, &addr1, nil, 100000, nil, []byte("test"), nil)
|
||||
// err := msg.Sign(chainID, priv1.ToECDSA())
|
||||
// require.Nil(t, err)
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
"pass",
|
||||
func() { msg.From = suite.from.Hex() },
|
||||
true,
|
||||
},
|
||||
{
|
||||
"no from address ",
|
||||
func() { msg.From = "" },
|
||||
false,
|
||||
},
|
||||
{
|
||||
"from address ≠ signer address",
|
||||
func() { msg.From = suite.to.Hex() },
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
// signer, err := msg.VerifySig(chainID)
|
||||
// require.NoError(t, err)
|
||||
// require.Equal(t, addr1, signer)
|
||||
// require.NotEqual(t, addr2, signer)
|
||||
for i, tc := range testCases {
|
||||
tc.malleate()
|
||||
err := msg.Sign(suite.chainID, suite.signer)
|
||||
if tc.expectPass {
|
||||
suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg)
|
||||
|
||||
// // require invalid chain ID fail validation
|
||||
// msg = NewMsgEthereumTx(nil, 0, &addr1, nil, 100000, nil, []byte("test"), nil)
|
||||
// err = msg.Sign(chainID, priv1.ToECDSA())
|
||||
// require.Nil(t, err)
|
||||
tx := msg.AsTransaction()
|
||||
signer := ethtypes.NewEIP2930Signer(suite.chainID)
|
||||
|
||||
// signer, err = msg.VerifySig(big.NewInt(4))
|
||||
// require.Error(t, err)
|
||||
// require.Equal(t, ethcmn.Address{}, signer)
|
||||
// }
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
log "github.com/xlab/suplog"
|
||||
|
||||
@@ -13,25 +11,9 @@ import (
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// ValidateSigner attempts to validate a signer for a given slice of bytes over
|
||||
// which a signature and signer is given. An error is returned if address
|
||||
// derived from the signature and bytes signed does not match the given signer.
|
||||
func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error {
|
||||
pk, err := ethcrypto.SigToPub(signBytes, sig)
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to derive public key from signature")
|
||||
} else if ethcrypto.PubkeyToAddress(*pk) != signer {
|
||||
return fmt.Errorf("invalid signature for signer: %s", signer)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (hash ethcmn.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
_ = rlp.Encode(hasher, x)
|
||||
@@ -88,45 +70,6 @@ func DecodeTransactionLogs(data []byte) (TransactionLogs, error) {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Auxiliary
|
||||
|
||||
// recoverEthSig recovers a signature according to the Ethereum specification and
|
||||
// returns the sender or an error.
|
||||
//
|
||||
// Ref: Ethereum Yellow Paper (BYZANTIUM VERSION 69351d5) Appendix F
|
||||
// nolint: gocritic
|
||||
func recoverEthSig(R, S, Vb *big.Int, sigHash ethcmn.Hash) (ethcmn.Address, error) {
|
||||
if Vb.BitLen() > 8 {
|
||||
return ethcmn.Address{}, errors.New("invalid signature")
|
||||
}
|
||||
|
||||
V := byte(Vb.Uint64() - 27)
|
||||
if !ethcrypto.ValidateSignatureValues(V, R, S, true) {
|
||||
return ethcmn.Address{}, errors.New("invalid signature")
|
||||
}
|
||||
|
||||
// encode the signature in uncompressed format
|
||||
r, s := R.Bytes(), S.Bytes()
|
||||
sig := make([]byte, 65)
|
||||
|
||||
copy(sig[32-len(r):32], r)
|
||||
copy(sig[64-len(s):64], s)
|
||||
sig[64] = V
|
||||
|
||||
// recover the public key from the signature
|
||||
pub, err := ethcrypto.Ecrecover(sigHash[:], sig)
|
||||
if err != nil {
|
||||
return ethcmn.Address{}, err
|
||||
}
|
||||
|
||||
if len(pub) == 0 || pub[0] != 4 {
|
||||
return ethcmn.Address{}, errors.New("invalid public key")
|
||||
}
|
||||
|
||||
var addr ethcmn.Address
|
||||
copy(addr[:], ethcrypto.Keccak256(pub[1:])[12:])
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// IsEmptyHash returns true if the hash corresponds to an empty ethereum hex hash.
|
||||
func IsEmptyHash(hash string) bool {
|
||||
return bytes.Equal(ethcmn.HexToHash(hash).Bytes(), ethcmn.Hash{}.Bytes())
|
||||
|
||||
Reference in New Issue
Block a user