Skip to content

Commit

Permalink
Add --generate-only flag to CLI commands
Browse files Browse the repository at this point in the history
Closes: #966
  • Loading branch information
alessio committed Aug 28, 2018
1 parent a804dec commit efba64c
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 47 deletions.
2 changes: 2 additions & 0 deletions client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type CLIContext struct {
Async bool
JSON bool
PrintResponse bool
GenerateOnly bool
}

// NewCLIContext returns a new initialized CLIContext with parameters from the
Expand All @@ -57,6 +58,7 @@ func NewCLIContext() CLIContext {
Async: viper.GetBool(client.FlagAsync),
JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse),
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
}
}

Expand Down
2 changes: 2 additions & 0 deletions client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
FlagAsync = "async"
FlagJson = "json"
FlagPrintResponse = "print-response"
FlagGenerateOnly = "generate-only"
)

// LineBreak can be included in a command list to provide a blank line
Expand Down Expand Up @@ -58,6 +59,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
c.Flags().Bool(FlagJson, false, "return output in json format")
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT")
}
return cmds
}
82 changes: 55 additions & 27 deletions client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/libs/common"
Expand All @@ -23,37 +24,11 @@ const DefaultGasAdjustment = 1.2
// addition, it builds and signs a transaction with the supplied messages.
// Finally, it broadcasts the signed transaction to a node.
func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) error {
if err := cliCtx.EnsureAccountExists(); err != nil {
return err
}

from, err := cliCtx.GetFromAddress()
txCtx, err := prepareTxContext(txCtx, cliCtx)
if err != nil {
return err
}

// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
if txCtx.AccountNumber == 0 {
accNum, err := cliCtx.GetAccountNumber(from)
if err != nil {
return err
}

txCtx = txCtx.WithAccountNumber(accNum)
}

// TODO: (ref #1903) Allow for user supplied account sequence without
// automatically doing a manual lookup.
if txCtx.Sequence == 0 {
accSeq, err := cliCtx.GetAccountSequence(from)
if err != nil {
return err
}

txCtx = txCtx.WithSequence(accSeq)
}

passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
if err != nil {
return err
Expand All @@ -75,6 +50,25 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
return cliCtx.EnsureBroadcastTx(txBytes)
}

// BuildStdSignMsg builds a StdSignMsg as per the parameters passed in the contexts.
func BuildStdSignMsg(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) (auth.StdSignMsg, error) {
txCtx, err := prepareTxContext(txCtx, cliCtx)
if err != nil {
return auth.StdSignMsg{}, err
}
stdMsg, err := txCtx.Build(msgs)
return stdMsg, err
}

// MarshalStdSignMsgJSON builds a StdSignMsg and returns its JSON formatting.
func MarshalStdSignMsgJSON(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) ([]byte, error) {
stdTx, err := BuildStdSignMsg(txCtx, cliCtx, msgs)
if err != nil {
return nil, err
}
return cliCtx.Codec.MarshalJSON(stdTx)
}

// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
// transaction and set the transaction's respective value accordingly.
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) {
Expand Down Expand Up @@ -113,6 +107,40 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
return
}

func prepareTxContext(txCtx authctx.TxContext, cliCtx context.CLIContext) (authctx.TxContext, error) {
if err := cliCtx.EnsureAccountExists(); err != nil {
return txCtx, err
}

from, err := cliCtx.GetFromAddress()
if err != nil {
return txCtx, err
}

if txCtx.AccountNumber == 0 {
// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
accNum, err := cliCtx.GetAccountNumber(from)
if err != nil {
return txCtx, err
}

txCtx = txCtx.WithAccountNumber(accNum)
}

// TODO: (ref #1903) Allow for user supplied account sequence without
// automatically doing a manual lookup.
if txCtx.Sequence == 0 {
accSeq, err := cliCtx.GetAccountSequence(from)
if err != nil {
return txCtx, err
}

txCtx = txCtx.WithSequence(accSeq)
}
return txCtx, nil
}

func adjustGasEstimate(estimate int64, adjustment float64) int64 {
if adjustment == 0 {
return int64(DefaultGasAdjustment * float64(estimate))
Expand Down
19 changes: 19 additions & 0 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,25 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, " 2 - Apples", proposalsQuery)
}

func TestGaiaCLISendGenerateOnly(t *testing.T) {
chainID, servAddr, port := initializeFixtures(t)
flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID)

// start gaiad server
proc := tests.GoExecuteTWithStdout(t, fmt.Sprintf("gaiad start --home=%s --rpc.laddr=%v", gaiadHome, servAddr))

defer proc.Stop(false)
tests.WaitForTMStart(port)
tests.WaitForNextNBlocksTM(2, port)

barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome))
// Test send
success := executeWrite(t, fmt.Sprintf(
"gaiacli send %v --amount=10steak --to=%s --from=foo --generate-only",
flags, barAddr), []string{}...)
require.True(t, success)
}

//___________________________________________________________________________________
// helper methods

Expand Down
12 changes: 6 additions & 6 deletions x/auth/stdtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ func StdSignBytes(chainID string, accnum int64, sequence int64, fee StdFee, msgs
// a Msg with the other requirements for a StdSignDoc before
// it is signed. For use in the CLI.
type StdSignMsg struct {
ChainID string
AccountNumber int64
Sequence int64
Fee StdFee
Msgs []sdk.Msg
Memo string
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Fee StdFee `json:"fee"`
Msgs []sdk.Msg `json:"msgs"`
Memo string `json:"memo"`
}

// get message bytes
Expand Down
8 changes: 8 additions & 0 deletions x/bank/client/cli/sendtx.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"fmt"
"os"

"github.com/cosmos/cosmos-sdk/client/context"
Expand Down Expand Up @@ -68,6 +69,13 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command {

// build and sign the transaction, then broadcast to Tendermint
msg := client.BuildMsg(from, to, coins)
if cliCtx.GenerateOnly {
json, err := utils.MarshalStdSignMsgJSON(txCtx, cliCtx, []sdk.Msg{msg})
if err == nil {
fmt.Printf("%s\n", json)
}
return err
}

return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
},
Expand Down
23 changes: 21 additions & 2 deletions x/gov/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome
}

msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)
if cliCtx.GenerateOnly {
json, err := utils.MarshalStdSignMsgJSON(txCtx, cliCtx, []sdk.Msg{msg})
if err == nil {
fmt.Printf("%s\n", json)
}
return err
}

err = msg.ValidateBasic()
if err != nil {
Expand Down Expand Up @@ -177,7 +184,13 @@ func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
}

msg := gov.NewMsgDeposit(depositerAddr, proposalID, amount)

if cliCtx.GenerateOnly {
json, err := utils.MarshalStdSignMsgJSON(txCtx, cliCtx, []sdk.Msg{msg})
if err == nil {
fmt.Printf("%s\n", json)
}
return err
}
err = msg.ValidateBasic()
if err != nil {
return err
Expand Down Expand Up @@ -221,7 +234,13 @@ func GetCmdVote(cdc *wire.Codec) *cobra.Command {
}

msg := gov.NewMsgVote(voterAddr, proposalID, byteVoteOption)

if cliCtx.GenerateOnly {
json, err := utils.MarshalStdSignMsgJSON(txCtx, cliCtx, []sdk.Msg{msg})
if err == nil {
fmt.Printf("%s\n", json)
}
return err
}
err = msg.ValidateBasic()
if err != nil {
return err
Expand Down
10 changes: 5 additions & 5 deletions x/gov/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ const MsgType = "gov"
//-----------------------------------------------------------
// MsgSubmitProposal
type MsgSubmitProposal struct {
Title string // Title of the proposal
Description string // Description of the proposal
ProposalType ProposalKind // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
Proposer sdk.AccAddress // Address of the proposer
InitialDeposit sdk.Coins // Initial deposit paid by sender. Must be strictly positive.
Title string `json:"title"` // Title of the proposal
Description string `json:"description"` // Description of the proposal
ProposalType ProposalKind `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
Proposer sdk.AccAddress `json:"proposer"` // Address of the proposer
InitialDeposit sdk.Coins `json:"initial_deposit"` // Initial deposit paid by sender. Must be strictly positive.
}

func NewMsgSubmitProposal(title string, description string, proposalType ProposalKind, proposer sdk.AccAddress, initialDeposit sdk.Coins) MsgSubmitProposal {
Expand Down
8 changes: 8 additions & 0 deletions x/ibc/client/cli/ibctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli

import (
"encoding/hex"
"fmt"
"os"

"github.com/cosmos/cosmos-sdk/client"
Expand Down Expand Up @@ -43,6 +44,13 @@ func IBCTransferCmd(cdc *wire.Codec) *cobra.Command {
if err != nil {
return err
}
if cliCtx.GenerateOnly {
json, err := utils.MarshalStdSignMsgJSON(txCtx, cliCtx, []sdk.Msg{msg})
if err == nil {
fmt.Printf("%s\n", json)
}
return err
}

return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
},
Expand Down
10 changes: 5 additions & 5 deletions x/ibc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ func init() {
// IBCPacket defines a piece of data that can be send between two separate
// blockchains.
type IBCPacket struct {
SrcAddr sdk.AccAddress
DestAddr sdk.AccAddress
Coins sdk.Coins
SrcChain string
DestChain string
SrcAddr sdk.AccAddress `json:"src_addr"`
DestAddr sdk.AccAddress `json:"dest_addr"`
Coins sdk.Coins `json:"coins"`
SrcChain string `json:"src_chain"`
DestChain string `json:"dest_chain"`
}

func NewIBCPacket(srcAddr sdk.AccAddress, destAddr sdk.AccAddress, coins sdk.Coins,
Expand Down
9 changes: 8 additions & 1 deletion x/slashing/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"fmt"
"os"

"github.com/cosmos/cosmos-sdk/client/context"
Expand Down Expand Up @@ -33,7 +34,13 @@ func GetCmdUnjail(cdc *wire.Codec) *cobra.Command {
}

msg := slashing.NewMsgUnjail(validatorAddr)

if cliCtx.GenerateOnly {
json, err := utils.MarshalStdSignMsgJSON(txCtx, cliCtx, []sdk.Msg{msg})
if err == nil {
fmt.Printf("%s\n", json)
}
return err
}
return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
},
}
Expand Down
Loading

0 comments on commit efba64c

Please sign in to comment.