diff --git a/CHANGELOG.md b/CHANGELOG.md index 608567f7ec..7ae54078b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,15 @@ # Changelog -## v0.2.5 +## v0.2.6-alpha.1 +This release includes 1 new feature. +Features: +* [#310](https://github.com/bnb-chain/greenfield-cosmos-sdk/pull/310) feat: add FlagPrintEIP712MsgType + +Chores: +* [#312](https://github.com/bnb-chain/greenfield-cosmos-sdk/pull/312) chore: improve the validations of parameters + +## v0.2.5 This release includes all the changes in the v0.2.5 alpha versions and 1 new feature. Features: diff --git a/client/cmd.go b/client/cmd.go index a3a9215911..adad8f6d40 100644 --- a/client/cmd.go +++ b/client/cmd.go @@ -107,6 +107,11 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont clientCtx = clientCtx.WithSimulation(dryRun) } + if !clientCtx.Simulate || flagSet.Changed(flags.FlagPrintEIP712MsgType) { + printEIP712, _ := flagSet.GetBool(flags.FlagPrintEIP712MsgType) + clientCtx = clientCtx.WithSimulation(printEIP712).WithPrintEIP712MsgType(printEIP712) + } + if clientCtx.KeyringDir == "" || flagSet.Changed(flags.FlagKeyringDir) { keyringDir, _ := flagSet.GetString(flags.FlagKeyringDir) diff --git a/client/context.go b/client/context.go index 12bd7bc420..636bf2efb2 100644 --- a/client/context.go +++ b/client/context.go @@ -59,6 +59,8 @@ type Context struct { // IsAux is true when the signer is an auxiliary signer (e.g. the tipper). IsAux bool + PrintEIP712MsgType bool + // TODO: Deprecated (remove). LegacyAmino *codec.LegacyAmino } @@ -267,6 +269,12 @@ func (ctx Context) WithAux(isAux bool) Context { return ctx } +// WithPrintEIP712MsgType returns a copy of the context with an updated PrintEIP712MsgType value. +func (ctx Context) WithPrintEIP712MsgType(printEIP712MsgType bool) Context { + ctx.PrintEIP712MsgType = printEIP712MsgType + return ctx +} + // WithLedgerHasProto returns the context with the provided boolean value, indicating // whether the target Ledger application can support Protobuf payloads. func (ctx Context) WithLedgerHasProtobuf(val bool) Context { @@ -369,7 +377,7 @@ func GetFromFields(clientCtx Context, kr keyring.Keyring, from string) (sdk.AccA switch { case clientCtx.Simulate: if err != nil { - return nil, "", 0, fmt.Errorf("a valid bech32 address must be provided in simulation mode: %w", err) + return nil, "", 0, fmt.Errorf("a valid hex address must be provided in simulation mode: %w", err) } return addr, "", 0, nil diff --git a/client/context_test.go b/client/context_test.go index 290003e73e..10c0601f17 100644 --- a/client/context_test.go +++ b/client/context_test.go @@ -205,7 +205,7 @@ func TestGetFromFields(t *testing.T) { }, from: "alice", clientCtx: client.Context{}.WithSimulation(true), - expectedErr: "a valid bech32 address must be provided in simulation mode", + expectedErr: "a valid hex address must be provided in simulation mode", }, { keyring: func() keyring.Keyring { diff --git a/client/flags/flags.go b/client/flags/flags.go index 6150e56745..51ce2b07f4 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -89,6 +89,8 @@ const ( // Tendermint logging flags FlagLogLevel = "log_level" FlagLogFormat = "log_format" + + FlagPrintEIP712MsgType = "print-eip712-msg-type" ) // LineBreak can be included in a command list to provide a blank line @@ -136,6 +138,7 @@ func AddTxFlagsToCmd(cmd *cobra.Command) { // --gas can accept integers and "auto" f.String(FlagGas, "", fmt.Sprintf("gas limit to set per-transaction; set to %q to calculate sufficient gas automatically. Note: %q option doesn't always report accurate results. Set a valid coin value to adjust the result. Can be used instead of %q. (default %d)", GasFlagAuto, GasFlagAuto, FlagFees, DefaultGasLimit)) + f.Bool(FlagPrintEIP712MsgType, false, "ignore the --gas flag and perform a simulation of a transaction(but don't broadcast it) and print the EIP712 message type") AddKeyringFlags(f) } diff --git a/client/tx/factory.go b/client/tx/factory.go index 05921de381..80bda86574 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -1,6 +1,8 @@ package tx import ( + "encoding/hex" + "encoding/json" "errors" "fmt" "os" @@ -15,11 +17,14 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/crypto/keys/eth/ethsecp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" "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" + "github.com/ethereum/go-ethereum/signer/core/apitypes" ) // Factory defines a client transaction factory that facilitates generating and @@ -80,8 +85,13 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e memo, _ := flagSet.GetString(flags.FlagNote) timeoutHeight, _ := flagSet.GetUint64(flags.FlagTimeoutHeight) - gasStr, _ := flagSet.GetString(flags.FlagGas) - gasSetting, _ := flags.ParseGasSetting(gasStr) + var gasSetting flags.GasSetting + dryRun, _ := flagSet.GetBool(flags.FlagDryRun) + printEIP712MsgType, _ := flagSet.GetBool(flags.FlagPrintEIP712MsgType) + if !dryRun && !printEIP712MsgType { + gasStr, _ := flagSet.GetString(flags.FlagGas) + gasSetting, _ = flags.ParseGasSetting(gasStr) + } f := Factory{ txConfig: clientCtx.TxConfig, @@ -170,7 +180,7 @@ func (f Factory) WithFees(fees string) Factory { } // WithTips returns a copy of the Factory with an updated tip. -func (f Factory) WithTips(tip string, tipper string) Factory { +func (f Factory) WithTips(tip, tipper string) Factory { parsedTips, err := sdk.ParseCoinsNormalized(tip) if err != nil { panic(err) @@ -399,6 +409,73 @@ func (f Factory) PrintUnsignedTx(clientCtx client.Context, msgs ...sdk.Msg) erro return clientCtx.PrintString(fmt.Sprintf("%s\n", json)) } +func (f Factory) PrintEIP712MsgType(clientCtx client.Context, msgs ...sdk.Msg) error { + if len(msgs) != 1 { + return errors.New("only one message is supported") + } + + unsignedTx, err := f.BuildUnsignedTx(msgs...) + if err != nil { + return err + } + + txRawBytes, err := clientCtx.TxConfig.TxEncoder()(unsignedTx.GetTx()) + if err != nil { + return err + } + txRawBytesHex := hex.EncodeToString(txRawBytes) + + signerData := authsigning.SignerData{ + Address: clientCtx.From, + ChainID: clientCtx.ChainID, + } + + chainID, err := sdk.ParseChainID(clientCtx.ChainID) + if err != nil { + return fmt.Errorf("failed to parse chainID: %s", err) + } + msgTypes, signDoc, err := authtx.GetMsgTypes(signerData, unsignedTx.GetTx(), chainID) + if err != nil { + return fmt.Errorf("failed to get msg types: %s", err) + } + typedData, err := authtx.WrapTxToTypedData(chainID.Uint64(), signDoc, msgTypes) + if err != nil { + return fmt.Errorf("failed to wrap tx to typedData: %s", err) + } + + eip712MsgTypes := typedData.Types + delete(eip712MsgTypes, "Tx") + delete(eip712MsgTypes, "Fee") + delete(eip712MsgTypes, "Coin") + delete(eip712MsgTypes, "EIP712Domain") + + msgData := typedData.Message["msg1"].(map[string]interface{}) + if msgData == nil { + return fmt.Errorf("failed to get msg data") + } + msgTypeUrl := msgData["type"].(string) + + type EIP712TypedData struct { + MsgTypeUrl string `json:"MsgTypeUrl"` + EIP712MessageType apitypes.Types `json:"EIP712MessageType"` + MessageData map[string]interface{} `json:"MessageData"` + TxRawBytes string `json:"TxRawBytes"` + } + + eip712TypedData := EIP712TypedData{ + MsgTypeUrl: msgTypeUrl, + EIP712MessageType: eip712MsgTypes, + MessageData: msgData, + TxRawBytes: txRawBytesHex, + } + bz, err := json.MarshalIndent(eip712TypedData, "", " ") + if err != nil { + return err + } + + return clientCtx.PrintString(fmt.Sprintf("%s\n", bz)) +} + // BuildSimTx creates an unsigned tx with an empty single signature and returns // the encoded transaction or an error if the unsigned transaction cannot be // built. @@ -437,7 +514,7 @@ func (f Factory) BuildSimTx(msgs ...sdk.Msg) ([]byte, error) { func (f Factory) getSimPK() (cryptotypes.PubKey, error) { var ( ok bool - pk cryptotypes.PubKey = &secp256k1.PubKey{} // use default public key type + pk cryptotypes.PubKey = ðsecp256k1.PubKey{} // use default public key type ) // Use the first element from the list of keys in order to generate a valid diff --git a/client/tx/tx.go b/client/tx/tx.go index 0b80419901..4689f481a8 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -59,6 +59,10 @@ func GenerateOrBroadcastTxWithFactory(clientCtx client.Context, txf Factory, msg return txf.PrintUnsignedTx(clientCtx, msgs...) } + if clientCtx.PrintEIP712MsgType { + return txf.PrintEIP712MsgType(clientCtx, msgs...) + } + return BroadcastTx(clientCtx, txf, msgs...) } diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index 14b0a952d3..0aec61d1e5 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -39,7 +39,7 @@ func NewSendTxCmd() *cobra.Command { Short: "Send funds from one account to another.", Long: `Send funds from one account to another. Note, the '--from' flag is ignored as it is implied from [from_key_or_address]. -When using '--dry-run' a key name cannot be used, only a bech32 address. +When using '--dry-run' a key name cannot be used, only a hex address. `, Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { @@ -80,7 +80,7 @@ func NewMultiSendTxCmd() *cobra.Command { By default, sends the [amount] to each address of the list. Using the '--split' flag, the [amount] is split equally between the addresses. Note, the '--from' flag is ignored as it is implied from [from_key_or_address]. -When using '--dry-run' a key name cannot be used, only a bech32 address. +When using '--dry-run' a key name cannot be used, only a hex address. `, Args: cobra.MinimumNArgs(4), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/gov/keeper/crosschain.go b/x/gov/keeper/crosschain.go index f82f8e45ea..21bbfdad70 100644 --- a/x/gov/keeper/crosschain.go +++ b/x/gov/keeper/crosschain.go @@ -66,13 +66,16 @@ func (k Keeper) SyncParams(ctx sdk.Context, destChainId sdk.ChainID, cpc govv1.C Target: addresses, } - encodedPackage := pack.MustSerialize() + encodedPackage, err := pack.Serialize() + if err != nil { + return sdkerrors.Wrapf(types.ErrInvalidSyncParamPackage, "fail to serialize, err: %s", err.Error()) + } if !k.crossChainKeeper.IsDestChainSupported(destChainId) { return sdkerrors.Wrapf(types.ErrChainNotSupported, "destination chain (%d) is not supported", destChainId) } - _, err := k.crossChainKeeper.CreateRawIBCPackageWithFee( + _, err = k.crossChainKeeper.CreateRawIBCPackageWithFee( ctx, destChainId, types.SyncParamsChannelID, diff --git a/x/gov/types/crosschain.go b/x/gov/types/crosschain.go index 259c0a2e0e..ac63a5c371 100644 --- a/x/gov/types/crosschain.go +++ b/x/gov/types/crosschain.go @@ -35,10 +35,10 @@ var ( } ) -func (p SyncParamsPackage) MustSerialize() []byte { +func (p SyncParamsPackage) Serialize() ([]byte, error) { encodedBytes, err := syncParamsPackageArgs.Pack(&p) if err != nil { - panic("encode params change sync package error") + return nil, err } - return encodedBytes + return encodedBytes, nil } diff --git a/x/gov/types/errors.go b/x/gov/types/errors.go index 71c4ac98b9..248a918d2f 100644 --- a/x/gov/types/errors.go +++ b/x/gov/types/errors.go @@ -33,7 +33,7 @@ var ( ErrAddressSizeNotMatch = errors.Register(ModuleName, 25, "number of old address not equal to new addresses") ErrAddressNotValid = errors.Register(ModuleName, 26, "address format is not valid") ErrExceedParamsChangeLimit = errors.Register(ModuleName, 27, "exceed params change limit") - ErrInvalidUpgradeProposal = errors.Register(ModuleName, 28, "invalid sync params package") + ErrInvalidSyncParamPackage = errors.Register(ModuleName, 28, "invalid sync params package") ErrInvalidValue = errors.Register(ModuleName, 29, "decode hex value failed") ErrChainNotSupported = errors.Register(ModuleName, 30, "crosschain: chain is not supported") diff --git a/x/gov/types/v1/crosschain.go b/x/gov/types/v1/crosschain.go index 2974c89631..000094576d 100644 --- a/x/gov/types/v1/crosschain.go +++ b/x/gov/types/v1/crosschain.go @@ -1,8 +1,10 @@ package v1 import ( + "encoding/hex" "strings" + sdkerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -36,6 +38,11 @@ func (m *CrossChainParamsChange) ValidateBasic() error { if err != nil { return types.ErrAddressNotValid } + } else { + _, err := hex.DecodeString(value) + if err != nil { + return sdkerrors.Wrapf(types.ErrInvalidValue, "value is not valid %s", value) + } } _, err := sdk.AccAddressFromHexUnsafe(target) if err != nil {