diff --git a/PENDING.md b/PENDING.md index ec7941b86fa9..0dabed15b931 100644 --- a/PENDING.md +++ b/PENDING.md @@ -35,6 +35,7 @@ FEATURES * [cosmos-sdk-cli] Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd * This allows SDK users to initialize a new project repository. * [tests] Remotenet commands for AWS (awsnet) +* [cli] added `gov query-proposals` command to CLI. Can filter by `depositer`, `voter`, and `status` IMPROVEMENTS * [baseapp] Allow any alphanumeric character in route diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 7fa438e9dcb1..70b2421b3fbf 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -176,6 +176,9 @@ func TestGaiaCLISubmitProposal(t *testing.T) { fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags)) require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak").Int64()) + proposalsQuery := tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals %v", flags)) + require.Equal(t, "No matching proposals found", proposalsQuery) + // unbond a single share spStr := fmt.Sprintf("gaiacli gov submit-proposal %v", flags) spStr += fmt.Sprintf(" --from=%s", "foo") @@ -194,6 +197,9 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, int64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus()) + proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals %v", flags)) + require.Equal(t, " 1 - Test", proposalsQuery) + depositStr := fmt.Sprintf("gaiacli gov deposit %v", flags) depositStr += fmt.Sprintf(" --from=%s", "foo") depositStr += fmt.Sprintf(" --deposit=%s", "10steak") @@ -224,6 +230,12 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Len(t, votes, 1) require.Equal(t, int64(1), votes[0].ProposalID) require.Equal(t, gov.OptionYes, votes[0].Option) + + proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --status=DepositPeriod %v", flags)) + require.Equal(t, "No matching proposals found", proposalsQuery) + + proposalsQuery = tests.ExecuteT(t, fmt.Sprintf("gaiacli gov query-proposals --status=VotingPeriod %v", flags)) + require.Equal(t, " 1 - Test", proposalsQuery) } //___________________________________________________________________________________ diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 7c66cb9ef7bf..9c4d67b8c4a8 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -113,6 +113,7 @@ func main() { govcmd.GetCmdQueryProposal("gov", cdc), govcmd.GetCmdQueryVote("gov", cdc), govcmd.GetCmdQueryVotes("gov", cdc), + govcmd.GetCmdQueryProposals("gov", cdc), )...) govCmd.AddCommand( client.PostCommands( diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 45b49a54223b..e8fae10b0f12 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -22,6 +22,8 @@ const ( flagDeposit = "deposit" flagVoter = "voter" flagOption = "option" + flagDepositer = "depositer" + flagStatus = "status" ) // submit a proposal tx @@ -203,6 +205,105 @@ func GetCmdQueryProposal(storeName string, cdc *wire.Codec) *cobra.Command { return cmd } +// nolint: gocyclo +// Command to Query Proposals +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) + + 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 + } + } + + ctx := context.NewCoreContextFromViper() + + res, err := ctx.QueryStore(gov.KeyNextProposalID, storeName) + if err != nil { + return err + } + var maxProposalID int64 + cdc.MustUnmarshalBinary(res, &maxProposalID) + + matchingProposals := []gov.Proposal{} + + for proposalID := int64(0); proposalID < maxProposalID; proposalID++ { + if voterAddr != nil { + res, err = ctx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) + if err != nil || len(res) == 0 { + continue + } + } + + if depositerAddr != nil { + res, err = ctx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) + if err != nil || len(res) == 0 { + continue + } + } + + res, err = ctx.QueryStore(gov.KeyProposal(proposalID), storeName) + 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()) + } + return nil + }, + } + + 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 +} + // Command to Get a Proposal Information func GetCmdQueryVote(storeName string, cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{