forked from cerc-io/laconicd-deprecated
evm: fix signature verification (#61)
* evm: fix sig verification * fixes * test fixes
This commit is contained in:
+16
-22
@@ -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, ðcmn.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
@@ -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 := ðtypes.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
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user