upgrade to ethermint v0.21.0 #99

Closed
0xmuralik wants to merge 384 commits from murali/update-fork into main
4 changed files with 219 additions and 75 deletions
Showing only changes of commit 9c41edb674 - Show all commits

View File

@ -387,7 +387,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
from, grantee, &banktypes.SendAuthorization{SpendLimit: gasAmount}, &expiresAt, from, grantee, &banktypes.SendAuthorization{SpendLimit: gasAmount}, &expiresAt,
) )
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx() return suite.CreateTestEIP712SingleMessageTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx()
}, false, false, true, }, false, false, true,
}, },
@ -457,6 +457,28 @@ func (suite AnteTestSuite) TestAnteHandler() {
return txBuilder.GetTx() return txBuilder.GetTx()
}, false, false, true, }, false, false, true,
}, },
{
"success- DeliverTx EIP712 Multiple MsgSend",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
amount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
txBuilder := suite.CreateTestEIP712MultipleMsgSend(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, true,
},
{
"fails - DeliverTx EIP712 Multiple Signers",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
amount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
txBuilder := suite.CreateTestEIP712MultipleSignerMsgs(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, false,
},
{ {
"fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID", "fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID",
func() sdk.Tx { func() sdk.Tx {

View File

@ -272,7 +272,7 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSend(from sdk.AccAddress
// Build MsgSend // Build MsgSend
recipient := sdk.AccAddress(common.Address{}.Bytes()) recipient := sdk.AccAddress(common.Address{}.Bytes())
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1)))) msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
} }
func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -280,7 +280,7 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAdd
valEthAddr := tests.GenerateAddress() valEthAddr := tests.GenerateAddress()
valAddr := sdk.ValAddress(valEthAddr.Bytes()) valAddr := sdk.ValAddress(valEthAddr.Bytes())
msgSend := types3.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20))) msgSend := types3.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20)))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
} }
func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -296,7 +296,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddre
sdk.OneInt(), sdk.OneInt(),
) )
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
} }
func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator2(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator2(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -313,7 +313,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator2(from sdk.AccAddr
sdk.OneInt(), sdk.OneInt(),
) )
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
} }
func (suite *AnteTestSuite) CreateTestEIP712SubmitProposal(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, deposit sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712SubmitProposal(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, deposit sdk.Coins) client.TxBuilder {
@ -321,7 +321,7 @@ func (suite *AnteTestSuite) CreateTestEIP712SubmitProposal(from sdk.AccAddress,
suite.Require().True(ok) suite.Require().True(ok)
msgSubmit, err := types5.NewMsgSubmitProposal(proposal, deposit, from) msgSubmit, err := types5.NewMsgSubmitProposal(proposal, deposit, from)
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSubmit) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSubmit)
} }
func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -335,7 +335,7 @@ func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress,
grantedAddr := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, granted.Bytes()) grantedAddr := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, granted.Bytes())
msgGrant, err := feegrant.NewMsgGrantAllowance(basic, from, grantedAddr.GetAddress()) msgGrant, err := feegrant.NewMsgGrantAllowance(basic, from, grantedAddr.GetAddress())
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgGrant) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgGrant)
} }
func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -346,7 +346,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress
nil, nil,
nil, nil,
) )
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit)
} }
func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -359,12 +359,12 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddres
}) })
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence)
} }
func (suite *AnteTestSuite) CreateTestEIP712MsgVoteV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712MsgVoteV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
msgVote := govtypes.NewMsgVote(from, 1, govtypes.VoteOption_VOTE_OPTION_YES, "") msgVote := govtypes.NewMsgVote(from, 1, govtypes.VoteOption_VOTE_OPTION_YES, "")
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgVote) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgVote)
} }
func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
@ -403,14 +403,28 @@ func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress
suite.Require().NoError(err) suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgProposal) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgProposal)
} }
func (suite *AnteTestSuite) CreateTestEIP712MsgExec(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { func (suite *AnteTestSuite) CreateTestEIP712MsgExec(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
recipient := sdk.AccAddress(common.Address{}.Bytes()) recipient := sdk.AccAddress(common.Address{}.Bytes())
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1)))) msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
msgExec := authz.NewMsgExec(from, []sdk.Msg{msgSend}) msgExec := authz.NewMsgExec(from, []sdk.Msg{msgSend})
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, &msgExec) return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, &msgExec)
}
func (suite *AnteTestSuite) CreateTestEIP712MultipleMsgSend(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
recipient := sdk.AccAddress(common.Address{}.Bytes())
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msgSend, msgSend, msgSend})
}
// Fails
func (suite *AnteTestSuite) CreateTestEIP712MultipleSignerMsgs(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
recipient := sdk.AccAddress(common.Address{}.Bytes())
msgSend1 := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
msgSend2 := types2.NewMsgSend(recipient, from, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msgSend1, msgSend2})
} }
// StdSignBytes returns the bytes to sign for a transaction. // StdSignBytes returns the bytes to sign for a transaction.
@ -451,8 +465,14 @@ func StdSignBytes(cdc *codec.LegacyAmino, chainID string, accnum uint64, sequenc
return sdk.MustSortJSON(bz) return sdk.MustSortJSON(bz)
} }
func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder( func (suite *AnteTestSuite) CreateTestEIP712SingleMessageTxBuilder(
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg, from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg,
) client.TxBuilder {
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msg})
}
func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msgs []sdk.Msg,
) client.TxBuilder { ) client.TxBuilder {
var err error var err error
@ -473,8 +493,8 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
fee := legacytx.NewStdFee(gas, gasAmount) fee := legacytx.NewStdFee(gas, gasAmount)
accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber() accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber()
data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, []sdk.Msg{msg}, "", nil) data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, msgs, "", nil)
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msg, data, &eip712.FeeDelegationOptions{ typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msgs[0], data, &eip712.FeeDelegationOptions{
FeePayer: from, FeePayer: from,
}) })
suite.Require().NoError(err) suite.Require().NoError(err)
@ -517,7 +537,7 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
err = builder.SetSignatures(sigsV2) err = builder.SetSignatures(sigsV2)
suite.Require().NoError(err) suite.Require().NoError(err)
err = builder.SetMsgs(msg) err = builder.SetMsgs(msgs...)
suite.Require().NoError(err) suite.Require().NoError(err)
return builder return builder

View File

@ -93,6 +93,9 @@ func (suite *EIP712TestSuite) TestEIP712SignatureVerification() {
signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
} }
// Fixed test address
testAddress := suite.createTestAddress()
testCases := []struct { testCases := []struct {
title string title string
chainId string chainId string
@ -176,7 +179,30 @@ func (suite *EIP712TestSuite) TestEIP712SignatureVerification() {
expectSuccess: true, expectSuccess: true,
}, },
{ {
title: "Fails - Two MsgVotes", title: "Succeeds - Two Single-Signer MsgDelegate",
fee: txtypes.Fee{
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
GasLimit: 20000,
},
memo: "",
msgs: []sdk.Msg{
stakingtypes.NewMsgDelegate(
testAddress,
sdk.ValAddress(suite.createTestAddress()),
suite.makeCoins("photon", math.NewInt(1))[0],
),
stakingtypes.NewMsgDelegate(
testAddress,
sdk.ValAddress(suite.createTestAddress()),
suite.makeCoins("photon", math.NewInt(5))[0],
),
},
accountNumber: 25,
sequence: 78,
expectSuccess: true,
},
{
title: "Fails - Two MsgVotes with Different Signers",
fee: txtypes.Fee{ fee: txtypes.Fee{
Amount: suite.makeCoins("aphoton", math.NewInt(2000)), Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
GasLimit: 20000, GasLimit: 20000,
@ -196,10 +222,22 @@ func (suite *EIP712TestSuite) TestEIP712SignatureVerification() {
}, },
accountNumber: 25, accountNumber: 25,
sequence: 78, sequence: 78,
expectSuccess: false, // Multiple messages are currently not allowed expectSuccess: false,
}, },
{ {
title: "Fails - MsgSend + MsgVote", title: "Fails - Empty transaction",
fee: txtypes.Fee{
Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
GasLimit: 20000,
},
memo: "",
msgs: []sdk.Msg{},
accountNumber: 25,
sequence: 78,
expectSuccess: false,
},
{
title: "Fails - Single-Signer MsgSend + MsgVote",
fee: txtypes.Fee{ fee: txtypes.Fee{
Amount: suite.makeCoins("aphoton", math.NewInt(2000)), Amount: suite.makeCoins("aphoton", math.NewInt(2000)),
GasLimit: 20000, GasLimit: 20000,
@ -207,12 +245,12 @@ func (suite *EIP712TestSuite) TestEIP712SignatureVerification() {
memo: "", memo: "",
msgs: []sdk.Msg{ msgs: []sdk.Msg{
govtypes.NewMsgVote( govtypes.NewMsgVote(
suite.createTestAddress(), testAddress,
5, 5,
govtypes.OptionNo, govtypes.OptionNo,
), ),
banktypes.NewMsgSend( banktypes.NewMsgSend(
suite.createTestAddress(), testAddress,
suite.createTestAddress(), suite.createTestAddress(),
suite.makeCoins("photon", math.NewInt(50)), suite.makeCoins("photon", math.NewInt(50)),
), ),

View File

@ -1,13 +1,14 @@
package eip712 package eip712
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/simapp/params"
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
cosmosTypes "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
txTypes "github.com/cosmos/cosmos-sdk/types/tx" txTypes "github.com/cosmos/cosmos-sdk/types/tx"
apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes" apitypes "github.com/ethereum/go-ethereum/signer/core/apitypes"
@ -16,9 +17,14 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
) )
type aminoMessage struct {
Type string `json:"type"`
Value interface{} `json:"value"`
}
var ( var (
ethermintProtoCodec codec.ProtoCodecMarshaler protoCodec codec.ProtoCodecMarshaler
ethermintAminoCodec *codec.LegacyAmino aminoCodec *codec.LegacyAmino
) )
// SetEncodingConfig set the encoding config to the singleton codecs (Amino and Protobuf). // SetEncodingConfig set the encoding config to the singleton codecs (Amino and Protobuf).
@ -26,8 +32,8 @@ var (
// populated with all relevant message types. As a result, we must call this method on app // populated with all relevant message types. As a result, we must call this method on app
// initialization with the app's encoding config. // initialization with the app's encoding config.
func SetEncodingConfig(cfg params.EncodingConfig) { func SetEncodingConfig(cfg params.EncodingConfig) {
ethermintAminoCodec = cfg.Amino aminoCodec = cfg.Amino
ethermintProtoCodec = codec.NewProtoCodec(cfg.InterfaceRegistry) protoCodec = codec.NewProtoCodec(cfg.InterfaceRegistry)
} }
// Get the EIP-712 object hash for the given SignDoc bytes by first decoding the bytes into // Get the EIP-712 object hash for the given SignDoc bytes by first decoding the bytes into
@ -59,67 +65,69 @@ func GetEIP712TypedDataForMsg(signDocBytes []byte) (apitypes.TypedData, error) {
// Attempt to decode as both Amino and Protobuf since the message format is unknown. // Attempt to decode as both Amino and Protobuf since the message format is unknown.
// If either decode works, we can move forward with the corresponding typed data. // If either decode works, we can move forward with the corresponding typed data.
typedDataAmino, errAmino := decodeAminoSignDoc(signDocBytes) typedDataAmino, errAmino := decodeAminoSignDoc(signDocBytes)
if errAmino == nil && verifyEIP712Payload(typedDataAmino) { if errAmino == nil && isValidEIP712Payload(typedDataAmino) {
return typedDataAmino, nil return typedDataAmino, nil
} }
typedDataProtobuf, errProtobuf := decodeProtobufSignDoc(signDocBytes) typedDataProtobuf, errProtobuf := decodeProtobufSignDoc(signDocBytes)
if errProtobuf == nil && verifyEIP712Payload(typedDataProtobuf) { if errProtobuf == nil && isValidEIP712Payload(typedDataProtobuf) {
return typedDataProtobuf, nil return typedDataProtobuf, nil
} }
return apitypes.TypedData{}, fmt.Errorf("could not decode sign doc as either Amino or Protobuf. amino: %v protobuf: %v", errAmino, errProtobuf) return apitypes.TypedData{}, fmt.Errorf("could not decode sign doc as either Amino or Protobuf.\n amino: %v\n protobuf: %v", errAmino, errProtobuf)
} }
// verifyEIP712Payload ensures that the given TypedData does not contain empty fields from // isValidEIP712Payload ensures that the given TypedData does not contain empty fields from
// an improper initialization. // an improper initialization.
func verifyEIP712Payload(typedData apitypes.TypedData) bool { func isValidEIP712Payload(typedData apitypes.TypedData) bool {
return len(typedData.Message) != 0 && len(typedData.Types) != 0 && typedData.PrimaryType != "" && typedData.Domain != apitypes.TypedDataDomain{} return len(typedData.Message) != 0 && len(typedData.Types) != 0 && typedData.PrimaryType != "" && typedData.Domain != apitypes.TypedDataDomain{}
} }
// Attempt to decode the SignDoc bytes as an Amino SignDoc and return an error on failure // decodeAminoSignDoc attempts to decode the provided sign doc (bytes) as an Amino payload
// and returns a signable EIP-712 TypedData object.
func decodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) { func decodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
var aminoDoc legacytx.StdSignDoc var aminoDoc legacytx.StdSignDoc
if err := aminoCodec.UnmarshalJSON(signDocBytes, &aminoDoc); err != nil {
if err := ethermintAminoCodec.UnmarshalJSON(signDocBytes, &aminoDoc); err != nil {
return apitypes.TypedData{}, err return apitypes.TypedData{}, err
} }
// Unwrap fees
var fees legacytx.StdFee var fees legacytx.StdFee
if err := ethermintAminoCodec.UnmarshalJSON(aminoDoc.Fee, &fees); err != nil { if err := aminoCodec.UnmarshalJSON(aminoDoc.Fee, &fees); err != nil {
return apitypes.TypedData{}, err return apitypes.TypedData{}, err
} }
if len(aminoDoc.Msgs) != 1 { // Validate payload messages
return apitypes.TypedData{}, fmt.Errorf("invalid number of messages in SignDoc, expected 1 but got %v", len(aminoDoc.Msgs)) msgs := make([]sdk.Msg, len(aminoDoc.Msgs))
for i, jsonMsg := range aminoDoc.Msgs {
var m sdk.Msg
if err := aminoCodec.UnmarshalJSON(jsonMsg, &m); err != nil {
return apitypes.TypedData{}, fmt.Errorf("failed to unmarshal sign doc message: %w", err)
}
msgs[i] = m
} }
var msg cosmosTypes.Msg if err := validatePayloadMessages(msgs); err != nil {
if err := ethermintAminoCodec.UnmarshalJSON(aminoDoc.Msgs[0], &msg); err != nil { return apitypes.TypedData{}, err
return apitypes.TypedData{}, fmt.Errorf("failed to unmarshal first message: %w", err)
} }
// By default, use first address in list of signers to cover fee // Use first message for fee payer and type inference
// Currently, support only one signer msg := msgs[0]
if len(msg.GetSigners()) != 1 {
return apitypes.TypedData{}, errors.New("expected exactly one signer for message") // By convention, the fee payer is the first address in the list of signers.
}
feePayer := msg.GetSigners()[0] feePayer := msg.GetSigners()[0]
feeDelegation := &FeeDelegationOptions{ feeDelegation := &FeeDelegationOptions{
FeePayer: feePayer, FeePayer: feePayer,
} }
// Parse ChainID
chainID, err := ethermint.ParseChainID(aminoDoc.ChainID) chainID, err := ethermint.ParseChainID(aminoDoc.ChainID)
if err != nil { if err != nil {
return apitypes.TypedData{}, errors.New("invalid chain ID passed as argument") return apitypes.TypedData{}, errors.New("invalid chain ID passed as argument")
} }
typedData, err := WrapTxToTypedData( typedData, err := WrapTxToTypedData(
ethermintProtoCodec, protoCodec,
chainID.Uint64(), chainID.Uint64(),
msg, msg,
signDocBytes, // Amino StdSignDocBytes signDocBytes,
feeDelegation, feeDelegation,
) )
if err != nil { if err != nil {
@ -129,21 +137,19 @@ func decodeAminoSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
return typedData, nil return typedData, nil
} }
// Attempt to decode the SignDoc bytes as a Protobuf SignDoc and return an error on failure // decodeProtobufSignDoc attempts to decode the provided sign doc (bytes) as a Protobuf payload
// and returns a signable EIP-712 TypedData object.
func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) { func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
// Decode sign doc
signDoc := &txTypes.SignDoc{} signDoc := &txTypes.SignDoc{}
if err := signDoc.Unmarshal(signDocBytes); err != nil { if err := signDoc.Unmarshal(signDocBytes); err != nil {
return apitypes.TypedData{}, err return apitypes.TypedData{}, err
} }
// Decode auth info
authInfo := &txTypes.AuthInfo{} authInfo := &txTypes.AuthInfo{}
if err := authInfo.Unmarshal(signDoc.AuthInfoBytes); err != nil { if err := authInfo.Unmarshal(signDoc.AuthInfoBytes); err != nil {
return apitypes.TypedData{}, err return apitypes.TypedData{}, err
} }
// Decode body
body := &txTypes.TxBody{} body := &txTypes.TxBody{}
if err := body.Unmarshal(signDoc.BodyBytes); err != nil { if err := body.Unmarshal(signDoc.BodyBytes); err != nil {
return apitypes.TypedData{}, err return apitypes.TypedData{}, err
@ -154,60 +160,60 @@ func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
return apitypes.TypedData{}, errors.New("body contains unsupported fields: TimeoutHeight, ExtensionOptions, or NonCriticalExtensionOptions") return apitypes.TypedData{}, errors.New("body contains unsupported fields: TimeoutHeight, ExtensionOptions, or NonCriticalExtensionOptions")
} }
// Verify single message if len(authInfo.SignerInfos) != 1 {
if len(body.Messages) != 1 { return apitypes.TypedData{}, fmt.Errorf("invalid number of signer infos provided, expected 1 got %v", len(authInfo.SignerInfos))
return apitypes.TypedData{}, fmt.Errorf("invalid number of messages, expected 1 got %v", len(body.Messages))
} }
// Decode signer info (single signer for now) // Validate payload messages
msgs := make([]sdk.Msg, len(body.Messages))
for i, protoMsg := range body.Messages {
var m sdk.Msg
if err := protoCodec.UnpackAny(protoMsg, &m); err != nil {
return apitypes.TypedData{}, fmt.Errorf("could not unpack message object with error %w", err)
}
msgs[i] = m
}
if err := validatePayloadMessages(msgs); err != nil {
return apitypes.TypedData{}, err
}
// Use first message for fee payer and type inference
msg := msgs[0]
signerInfo := authInfo.SignerInfos[0] signerInfo := authInfo.SignerInfos[0]
// Parse ChainID
chainID, err := ethermint.ParseChainID(signDoc.ChainId) chainID, err := ethermint.ParseChainID(signDoc.ChainId)
if err != nil { if err != nil {
return apitypes.TypedData{}, fmt.Errorf("invalid chain ID passed as argument: %w", err) return apitypes.TypedData{}, fmt.Errorf("invalid chain ID passed as argument: %w", err)
} }
// Create StdFee
stdFee := &legacytx.StdFee{ stdFee := &legacytx.StdFee{
Amount: authInfo.Fee.Amount, Amount: authInfo.Fee.Amount,
Gas: authInfo.Fee.GasLimit, Gas: authInfo.Fee.GasLimit,
} }
// Parse Message (single message only)
var msg cosmosTypes.Msg
if err := ethermintProtoCodec.UnpackAny(body.Messages[0], &msg); err != nil {
return apitypes.TypedData{}, fmt.Errorf("could not unpack message object with error %w", err)
}
// Verify single signer (single signer for now)
if len(msg.GetSigners()) != 1 {
return apitypes.TypedData{}, fmt.Errorf("invalid number of signers, expected 1 got %v", len(authInfo.SignerInfos))
}
// Init fee payer
feePayer := msg.GetSigners()[0] feePayer := msg.GetSigners()[0]
feeDelegation := &FeeDelegationOptions{ feeDelegation := &FeeDelegationOptions{
FeePayer: feePayer, FeePayer: feePayer,
} }
// Get tip
tip := authInfo.Tip tip := authInfo.Tip
// Create Legacy SignBytes (expected type for WrapTxToTypedData) // WrapTxToTypedData expects the payload as an Amino Sign Doc
signBytes := legacytx.StdSignBytes( signBytes := legacytx.StdSignBytes(
signDoc.ChainId, signDoc.ChainId,
signDoc.AccountNumber, signDoc.AccountNumber,
signerInfo.Sequence, signerInfo.Sequence,
body.TimeoutHeight, body.TimeoutHeight,
*stdFee, *stdFee,
[]cosmosTypes.Msg{msg}, msgs,
body.Memo, body.Memo,
tip, tip,
) )
typedData, err := WrapTxToTypedData( typedData, err := WrapTxToTypedData(
ethermintProtoCodec, protoCodec,
chainID.Uint64(), chainID.Uint64(),
msg, msg,
signBytes, signBytes,
@ -219,3 +225,61 @@ func decodeProtobufSignDoc(signDocBytes []byte) (apitypes.TypedData, error) {
return typedData, nil return typedData, nil
} }
// validatePayloadMessages ensures that the transaction messages can be represented in an EIP-712
// encoding by checking that messages exist, are of the same type, and share a single signer.
func validatePayloadMessages(msgs []sdk.Msg) error {
if len(msgs) == 0 {
return errors.New("unable to build EIP-712 payload: transaction does contain any messages")
}
var msgType string
var msgSigner sdk.AccAddress
for i, m := range msgs {
t, err := getMsgType(m)
if err != nil {
return err
}
if len(m.GetSigners()) != 1 {
return errors.New("unable to build EIP-712 payload: expect exactly 1 signer")
}
if i == 0 {
msgType = t
msgSigner = m.GetSigners()[0]
continue
}
if t != msgType {
return errors.New("unable to build EIP-712 payload: different types of messages detected")
}
if !msgSigner.Equals(m.GetSigners()[0]) {
return errors.New("unable to build EIP-712 payload: multiple signers detected")
}
}
return nil
}
// getMsgType returns the message type prefix for the given Cosmos SDK Msg
func getMsgType(msg sdk.Msg) (string, error) {
jsonBytes, err := aminoCodec.MarshalJSON(msg)
if err != nil {
return "", err
}
var jsonMsg aminoMessage
if err := json.Unmarshal(jsonBytes, &jsonMsg); err != nil {
return "", err
}
// Verify Type was successfully filled in
if jsonMsg.Type == "" {
return "", errors.New("could not decode message: type is missing")
}
return jsonMsg.Type, nil
}