diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f6e6c4d70..80f814a4e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (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. +* (eth) [#1430](https://github.com/evmos/ethermint/pull/1430) Added support for array of type `Any` on eip712.  * (ante) [1460](https://github.com/evmos/ethermint/pull/1460) Add KV Gas config on ethereum Txs. ### API Breaking diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 689f56cef4..34be0e0c9b 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -409,7 +409,29 @@ func (suite AnteTestSuite) TestAnteHandler() { 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) + txBuilder := suite.CreateTestEIP712MsgSubmitEvidence(from, privKey, "ethermint_9000-1", gas, amount) + return txBuilder.GetTx() + }, false, false, true, + }, + { + "success- DeliverTx EIP712 submit proposal v1", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + amount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + txBuilder := suite.CreateTestEIP712SubmitProposalV1(from, privKey, "ethermint_9000-1", gas, amount) + return txBuilder.GetTx() + }, false, false, true, + }, + { + "success- DeliverTx EIP712 MsgExec", + func() sdk.Tx { + from := acc.GetAddress() + coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)) + amount := sdk.NewCoins(coinAmount) + gas := uint64(200000) + txBuilder := suite.CreateTestEIP712MsgExec(from, privKey, "ethermint_9000-1", gas, amount) return txBuilder.GetTx() }, false, false, true, }, diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index 14b79b581c..6f9eb80137 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -37,12 +37,14 @@ import ( 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" + 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" evtypes "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/cosmos/cosmos-sdk/x/feegrant" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" types5 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" "github.com/evmos/ethermint/app" ante "github.com/evmos/ethermint/app/ante" @@ -343,6 +345,52 @@ func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddres return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence) } +func (suite *AnteTestSuite) CreateTestEIP712SubmitProposalV1(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder { + // Build V1 proposal messages. Must all be same-type, since EIP-712 + // does not support arrays of variable type. + authAcc := suite.app.GovKeeper.GetGovernanceAccount(suite.ctx) + + proposal1, ok := types5.ContentFromProposalType("My proposal 1", "My description 1", types5.ProposalTypeText) + suite.Require().True(ok) + content1, err := govtypes.NewLegacyContent( + proposal1, + sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), authAcc.GetAddress().Bytes()), + ) + suite.Require().NoError(err) + + proposal2, ok := types5.ContentFromProposalType("My proposal 2", "My description 2", types5.ProposalTypeText) + suite.Require().True(ok) + content2, err := govtypes.NewLegacyContent( + proposal2, + sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), authAcc.GetAddress().Bytes()), + ) + suite.Require().NoError(err) + + proposalMsgs := []sdk.Msg{ + content1, + content2, + } + + // Build V1 proposal + msgProposal, err := govtypes.NewMsgSubmitProposal( + proposalMsgs, + sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100))), + sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), from.Bytes()), + "Metadata", + ) + + suite.Require().NoError(err) + + return suite.CreateTestEIP712CosmosTxBuilder(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 { + recipient := sdk.AccAddress(common.Address{}.Bytes()) + msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1)))) + msgExec := authz.NewMsgExec(from, []sdk.Msg{msgSend}) + return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, &msgExec) +} + // 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)) diff --git a/ethereum/eip712/eip712.go b/ethereum/eip712/eip712.go index c621aaa98c..f2549096f7 100644 --- a/ethereum/eip712/eip712.go +++ b/ethereum/eip712/eip712.go @@ -207,7 +207,11 @@ func traverseFields( } for i := 0; i < n; i++ { - var field reflect.Value + var ( + field reflect.Value + err error + ) + if v.IsValid() { field = v.Field(i) } @@ -216,23 +220,10 @@ func traverseFields( fieldName := jsonNameFromTag(t.Field(i).Tag) if fieldType == cosmosAnyType { - any, ok := field.Interface().(*codectypes.Any) - if !ok { - return errorsmod.Wrapf(errortypes.ErrPackAny, "%T", field.Interface()) - } - - anyWrapper := &cosmosAnyWrapper{ - Type: any.TypeUrl, - } - - if err := cdc.UnpackAny(any, &anyWrapper.Value); err != nil { - return errorsmod.Wrap(err, "failed to unpack Any in msg struct") + // Unpack field, value as Any + if fieldType, field, err = unpackAny(cdc, field); err != nil { + return err } - - fieldType = reflect.TypeOf(anyWrapper) - field = reflect.ValueOf(anyWrapper) - - // then continue as normal } // If its a nil pointer, do not include in types @@ -274,6 +265,12 @@ func traverseFields( fieldType = fieldType.Elem() field = field.Index(0) isCollection = true + + if fieldType == cosmosAnyType { + if fieldType, field, err = unpackAny(cdc, field); err != nil { + return err + } + } } for { @@ -364,6 +361,27 @@ func jsonNameFromTag(tag reflect.StructTag) string { return parts[0] } +// Unpack the given Any value with Type/Value deconstruction +func unpackAny(cdc codectypes.AnyUnpacker, field reflect.Value) (reflect.Type, reflect.Value, error) { + any, ok := field.Interface().(*codectypes.Any) + if !ok { + return nil, reflect.Value{}, errorsmod.Wrapf(errortypes.ErrPackAny, "%T", field.Interface()) + } + + anyWrapper := &cosmosAnyWrapper{ + Type: any.TypeUrl, + } + + if err := cdc.UnpackAny(any, &anyWrapper.Value); err != nil { + return nil, reflect.Value{}, errorsmod.Wrap(err, "failed to unpack Any in msg struct") + } + + fieldType := reflect.TypeOf(anyWrapper) + field = reflect.ValueOf(anyWrapper) + + return fieldType, field, nil +} + // _.foo_bar.baz -> TypeFooBarBaz // // this is needed for Geth's own signing code which doesn't