2018-06-21 17:19:14 -07:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2018-08-06 11:11:30 -07:00
|
|
|
"os"
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/client/context"
|
2018-08-06 11:11:30 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/client/utils"
|
2018-06-21 17:19:14 -07:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/wire"
|
|
|
|
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
2018-08-06 11:11:30 -07:00
|
|
|
authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context"
|
2018-06-21 17:19:14 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
2018-08-06 11:11:30 -07:00
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
"github.com/pkg/errors"
|
2018-08-06 11:11:30 -07:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/viper"
|
2018-06-21 17:19:14 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2018-07-30 16:48:00 -07:00
|
|
|
flagProposalID = "proposal-id"
|
|
|
|
flagTitle = "title"
|
|
|
|
flagDescription = "description"
|
|
|
|
flagProposalType = "type"
|
|
|
|
flagDeposit = "deposit"
|
|
|
|
flagVoter = "voter"
|
|
|
|
flagOption = "option"
|
|
|
|
flagDepositer = "depositer"
|
|
|
|
flagStatus = "status"
|
|
|
|
flagLatestProposalIDs = "latest"
|
2018-06-21 17:19:14 -07:00
|
|
|
)
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdSubmitProposal implements submitting a proposal transaction command.
|
2018-06-21 17:19:14 -07:00
|
|
|
func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
2018-07-05 22:22:50 -07:00
|
|
|
Use: "submit-proposal",
|
2018-06-21 17:19:14 -07:00
|
|
|
Short: "Submit a proposal along with an initial deposit",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
title := viper.GetString(flagTitle)
|
|
|
|
description := viper.GetString(flagDescription)
|
|
|
|
strProposalType := viper.GetString(flagProposalType)
|
|
|
|
initialDeposit := viper.GetString(flagDeposit)
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
|
|
|
|
cliCtx := context.NewCLIContext().
|
|
|
|
WithCodec(cdc).
|
|
|
|
WithLogger(os.Stdout).
|
|
|
|
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
|
|
|
|
|
|
|
|
fromAddr, err := cliCtx.GetFromAddress()
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
amount, err := sdk.ParseCoins(initialDeposit)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-10 17:59:07 -07:00
|
|
|
proposalType, err := gov.ProposalTypeFromString(strProposalType)
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-18 23:53:12 -07:00
|
|
|
msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount)
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
err = msg.ValidateBasic()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// 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.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
|
2018-06-21 17:19:14 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagTitle, "", "title of proposal")
|
|
|
|
cmd.Flags().String(flagDescription, "", "description of proposal")
|
|
|
|
cmd.Flags().String(flagProposalType, "", "proposalType of proposal")
|
|
|
|
cmd.Flags().String(flagDeposit, "", "deposit of proposal")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdDeposit implements depositing tokens for an active proposal.
|
2018-06-21 17:19:14 -07:00
|
|
|
func GetCmdDeposit(cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "deposit",
|
|
|
|
Short: "deposit tokens for activing proposal",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2018-08-06 11:11:30 -07:00
|
|
|
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
|
|
|
|
cliCtx := context.NewCLIContext().
|
|
|
|
WithCodec(cdc).
|
|
|
|
WithLogger(os.Stdout).
|
|
|
|
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
|
2018-07-18 23:53:12 -07:00
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
depositerAddr, err := cliCtx.GetFromAddress()
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
proposalID := viper.GetInt64(flagProposalID)
|
|
|
|
|
|
|
|
amount, err := sdk.ParseCoins(viper.GetString(flagDeposit))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-18 23:53:12 -07:00
|
|
|
msg := gov.NewMsgDeposit(depositerAddr, proposalID, amount)
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
err = msg.ValidateBasic()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// Build and sign the transaction, then broadcast to a Tendermint
|
|
|
|
// node.
|
|
|
|
return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
|
2018-06-21 17:19:14 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagProposalID, "", "proposalID of proposal depositing on")
|
|
|
|
cmd.Flags().String(flagDeposit, "", "amount of deposit")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdVote implements creating a new vote command.
|
2018-06-21 17:19:14 -07:00
|
|
|
func GetCmdVote(cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "vote",
|
|
|
|
Short: "vote for an active proposal, options: Yes/No/NoWithVeto/Abstain",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2018-08-06 11:11:30 -07:00
|
|
|
txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc)
|
|
|
|
cliCtx := context.NewCLIContext().
|
|
|
|
WithCodec(cdc).
|
|
|
|
WithLogger(os.Stdout).
|
|
|
|
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
voterAddr, err := cliCtx.GetFromAddress()
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
proposalID := viper.GetInt64(flagProposalID)
|
|
|
|
option := viper.GetString(flagOption)
|
|
|
|
|
2018-07-10 17:59:07 -07:00
|
|
|
byteVoteOption, err := gov.VoteOptionFromString(option)
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-18 23:53:12 -07:00
|
|
|
msg := gov.NewMsgVote(voterAddr, proposalID, byteVoteOption)
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
err = msg.ValidateBasic()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-18 23:53:12 -07:00
|
|
|
fmt.Printf("Vote[Voter:%s,ProposalID:%d,Option:%s]",
|
2018-08-06 11:11:30 -07:00
|
|
|
voterAddr.String(), msg.ProposalID, msg.Option.String(),
|
|
|
|
)
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// Build and sign the transaction, then broadcast to a Tendermint
|
|
|
|
// node.
|
|
|
|
return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg})
|
2018-06-21 17:19:14 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on")
|
|
|
|
cmd.Flags().String(flagOption, "", "vote option {Yes, No, NoWithVeto, Abstain}")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdQueryProposal implements the query proposal command.
|
2018-06-21 17:19:14 -07:00
|
|
|
func GetCmdQueryProposal(storeName string, cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "query-proposal",
|
|
|
|
Short: "query proposal details",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2018-08-06 11:11:30 -07:00
|
|
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
2018-06-21 17:19:14 -07:00
|
|
|
proposalID := viper.GetInt64(flagProposalID)
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
|
2018-06-21 17:19:14 -07:00
|
|
|
if len(res) == 0 || err != nil {
|
|
|
|
return errors.Errorf("proposalID [%d] is not existed", proposalID)
|
|
|
|
}
|
|
|
|
|
|
|
|
var proposal gov.Proposal
|
|
|
|
cdc.MustUnmarshalBinary(res, &proposal)
|
2018-08-06 11:11:30 -07:00
|
|
|
|
2018-07-10 17:59:07 -07:00
|
|
|
output, err := wire.MarshalJSONIndent(cdc, proposal)
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-06 11:11:30 -07:00
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
fmt.Println(string(output))
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagProposalID, "", "proposalID of proposal being queried")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-07-30 16:48:00 -07:00
|
|
|
// nolint: gocyclo
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdQueryProposals implements a query proposals command.
|
2018-07-30 16:48:00 -07:00
|
|
|
func GetCmdQueryProposals(storeName string, cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "query-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)
|
|
|
|
latestProposalsIDs := viper.GetInt64(flagLatestProposalIDs)
|
|
|
|
|
|
|
|
var err error
|
|
|
|
var voterAddr sdk.AccAddress
|
|
|
|
var depositerAddr sdk.AccAddress
|
|
|
|
var proposalStatus gov.ProposalStatus
|
|
|
|
|
|
|
|
if len(bechDepositerAddr) != 0 {
|
|
|
|
depositerAddr, err = sdk.AccAddressFromBech32(bechDepositerAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(bechVoterAddr) != 0 {
|
|
|
|
voterAddr, err = sdk.AccAddressFromBech32(bechVoterAddr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(strProposalStatus) != 0 {
|
|
|
|
proposalStatus, err = gov.ProposalStatusFromString(strProposalStatus)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
2018-07-30 16:48:00 -07:00
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err := cliCtx.QueryStore(gov.KeyNextProposalID, storeName)
|
2018-07-30 16:48:00 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var maxProposalID int64
|
|
|
|
cdc.MustUnmarshalBinary(res, &maxProposalID)
|
|
|
|
|
|
|
|
matchingProposals := []gov.Proposal{}
|
|
|
|
|
|
|
|
if latestProposalsIDs == 0 {
|
|
|
|
latestProposalsIDs = maxProposalID
|
|
|
|
}
|
|
|
|
|
|
|
|
for proposalID := maxProposalID - latestProposalsIDs; proposalID < maxProposalID; proposalID++ {
|
|
|
|
if voterAddr != nil {
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err = cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName)
|
2018-07-30 16:48:00 -07:00
|
|
|
if err != nil || len(res) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if depositerAddr != nil {
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err = cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName)
|
2018-07-30 16:48:00 -07:00
|
|
|
if err != nil || len(res) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err = cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
|
2018-07-30 16:48:00 -07:00
|
|
|
if err != nil || len(res) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var proposal gov.Proposal
|
|
|
|
cdc.MustUnmarshalBinary(res, &proposal)
|
|
|
|
|
|
|
|
if len(strProposalStatus) != 0 {
|
|
|
|
if proposal.GetStatus() != proposalStatus {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
matchingProposals = append(matchingProposals, proposal)
|
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
|
|
|
}
|
2018-08-06 11:11:30 -07:00
|
|
|
|
2018-07-30 16:48:00 -07:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagLatestProposalIDs, "", "(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")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
// Command to Get a Proposal Information
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdQueryVote implements the query proposal vote command.
|
2018-06-21 17:19:14 -07:00
|
|
|
func GetCmdQueryVote(storeName string, cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "query-vote",
|
|
|
|
Short: "query vote",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2018-08-06 11:11:30 -07:00
|
|
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
2018-06-21 17:19:14 -07:00
|
|
|
proposalID := viper.GetInt64(flagProposalID)
|
|
|
|
|
2018-07-09 16:06:05 -07:00
|
|
|
voterAddr, err := sdk.AccAddressFromBech32(viper.GetString(flagVoter))
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err := cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName)
|
2018-06-21 17:19:14 -07:00
|
|
|
if len(res) == 0 || err != nil {
|
|
|
|
return errors.Errorf("proposalID [%d] does not exist", proposalID)
|
|
|
|
}
|
|
|
|
|
|
|
|
var vote gov.Vote
|
|
|
|
cdc.MustUnmarshalBinary(res, &vote)
|
2018-08-06 11:11:30 -07:00
|
|
|
|
2018-07-10 17:59:07 -07:00
|
|
|
output, err := wire.MarshalJSONIndent(cdc, vote)
|
2018-06-21 17:19:14 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-08-06 11:11:30 -07:00
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
fmt.Println(string(output))
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagProposalID, "", "proposalID of proposal voting on")
|
|
|
|
cmd.Flags().String(flagVoter, "", "bech32 voter address")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
2018-07-19 17:02:46 -07:00
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
// GetCmdQueryVotes implements the command to query for proposal votes.
|
2018-07-19 17:02:46 -07:00
|
|
|
func GetCmdQueryVotes(storeName string, cdc *wire.Codec) *cobra.Command {
|
|
|
|
cmd := &cobra.Command{
|
|
|
|
Use: "query-votes",
|
|
|
|
Short: "query votes on a proposal",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
2018-08-06 11:11:30 -07:00
|
|
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
2018-07-19 17:02:46 -07:00
|
|
|
proposalID := viper.GetInt64(flagProposalID)
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName)
|
2018-07-19 17:02:46 -07:00
|
|
|
if len(res) == 0 || err != nil {
|
|
|
|
return errors.Errorf("proposalID [%d] does not exist", proposalID)
|
|
|
|
}
|
|
|
|
|
|
|
|
var proposal gov.Proposal
|
|
|
|
cdc.MustUnmarshalBinary(res, &proposal)
|
|
|
|
|
|
|
|
if proposal.GetStatus() != gov.StatusVotingPeriod {
|
|
|
|
fmt.Println("Proposal not in voting period.")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:11:30 -07:00
|
|
|
res2, err := cliCtx.QuerySubspace(gov.KeyVotesSubspace(proposalID), storeName)
|
2018-07-19 17:02:46 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var votes []gov.Vote
|
|
|
|
for i := 0; i < len(res2); i++ {
|
|
|
|
var vote gov.Vote
|
|
|
|
cdc.MustUnmarshalBinary(res2[i].Value, &vote)
|
|
|
|
votes = append(votes, vote)
|
|
|
|
}
|
|
|
|
|
|
|
|
output, err := wire.MarshalJSONIndent(cdc, votes)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println(string(output))
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Flags().String(flagProposalID, "", "proposalID of which proposal's votes are being queried")
|
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|