evm: fix signature verification (#61)

* evm: fix sig verification

* fixes

* test fixes
This commit is contained in:
Federico Kunze
2021-06-01 13:14:33 -04:00
committed by GitHub
parent abcfc9a6ba
commit 8e7ebe80e9
8 changed files with 169 additions and 92 deletions
+16 -22
View File
@@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/common"
ethcmn "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
@@ -21,7 +22,6 @@ import (
"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,10 @@ type EvmTestSuite struct {
codec codec.BinaryMarshaler
chainID *big.Int
signer keyring.Signer
from ethcmn.Address
to sdk.AccAddress
signer keyring.Signer
ethSigner ethtypes.Signer
from ethcmn.Address
to sdk.AccAddress
}
func (suite *EvmTestSuite) SetupTest() {
@@ -61,6 +62,7 @@ func (suite *EvmTestSuite) SetupTest() {
suite.Require().NoError(err)
suite.signer = tests.NewSigner(privKey)
suite.ethSigner = ethtypes.LatestSignerForChainID(suite.chainID)
suite.from = ethcmn.BytesToAddress(privKey.PubKey().Address().Bytes())
}
@@ -86,12 +88,8 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
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.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
},
true,
@@ -101,12 +99,8 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() {
func() {
tx = types.NewMsgEthereumTxContract(suite.chainID, 0, big.NewInt(100), 0, big.NewInt(10000), nil, nil)
// 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.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
},
false,
@@ -181,7 +175,7 @@ func (suite *EvmTestSuite) TestHandlerLogs() {
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err := tx.Sign(suite.chainID, suite.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
result, err := suite.handler(suite.ctx, tx)
@@ -212,7 +206,7 @@ func (suite *EvmTestSuite) TestQueryTxLogs() {
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err := tx.Sign(suite.chainID, suite.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
result, err := suite.handler(suite.ctx, tx)
@@ -297,7 +291,7 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err := tx.Sign(suite.chainID, suite.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
result, err := suite.handler(suite.ctx, tx)
@@ -316,7 +310,7 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err = tx.Sign(suite.chainID, suite.signer)
err = tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
result, err = suite.handler(suite.ctx, tx)
@@ -329,7 +323,7 @@ func (suite *EvmTestSuite) TestDeployAndCallContract() {
bytecode = common.FromHex("0x893d20e8")
tx = types.NewMsgEthereumTx(suite.chainID, 2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err = tx.Sign(suite.chainID, suite.signer)
err = tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
result, err = suite.handler(suite.ctx, tx)
@@ -351,7 +345,7 @@ func (suite *EvmTestSuite) TestSendTransaction() {
// send simple value transfer with gasLimit=21000
tx := types.NewMsgEthereumTx(suite.chainID, 1, &ethcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil)
tx.From = suite.from.String()
err := tx.Sign(suite.chainID, suite.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
result, err := suite.handler(suite.ctx, tx)
@@ -423,7 +417,7 @@ func (suite *EvmTestSuite) TestOutOfGasWhenDeployContract() {
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err := tx.Sign(suite.chainID, suite.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
@@ -452,7 +446,7 @@ func (suite *EvmTestSuite) TestErrorWhenDeployContract() {
tx := types.NewMsgEthereumTx(suite.chainID, 1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode, nil)
tx.From = suite.from.String()
err := tx.Sign(suite.chainID, suite.signer)
err := tx.Sign(suite.ethSigner, suite.signer)
suite.Require().NoError(err)
snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB)
+41 -40
View File
@@ -14,7 +14,6 @@ import (
ethcmn "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -89,6 +88,33 @@ func newMsgEthereumTx(
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(),
}
msg.Size_ = float64(tx.Size())
}
// Route returns the route value of an MsgEthereumTx.
func (msg MsgEthereumTx) Route() string { return RouterKey }
@@ -190,22 +216,19 @@ func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash {
// EncodeRLP implements the rlp.Encoder interface.
func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &msg.Data)
tx := msg.AsTransaction()
return tx.EncodeRLP(w)
}
// DecodeRLP implements the rlp.Decoder interface.
func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error {
_, size, err := s.Kind()
if err != nil {
// return error if stream is too large
func (msg *MsgEthereumTx) DecodeRLP(stream *rlp.Stream) error {
tx := &ethtypes.Transaction{}
if err := tx.DecodeRLP(stream); err != nil {
return err
}
if err := s.Decode(&msg.Data); err != nil {
return err
}
msg.fromEthereumTx(tx)
msg.Size_ = float64(ethcmn.StorageSize(rlp.ListSize(size)))
return nil
}
@@ -216,48 +239,26 @@ func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error {
// 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(chainID *big.Int, signer keyring.Signer) error {
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")
}
if chainID == nil {
return fmt.Errorf("chain id cannot be nil")
}
tx := msg.AsTransaction()
txHash := ethSigner.Hash(tx)
txHash := msg.RLPSignBytes(chainID)
sig, _, err := signer.SignByAddress(from, txHash[:])
sig, _, err := keyringSigner.SignByAddress(from, txHash.Bytes())
if err != nil {
return err
}
if len(sig) != crypto.SignatureLength {
return fmt.Errorf(
"wrong size for signature: got %d, want %d",
len(sig),
crypto.SignatureLength,
)
tx, err = tx.WithSignature(ethSigner, sig)
if err != nil {
return err
}
r := new(big.Int).SetBytes(sig[:32])
s := new(big.Int).SetBytes(sig[32:64])
var v *big.Int
if chainID.Sign() == 0 {
v = new(big.Int).SetBytes([]byte{sig[64] + 27})
} else {
v = big.NewInt(int64(sig[64] + 35))
chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2))
v.Add(v, chainIDMul)
}
msg.Data.V = v.Bytes()
msg.Data.R = r.Bytes()
msg.Data.S = s.Bytes()
msg.fromEthereumTx(tx)
return nil
}
+26 -2
View File
@@ -113,21 +113,44 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() {
testCases := []struct {
msg string
ethSigner ethtypes.Signer
malleate func()
expectPass bool
}{
{
"pass",
"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,
},
@@ -135,7 +158,8 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() {
for i, tc := range testCases {
tc.malleate()
err := msg.Sign(suite.chainID, suite.signer)
err := msg.Sign(tc.ethSigner, suite.signer)
if tc.expectPass {
suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg)