cosmos-sdk/x/gov/client/cli/tx.go
gamarin2 addcfbf5cb Documentation Structure Change and Cleanup (#2808)
* Update docs/sdk/clients.md
* organize ADR directory like tendermint
* docs: move spec-proposals into spec/
* remove lotion, moved to website repo
* move getting-started to cosmos-hub, and voyager to website
* docs: move lite/ into clients/lite/
* move introduction/ content to website repo
* move resources/ content to website repo
* mv sdk/clients.md to clients/clients.md
* mv validators to cosmos-hub/validators
* move deprecated sdk/ content to _attic
* sdk/modules.md is duplicate with modules/README.md
* consolidate remianing sdk/ files into a single sdk.md
* move examples/ to docs/examples/
* mv docs/cosmos-hub to docs/gaia
* Add keys/accounts section to localnet docs
2018-11-14 11:44:17 -08:00

562 lines
15 KiB
Go

package cli
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
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/gov"
"encoding/json"
"io/ioutil"
"strings"
"github.com/cosmos/cosmos-sdk/x/gov/client"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
flagProposalID = "proposal-id"
flagTitle = "title"
flagDescription = "description"
flagProposalType = "type"
flagDeposit = "deposit"
flagVoter = "voter"
flagOption = "option"
flagDepositer = "depositer"
flagStatus = "status"
flagNumLimit = "limit"
flagProposal = "proposal"
)
type proposal struct {
Title string
Description string
Type string
Deposit string
}
var proposalFlags = []string{
flagTitle,
flagDescription,
flagProposalType,
flagDeposit,
}
// GetCmdSubmitProposal implements submitting a proposal transaction command.
func GetCmdSubmitProposal(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "submit-proposal",
Short: "Submit a proposal along with an initial deposit",
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"
where proposal.json contains:
{
"title": "Test Proposal",
"description": "My awesome proposal",
"type": "Text",
"deposit": "1000test"
}
is equivalent to
$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test"
`),
RunE: func(cmd *cobra.Command, args []string) error {
proposal, err := parseSubmitProposalFlags()
if err != nil {
return err
}
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
fromAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
amount, err := sdk.ParseCoins(proposal.Deposit)
if err != nil {
return err
}
proposalType, err := gov.ProposalTypeFromString(proposal.Type)
if err != nil {
return err
}
msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount)
err = msg.ValidateBasic()
if err != nil {
return err
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// Build and sign the transaction, then broadcast to Tendermint
// proposalID must be returned, and it is a part of response.
cliCtx.PrintResponse = true
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagTitle, "", "title of proposal")
cmd.Flags().String(flagDescription, "", "description of proposal")
cmd.Flags().String(flagProposalType, "", "proposalType of proposal, types: text/parameter_change/software_upgrade")
cmd.Flags().String(flagDeposit, "", "deposit of proposal")
cmd.Flags().String(flagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)")
return cmd
}
func parseSubmitProposalFlags() (*proposal, error) {
proposal := &proposal{}
proposalFile := viper.GetString(flagProposal)
if proposalFile == "" {
proposal.Title = viper.GetString(flagTitle)
proposal.Description = viper.GetString(flagDescription)
proposal.Type = client.NormalizeProposalType(viper.GetString(flagProposalType))
proposal.Deposit = viper.GetString(flagDeposit)
return proposal, nil
}
for _, flag := range proposalFlags {
if viper.GetString(flag) != "" {
return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag)
}
}
contents, err := ioutil.ReadFile(proposalFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(contents, proposal)
if err != nil {
return nil, err
}
return proposal, nil
}
// GetCmdDeposit implements depositing tokens for an active proposal.
func GetCmdDeposit(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "deposit",
Short: "Deposit tokens for activing proposal",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
depositerAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
proposalID := uint64(viper.GetInt64(flagProposalID))
amount, err := sdk.ParseCoins(viper.GetString(flagDeposit))
if err != nil {
return err
}
msg := gov.NewMsgDeposit(depositerAddr, proposalID, amount)
err = msg.ValidateBasic()
if err != nil {
return err
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg}, false)
}
// 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 {
cmd := &cobra.Command{
Use: "vote",
Short: "Vote for an active proposal, options: yes/no/no_with_veto/abstain",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
voterAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
proposalID := uint64(viper.GetInt64(flagProposalID))
option := viper.GetString(flagOption)
byteVoteOption, err := gov.VoteOptionFromString(client.NormalizeVoteOption(option))
if err != nil {
return err
}
msg := gov.NewMsgVote(voterAddr, proposalID, byteVoteOption)
err = msg.ValidateBasic()
if err != nil {
return err
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(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.
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
}
// GetCmdQueryProposal implements the query proposal 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),
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)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
return cmd
}
// GetCmdQueryProposal implements the query proposal command.
func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "proposal",
Short: "Query details of a single proposal",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
params := gov.QueryProposalParams{
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposal", queryRoute), bz)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal being queried")
return cmd
}
// 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",
RunE: func(cmd *cobra.Command, args []string) error {
bechDepositerAddr := viper.GetString(flagDepositer)
bechVoterAddr := viper.GetString(flagVoter)
strProposalStatus := viper.GetString(flagStatus)
numLimit := uint64(viper.GetInt64(flagNumLimit))
params := gov.QueryProposalsParams{
Limit: numLimit,
}
if len(bechDepositerAddr) != 0 {
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
if err != nil {
return err
}
params.Depositer = depositerAddr
}
if len(bechVoterAddr) != 0 {
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
return err
}
params.Voter = voterAddr
}
if len(strProposalStatus) != 0 {
proposalStatus, err := gov.ProposalStatusFromString(client.NormalizeProposalStatus(strProposalStatus))
if err != nil {
return err
}
params.ProposalStatus = proposalStatus
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
cliCtx := context.NewCLIContext().WithCodec(cdc)
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/proposals", queryRoute), bz)
if err != nil {
return err
}
var matchingProposals []gov.Proposal
err = cdc.UnmarshalJSON(res, &matchingProposals)
if err != nil {
return err
}
if len(matchingProposals) == 0 {
fmt.Println("No matching proposals found")
return nil
}
for _, proposal := range matchingProposals {
fmt.Printf(" %d - %s\n", proposal.GetProposalID(), proposal.GetTitle())
}
return nil
},
}
cmd.Flags().String(flagNumLimit, "", "(optional) limit to latest [number] proposals. Defaults to all proposals")
cmd.Flags().String(flagDepositer, "", "(optional) filter by proposals deposited on by depositer")
cmd.Flags().String(flagVoter, "", "(optional) filter by proposals voted on by voted")
cmd.Flags().String(flagStatus, "", "(optional) filter proposals by proposal status, status: deposit_period/voting_period/passed/rejected")
return cmd
}
// Command to Get a Proposal Information
// GetCmdQueryVote implements the query proposal vote command.
func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "vote",
Short: "Query details of a single vote",
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))
if err != nil {
return err
}
params := gov.QueryVoteParams{
Voter: voterAddr,
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/vote", queryRoute), bz)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
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",
Short: "Query votes on a proposal",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
params := gov.QueryVotesParams{
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's votes are being queried")
return cmd
}
// Command to Get a specific Deposit Information
// GetCmdQueryDeposit implements the query proposal deposit command.
func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "deposit",
Short: "Query details of a deposit",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
depositerAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagDepositer))
if err != nil {
return err
}
params := gov.QueryDepositParams{
Depositer: depositerAddr,
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposit", queryRoute), bz)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of proposal deposited on")
cmd.Flags().String(flagDepositer, "", "bech32 depositer 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",
Short: "Query deposits on a proposal",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
params := gov.QueryDepositsParams{
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
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",
Short: "Get the tally of a proposal vote",
RunE: func(cmd *cobra.Command, args []string) error {
cliCtx := context.NewCLIContext().WithCodec(cdc)
proposalID := uint64(viper.GetInt64(flagProposalID))
params := gov.QueryTallyParams{
ProposalID: proposalID,
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
return err
}
res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/tally", queryRoute), bz)
if err != nil {
return err
}
fmt.Println(string(res))
return nil
},
}
cmd.Flags().String(flagProposalID, "", "proposalID of which proposal is being tallied")
return cmd
}