Skip to content

Commit

Permalink
Merge PR #4306: Allow generate-only to fully operate offline
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderbez authored May 8, 2019
1 parent 2d93efd commit 85ffce5
Show file tree
Hide file tree
Showing 17 changed files with 63 additions and 86 deletions.
1 change: 1 addition & 0 deletions .pending/breaking/sdk/4305-GenerateOrBroad
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#4305 `GenerateOrBroadcastMsgs` no longer takes an `offline` parameter.
1 change: 1 addition & 0 deletions .pending/improvements/sdk/4305-The---generate-
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#4305 The `--generate-only` CLI flag fully respects offline tx processing.
13 changes: 8 additions & 5 deletions client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,23 @@ type CLIContext struct {
// command line using Viper. It takes a key name or address and populates the FromName and
// FromAddress field accordingly.
func NewCLIContextWithFrom(from string) CLIContext {
var nodeURI string
var rpc rpcclient.Client

nodeURI := viper.GetString(client.FlagNode)
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}

genOnly := viper.GetBool(client.FlagGenerateOnly)
fromAddress, fromName, err := GetFromFields(from, genOnly)
if err != nil {
fmt.Printf("failed to get from fields: %v", err)
os.Exit(1)
}

if !genOnly {
nodeURI = viper.GetString(client.FlagNode)
if nodeURI != "" {
rpc = rpcclient.NewHTTP(nodeURI, "/websocket")
}
}

// We need to use a single verifier for all contexts
if verifier == nil || verifierHome != viper.GetString(cli.HomeFlag) {
verifier = createVerifier()
Expand Down
2 changes: 1 addition & 1 deletion client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible)")
c.Flags().Bool(FlagGenerateOnly, false, "Build an unsigned transaction and write it to STDOUT (when enabled, the local Keybase is not accessible and the node operates offline)")
c.Flags().BoolP(FlagSkipConfirmation, "y", false, "Skip tx broadcasting prompt confirmation")

// --gas can accept integers and "simulate"
Expand Down
53 changes: 21 additions & 32 deletions client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"

"github.com/pkg/errors"
"github.com/spf13/viper"

"github.com/cosmos/cosmos-sdk/client"
Expand All @@ -30,11 +31,15 @@ func (gr GasEstimateResponse) String() string {
return fmt.Sprintf("gas estimate: %d", gr.GasEstimate)
}

// GenerateOrBroadcastMsgs respects CLI flags and outputs a message
func GenerateOrBroadcastMsgs(cliCtx context.CLIContext, txBldr authtxb.TxBuilder, msgs []sdk.Msg, offline bool) error {
// GenerateOrBroadcastMsgs creates a StdTx given a series of messages. If
// the provided context has generate-only enabled, the tx will only be printed
// to STDOUT in a fully offline manner. Otherwise, the tx will be signed and
// broadcasted.
func GenerateOrBroadcastMsgs(cliCtx context.CLIContext, txBldr authtxb.TxBuilder, msgs []sdk.Msg) error {
if cliCtx.GenerateOnly {
return PrintUnsignedStdTx(txBldr, cliCtx, msgs, offline)
return PrintUnsignedStdTx(txBldr, cliCtx, msgs)
}

return CompleteAndBroadcastTxCLI(txBldr, cliCtx, msgs)
}

Expand Down Expand Up @@ -141,29 +146,19 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error),
}

// 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) {

var stdTx auth.StdTx

if offline {
stdTx, err = buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
} else {
stdTx, err = buildUnsignedStdTx(txBldr, cliCtx, msgs)
}

func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error {
stdTx, err := buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
if err != nil {
return
return err
}

json, err := cliCtx.Codec.MarshalJSON(stdTx)
if err == nil {
fmt.Fprintf(cliCtx.Output, "%s\n", json)
if err != nil {
return err
}

return
_, _ = fmt.Fprintf(cliCtx.Output, "%s\n", json)
return nil
}

// SignStdTx appends a signature to a StdTx and returns a copy of it. If appendSig
Expand Down Expand Up @@ -327,29 +322,23 @@ func PrepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (auth
return txBldr, nil
}

// buildUnsignedStdTx builds a StdTx as per the parameters passed in the
// contexts. Gas is automatically estimated if gas wanted is set to 0.
func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) {
txBldr, err = PrepareTxBuilder(txBldr, cliCtx)
if err != nil {
return
}
return buildUnsignedStdTxOffline(txBldr, cliCtx, msgs)
}

func buildUnsignedStdTxOffline(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) {
if txBldr.SimulateAndExecute() {
if cliCtx.GenerateOnly {
return stdTx, errors.New("cannot estimate gas with generate-only")
}

txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs)
if err != nil {
return
return stdTx, err
}

fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas())
}

stdSignMsg, err := txBldr.BuildSignMsg(msgs)
if err != nil {
return
return stdTx, nil
}

return auth.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil
Expand Down
11 changes: 2 additions & 9 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,9 +812,9 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
require.Equal(t, 0, len(msg.GetSignatures()))

// Test generate sendTx, estimate gas
success, stdout, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "--generate-only")
success, stdout, stderr = f.TxSend(fooAddr.String(), barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only")
require.True(t, success)
require.NotEmpty(t, stderr)
require.Empty(t, stderr)
msg = unmarshalStdTx(t, stdout)
require.True(t, msg.Fee.Gas > 0)
require.Equal(t, len(msg.Msgs), 1)
Expand Down Expand Up @@ -854,13 +854,6 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) {
// Test broadcast
success, stdout, _ = f.TxBroadcast(signedTxFile.Name())
require.True(t, success)

var result sdk.TxResponse

// Unmarshal the response and ensure that gas was properly used
require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result))
require.Equal(t, msg.Fee.Gas, uint64(result.GasUsed))
require.Equal(t, msg.Fee.Gas, uint64(result.GasWanted))
tests.WaitForNextNBlocksTM(1, f.Port)

// Ensure account state
Expand Down
4 changes: 2 additions & 2 deletions cmd/gaia/init/gentx.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ following delegation and commission default parameters:

if info.GetType() == kbkeys.TypeOffline || info.GetType() == kbkeys.TypeMulti {
fmt.Println("Offline key passed in. Use `gaiacli tx sign` command to sign:")
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true)
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}

// write the unsigned transaction to the buffer
w := bytes.NewBuffer([]byte{})
cliCtx = cliCtx.WithOutput(w)

if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, true); err != nil {
if err = utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}); err != nil {
return err
}

Expand Down
2 changes: 1 addition & 1 deletion x/bank/client/cli/sendtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command {

// build and sign the transaction, then broadcast to Tendermint
msg := bank.NewMsgSend(cliCtx.GetFromAddress(), to, coins)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}

Expand Down
2 changes: 1 addition & 1 deletion x/crisis/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func GetCmdInvariantBroken(cdc *codec.Codec) *cobra.Command {
senderAddr := cliCtx.GetFromAddress()
moduleName, route := args[0], args[1]
msg := crisis.NewMsgVerifyInvariant(senderAddr, moduleName, route)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
return cmd
Expand Down
6 changes: 3 additions & 3 deletions x/distribution/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ $ gaiacli tx distr withdraw-rewards cosmosvaloper1gghjut3ccd8ay0zduzj64hwre2fxs9
msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr))
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs)
},
}
cmd.Flags().Bool(flagComission, false, "also withdraw validator's commission")
Expand Down Expand Up @@ -98,7 +98,7 @@ $ gaiacli tx distr withdraw-all-rewards --from mykey
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, msgs)
},
}
}
Expand Down Expand Up @@ -127,7 +127,7 @@ $ gaiacli tx set-withdraw-addr cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --f
}

msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
return cmd
Expand Down
4 changes: 2 additions & 2 deletions x/gov/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ $ gaiacli query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
// check to see if the proposal is in the store
_, err = gcutils.QueryProposalByID(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
}

voterAddr, err := sdk.AccAddressFromBech32(args[1])
Expand Down Expand Up @@ -224,7 +224,7 @@ $ gaiacli query gov votes 1
// check to see if the proposal is in the store
res, err := gcutils.QueryProposalByID(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
return fmt.Errorf("failed to fetch proposal-id %d: %s", proposalID, err)
}

var proposal gov.Proposal
Expand Down
30 changes: 10 additions & 20 deletions x/gov/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}

Expand All @@ -108,14 +108,15 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome
}

// GetCmdDeposit implements depositing tokens for an active proposal.
func GetCmdDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command {
func GetCmdDeposit(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "deposit [proposal-id] [deposit]",
Args: cobra.ExactArgs(2),
Short: "Deposit tokens for activing proposal",
Short: "Deposit tokens for an active proposal",
Long: strings.TrimSpace(`
Submit a deposit for an acive proposal. You can find the proposal-id by running gaiacli query gov proposals:
Submit a deposit for an active proposal. You can find the proposal-id by running "gaiacli query gov proposals":
Example:
$ gaiacli tx gov deposit 1 10stake --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -130,12 +131,6 @@ $ gaiacli tx gov deposit 1 10stake --from mykey
return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0])
}

// check to see if the proposal is in the store
_, err = govClientUtils.QueryProposalByID(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}

// Get depositor address
from := cliCtx.GetFromAddress()

Expand All @@ -151,20 +146,21 @@ $ gaiacli tx gov deposit 1 10stake --from mykey
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}

// GetCmdVote implements creating a new vote command.
func GetCmdVote(queryRoute string, cdc *codec.Codec) *cobra.Command {
func GetCmdVote(cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Use: "vote [proposal-id] [option]",
Args: cobra.ExactArgs(2),
Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain",
Long: strings.TrimSpace(`
Submit a vote for an acive proposal. You can find the proposal-id by running gaiacli query gov proposals:
Submit a vote for an active proposal. You can find the proposal-id by running "gaiacli query gov proposals":
Example:
$ gaiacli tx gov vote 1 yes --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -182,12 +178,6 @@ $ gaiacli tx gov vote 1 yes --from mykey
return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
}

// check to see if the proposal is in the store
_, err = govClientUtils.QueryProposalByID(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}

// Find out which vote option user chose
byteVoteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(args[1]))
if err != nil {
Expand All @@ -201,7 +191,7 @@ $ gaiacli tx gov vote 1 yes --from mykey
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}
Expand Down
4 changes: 2 additions & 2 deletions x/gov/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ func (mc ModuleClient) GetTxCmd() *cobra.Command {
}

govTxCmd.AddCommand(client.PostCommands(
govCli.GetCmdDeposit(mc.storeKey, mc.cdc),
govCli.GetCmdVote(mc.storeKey, mc.cdc),
govCli.GetCmdDeposit(mc.cdc),
govCli.GetCmdVote(mc.cdc),
cmdSubmitProp,
)...)

Expand Down
2 changes: 1 addition & 1 deletion x/ibc/client/cli/ibctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command {
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}

Expand Down
2 changes: 1 addition & 1 deletion x/params/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ where proposal.json contains:
return err
}

return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}

Expand Down
2 changes: 1 addition & 1 deletion x/slashing/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ $ gaiacli tx slashing unjail --from mykey
valAddr := cliCtx.GetFromAddress()

msg := slashing.NewMsgUnjail(sdk.ValAddress(valAddr))
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
},
}
}
Loading

0 comments on commit 85ffce5

Please sign in to comment.