Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Migrate updated EIP-712 algorithm from Evmos #1746

Merged
merged 5 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/ante/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
from, grantee, &banktypes.SendAuthorization{SpendLimit: gasAmount}, &expiresAt,
)
suite.Require().NoError(err)
return suite.CreateTestEIP712SingleMessageTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx()
return suite.CreateTestEIP712SingleMessageTxBuilder(privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx()
}, false, false, true,
},

Expand Down
2 changes: 1 addition & 1 deletion app/ante/eip712.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ func VerifySignature(
FeePayer: feePayer,
}

typedData, err := eip712.WrapTxToTypedData(ethermintCodec, extOpt.TypedDataChainID, msgs[0], txBytes, feeDelegation)
typedData, err := eip712.LegacyWrapTxToTypedData(ethermintCodec, extOpt.TypedDataChainID, msgs[0], txBytes, feeDelegation)
if err != nil {
return errorsmod.Wrap(err, "failed to create EIP-712 typed data from tx")
}
Expand Down
150 changes: 59 additions & 91 deletions app/ante/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
"github.com/evmos/ethermint/ethereum/eip712"
"github.com/evmos/ethermint/testutil"
"github.com/evmos/ethermint/types"
utiltx "github.com/evmos/ethermint/testutil/tx"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
Expand All @@ -40,7 +38,6 @@ import (
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
authz "github.com/cosmos/cosmos-sdk/x/authz"
cryptocodec "github.com/evmos/ethermint/crypto/codec"
"github.com/evmos/ethermint/crypto/ethsecp256k1"

"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
Expand All @@ -63,15 +60,17 @@ import (
type AnteTestSuite struct {
suite.Suite

ctx sdk.Context
app *app.EthermintApp
clientCtx client.Context
anteHandler sdk.AnteHandler
priv cryptotypes.PrivKey
ethSigner ethtypes.Signer
enableFeemarket bool
enableLondonHF bool
evmParamsOption func(*evmtypes.Params)
ctx sdk.Context
app *app.EthermintApp
clientCtx client.Context
anteHandler sdk.AnteHandler
priv cryptotypes.PrivKey
ethSigner ethtypes.Signer
enableFeemarket bool
enableLondonHF bool
evmParamsOption func(*evmtypes.Params)
useLegacyEIP712Extension bool
useLegacyEIP712TypedData bool
}

const TestGasLimit uint64 = 100000
Expand Down Expand Up @@ -173,6 +172,20 @@ func TestAnteTestSuite(t *testing.T) {
suite.Run(t, &AnteTestSuite{
enableLondonHF: true,
})

austinchandra marked this conversation as resolved.
Show resolved Hide resolved
// Re-run the tests with EIP-712 Legacy encodings to ensure backwards compatibility.
// LegacyEIP712Extension should not be run with current TypedData encodings, since they are not compatible.
suite.Run(t, &AnteTestSuite{
enableLondonHF: true,
useLegacyEIP712Extension: true,
useLegacyEIP712TypedData: true,
})

suite.Run(t, &AnteTestSuite{
enableLondonHF: true,
useLegacyEIP712Extension: false,
useLegacyEIP712TypedData: true,
})
}

func (s *AnteTestSuite) BuildTestEthTx(
Expand Down Expand Up @@ -303,15 +316,15 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSend(from sdk.AccAddress
// Build MsgSend
recipient := sdk.AccAddress(common.Address{}.Bytes())
msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgSend)
}

func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
// Build MsgSend
valEthAddr := tests.GenerateAddress()
valAddr := sdk.ValAddress(valEthAddr.Bytes())
msgSend := stakingtypes.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20)))
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgSend)
}

func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
Expand All @@ -327,7 +340,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddre
sdk.OneInt(),
)
suite.Require().NoError(err)
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgCreate)
}

func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator2(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
Expand All @@ -344,15 +357,15 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator2(from sdk.AccAddr
sdk.OneInt(),
)
suite.Require().NoError(err)
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
return suite.CreateTestEIP712SingleMessageTxBuilder(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 {
proposal, ok := govtypes.ContentFromProposalType("My proposal", "My description", govtypes.ProposalTypeText)
suite.Require().True(ok)
msgSubmit, err := govtypes.NewMsgSubmitProposal(proposal, deposit, from)
suite.Require().NoError(err)
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgSubmit)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgSubmit)
}

func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
Expand All @@ -366,7 +379,7 @@ func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress,
grantedAddr := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, granted.Bytes())
msgGrant, err := feegrant.NewMsgGrantAllowance(basic, from, grantedAddr.GetAddress())
suite.Require().NoError(err)
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgGrant)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgGrant)
}

func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
Expand All @@ -377,7 +390,7 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress
nil,
nil,
)
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgEdit)
}

func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
Expand All @@ -390,12 +403,12 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddres
})
suite.Require().NoError(err)

return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgEvidence)
}

func (suite *AnteTestSuite) CreateTestEIP712MsgVoteV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
msgVote := govtypesv1.NewMsgVote(from, 1, govtypesv1.VoteOption_VOTE_OPTION_YES, "")
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgVote)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgVote)
}

func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
Expand Down Expand Up @@ -434,28 +447,28 @@ func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress

suite.Require().NoError(err)

return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, msgProposal)
return suite.CreateTestEIP712SingleMessageTxBuilder(priv, chainId, gas, gasAmount, msgProposal)
}

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())
msgSend := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
msgExec := authz.NewMsgExec(from, []sdk.Msg{msgSend})
return suite.CreateTestEIP712SingleMessageTxBuilder(from, priv, chainId, gas, gasAmount, &msgExec)
return suite.CreateTestEIP712SingleMessageTxBuilder(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 := banktypes.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})
return suite.CreateTestEIP712CosmosTxBuilder(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 := banktypes.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
msgSend2 := banktypes.NewMsgSend(recipient, from, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, []sdk.Msg{msgSend1, msgSend2})
return suite.CreateTestEIP712CosmosTxBuilder(priv, chainId, gas, gasAmount, []sdk.Msg{msgSend1, msgSend2})
}

// StdSignBytes returns the bytes to sign for a transaction.
Expand Down Expand Up @@ -497,80 +510,35 @@ func StdSignBytes(cdc *codec.LegacyAmino, chainID string, accnum uint64, sequenc
}

func (suite *AnteTestSuite) CreateTestEIP712SingleMessageTxBuilder(
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg,
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})
return suite.CreateTestEIP712CosmosTxBuilder(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,
priv cryptotypes.PrivKey, chainID string, gas uint64, gasAmount sdk.Coins, msgs []sdk.Msg,
) client.TxBuilder {
var err error

nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, from)
suite.Require().NoError(err)

pc, err := types.ParseChainID(chainId)
suite.Require().NoError(err)
ethChainId := pc.Uint64()

// GenerateTypedData TypedData
var ethermintCodec codec.ProtoCodecMarshaler
registry := codectypes.NewInterfaceRegistry()
types.RegisterInterfaces(registry)
ethermintCodec = codec.NewProtoCodec(registry)
cryptocodec.RegisterInterfaces(registry)

fee := legacytx.NewStdFee(gas, gasAmount)
accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber()

data := legacytx.StdSignBytes(chainId, accNumber, nonce, 0, fee, msgs, "", nil)
typedData, err := eip712.WrapTxToTypedData(ethermintCodec, ethChainId, msgs[0], data, &eip712.FeeDelegationOptions{
FeePayer: from,
})
suite.Require().NoError(err)

sigHash, _, err := apitypes.TypedDataAndHash(typedData)
suite.Require().NoError(err)

// Sign typedData
keyringSigner := tests.NewSigner(priv)
signature, pubKey, err := keyringSigner.SignByAddress(from, sigHash)
suite.Require().NoError(err)
signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper

// Add ExtensionOptionsWeb3Tx extension
var option *codectypes.Any
option, err = codectypes.NewAnyWithValue(&types.ExtensionOptionsWeb3Tx{
FeePayer: from.String(),
TypedDataChainID: ethChainId,
FeePayerSig: signature,
})
suite.Require().NoError(err)

suite.clientCtx.TxConfig.SignModeHandler()
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
suite.Require().True(ok)

builder.SetExtensionOptions(option)
builder.SetFeeAmount(gasAmount)
builder.SetGasLimit(gas)

sigsV2 := signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
},
Sequence: nonce,
config := suite.clientCtx.TxConfig
cosmosTxArgs := utiltx.CosmosTxArgs{
TxCfg: config,
Priv: priv,
ChainID: chainID,
Gas: gas,
Fees: gasAmount,
Msgs: msgs,
}

err = builder.SetSignatures(sigsV2)
suite.Require().NoError(err)
builder, err := utiltx.PrepareEIP712CosmosTx(
suite.ctx,
suite.app,
utiltx.EIP712TxArgs{
CosmosTxArgs: cosmosTxArgs,
UseLegacyExtension: suite.useLegacyEIP712Extension,
UseLegacyTypedData: suite.useLegacyEIP712TypedData,
},
)

err = builder.SetMsgs(msgs...)
suite.Require().NoError(err)

return builder
}

Expand Down
12 changes: 11 additions & 1 deletion crypto/ethsecp256k1/ethsecp256k1.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,17 @@ func (pubKey PubKey) verifySignatureAsEIP712(msg, sig []byte) bool {
return false
}

return pubKey.verifySignatureECDSA(eip712Bytes, sig)
if pubKey.verifySignatureECDSA(eip712Bytes, sig) {
return true
}

// Try verifying the signature using the legacy EIP-712 encoding
legacyEIP712Bytes, err := eip712.LegacyGetEIP712BytesForMsg(msg)
if err != nil {
return false
}

return pubKey.verifySignatureECDSA(legacyEIP712Bytes, sig)
}

// Perform standard ECDSA signature verification for the given raw bytes and signature.
Expand Down
34 changes: 34 additions & 0 deletions ethereum/eip712/domain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2023 Evmos Foundation
// This file is part of Evmos' Ethermint library.
//
// The Ethermint library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ethermint library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE
package eip712

import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/signer/core/apitypes"
)

// createEIP712Domain creates the typed data domain for the given chainID.
func createEIP712Domain(chainID uint64) apitypes.TypedDataDomain {
domain := apitypes.TypedDataDomain{
Name: "Cosmos Web3",
Version: "1.0.0",
ChainId: math.NewHexOrDecimal256(int64(chainID)), // #nosec G701

Check failure

Code scanning / gosec

Potential integer overflow by integer type conversion

Potential integer overflow by integer type conversion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ignore not working?

VerifyingContract: "cosmos",
Salt: "0",
}

return domain
}
Loading