Address gov cli ux issues and add additional input validation for better errors (#2938)

* Fix governance cli ux issues and add additional transaction validation
This commit is contained in:
Jack Zampolin 2018-12-03 12:48:51 -08:00 committed by GitHub
parent 13e78166ee
commit 886bd35670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 263 additions and 102 deletions

View File

@ -359,7 +359,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64())
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query gov proposal --proposal-id=1 --output=json %v", flags))
proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query gov proposal 1 --output=json %v", flags))
require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus())
@ -367,14 +367,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, " 1 - Test", proposalsQuery)
deposit := executeGetDeposit(t,
fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositor=%s --output=json %v",
fmt.Sprintf("gaiacli query gov deposit 1 %s --output=json %v",
fooAddr, flags))
require.Equal(t, int64(5), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64())
depositStr := fmt.Sprintf("gaiacli tx gov deposit %v", flags)
depositStr := fmt.Sprintf("gaiacli tx gov deposit 1 %s %v", fmt.Sprintf("10%s", stakeTypes.DefaultBondDenom), flags)
depositStr += fmt.Sprintf(" --from=%s", "foo")
depositStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("10%s", stakeTypes.DefaultBondDenom))
depositStr += fmt.Sprintf(" --proposal-id=%s", "1")
// Test generate only
success, stdout, stderr = executeWriteRetStdStreams(t, depositStr+" --generate-only", app.DefaultKeyPass)
@ -391,12 +389,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
// test query deposit
deposits := executeGetDeposits(t,
fmt.Sprintf("gaiacli query gov deposits --proposal-id=1 --output=json %v", flags))
fmt.Sprintf("gaiacli query gov deposits 1 --output=json %v", flags))
require.Len(t, deposits, 1)
require.Equal(t, int64(15), deposits[0].Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64())
deposit = executeGetDeposit(t,
fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositor=%s --output=json %v",
fmt.Sprintf("gaiacli query gov deposit 1 %s --output=json %v",
fooAddr, flags))
require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64())
@ -406,14 +404,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags))
require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64())
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query gov proposal --proposal-id=1 --output=json %v", flags))
proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query gov proposal 1 --output=json %v", flags))
require.Equal(t, uint64(1), proposal1.GetProposalID())
require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus())
voteStr := fmt.Sprintf("gaiacli tx gov vote %v", flags)
voteStr := fmt.Sprintf("gaiacli tx gov vote 1 Yes %v", flags)
voteStr += fmt.Sprintf(" --from=%s", "foo")
voteStr += fmt.Sprintf(" --proposal-id=%s", "1")
voteStr += fmt.Sprintf(" --option=%s", "Yes")
// Test generate only
success, stdout, stderr = executeWriteRetStdStreams(t, voteStr+" --generate-only", app.DefaultKeyPass)
@ -428,11 +424,11 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
executeWrite(t, voteStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(1, port)
vote := executeGetVote(t, fmt.Sprintf("gaiacli query gov vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags))
vote := executeGetVote(t, fmt.Sprintf("gaiacli query gov vote 1 %s --output=json %v", fooAddr, flags))
require.Equal(t, uint64(1), vote.ProposalID)
require.Equal(t, gov.OptionYes, vote.Option)
votes := executeGetVotes(t, fmt.Sprintf("gaiacli query gov votes --proposal-id=1 --output=json %v", flags))
votes := executeGetVotes(t, fmt.Sprintf("gaiacli query gov votes 1 --output=json %v", flags))
require.Len(t, votes, 1)
require.Equal(t, uint64(1), votes[0].ProposalID)
require.Equal(t, gov.OptionYes, votes[0].Option)

View File

@ -461,7 +461,7 @@ gaiacli tx gov submit-proposal \
Once created, you can now query information of the proposal:
```bash
gaiacli query gov proposal --proposal-id=<proposal_id>
gaiacli query gov proposal <proposal_id>
```
Or query all available proposals:
@ -477,9 +477,7 @@ You can also query proposals filtered by `voter` or `depositor` by using the cor
In order for a proposal to be broadcasted to the network, the amount deposited must be above a `minDeposit` value (default: `10 steak`). If the proposal you previously created didn't meet this requirement, you can still increase the total amount deposited to activate it. Once the minimum deposit is reached, the proposal enters voting period:
```bash
gaiacli tx gov deposit \
--proposal-id=<proposal_id> \
--deposit=<200steak> \
gaiacli tx gov deposit <proposal_id> <200steak> \
--from=<name> \
--chain-id=<chain_id>
```
@ -491,15 +489,13 @@ gaiacli tx gov deposit \
Once a new proposal is created, you can query all the deposits submitted to it:
```bash
gaiacli query gov deposits --proposal-id=<proposal_id>
gaiacli query gov deposits <proposal_id>
```
You can also query a deposit submitted by a specific address:
```bash
gaiacli query gov deposit \
--proposal-id=<proposal_id> \
--depositor=<account_cosmos>
gaiacli query gov deposit <proposal_id> <depositor_address>
```
#### Vote on a proposal
@ -507,9 +503,7 @@ gaiacli query gov deposit \
After a proposal's deposit reaches the `MinDeposit` value, the voting period opens. Bonded `Atom` holders can then cast vote on it:
```bash
gaiacli tx gov vote \
--proposal-id=<proposal_id> \
--option=<Yes/No/NoWithVeto/Abstain> \
gaiacli tx gov vote <proposal_id> <Yes/No/NoWithVeto/Abstain> \
--from=<name> \
--chain-id=<chain_id>
```
@ -519,15 +513,13 @@ gaiacli tx gov vote \
Check the vote with the option you just submitted:
```bash
gaiacli query gov vote \
--proposal-id=<proposal_id> \
--voter=<account_cosmos>
gaiacli query gov vote <proposal_id> <voter_address>
```
You can also get all the previous votes submitted to the proposal with:
```bash
gaiacli query gov votes --proposal-id=<proposal_id>
gaiacli query gov votes <proposal_id>
```
#### Query proposal tally results
@ -535,5 +527,15 @@ gaiacli query gov votes --proposal-id=<proposal_id>
To check the current tally of a given proposal you can use the `tally` command:
```bash
gaiacli query gov tally --proposal-id=<proposal_id>
gaiacli query gov tally <proposal_id>
```
#### Query governance parameters
To check the current governance parameters run:
```bash
gaiacli query gov param voting
gaiacli query gov param tallying
gaiacli query gov param deposit
```

View File

@ -2,6 +2,8 @@ package cli
import (
"fmt"
"strconv"
"strings"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
@ -15,19 +17,25 @@ import (
// GetCmdQueryProposal implements the query proposal command.
func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "proposal",
Use: "proposal [proposal-id]",
Args: cobra.ExactArgs(1),
Short: "Query details of a single proposal",
Long: strings.TrimSpace(`
Query details for a proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli query gov proposal 1
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
params := gov.NewQueryProposalParams(proposalID)
bz, err := cdc.MarshalJSON(params)
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0])
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz)
// Query the proposal
res, err := queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return err
}
@ -37,16 +45,37 @@ func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal being queried")
return cmd
}
func queryProposal(proposalID uint64, cliCtx context.CLIContext, cdc *codec.Codec, queryRoute string) ([]byte, error) {
// Construct query
params := gov.NewQueryProposalParams(proposalID)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return nil, err
}
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz)
if err != nil {
return nil, err
}
return res, err
}
// GetCmdQueryProposals implements a query proposals command.
func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "proposals",
Short: "Query proposals with optional filters",
Long: strings.TrimSpace(`
Query for a all proposals. You can filter the returns with the following flags:
$ gaiacli query gov proposals --depositor cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
$ gaiacli query gov proposals --voter cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
$ gaiacli query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Rejected)
`),
RunE: func(cmd *cobra.Command, args []string) error {
bechDepositorAddr := viper.GetString(flagDepositor)
bechVoterAddr := viper.GetString(flagVoter)
@ -126,23 +155,43 @@ func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command {
// GetCmdQueryVote implements the query proposal vote command.
func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "vote",
Use: "vote [proposal-id] [voter-address]",
Args: cobra.ExactArgs(2),
Short: "Query details of a single vote",
Long: strings.TrimSpace(`
Query details for a single vote on a proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
voterAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagVoter))
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
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 = queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}
// get voter address
voterAddr, err := sdk.AccAddressFromBech32(args[1])
if err != nil {
return err
}
// Construct query
params := gov.NewQueryVoteParams(proposalID, voterAddr)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/vote", queryRoute), bz)
if err != nil {
return err
@ -153,27 +202,43 @@ func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on")
cmd.Flags().String(flagVoter, "", "bech32 voter address")
return cmd
}
// GetCmdQueryVotes implements the command to query for proposal votes.
func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "votes",
Use: "votes [proposal-id]",
Args: cobra.ExactArgs(1),
Short: "Query votes on a proposal",
Long: strings.TrimSpace(`
Query vote details for a single proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli query gov votes 1
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
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 = queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}
// Construct query
params := gov.NewQueryProposalParams(proposalID)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz)
if err != nil {
return err
@ -184,8 +249,6 @@ func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's votes are being queried")
return cmd
}
@ -193,23 +256,43 @@ func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command {
// GetCmdQueryDeposit implements the query proposal deposit command.
func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "deposit",
Use: "deposit [proposal-id] [depositer-address]",
Args: cobra.ExactArgs(2),
Short: "Query details of a deposit",
Long: strings.TrimSpace(`
Query details for a single proposal deposit on a proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
depositorAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositor))
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
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 = queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}
// Get the depositer address
depositorAddr, err := sdk.AccAddressFromBech32(args[1])
if err != nil {
return err
}
// Construct query
params := gov.NewQueryDepositParams(proposalID, depositorAddr)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposit", queryRoute), bz)
if err != nil {
return err
@ -220,27 +303,43 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal deposited on")
cmd.Flags().String(flagDepositor, "", "bech32 depositor address")
return cmd
}
// GetCmdQueryDeposits implements the command to query for proposal deposits.
func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "deposits",
Use: "deposits [proposal-id]",
Args: cobra.ExactArgs(1),
Short: "Query deposits on a proposal",
Long: strings.TrimSpace(`
Query details for all deposits on a proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli query gov deposits 1
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
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 = queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}
// Construct query
params := gov.NewQueryProposalParams(proposalID)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz)
if err != nil {
return err
@ -251,26 +350,43 @@ func GetCmdQueryDeposits(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's deposits are being queried")
return cmd
}
// GetCmdQueryTally implements the command to query for proposal tally result.
func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "tally",
Use: "tally [proposal-id]",
Args: cobra.ExactArgs(1),
Short: "Get the tally of a proposal vote",
Long: strings.TrimSpace(`
Query tally of votes on a proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli query gov tally 1
`),
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
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 = queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}
// Construct query
params := gov.NewQueryProposalParams(proposalID)
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/tally", queryRoute), bz)
if err != nil {
return err
@ -281,8 +397,6 @@ func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command {
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of which proposal is being tallied")
return cmd
}
@ -290,14 +404,13 @@ func GetCmdQueryTally(queryRoute string, cdc *codec.Codec) *cobra.Command {
func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "param [param-type]",
Short: "Query the parameters (voting|tallying|deposit) of the governance process",
Args: cobra.ExactArgs(1),
Short: "Query the parameters (voting|tallying|deposit) of the governance process",
RunE: func(cmd *cobra.Command, args []string) error {
paramType := args[0]
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, paramType), nil)
// Query store
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, args[0]), nil)
if err != nil {
return err
}

View File

@ -3,6 +3,7 @@ package cli
import (
"fmt"
"os"
"strconv"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
@ -10,6 +11,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/gov"
"github.com/pkg/errors"
"encoding/json"
"io/ioutil"
@ -21,7 +23,6 @@ import (
)
const (
flagProposalID = "proposal-id"
flagTitle = "title"
flagDescription = "description"
flagProposalType = "type"
@ -56,7 +57,7 @@ func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
Long: strings.TrimSpace(`
Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example:
$ gaiacli gov submit-proposal --proposal="path/to/proposal.json"
$ gaiacli gov submit-proposal --proposal="path/to/proposal.json" --from mykey
where proposal.json contains:
@ -64,12 +65,12 @@ where proposal.json contains:
"title": "Test Proposal",
"description": "My awesome proposal",
"type": "Text",
"deposit": "1000test"
"deposit": "10test"
}
is equivalent to
$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test"
$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="10test" --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
proposal, err := parseSubmitProposalFlags()
@ -82,22 +83,35 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome
WithCodec(cdc).
WithAccountDecoder(cdc)
fromAddr, err := cliCtx.GetFromAddress()
// Get from address
from, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
// Pull associated account
account, err := cliCtx.GetAccount(from)
if err != nil {
return err
}
// Find deposit amount
amount, err := sdk.ParseCoins(proposal.Deposit)
if err != nil {
return err
}
// ensure account has enough coins
if !account.GetCoins().IsAllGTE(amount) {
return errors.Errorf("Address %s doesn't have enough coins to pay for this transaction.", from)
}
proposalType, err := gov.ProposalTypeFromString(proposal.Type)
if err != nil {
return err
}
msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)
msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, from, amount)
err = msg.ValidateBasic()
if err != nil {
return err
@ -155,29 +169,58 @@ func parseSubmitProposalFlags() (*proposal, error) {
}
// GetCmdDeposit implements depositing tokens for an active proposal.
func GetCmdDeposit(cdc *codec.Codec) *cobra.Command {
func GetCmdDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "deposit",
Use: "deposit [proposal-id] [deposit]",
Args: cobra.ExactArgs(2),
Short: "Deposit tokens for activing proposal",
Long: strings.TrimSpace(`
Submit a deposit for an acive proposal. You can find the proposal-id by running gaiacli query gov proposals:
$ gaiacli tx gov deposit 1 10STAKE --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(cdc)
depositorAddr, err := cliCtx.GetFromAddress()
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
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 = queryProposal(proposalID, cliCtx, cdc, queryRoute)
if err != nil {
return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err)
}
// Get from address
from, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
proposalID := uint64(viper.GetInt64(flagProposalID))
amount, err := sdk.ParseCoins(viper.GetString(flagDeposit))
// Fetch associated account
account, err := cliCtx.GetAccount(from)
if err != nil {
return err
}
msg := gov.NewMsgDeposit(depositorAddr, proposalID, amount)
// Get amount of coins
amount, err := sdk.ParseCoins(args[1])
if err != nil {
return err
}
// ensure account has enough coins
if !account.GetCoins().IsAllGTE(amount) {
return errors.Errorf("Address %s doesn't have enough coins to pay for this transaction.", from)
}
msg := gov.NewMsgDeposit(from, proposalID, amount)
err = msg.ValidateBasic()
if err != nil {
return err
@ -187,64 +230,71 @@ func GetCmdDeposit(cdc *codec.Codec) *cobra.Command {
return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// Build and sign the transaction, then broadcast to a Tendermint
// node.
// Build and sign the transaction, then broadcast to a Tendermint node.
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal depositing on")
cmd.Flags().String(flagDeposit, "", "amount of deposit")
return cmd
}
// GetCmdVote implements creating a new vote command.
func GetCmdVote(cdc *codec.Codec) *cobra.Command {
func GetCmdVote(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "vote",
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:
$ gaiacli tx gov vote 1 yes --from mykey
`),
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(cdc)
voterAddr, err := cliCtx.GetFromAddress()
// Get voting address
from, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
proposalID := uint64(viper.GetInt64(flagProposalID))
option := viper.GetString(flagOption)
// validate that the proposal id is a uint
proposalID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0])
}
byteVoteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(option))
// check to see if the proposal is in the store
_, err = queryProposal(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 {
return err
}
msg := gov.NewMsgVote(voterAddr, proposalID, byteVoteOption)
// Build vote message and run basic validation
msg := gov.NewMsgVote(from, proposalID, byteVoteOption)
err = msg.ValidateBasic()
if err != nil {
return err
}
// If generate only print the transaction
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false)
}
fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]",
voterAddr.String(), msg.ProposalID, msg.Option.String(),
)
// Build and sign the transaction, then broadcast to a Tendermint
// node.
// Build and sign the transaction, then broadcast to a Tendermint node.
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on")
cmd.Flags().String(flagOption, "", "vote option {yes, no, no_with_veto, abstain}")
return cmd
}

View File

@ -46,8 +46,8 @@ func (mc ModuleClient) GetTxCmd() *cobra.Command {
}
govTxCmd.AddCommand(client.PostCommands(
govCli.GetCmdDeposit(mc.cdc),
govCli.GetCmdVote(mc.cdc),
govCli.GetCmdDeposit(mc.storeKey, mc.cdc),
govCli.GetCmdVote(mc.storeKey, mc.cdc),
govCli.GetCmdSubmitProposal(mc.cdc),
)...)