From 14afa31be6dec7637bdcaf955afc545d99997d2e Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Sun, 4 Sep 2022 13:31:20 +0530 Subject: [PATCH 1/9] feat(cli): add `multi-msg-sign` --- simapp/simd/cmd/root.go | 1 + x/auth/client/cli/tx_multisign.go | 74 ++++++++++++ x/auth/client/cli/tx_sign.go | 181 +++++++++++++++--------------- 3 files changed, 164 insertions(+), 92 deletions(-) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index b24a1bd1bc67..c0d9027898f9 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -239,6 +239,7 @@ func txCommand() *cobra.Command { cmd.AddCommand( authcmd.GetSignCommand(), authcmd.GetSignBatchCommand(), + authcmd.GetMultiMsgSignCommand(), authcmd.GetMultiSignCommand(), authcmd.GetMultiSignBatchCmd(), authcmd.GetValidateSignaturesCommand(), diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index 0a2824ffe6d0..9a635e46ce59 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -29,6 +29,80 @@ type BroadcastReq struct { Mode string `json:"mode" yaml:"mode"` } +// GetMultiMsgSignCommand returns the multi-msg-sign cmd +func GetMultiMsgSignCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "multi-msg-sign [file] [file]...", + Aliases: []string{"multimsgsign"}, + Short: "Combine messages from transactions which are generated offline.", + PreRun: preSignCmd, + Example: fmt.Sprintf("%s tx multi-msg-sign tx1.json tx2.json tx3.json ", version.AppName), + Long: strings.TrimSpace( + fmt.Sprintf( + `Combine the transactions created with the --generate-only flag and Sign those transactions. + +Example: +$ %s tx multi-msg-sign tx1.json tx2.json tx3.json + +It will read a transactions from [files], sign it, and print its JSON encoding. +3EFBCD425B719FD8CF960552D33E7721C26B0114D33E49D5EBD79E3B70618775 +The --offline flag makes sure that the client will not reach out to full node. +As a result, the account and sequence number queries will not be performed and +it is required to set such parameters manually. Note, invalid values will cause +the transaction to fail. + +The --multisig= flag generates a signature on behalf of a multisig account +key. It implies --signature-only. Full multisig signed transactions may eventually +be generated via the 'multisign' command. +`, version.AppName, + )), + + RunE: makeMultiMsgSignCmd(), + } + + cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") + cmd.Flags().Bool(flagOverwrite, false, "Overwrite existing signatures with a new one. If disabled, new signature will be appended") + cmd.Flags().Bool(flagSigOnly, false, "Print only the signatures") + cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") + cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") + flags.AddTxFlagsToCmd(cmd) + + cmd.MarkFlagRequired(flags.FlagFrom) + + return cmd +} + +func makeMultiMsgSignCmd() func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + newb := clientCtx.TxConfig.NewTxBuilder() + msgs := make([]sdk.Msg, 0) + for i := 0; i < len(args); i++ { + parsedTx, err := authclient.ReadTxFromFile(clientCtx, args[i]) + if err != nil { + return err + } + fe, err := clientCtx.TxConfig.WrapTxBuilder(parsedTx) + if err != nil { + return err + } + msgs = append(msgs, parsedTx.GetMsgs()...) + newb.SetMemo(fe.GetTx().GetMemo()) + newb.SetTip(fe.GetTx().GetTip()) + newb.SetGasLimit(fe.GetTx().GetGas()) + } + + newb.SetMsgs(msgs...) + newb.SetGasLimit(newb.GetTx().GetGas() * uint64(len(msgs))) + + return signTx(cmd, clientCtx, tx.NewFactoryCLI(clientCtx, cmd.Flags()), newb.GetTx()) + } +} + // GetSignCommand returns the sign command func GetMultiSignCommand() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index 45fd71fa083c..2d981daa58c1 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" + "github.com/cosmos/cosmos-sdk/types" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" ) @@ -216,131 +217,127 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error { if err != nil { return err } - f := cmd.Flags() clientCtx, txF, newTx, err := readTxAndInitContexts(clientCtx, cmd, args[0]) if err != nil { return err } - txCfg := clientCtx.TxConfig - txBuilder, err := txCfg.WrapTxBuilder(newTx) + return signTx(cmd, clientCtx, txF, newTx) + } +} + +func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx types.Tx) error { + f := cmd.Flags() + txCfg := clientCtx.TxConfig + txBuilder, err := txCfg.WrapTxBuilder(newTx) + if err != nil { + return err + } + + printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) + multisig, _ := cmd.Flags().GetString(flagMultisig) + if err != nil { + return err + } + from, _ := cmd.Flags().GetString(flags.FlagFrom) + _, fromName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), from) + if err != nil { + return fmt.Errorf("error getting account from keybase: %w", err) + } + + overwrite, _ := f.GetBool(flagOverwrite) + if multisig != "" { + // Bech32 decode error, maybe it's a name, we try to fetch from keyring + multisigAddr, multisigName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), multisig) if err != nil { - return err + return fmt.Errorf("error getting account from keybase: %w", err) } - - printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) - multisig, _ := cmd.Flags().GetString(flagMultisig) + multisigkey, err := getMultisigRecord(clientCtx, multisigName) if err != nil { return err } - from, _ := cmd.Flags().GetString(flags.FlagFrom) - _, fromName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), from) + multisigPubKey, err := multisigkey.GetPubKey() if err != nil { - return fmt.Errorf("error getting account from keybase: %w", err) + return err } + multisigLegacyPub := multisigPubKey.(*kmultisig.LegacyAminoPubKey) - overwrite, _ := f.GetBool(flagOverwrite) - if multisig != "" { - // Bech32 decode error, maybe it's a name, we try to fetch from keyring - multisigAddr, multisigName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), multisig) - if err != nil { - return fmt.Errorf("error getting account from keybase: %w", err) - } - multisigkey, err := getMultisigRecord(clientCtx, multisigName) - if err != nil { - return err - } - multisigPubKey, err := multisigkey.GetPubKey() - if err != nil { - return err - } - multisigLegacyPub := multisigPubKey.(*kmultisig.LegacyAminoPubKey) - - fromRecord, err := clientCtx.Keyring.Key(fromName) - if err != nil { - return fmt.Errorf("error getting account from keybase: %w", err) - } - fromPubKey, err := fromRecord.GetPubKey() - if err != nil { - return err - } - - var found bool - for _, pubkey := range multisigLegacyPub.GetPubKeys() { - if pubkey.Equals(fromPubKey) { - found = true - } - } - if !found { - return fmt.Errorf("signing key is not a part of multisig key") - } - err = authclient.SignTxWithSignerAddress( - txF, clientCtx, multisigAddr, fromName, txBuilder, clientCtx.Offline, overwrite) - if err != nil { - return err - } - printSignatureOnly = true - } else { - err = authclient.SignTx(txF, clientCtx, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, overwrite) + fromRecord, err := clientCtx.Keyring.Key(fromName) + if err != nil { + return fmt.Errorf("error getting account from keybase: %w", err) } + fromPubKey, err := fromRecord.GetPubKey() if err != nil { return err } - aminoJSON, err := f.GetBool(flagAmino) + var found bool + for _, pubkey := range multisigLegacyPub.GetPubKeys() { + if pubkey.Equals(fromPubKey) { + found = true + } + } + if !found { + return fmt.Errorf("signing key is not a part of multisig key") + } + err = authclient.SignTxWithSignerAddress( + txF, clientCtx, multisigAddr, fromName, txBuilder, clientCtx.Offline, overwrite) if err != nil { return err } + printSignatureOnly = true + } else { + err = authclient.SignTx(txF, clientCtx, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, overwrite) + } + if err != nil { + return err + } + + aminoJSON, err := f.GetBool(flagAmino) + if err != nil { + return err + } - bMode, err := f.GetString(flags.FlagBroadcastMode) + bMode, err := f.GetString(flags.FlagBroadcastMode) + if err != nil { + return err + } + + // set output + closeFunc, err := setOutputFile(cmd) + if err != nil { + return err + } + + defer closeFunc() + clientCtx.WithOutput(cmd.OutOrStdout()) + + var json []byte + if aminoJSON { + stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) if err != nil { return err } - - var json []byte - if aminoJSON { - stdTx, err := tx.ConvertTxToStdTx(clientCtx.LegacyAmino, txBuilder.GetTx()) - if err != nil { - return err - } - req := BroadcastReq{ - Tx: stdTx, - Mode: bMode, - } - json, err = clientCtx.LegacyAmino.MarshalJSON(req) - if err != nil { - return err - } - } else { - json, err = marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) - if err != nil { - return err - } + req := BroadcastReq{ + Tx: stdTx, + Mode: bMode, } - - outputDoc, _ := cmd.Flags().GetString(flags.FlagOutputDocument) - if outputDoc == "" { - cmd.Printf("%s\n", json) - return nil + json, err = clientCtx.LegacyAmino.MarshalJSON(req) + if err != nil { + return err } - - fp, err := os.OpenFile(outputDoc, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + } else { + json, err = marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) if err != nil { return err } - defer func() { - err2 := fp.Close() - if err == nil { - err = err2 - } - }() - - _, err = fp.Write(append(json, '\n')) - return err } -} + cmd.Printf("%s\n", json) + + return err +} func marshalSignatureJSON(txConfig client.TxConfig, txBldr client.TxBuilder, signatureOnly bool) ([]byte, error) { parsedTx := txBldr.GetTx() if signatureOnly { From 419b65d15c5688f9ea9240126a724d1a2a2af07e Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Sun, 4 Sep 2022 13:34:56 +0530 Subject: [PATCH 2/9] chore: update the changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74ef913933cb..58a8951c3c43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,7 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (client) [#12936](https://github.com/cosmos/cosmos-sdk/pull/12936) Add capability to preprocess transactions before broadcasting from a higher level chain. * (x/authz) [#13047](https://github.com/cosmos/cosmos-sdk/pull/13047) Add a GetAuthorization function to the keeper. * (cli) [#12742](https://github.com/cosmos/cosmos-sdk/pull/12742) Add the `prune` CLI cmd to manually prune app store history versions based on the pruning options. - +* (cli) [#13147](https://github.com/cosmos/cosmos-sdk/pull/13147) Add the `multi-msg-sign` CLI cmd to combine the messages and sign those txs which are created with `--generate-only`. ### Improvements * [#12981](https://github.com/cosmos/cosmos-sdk/pull/12981) Return proper error when parsing telemetry configuration. From 8c875729eef954586d21a3116b5a41b01cc6fa15 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Sun, 4 Sep 2022 13:52:54 +0530 Subject: [PATCH 3/9] chore: fix the lint issue --- x/auth/client/cli/tx_sign.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index 2d981daa58c1..3d266725db98 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -338,6 +338,7 @@ func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx return err } + func marshalSignatureJSON(txConfig client.TxConfig, txBldr client.TxBuilder, signatureOnly bool) ([]byte, error) { parsedTx := txBldr.GetTx() if signatureOnly { From fe690ca336e9400e44dc1e089dd394cb8e8ed3e1 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Mon, 5 Sep 2022 11:35:24 +0530 Subject: [PATCH 4/9] chore: address the pr comments --- x/auth/client/cli/tx_multisign.go | 17 ++++++++--------- x/auth/client/cli/tx_sign.go | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index 9a635e46ce59..a0bee906dbea 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -33,7 +33,6 @@ type BroadcastReq struct { func GetMultiMsgSignCommand() *cobra.Command { cmd := &cobra.Command{ Use: "multi-msg-sign [file] [file]...", - Aliases: []string{"multimsgsign"}, Short: "Combine messages from transactions which are generated offline.", PreRun: preSignCmd, Example: fmt.Sprintf("%s tx multi-msg-sign tx1.json tx2.json tx3.json ", version.AppName), @@ -45,7 +44,7 @@ Example: $ %s tx multi-msg-sign tx1.json tx2.json tx3.json It will read a transactions from [files], sign it, and print its JSON encoding. -3EFBCD425B719FD8CF960552D33E7721C26B0114D33E49D5EBD79E3B70618775 + The --offline flag makes sure that the client will not reach out to full node. As a result, the account and sequence number queries will not be performed and it is required to set such parameters manually. Note, invalid values will cause @@ -79,7 +78,7 @@ func makeMultiMsgSignCmd() func(cmd *cobra.Command, args []string) error { return err } - newb := clientCtx.TxConfig.NewTxBuilder() + txBuilder := clientCtx.TxConfig.NewTxBuilder() msgs := make([]sdk.Msg, 0) for i := 0; i < len(args); i++ { parsedTx, err := authclient.ReadTxFromFile(clientCtx, args[i]) @@ -91,15 +90,15 @@ func makeMultiMsgSignCmd() func(cmd *cobra.Command, args []string) error { return err } msgs = append(msgs, parsedTx.GetMsgs()...) - newb.SetMemo(fe.GetTx().GetMemo()) - newb.SetTip(fe.GetTx().GetTip()) - newb.SetGasLimit(fe.GetTx().GetGas()) + txBuilder.SetMemo(fe.GetTx().GetMemo()) + txBuilder.SetTip(fe.GetTx().GetTip()) + txBuilder.SetGasLimit(fe.GetTx().GetGas()) } - newb.SetMsgs(msgs...) - newb.SetGasLimit(newb.GetTx().GetGas() * uint64(len(msgs))) + txBuilder.SetMsgs(msgs...) + txBuilder.SetGasLimit(txBuilder.GetTx().GetGas() * uint64(len(msgs))) - return signTx(cmd, clientCtx, tx.NewFactoryCLI(clientCtx, cmd.Flags()), newb.GetTx()) + return signTx(cmd, clientCtx, tx.NewFactoryCLI(clientCtx, cmd.Flags()), txBuilder.GetTx()) } } diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index 3d266725db98..8bd36fa77448 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -235,18 +235,31 @@ func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx return err } - printSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly) - multisig, _ := cmd.Flags().GetString(flagMultisig) + printSignatureOnly, err := cmd.Flags().GetBool(flagSigOnly) if err != nil { return err } - from, _ := cmd.Flags().GetString(flags.FlagFrom) + + multisig, err := cmd.Flags().GetString(flagMultisig) + if err != nil { + return err + } + + from, err := cmd.Flags().GetString(flags.FlagFrom) + if err != nil { + return err + } + _, fromName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), from) if err != nil { return fmt.Errorf("error getting account from keybase: %w", err) } - overwrite, _ := f.GetBool(flagOverwrite) + overwrite, err := f.GetBool(flagOverwrite) + if err != nil { + return err + } + if multisig != "" { // Bech32 decode error, maybe it's a name, we try to fetch from keyring multisigAddr, multisigName, _, err := client.GetFromFields(clientCtx, txF.Keybase(), multisig) From a7b7234c0a84c0d7037101c0f68cf42e97ba6e57 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Thu, 8 Sep 2022 21:59:43 +0530 Subject: [PATCH 5/9] chore: address the pr comments --- x/auth/client/cli/tx_multisign.go | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index a0bee906dbea..d6f93213436a 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -38,12 +38,12 @@ func GetMultiMsgSignCommand() *cobra.Command { Example: fmt.Sprintf("%s tx multi-msg-sign tx1.json tx2.json tx3.json ", version.AppName), Long: strings.TrimSpace( fmt.Sprintf( - `Combine the transactions created with the --generate-only flag and Sign those transactions. + `Combine the unsigned transactions generated with the --generate-only flag and sign those transactions to construct a signle signed multi msg transaction. Example: $ %s tx multi-msg-sign tx1.json tx2.json tx3.json -It will read a transactions from [files], sign it, and print its JSON encoding. +It will read all the transactions from [files], sign it, and print its JSON encoding. The --offline flag makes sure that the client will not reach out to full node. As a result, the account and sequence number queries will not be performed and @@ -55,8 +55,8 @@ key. It implies --signature-only. Full multisig signed transactions may eventual be generated via the 'multisign' command. `, version.AppName, )), - RunE: makeMultiMsgSignCmd(), + Args: cobra.MinimumNArgs(1), } cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") @@ -80,25 +80,40 @@ func makeMultiMsgSignCmd() func(cmd *cobra.Command, args []string) error { txBuilder := clientCtx.TxConfig.NewTxBuilder() msgs := make([]sdk.Msg, 0) + newGasLimit := uint64(0) + for i := 0; i < len(args); i++ { parsedTx, err := authclient.ReadTxFromFile(clientCtx, args[i]) if err != nil { return err } + fe, err := clientCtx.TxConfig.WrapTxBuilder(parsedTx) if err != nil { return err } + + // increment the gas which + newGasLimit += fe.GetTx().GetGas() msgs = append(msgs, parsedTx.GetMsgs()...) - txBuilder.SetMemo(fe.GetTx().GetMemo()) - txBuilder.SetTip(fe.GetTx().GetTip()) - txBuilder.SetGasLimit(fe.GetTx().GetGas()) } + // new factory cli with cmd flags + txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()) + + // set the new appened msgs into builder txBuilder.SetMsgs(msgs...) - txBuilder.SetGasLimit(txBuilder.GetTx().GetGas() * uint64(len(msgs))) - return signTx(cmd, clientCtx, tx.NewFactoryCLI(clientCtx, cmd.Flags()), txBuilder.GetTx()) + // set the memo,fees,feeGranter,feePayer from cmd flags + txBuilder.SetMemo(txf.Memo()) + txBuilder.SetFeeAmount(txf.Fees()) + txBuilder.SetFeeGranter(clientCtx.FeeGranter) + txBuilder.SetFeePayer(clientCtx.FeePayer) + + // set the gasLimit + txBuilder.SetGasLimit(newGasLimit) + + return signTx(cmd, clientCtx, txf, txBuilder.GetTx()) } } From 565ed1af6fa55f115f579d38fe8fc68ac6dfdae6 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Thu, 22 Sep 2022 11:26:39 +0530 Subject: [PATCH 6/9] refactor: refactored `sign-batch` to generate single signed tx --- simapp/simd/cmd/root.go | 1 - x/auth/client/cli/tx_multisign.go | 88 ------------------------------- x/auth/client/cli/tx_sign.go | 84 ++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 96 deletions(-) diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 5da96dd922c9..bcfaa1bd3619 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -240,7 +240,6 @@ func txCommand() *cobra.Command { cmd.AddCommand( authcmd.GetSignCommand(), authcmd.GetSignBatchCommand(), - authcmd.GetMultiMsgSignCommand(), authcmd.GetMultiSignCommand(), authcmd.GetMultiSignBatchCmd(), authcmd.GetValidateSignaturesCommand(), diff --git a/x/auth/client/cli/tx_multisign.go b/x/auth/client/cli/tx_multisign.go index d6f93213436a..0a2824ffe6d0 100644 --- a/x/auth/client/cli/tx_multisign.go +++ b/x/auth/client/cli/tx_multisign.go @@ -29,94 +29,6 @@ type BroadcastReq struct { Mode string `json:"mode" yaml:"mode"` } -// GetMultiMsgSignCommand returns the multi-msg-sign cmd -func GetMultiMsgSignCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "multi-msg-sign [file] [file]...", - Short: "Combine messages from transactions which are generated offline.", - PreRun: preSignCmd, - Example: fmt.Sprintf("%s tx multi-msg-sign tx1.json tx2.json tx3.json ", version.AppName), - Long: strings.TrimSpace( - fmt.Sprintf( - `Combine the unsigned transactions generated with the --generate-only flag and sign those transactions to construct a signle signed multi msg transaction. - -Example: -$ %s tx multi-msg-sign tx1.json tx2.json tx3.json - -It will read all the transactions from [files], sign it, and print its JSON encoding. - -The --offline flag makes sure that the client will not reach out to full node. -As a result, the account and sequence number queries will not be performed and -it is required to set such parameters manually. Note, invalid values will cause -the transaction to fail. - -The --multisig= flag generates a signature on behalf of a multisig account -key. It implies --signature-only. Full multisig signed transactions may eventually -be generated via the 'multisign' command. -`, version.AppName, - )), - RunE: makeMultiMsgSignCmd(), - Args: cobra.MinimumNArgs(1), - } - - cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") - cmd.Flags().Bool(flagOverwrite, false, "Overwrite existing signatures with a new one. If disabled, new signature will be appended") - cmd.Flags().Bool(flagSigOnly, false, "Print only the signatures") - cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") - cmd.Flags().Bool(flagAmino, false, "Generate Amino encoded JSON suitable for submiting to the txs REST endpoint") - flags.AddTxFlagsToCmd(cmd) - - cmd.MarkFlagRequired(flags.FlagFrom) - - return cmd -} - -func makeMultiMsgSignCmd() func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - clientCtx, err := client.GetClientTxContext(cmd) - if err != nil { - return err - } - - txBuilder := clientCtx.TxConfig.NewTxBuilder() - msgs := make([]sdk.Msg, 0) - newGasLimit := uint64(0) - - for i := 0; i < len(args); i++ { - parsedTx, err := authclient.ReadTxFromFile(clientCtx, args[i]) - if err != nil { - return err - } - - fe, err := clientCtx.TxConfig.WrapTxBuilder(parsedTx) - if err != nil { - return err - } - - // increment the gas which - newGasLimit += fe.GetTx().GetGas() - msgs = append(msgs, parsedTx.GetMsgs()...) - } - - // new factory cli with cmd flags - txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()) - - // set the new appened msgs into builder - txBuilder.SetMsgs(msgs...) - - // set the memo,fees,feeGranter,feePayer from cmd flags - txBuilder.SetMemo(txf.Memo()) - txBuilder.SetFeeAmount(txf.Fees()) - txBuilder.SetFeeGranter(clientCtx.FeeGranter) - txBuilder.SetFeePayer(clientCtx.FeePayer) - - // set the gasLimit - txBuilder.SetGasLimit(newGasLimit) - - return signTx(cmd, clientCtx, txf, txBuilder.GetTx()) - } -} - // GetSignCommand returns the sign command func GetMultiSignCommand() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index bda1c695e46d..ef4149ce4fc7 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -11,6 +11,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" ) @@ -21,6 +22,7 @@ const ( flagSigOnly = "signature-only" flagAmino = "amino" flagNoAutoIncrement = "no-auto-increment" + flagAppend = "append" ) // GetSignBatchCommand returns the transaction sign-batch command. @@ -54,7 +56,9 @@ account key. It implies --signature-only. cmd.Flags().String(flagMultisig, "", "Address or key name of the multisig account on behalf of which the transaction shall be signed") cmd.Flags().String(flags.FlagOutputDocument, "", "The document will be written to the given file instead of STDOUT") - cmd.Flags().Bool(flagSigOnly, true, "Print only the generated signature, then exit") + cmd.Flags().Bool(flagSigOnly, false, "Print only the generated signature, then exit") + cmd.Flags().Bool(flagAppend, false, "Combine all message and generate single signed transaction for broadcast.") + flags.AddTxFlagsToCmd(cmd) cmd.MarkFlagRequired(flags.FlagFrom) @@ -118,13 +122,36 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { } } - for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ { - unsignedStdTx := scanner.Tx() - txFactory = txFactory.WithSequence(sequence) - txBuilder, err := txCfg.WrapTxBuilder(unsignedStdTx) - if err != nil { - return err + appendMessagesToSingleMsg, _ := cmd.Flags().GetBool(flagAppend) + if appendMessagesToSingleMsg { + // It will combine all tx msgs and create single signed transaction + txBuilder := clientCtx.TxConfig.NewTxBuilder() + msgs := make([]sdk.Msg, 0) + newGasLimit := uint64(0) + + for scanner.Scan() { + unsignedStdTx := scanner.Tx() + fe, err := clientCtx.TxConfig.WrapTxBuilder(unsignedStdTx) + if err != nil { + return err + } + // increment the gas + newGasLimit += fe.GetTx().GetGas() + // append messages + msgs = append(msgs, unsignedStdTx.GetMsgs()...) } + // set the new appened msgs into builder + txBuilder.SetMsgs(msgs...) + + // set the memo,fees,feeGranter,feePayer from cmd flags + txBuilder.SetMemo(txFactory.Memo()) + txBuilder.SetFeeAmount(txFactory.Fees()) + txBuilder.SetFeeGranter(clientCtx.FeeGranter) + txBuilder.SetFeePayer(clientCtx.FeePayer) + + // set the gasLimit + txBuilder.SetGasLimit(newGasLimit) + if ms == "" { from, _ := cmd.Flags().GetString(flags.FlagFrom) _, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from) @@ -157,6 +184,49 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { } cmd.Printf("%s\n", json) + + } else { + // It will generate signed tx for each tx + for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ { + unsignedStdTx := scanner.Tx() + txFactory = txFactory.WithSequence(sequence) + txBuilder, err := txCfg.WrapTxBuilder(unsignedStdTx) + if err != nil { + return err + } + if ms == "" { + from, _ := cmd.Flags().GetString(flags.FlagFrom) + _, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from) + if err != nil { + return fmt.Errorf("error getting account from keybase: %w", err) + } + err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true, true) + if err != nil { + return err + } + } else { + multisigAddr, _, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), ms) + if err != nil { + return fmt.Errorf("error getting account from keybase: %w", err) + } + err = authclient.SignTxWithSignerAddress( + txFactory, clientCtx, multisigAddr, clientCtx.GetFromName(), txBuilder, clientCtx.Offline, true) + if err != nil { + return err + } + } + + if err != nil { + return err + } + + json, err := marshalSignatureJSON(txCfg, txBuilder, printSignatureOnly) + if err != nil { + return err + } + + cmd.Printf("%s\n", json) + } } if err := scanner.UnmarshalErr(); err != nil { From 6fb556ee3cf238abf48d67cd635ab2cce55dbf17 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Thu, 22 Sep 2022 11:27:52 +0530 Subject: [PATCH 7/9] chore: updated the changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 429b814c9a70..c1080eb17a08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (ledger) [#12935](https://github.com/cosmos/cosmos-sdk/pull/12935) Generalize Ledger integration to allow for different apps or keytypes that use SECP256k1. * (x/bank) [#11981](https://github.com/cosmos/cosmos-sdk/pull/11981) Create the `SetSendEnabled` endpoint for managing the bank's SendEnabled settings. * (x/auth) [#13210](https://github.com/cosmos/cosmos-sdk/pull/13210) Add `Query/AccountInfo` endpoint for simplified access to basic account info. -* (cli) [#13147](https://github.com/cosmos/cosmos-sdk/pull/13147) Add the `multi-msg-sign` CLI cmd to combine the messages and sign those txs which are created with `--generate-only`. +* (cli) [#13147](https://github.com/cosmos/cosmos-sdk/pull/13147) Add the `--append` flag to the `sign-batch` CLI cmd to combine the messages and sign those txs which are created with `--generate-only`. ### Improvements From 861e5c5cdb54a11cbe00309461c7d4d6a3f6bd4a Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Thu, 22 Sep 2022 11:33:51 +0530 Subject: [PATCH 8/9] fix: fix the lint --- x/auth/client/cli/tx_sign.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index ef4149ce4fc7..ebc2ffc4adb3 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -10,7 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig" - "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" @@ -316,7 +315,7 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error { } } -func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx types.Tx) error { +func signTx(cmd *cobra.Command, clientCtx client.Context, txF tx.Factory, newTx sdk.Tx) error { f := cmd.Flags() txCfg := clientCtx.TxConfig txBuilder, err := txCfg.WrapTxBuilder(newTx) From a5c6e151277031b78f727942add77c6301d29292 Mon Sep 17 00:00:00 2001 From: Sai Kumar Date: Thu, 22 Sep 2022 12:10:18 +0530 Subject: [PATCH 9/9] test: fix the tests on auth --- x/auth/client/testutil/suite.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 4e413378cdec..9093730f1060 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -1244,7 +1244,7 @@ func (s *IntegrationTestSuite) TestSignBatchMultisig() { addr1, err := account1.GetAddress() s.Require().NoError(err) // sign-batch file - res, err := TxSignBatchExec(val.ClientCtx, addr1, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String()) + res, err := TxSignBatchExec(val.ClientCtx, addr1, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String(), "--signature-only") s.Require().NoError(err) s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) // write sigs to file @@ -1253,7 +1253,7 @@ func (s *IntegrationTestSuite) TestSignBatchMultisig() { addr2, err := account2.GetAddress() s.Require().NoError(err) // sign-batch file with account2 - res, err = TxSignBatchExec(val.ClientCtx, addr2, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String()) + res, err = TxSignBatchExec(val.ClientCtx, addr2, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String(), "--signature-only") s.Require().NoError(err) s.Require().Equal(1, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) // write sigs to file2 @@ -1311,7 +1311,7 @@ func (s *IntegrationTestSuite) TestMultisignBatch() { // sign-batch file addr1, err := account1.GetAddress() s.Require().NoError(err) - res, err := TxSignBatchExec(val.ClientCtx, addr1, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) + res, err := TxSignBatchExec(val.ClientCtx, addr1, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence())), "--signature-only") s.Require().NoError(err) s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) // write sigs to file @@ -1320,7 +1320,7 @@ func (s *IntegrationTestSuite) TestMultisignBatch() { // sign-batch file with account2 addr2, err := account2.GetAddress() s.Require().NoError(err) - res, err = TxSignBatchExec(val.ClientCtx, addr2, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence()))) + res, err = TxSignBatchExec(val.ClientCtx, addr2, filename.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--multisig", addr.String(), fmt.Sprintf("--%s", flags.FlagOffline), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, fmt.Sprint(account.GetAccountNumber())), fmt.Sprintf("--%s=%s", flags.FlagSequence, fmt.Sprint(account.GetSequence())), "--signature-only") s.Require().NoError(err) s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n")))