diff --git a/CHANGELOG.md b/CHANGELOG.md index aa4afd65..d6d06345 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (deps) [#1167](https://github.com/evmos/ethermint/pull/1167) Bump ibc-go to [`v4.0.0-rc2`](https://github.com/cosmos/ibc-go/releases/tag/v4.0.0-rc2) * (deps) [#1168](https://github.com/evmos/ethermint/pull/1168) Upgrade Cosmos SDK to `v0.46`. * (feemarket) [#1194](https://github.com/evmos/ethermint/pull/1194) Apply feemarket to native cosmos tx. +* (eth) [#1346](https://github.com/evmos/ethermint/pull/1346) Added support for `sdk.Dec` and `ed25519` type on eip712. ### API Breaking diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 580d84f2..094fa802 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -4,11 +4,13 @@ import ( "errors" "math/big" "strings" + "time" sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/types/tx/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/authz" sdk "github.com/cosmos/cosmos-sdk/types" @@ -16,6 +18,8 @@ import ( ethparams "github.com/ethereum/go-ethereum/params" "github.com/evmos/ethermint/tests" evmtypes "github.com/evmos/ethermint/x/evm/types" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) func (suite AnteTestSuite) TestAnteHandler() { @@ -322,6 +326,81 @@ func (suite AnteTestSuite) TestAnteHandler() { return txBuilder.GetTx() }, false, false, true, }, + { + "success- DeliverTx EIP712 create validator", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + amount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + txBuilder := suite.CreateTestEIP712MsgCreateValidator(from, privKey, "ethermint_9000-1", gas, amount) + return txBuilder.GetTx() + }, false, false, true, + }, + { + "success- DeliverTx EIP712 MsgSubmitProposal", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + gasAmount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + //reusing the gasAmount for deposit + deposit := sdk.NewCoins(coinAmount) + txBuilder := suite.CreateTestEIP712SubmitProposal(from, privKey, "ethermint_9000-1", gas, gasAmount, deposit) + return txBuilder.GetTx() + }, false, false, true, + }, + { + "success- DeliverTx EIP712 MsgGrant", + func() sdk.Tx { + from := acc.GetAddress() + grantee := sdk.AccAddress("_______grantee______") + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + gasAmount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + blockTime := time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC) + expiresAt := blockTime.Add(time.Hour) + msg, err := authz.NewMsgGrant( + from, grantee, &banktypes.SendAuthorization{SpendLimit: gasAmount}, &expiresAt, + ) + suite.Require().NoError(err) + return suite.CreateTestEIP712CosmosTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx() + }, false, false, true, + }, + + { + "success- DeliverTx EIP712 MsgGrantAllowance", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + gasAmount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + txBuilder := suite.CreateTestEIP712GrantAllowance(from, privKey, "ethermint_9000-1", gas, gasAmount) + return txBuilder.GetTx() + }, false, false, true, + }, + { + "success- DeliverTx EIP712 edit validator", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + amount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + txBuilder := suite.CreateTestEIP712MsgEditValidator(from, privKey, "ethermint_9000-1", gas, amount) + return txBuilder.GetTx() + }, false, false, true, + }, + { + "success- DeliverTx EIP712 submit evidence", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + amount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + txBuilder := suite.CreateTestEIP712MsgEditValidator(from, privKey, "ethermint_9000-1", gas, amount) + return txBuilder.GetTx() + }, false, false, true, + }, { "fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID", func() sdk.Tx { diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index 7a7b9b89..c297820e 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -1,6 +1,8 @@ package ante_test import ( + "encoding/json" + "fmt" "math" "math/big" "testing" @@ -27,11 +29,17 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + cryptocodec "github.com/evmos/ethermint/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + evtypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" + types5 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" "github.com/evmos/ethermint/app" ante "github.com/evmos/ethermint/app/ante" "github.com/evmos/ethermint/encoding" @@ -267,6 +275,107 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAdd return suite.CreateTestEIP712CosmosTxBuilder(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 { + // Build MsgCreateValidator + valAddr := sdk.ValAddress(from.Bytes()) + privEd := ed25519.GenPrivKey() + msgCreate, err := types3.NewMsgCreateValidator( + valAddr, + privEd.PubKey(), + sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)), + // TODO: can this values be empty strings? + types3.NewDescription("moniker", "indentity", "website", "security_contract", "details"), + types3.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + suite.Require().NoError(err) + return suite.CreateTestEIP712CosmosTxBuilder(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 { + proposal, ok := types5.ContentFromProposalType("My proposal", "My description", types5.ProposalTypeText) + suite.Require().True(ok) + msgSubmit, err := types5.NewMsgSubmitProposal(proposal, deposit, from) + suite.Require().NoError(err) + return suite.CreateTestEIP712CosmosTxBuilder(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 { + spendLimit := sdk.NewCoins(sdk.NewInt64Coin(evmtypes.DefaultEVMDenom, 10)) + threeHours := time.Now().Add(3 * time.Hour) + basic := &feegrant.BasicAllowance{ + SpendLimit: spendLimit, + Expiration: &threeHours, + } + granted := tests.GenerateAddress() + grantedAddr := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, granted.Bytes()) + msgGrant, err := feegrant.NewMsgGrantAllowance(basic, from, grantedAddr.GetAddress()) + suite.Require().NoError(err) + return suite.CreateTestEIP712CosmosTxBuilder(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 { + valAddr := sdk.ValAddress(from.Bytes()) + msgEdit := types3.NewMsgEditValidator( + valAddr, + types3.NewDescription("moniker", "identity", "website", "security_contract", "details"), + nil, + nil, + ) + return suite.CreateTestEIP712CosmosTxBuilder(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 { + pk := ed25519.GenPrivKey() + msgEvidence, err := evtypes.NewMsgSubmitEvidence(from, &evtypes.Equivocation{ + Height: 11, + Time: time.Now().UTC(), + Power: 100, + ConsensusAddress: pk.PubKey().Address().String(), + }) + suite.Require().NoError(err) + + return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence) +} + +// StdSignBytes returns the bytes to sign for a transaction. +func StdSignBytes(cdc *codec.LegacyAmino, chainID string, accnum uint64, sequence uint64, timeout uint64, fee legacytx.StdFee, msgs []sdk.Msg, memo string, tip *txtypes.Tip) []byte { + msgsBytes := make([]json.RawMessage, 0, len(msgs)) + for _, msg := range msgs { + legacyMsg, ok := msg.(legacytx.LegacyMsg) + if !ok { + panic(fmt.Errorf("expected %T when using amino JSON", (*legacytx.LegacyMsg)(nil))) + } + + msgsBytes = append(msgsBytes, json.RawMessage(legacyMsg.GetSignBytes())) + } + + var stdTip *legacytx.StdTip + if tip != nil { + if tip.Tipper == "" { + panic(fmt.Errorf("tipper cannot be empty")) + } + + stdTip = &legacytx.StdTip{Amount: tip.Amount, Tipper: tip.Tipper} + } + + bz, err := cdc.MarshalJSON(legacytx.StdSignDoc{ + AccountNumber: accnum, + ChainID: chainID, + Fee: json.RawMessage(fee.Bytes()), + Memo: memo, + Msgs: msgsBytes, + Sequence: sequence, + TimeoutHeight: timeout, + Tip: stdTip, + }) + if err != nil { + panic(err) + } + + return sdk.MustSortJSON(bz) +} + func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder( from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg, ) client.TxBuilder { @@ -281,6 +390,11 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder( // 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() diff --git a/ethereum/eip712/eip712.go b/ethereum/eip712/eip712.go index 8f27a721..50860093 100644 --- a/ethereum/eip712/eip712.go +++ b/ethereum/eip712/eip712.go @@ -10,6 +10,7 @@ import ( "time" sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -391,8 +392,11 @@ var ( addressType = reflect.TypeOf(common.Address{}) bigIntType = reflect.TypeOf(big.Int{}) cosmIntType = reflect.TypeOf(sdkmath.Int{}) + cosmDecType = reflect.TypeOf(sdk.Dec{}) cosmosAnyType = reflect.TypeOf(&codectypes.Any{}) timeType = reflect.TypeOf(time.Time{}) + + edType = reflect.TypeOf(ed25519.PubKey{}) ) // typToEth supports only basic types and arrays of basic types. @@ -438,6 +442,8 @@ func typToEth(typ reflect.Type) string { case reflect.Ptr: if typ.Elem().ConvertibleTo(bigIntType) || typ.Elem().ConvertibleTo(timeType) || + typ.Elem().ConvertibleTo(edType) || + typ.Elem().ConvertibleTo(cosmDecType) || typ.Elem().ConvertibleTo(cosmIntType) { return str } @@ -445,7 +451,9 @@ func typToEth(typ reflect.Type) string { if typ.ConvertibleTo(hashType) || typ.ConvertibleTo(addressType) || typ.ConvertibleTo(bigIntType) || + typ.ConvertibleTo(edType) || typ.ConvertibleTo(timeType) || + typ.ConvertibleTo(cosmDecType) || typ.ConvertibleTo(cosmIntType) { return str }