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

Add EIP-712 encoding support type for any array #1430

Merged
merged 13 commits into from
Nov 16, 2022
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 23 additions & 1 deletion app/ante/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
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,
},
Expand Down
48 changes: 48 additions & 0 deletions app/ante/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
0a1c marked this conversation as resolved.
Show resolved Hide resolved
suite.Require().True(ok)
content1, err := govtypes.NewLegacyContent(
proposal1,
sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), authAcc.GetAddress().Bytes()),
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you need to use the config? is it using another prefix? If so we should change it in the setup so that we don't need to call this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is used because NewLegacyContent(...) expects the Governance module's address as a Bech32 string. Is there a better way to do this?

)
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()),
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

)
suite.Require().NoError(err)

proposalMsgs := []sdk.Msg{
content1,
content2,
}

// Build V1 proposal
msgProposal, err := govtypes.NewMsgSubmitProposal(
proposalMsgs,
0a1c marked this conversation as resolved.
Show resolved Hide resolved
sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100))),
sdk.MustBech32ifyAddressBytes(sdk.GetConfig().GetBech32AccountAddrPrefix(), from.Bytes()),
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

"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))
Expand Down
52 changes: 35 additions & 17 deletions ethereum/eip712/eip712.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down