From 3e68e440634ec5e3faee53776961cd059e8dba34 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 21 Nov 2018 23:44:13 +0000 Subject: [PATCH] Merge PR #2874: gaiad gentx subcommands refactoring * gaiad gentx subcommands refactoring - Replace STDIN/STDOUT redirection in `gaiad gentx` with subcommands command line options to redirect streams to file since viper does not handle redirection well. - Use `BuildCreateValidatorMsg` to build a `MsgCreateValidator` rather than redirecting to `gaiacli tx stake create-validator`. - `PrintUnsignedStdTx` now takes an `io.Writer` object. - Mark `--pubkey`, `--amount` and `--moniker` as required flags instead of validating them manually. - Use stake.NewDescription() to make a new Description - ref #2835 * Refresh PENDING.md --- PENDING.md | 1 + client/utils/utils.go | 5 +- cmd/gaia/init/gentx.go | 36 ++++++---- x/auth/client/cli/sign.go | 24 +++++-- x/bank/client/cli/sendtx.go | 3 +- x/gov/client/cli/tx.go | 7 +- x/ibc/client/cli/ibctx.go | 3 +- x/slashing/client/cli/tx.go | 3 +- x/stake/client/cli/tx.go | 140 ++++++++++++++++++------------------ 9 files changed, 128 insertions(+), 94 deletions(-) diff --git a/PENDING.md b/PENDING.md index b6273d186a39..f462a7e88de0 100644 --- a/PENDING.md +++ b/PENDING.md @@ -10,6 +10,7 @@ BREAKING CHANGES * [cli] [\#2786](https://github.com/cosmos/cosmos-sdk/pull/2786) Fix redelegation command flow * [cli] [\#2829](https://github.com/cosmos/cosmos-sdk/pull/2829) add-genesis-account command now validates state when adding accounts * [cli] [\#2804](https://github.com/cosmos/cosmos-sdk/issues/2804) Check whether key exists before passing it on to `tx create-validator`. + * [cli] [\#2874](https://github.com/cosmos/cosmos-sdk/pull/2874) `gaiacli tx sign` takes an optional `--output-document` flag to support output redirection. * Gaia diff --git a/client/utils/utils.go b/client/utils/utils.go index 46bb9799c1c2..08b8d87f27d1 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -3,6 +3,7 @@ package utils import ( "bytes" "fmt" + "io" "os" "github.com/cosmos/cosmos-sdk/client/context" @@ -88,7 +89,7 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc * // PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout. // Don't perform online validation or lookups if offline is true. -func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) { +func PrintUnsignedStdTx(w io.Writer, txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg, offline bool) (err error) { var stdTx auth.StdTx if offline { stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs) @@ -100,7 +101,7 @@ func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg } json, err := txBldr.Codec.MarshalJSON(stdTx) if err == nil { - fmt.Printf("%s\n", json) + fmt.Fprintf(w, "%s\n", json) } return } diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 096097e1883e..91dfbd2e1648 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -6,17 +6,21 @@ import ( "os" "path/filepath" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake/client/cli" stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/spf13/cobra" - "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" tmcli "github.com/tendermint/tendermint/libs/cli" @@ -80,7 +84,13 @@ following delegation and commission default parameters: } // Run gaiad tx create-validator prepareFlagsForTxCreateValidator(config, nodeID, ip, genDoc.ChainID, valPubKey) - createValidatorCmd := cli.GetCmdCreateValidator(cdc) + cliCtx, txBldr, msg, err := cli.BuildCreateValidatorMsg( + context.NewCLIContext().WithCodec(cdc), + authtxb.NewTxBuilderFromCLI().WithCodec(cdc), + ) + if err != nil { + return err + } w, err := ioutil.TempFile("", "gentx") if err != nil { @@ -88,18 +98,19 @@ following delegation and commission default parameters: } unsignedGenTxFilename := w.Name() defer os.Remove(unsignedGenTxFilename) - os.Stdout = w - if err = createValidatorCmd.RunE(nil, args); err != nil { + + if err := utils.PrintUnsignedStdTx(w, txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil { return err } - w.Close() prepareFlagsForTxSign() signCmd := authcmd.GetSignCommand(cdc) - if w, err = prepareOutputFile(config.RootDir, nodeID); err != nil { + + outputDocument, err := makeOutputFilepath(config.RootDir, nodeID) + if err != nil { return err } - os.Stdout = w + viper.Set("output-document", outputDocument) return signCmd.RunE(nil, []string{unsignedGenTxFilename}) }, } @@ -145,11 +156,10 @@ func prepareFlagsForTxSign() { viper.Set("offline", true) } -func prepareOutputFile(rootDir, nodeID string) (w *os.File, err error) { +func makeOutputFilepath(rootDir, nodeID string) (string, error) { writePath := filepath.Join(rootDir, "config", "gentx") - if err = common.EnsureDir(writePath, 0700); err != nil { - return + if err := common.EnsureDir(writePath, 0700); err != nil { + return "", err } - filename := filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)) - return os.Create(filename) + return filepath.Join(writePath, fmt.Sprintf("gentx-%v.json", nodeID)), nil } diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go index 9075308e1d0f..6fc57ac16634 100644 --- a/x/auth/client/cli/sign.go +++ b/x/auth/client/cli/sign.go @@ -3,9 +3,7 @@ package cli import ( "fmt" "io/ioutil" - - "github.com/pkg/errors" - "github.com/spf13/viper" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -13,7 +11,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/tendermint/go-amino" ) @@ -22,6 +22,7 @@ const ( flagValidateSigs = "validate-signatures" flagOffline = "offline" flagSigOnly = "signature-only" + flagOutfile = "output-document" ) // GetSignCommand returns the sign command @@ -52,6 +53,8 @@ recommended to set such parameters manually.`, cmd.Flags().Bool(flagValidateSigs, false, "Print the addresses that must sign the transaction, "+ "those who have already signed it, and make sure that signatures are in the correct order.") cmd.Flags().Bool(flagOffline, false, "Offline mode. Do not query local cache.") + cmd.Flags().String(flagOutfile, "", + "The document will be written to the given file instead of STDOUT") // Add the flags here and return the command return client.PostCommands(cmd)[0] @@ -107,7 +110,20 @@ func makeSignCmd(cdc *amino.Codec) func(cmd *cobra.Command, args []string) error if err != nil { return err } - fmt.Printf("%s\n", json) + + if viper.GetString(flagOutfile) == "" { + fmt.Printf("%s\n", json) + return + } + + fp, err := os.OpenFile( + viper.GetString(flagOutfile), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644, + ) + if err != nil { + return err + } + defer fp.Close() + fmt.Fprintf(fp, "%s\n", json) return } } diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 29a101cf73b6..1a7c444afba9 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" bankClient "github.com/cosmos/cosmos-sdk/x/bank/client" + "os" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -66,7 +67,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { // build and sign the transaction, then broadcast to Tendermint msg := bankClient.CreateMsg(from, to, coins) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index e804863d112e..461732cf5979 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" @@ -103,7 +104,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // Build and sign the transaction, then broadcast to Tendermint @@ -183,7 +184,7 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command { } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // Build and sign the transaction, then broadcast to a Tendermint @@ -229,7 +230,7 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]", diff --git a/x/ibc/client/cli/ibctx.go b/x/ibc/client/cli/ibctx.go index e8b107d9afbd..afe3824fcc15 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/ibc/client/cli/ibctx.go @@ -2,6 +2,7 @@ package cli import ( "encoding/hex" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -41,7 +42,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { return err } if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 7124b3544eeb..513710ce42df 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/slashing" + "os" "github.com/spf13/cobra" ) @@ -30,7 +31,7 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { msg := slashing.NewMsgUnjail(sdk.ValAddress(valAddr)) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) }, diff --git a/x/stake/client/cli/tx.go b/x/stake/client/cli/tx.go index 09b235abb1cd..f730e017f2ac 100644 --- a/x/stake/client/cli/tx.go +++ b/x/stake/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -26,76 +27,13 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - amounstStr := viper.GetString(FlagAmount) - if amounstStr == "" { - return fmt.Errorf("Must specify amount to stake using --amount") - } - amount, err := sdk.ParseCoin(amounstStr) - if err != nil { - return err - } - - valAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - - pkStr := viper.GetString(FlagPubKey) - if len(pkStr) == 0 { - return fmt.Errorf("must use --pubkey flag") - } - - pk, err := sdk.GetConsPubKeyBech32(pkStr) - if err != nil { - return err - } - - if viper.GetString(FlagMoniker) == "" { - return fmt.Errorf("please enter a moniker for the validator using --moniker") - } - - description := stake.Description{ - Moniker: viper.GetString(FlagMoniker), - Identity: viper.GetString(FlagIdentity), - Website: viper.GetString(FlagWebsite), - Details: viper.GetString(FlagDetails), - } - - // get the initial validator commission parameters - rateStr := viper.GetString(FlagCommissionRate) - maxRateStr := viper.GetString(FlagCommissionMaxRate) - maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) - commissionMsg, err := buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr) + cliCtx, txBldr, msg, err := BuildCreateValidatorMsg(cliCtx, txBldr) if err != nil { return err } - var msg sdk.Msg - if viper.GetString(FlagAddressDelegator) != "" { - delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) - if err != nil { - return err - } - - msg = stake.NewMsgCreateValidatorOnBehalfOf( - delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, - ) - } else { - msg = stake.NewMsgCreateValidator( - sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, - ) - } - - if viper.GetBool(FlagGenesisFormat) { - ip := viper.GetString(FlagIP) - nodeID := viper.GetString(FlagNodeID) - if nodeID != "" && ip != "" { - txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) - } - } - if viper.GetBool(FlagGenesisFormat) || cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, true) } // build and sign the transaction, then broadcast to Tendermint @@ -112,6 +50,9 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(FlagIP, "", fmt.Sprintf("Node's public IP. It takes effect only when used in combination with --%s", FlagGenesisFormat)) cmd.Flags().String(FlagNodeID, "", "Node's ID") cmd.MarkFlagRequired(client.FlagFrom) + cmd.MarkFlagRequired(FlagAmount) + cmd.MarkFlagRequired(FlagPubKey) + cmd.MarkFlagRequired(FlagMoniker) return cmd } @@ -154,7 +95,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint @@ -197,7 +138,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgDelegate(delAddr, valAddr, amount) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -252,7 +193,7 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, sharesAmount) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -300,7 +241,7 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { msg := stake.NewMsgBeginUnbonding(delAddr, valAddr, sharesAmount) if cliCtx.GenerateOnly { - return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false) + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) @@ -312,3 +253,64 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { return cmd } + +// BuildCreateValidatorMsg makes a new MsgCreateValidator. +func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder) (context.CLIContext, authtxb.TxBuilder, sdk.Msg, error) { + amounstStr := viper.GetString(FlagAmount) + amount, err := sdk.ParseCoin(amounstStr) + if err != nil { + return cliCtx, txBldr, nil, err + } + + valAddr, err := cliCtx.GetFromAddress() + if err != nil { + return cliCtx, txBldr, nil, err + } + + pkStr := viper.GetString(FlagPubKey) + pk, err := sdk.GetConsPubKeyBech32(pkStr) + if err != nil { + return cliCtx, txBldr, nil, err + } + + description := stake.NewDescription( + viper.GetString(FlagMoniker), + viper.GetString(FlagIdentity), + viper.GetString(FlagWebsite), + viper.GetString(FlagDetails), + ) + + // get the initial validator commission parameters + rateStr := viper.GetString(FlagCommissionRate) + maxRateStr := viper.GetString(FlagCommissionMaxRate) + maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate) + commissionMsg, err := buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr) + if err != nil { + return cliCtx, txBldr, nil, err + } + + var msg sdk.Msg + if viper.GetString(FlagAddressDelegator) != "" { + delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator)) + if err != nil { + return cliCtx, txBldr, nil, err + } + + msg = stake.NewMsgCreateValidatorOnBehalfOf( + delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, + ) + } else { + msg = stake.NewMsgCreateValidator( + sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, + ) + } + + if viper.GetBool(FlagGenesisFormat) { + ip := viper.GetString(FlagIP) + nodeID := viper.GetString(FlagNodeID) + if nodeID != "" && ip != "" { + txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) + } + } + return cliCtx, txBldr, msg, nil +}