diff --git a/PENDING.md b/PENDING.md index 9a5f51d8aa7a..3da2554fc79a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -32,8 +32,14 @@ FEATURES IMPROVEMENTS * Gaia REST API (`gaiacli advanced rest-server`) + * \#2879, \#2880 Update deposit and vote endpoints to perform a direct txs query + when a given proposal is inactive and thus having votes and deposits removed + from state. * Gaia CLI (`gaiacli`) + * \#2879, \#2880 Update deposit and vote CLI commands to perform a direct txs query + when a given proposal is inactive and thus having votes and deposits removed + from state. * Gaia * [\#3021](https://github.com/cosmos/cosmos-sdk/pull/3021) Add `--gentx-dir` to `gaiad collect-gentxs` to specify a directory from which collect and load gentxs. diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 164edb0f39cf..8e1926a1cccc 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -686,6 +686,7 @@ func TestProposalsQuery(t *testing.T) { proposals := getProposalsFilterStatus(t, port, gov.StatusDepositPeriod) require.Len(t, proposals, 1) require.Equal(t, proposalID1, proposals[0].GetProposalID()) + // Only proposals #2 and #3 should be in Voting Period proposals = getProposalsFilterStatus(t, port, gov.StatusVotingPeriod) require.Len(t, proposals, 2) diff --git a/client/tx/search.go b/client/tx/search.go index 422f4827668a..4f104008ad37 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -58,7 +58,7 @@ $ gaiacli query txs --tags ':&:' } cliCtx := context.NewCLIContext().WithCodec(cdc) - txs, err := searchTxs(cliCtx, cdc, tmTags) + txs, err := SearchTxs(cliCtx, cdc, tmTags) if err != nil { return err } @@ -89,7 +89,10 @@ $ gaiacli query txs --tags ':&:' return cmd } -func searchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]Info, error) { +// SearchTxs performs a search for transactions for a given set of tags via +// Tendermint RPC. It returns a slice of Info object containing txs and metadata. +// An error is returned if the query fails. +func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string) ([]Info, error) { if len(tags) == 0 { return nil, errors.New("must declare at least one tag to search") } @@ -172,7 +175,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. tags = append(tags, tag) } - txs, err = searchTxs(cliCtx, cdc, tags) + txs, err = SearchTxs(cliCtx, cdc, tags) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index 06dabfcddfd5..61cc00242891 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" - govClientUtils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" + gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" ) // GetCmdQueryProposal implements the query proposal command. @@ -106,7 +106,7 @@ $ gaiacli query gov proposals --status (DepositPeriod|VotingPeriod|Passed|Reject } if len(strProposalStatus) != 0 { - proposalStatus, err := gov.ProposalStatusFromString(govClientUtils.NormalizeProposalStatus(strProposalStatus)) + proposalStatus, err := gov.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus)) if err != nil { return err } @@ -160,8 +160,9 @@ func GetCmdQueryVote(queryRoute string, cdc *codec.Codec) *cobra.Command { 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: +Query details for a single vote on a proposal given its identifier. +Example: $ gaiacli query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk `), RunE: func(cmd *cobra.Command, args []string) error { @@ -179,25 +180,32 @@ $ gaiacli query gov vote 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk 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 } + var vote gov.Vote + cdc.UnmarshalJSON(res, &vote) + + if vote.Empty() { + res, err = gcutils.QueryVoteByTxQuery(cdc, cliCtx, params) + if err != nil { + return err + } + } + fmt.Println(string(res)) return nil }, @@ -213,8 +221,9 @@ func GetCmdQueryVotes(queryRoute string, cdc *codec.Codec) *cobra.Command { 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: +Query vote details for a single proposal by its identifier. +Example: $ gaiacli query gov votes 1 `), RunE: func(cmd *cobra.Command, args []string) error { @@ -226,21 +235,30 @@ $ gaiacli query gov votes 1 return fmt.Errorf("proposal-id %s not a valid int, please input a valid proposal-id", args[0]) } + params := gov.NewQueryProposalParams(proposalID) + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + // check to see if the proposal is in the store - _, err = queryProposal(proposalID, cliCtx, cdc, queryRoute) + res, 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 { + var proposal gov.Proposal + if err := cdc.UnmarshalJSON(res, &proposal); err != nil { return err } - // Query store - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz) + propStatus := proposal.GetStatus() + if !(propStatus == gov.StatusVotingPeriod || propStatus == gov.StatusDepositPeriod) { + res, err = gcutils.QueryVotesByTxQuery(cdc, cliCtx, params) + } else { + res, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz) + } + if err != nil { return err } @@ -261,8 +279,9 @@ func GetCmdQueryDeposit(queryRoute string, cdc *codec.Codec) *cobra.Command { 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: +Query details for a single proposal deposit on a proposal by its identifier. +Example: $ gaiacli query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk `), RunE: func(cmd *cobra.Command, args []string) error { @@ -280,25 +299,32 @@ $ gaiacli query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk 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 } + var deposit gov.Deposit + cdc.UnmarshalJSON(res, &deposit) + + if deposit.Empty() { + res, err = gcutils.QueryDepositByTxQuery(cdc, cliCtx, params) + if err != nil { + return err + } + } + fmt.Println(string(res)) return nil }, @@ -327,21 +353,30 @@ $ gaiacli query gov deposits 1 return fmt.Errorf("proposal-id %s not a valid uint, please input a valid proposal-id", args[0]) } + params := gov.NewQueryProposalParams(proposalID) + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + // check to see if the proposal is in the store - _, err = queryProposal(proposalID, cliCtx, cdc, queryRoute) + res, 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 { + var proposal gov.Proposal + if err := cdc.UnmarshalJSON(res, &proposal); err != nil { return err } - // Query store - res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz) + propStatus := proposal.GetStatus() + if !(propStatus == gov.StatusVotingPeriod || propStatus == gov.StatusDepositPeriod) { + res, err = gcutils.QueryDepositsByTxQuery(cdc, cliCtx, params) + } else { + res, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/deposits", queryRoute), bz) + } + if err != nil { return err } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 821da0cc7de6..1b51566f8442 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" + gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/gorilla/mux" "github.com/pkg/errors" @@ -225,9 +226,7 @@ func queryProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return } - params := gov.QueryProposalParams{ - ProposalID: proposalID, - } + params := gov.NewQueryProposalParams(proposalID) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -263,7 +262,27 @@ func queryDepositsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return } - res, err := cliCtx.QueryWithData("custom/gov/deposits", bz) + res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + var proposal gov.Proposal + if err := cdc.UnmarshalJSON(res, &proposal); err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + // For inactive proposals we must query the txs directly to get the deposits + // as they're no longer in state. + propStatus := proposal.GetStatus() + if !(propStatus == gov.StatusVotingPeriod || propStatus == gov.StatusDepositPeriod) { + res, err = gcutils.QueryDepositsByTxQuery(cdc, cliCtx, params) + } else { + res, err = cliCtx.QueryWithData("custom/gov/deposits", bz) + } + if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return @@ -302,10 +321,7 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } - params := gov.QueryDepositParams{ - ProposalID: proposalID, - Depositor: depositorAddr, - } + params := gov.NewQueryDepositParams(proposalID, depositorAddr) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -321,16 +337,29 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han var deposit gov.Deposit cdc.UnmarshalJSON(res, &deposit) + + // For an empty deposit, either the proposal does not exist or is inactive in + // which case the deposit would be removed from state and should be queried + // for directly via a txs query. if deposit.Empty() { - res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinaryLengthPrefixed(gov.QueryProposalParams{params.ProposalID})) + bz, err := cdc.MarshalJSON(gov.NewQueryProposalParams(proposalID)) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, err = cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { - err := errors.Errorf("proposalID [%d] does not exist", proposalID) + err := fmt.Errorf("proposalID %d does not exist", proposalID) utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - err = errors.Errorf("depositor [%s] did not deposit on proposalID [%d]", bechDepositorAddr, proposalID) - utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return + + res, err = gcutils.QueryDepositByTxQuery(cdc, cliCtx, params) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } } utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) @@ -366,10 +395,8 @@ func queryVoteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle return } - params := gov.QueryVoteParams{ - Voter: voterAddr, - ProposalID: proposalID, - } + params := gov.NewQueryVoteParams(proposalID, voterAddr) + bz, err := cdc.MarshalJSON(params) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) @@ -384,22 +411,31 @@ func queryVoteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle var vote gov.Vote cdc.UnmarshalJSON(res, &vote) + + // For an empty vote, either the proposal does not exist or is inactive in + // which case the vote would be removed from state and should be queried for + // directly via a txs query. if vote.Empty() { - bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID}) + bz, err := cdc.MarshalJSON(gov.NewQueryProposalParams(proposalID)) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) + + res, err = cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { - err := errors.Errorf("proposalID [%d] does not exist", proposalID) + err := fmt.Errorf("proposalID %d does not exist", proposalID) utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID) - utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) - return + + res, err = gcutils.QueryVoteByTxQuery(cdc, cliCtx, params) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } } + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -429,7 +465,27 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) return } - res, err := cliCtx.QueryWithData("custom/gov/votes", bz) + res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + var proposal gov.Proposal + if err := cdc.UnmarshalJSON(res, &proposal); err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + // For inactive proposals we must query the txs directly to get the votes + // as they're no longer in state. + propStatus := proposal.GetStatus() + if !(propStatus == gov.StatusVotingPeriod || propStatus == gov.StatusDepositPeriod) { + res, err = gcutils.QueryVotesByTxQuery(cdc, cliCtx, params) + } else { + res, err = cliCtx.QueryWithData("custom/gov/votes", bz) + } + if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go new file mode 100644 index 000000000000..fdae6f3b059f --- /dev/null +++ b/x/gov/client/utils/query.go @@ -0,0 +1,180 @@ +package utils + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov/tags" +) + +// QueryDepositsByTxQuery will query for deposits via a direct txs tags query. It +// will fetch and build deposits directly from the returned txs and return a +// JSON marshalled result or any error that occurred. +// +// NOTE: SearchTxs is used to facilitate the txs query which does not currently +// support configurable pagination. +func QueryDepositsByTxQuery( + cdc *codec.Codec, cliCtx context.CLIContext, params gov.QueryProposalParams, +) ([]byte, error) { + + tags := []string{ + fmt.Sprintf("%s='%s'", tags.Action, tags.ActionProposalDeposit), + fmt.Sprintf("%s='%s'", tags.ProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + } + + infos, err := tx.SearchTxs(cliCtx, cdc, tags) + if err != nil { + return nil, err + } + + var deposits []gov.Deposit + + for _, info := range infos { + for _, msg := range info.Tx.GetMsgs() { + if msg.Type() == gov.TypeMsgDeposit { + depMsg := msg.(gov.MsgDeposit) + + deposits = append(deposits, gov.Deposit{ + Depositor: depMsg.Depositor, + ProposalID: params.ProposalID, + Amount: depMsg.Amount, + }) + } + } + } + + if cliCtx.Indent { + return cdc.MarshalJSONIndent(deposits, "", " ") + } + + return cdc.MarshalJSON(deposits) +} + +// QueryVotesByTxQuery will query for votes via a direct txs tags query. It +// will fetch and build votes directly from the returned txs and return a JSON +// marshalled result or any error that occurred. +// +// NOTE: SearchTxs is used to facilitate the txs query which does not currently +// support configurable pagination. +func QueryVotesByTxQuery( + cdc *codec.Codec, cliCtx context.CLIContext, params gov.QueryProposalParams, +) ([]byte, error) { + + tags := []string{ + fmt.Sprintf("%s='%s'", tags.Action, tags.ActionProposalVote), + fmt.Sprintf("%s='%s'", tags.ProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + } + + infos, err := tx.SearchTxs(cliCtx, cdc, tags) + if err != nil { + return nil, err + } + + var votes []gov.Vote + + for _, info := range infos { + for _, msg := range info.Tx.GetMsgs() { + if msg.Type() == gov.TypeMsgVote { + voteMsg := msg.(gov.MsgVote) + + votes = append(votes, gov.Vote{ + Voter: voteMsg.Voter, + ProposalID: params.ProposalID, + Option: voteMsg.Option, + }) + } + } + } + + if cliCtx.Indent { + return cdc.MarshalJSONIndent(votes, "", " ") + } + + return cdc.MarshalJSON(votes) +} + +// QueryVoteByTxQuery will query for a single vote via a direct txs tags query. +func QueryVoteByTxQuery( + cdc *codec.Codec, cliCtx context.CLIContext, params gov.QueryVoteParams, +) ([]byte, error) { + + tags := []string{ + fmt.Sprintf("%s='%s'", tags.Action, tags.ActionProposalVote), + fmt.Sprintf("%s='%s'", tags.ProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s='%s'", tags.Voter, []byte(params.Voter.String())), + } + + infos, err := tx.SearchTxs(cliCtx, cdc, tags) + if err != nil { + return nil, err + } + + for _, info := range infos { + for _, msg := range info.Tx.GetMsgs() { + // there should only be a single vote under the given conditions + if msg.Type() == gov.TypeMsgVote { + voteMsg := msg.(gov.MsgVote) + + vote := gov.Vote{ + Voter: voteMsg.Voter, + ProposalID: params.ProposalID, + Option: voteMsg.Option, + } + + if cliCtx.Indent { + return cdc.MarshalJSONIndent(vote, "", " ") + } + + return cdc.MarshalJSON(vote) + } + } + } + + err = fmt.Errorf("address '%s' did not vote on proposalID %d", params.Voter, params.ProposalID) + return nil, err +} + +// QueryDepositByTxQuery will query for a single deposit via a direct txs tags +// query. +func QueryDepositByTxQuery( + cdc *codec.Codec, cliCtx context.CLIContext, params gov.QueryDepositParams, +) ([]byte, error) { + + tags := []string{ + fmt.Sprintf("%s='%s'", tags.Action, tags.ActionProposalDeposit), + fmt.Sprintf("%s='%s'", tags.ProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), + fmt.Sprintf("%s='%s'", tags.Depositor, []byte(params.Depositor.String())), + } + + infos, err := tx.SearchTxs(cliCtx, cdc, tags) + if err != nil { + return nil, err + } + + for _, info := range infos { + for _, msg := range info.Tx.GetMsgs() { + // there should only be a single deposit under the given conditions + if msg.Type() == gov.TypeMsgDeposit { + depMsg := msg.(gov.MsgDeposit) + + deposit := gov.Deposit{ + Depositor: depMsg.Depositor, + ProposalID: params.ProposalID, + Amount: depMsg.Amount, + } + + if cliCtx.Indent { + return cdc.MarshalJSONIndent(deposit, "", " ") + } + + return cdc.MarshalJSON(deposit) + } + } + } + + err = fmt.Errorf("address '%s' did not deposit to proposalID %d", params.Depositor, params.ProposalID) + return nil, err +} diff --git a/x/gov/handler.go b/x/gov/handler.go index 80d8bda40fb9..4c2d298ff37c 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -18,24 +18,24 @@ func NewHandler(keeper Keeper) sdk.Handler { case MsgVote: return handleMsgVote(ctx, keeper, msg) default: - errMsg := "Unrecognized gov msg type" + errMsg := fmt.Sprintf("Unrecognized gov msg type: %T", msg) return sdk.ErrUnknownRequest(errMsg).Result() } } } func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitProposal) sdk.Result { - proposal := keeper.NewTextProposal(ctx, msg.Title, msg.Description, msg.ProposalType) + proposalID := proposal.GetProposalID() + proposalIDBytes := []byte(fmt.Sprintf("%d", proposalID)) - err, votingStarted := keeper.AddDeposit(ctx, proposal.GetProposalID(), msg.Proposer, msg.InitialDeposit) + err, votingStarted := keeper.AddDeposit(ctx, proposalID, msg.Proposer, msg.InitialDeposit) if err != nil { return err.Result() } - proposalIDBytes := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal.GetProposalID()) - resTags := sdk.NewTags( + tags.Action, tags.ActionProposalSubmitted, tags.Proposer, []byte(msg.Proposer.String()), tags.ProposalID, proposalIDBytes, ) @@ -45,22 +45,20 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos } return sdk.Result{ - Data: proposalIDBytes, + Data: keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID), Tags: resTags, } } func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result { - err, votingStarted := keeper.AddDeposit(ctx, msg.ProposalID, msg.Depositor, msg.Amount) if err != nil { return err.Result() } - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(msg.ProposalID) - - // TODO: Add tag for if voting period started + proposalIDBytes := []byte(fmt.Sprintf("%d", msg.ProposalID)) resTags := sdk.NewTags( + tags.Action, tags.ActionProposalDeposit, tags.Depositor, []byte(msg.Depositor.String()), tags.ProposalID, proposalIDBytes, ) @@ -75,33 +73,29 @@ func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result } func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) sdk.Result { - err := keeper.AddVote(ctx, msg.ProposalID, msg.Voter, msg.Option) if err != nil { return err.Result() } - proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(msg.ProposalID) - - resTags := sdk.NewTags( - tags.Voter, []byte(msg.Voter.String()), - tags.ProposalID, proposalIDBytes, - ) return sdk.Result{ - Tags: resTags, + Tags: sdk.NewTags( + tags.Action, tags.ActionProposalVote, + tags.Voter, []byte(msg.Voter.String()), + tags.ProposalID, []byte(fmt.Sprintf("%d", msg.ProposalID)), + ), } } // Called every block, process inflation, update validator set func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { - logger := ctx.Logger().With("module", "x/gov") - resTags = sdk.NewTags() inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) for ; inactiveIterator.Valid(); inactiveIterator.Next() { var proposalID uint64 + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID) inactiveProposal := keeper.GetProposal(ctx, proposalID) keeper.DeleteProposal(ctx, proposalID) @@ -119,11 +113,13 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { ), ) } + inactiveIterator.Close() activeIterator := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) for ; activeIterator.Valid(); activeIterator.Next() { var proposalID uint64 + keeper.cdc.MustUnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID) activeProposal := keeper.GetProposal(ctx, proposalID) passes, tallyResults := tally(ctx, keeper, activeProposal) @@ -138,9 +134,9 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { activeProposal.SetStatus(StatusRejected) action = tags.ActionProposalRejected } + activeProposal.SetTallyResult(tallyResults) keeper.SetProposal(ctx, activeProposal) - keeper.RemoveFromActiveProposalQueue(ctx, activeProposal.GetVotingEndTime(), activeProposal.GetProposalID()) logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", @@ -149,6 +145,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { resTags = resTags.AppendTag(tags.Action, action) resTags = resTags.AppendTag(tags.ProposalID, []byte(string(proposalID))) } + activeIterator.Close() return resTags diff --git a/x/gov/msgs.go b/x/gov/msgs.go index 6465b2a2d5dd..12f5fd33af26 100644 --- a/x/gov/msgs.go +++ b/x/gov/msgs.go @@ -6,8 +6,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// name to idetify transaction types -const MsgRoute = "gov" +// Governance message types and routes +const ( + MsgRoute = "gov" + TypeMsgDeposit = "deposit" + TypeMsgVote = "vote" + TypeMsgSubmitProposal = "submit_proposal" +) var _, _, _ sdk.Msg = MsgSubmitProposal{}, MsgDeposit{}, MsgVote{} @@ -33,7 +38,7 @@ func NewMsgSubmitProposal(title string, description string, proposalType Proposa //nolint func (msg MsgSubmitProposal) Route() string { return MsgRoute } -func (msg MsgSubmitProposal) Type() string { return "submit_proposal" } +func (msg MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal } // Implements Msg. func (msg MsgSubmitProposal) ValidateBasic() sdk.Error { @@ -100,7 +105,7 @@ func NewMsgDeposit(depositor sdk.AccAddress, proposalID uint64, amount sdk.Coins // Implements Msg. // nolint func (msg MsgDeposit) Route() string { return MsgRoute } -func (msg MsgDeposit) Type() string { return "deposit" } +func (msg MsgDeposit) Type() string { return TypeMsgDeposit } // Implements Msg. func (msg MsgDeposit) ValidateBasic() sdk.Error { @@ -161,7 +166,7 @@ func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) MsgV // Implements Msg. // nolint func (msg MsgVote) Route() string { return MsgRoute } -func (msg MsgVote) Type() string { return "vote" } +func (msg MsgVote) Type() string { return TypeMsgVote } // Implements Msg. func (msg MsgVote) ValidateBasic() sdk.Error { diff --git a/x/gov/tags/tags.go b/x/gov/tags/tags.go index a58729dfed54..33986f9720d3 100644 --- a/x/gov/tags/tags.go +++ b/x/gov/tags/tags.go @@ -1,14 +1,17 @@ -// nolint package tags import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +// Governance tags var ( - ActionProposalDropped = []byte("proposal-dropped") - ActionProposalPassed = []byte("proposal-passed") - ActionProposalRejected = []byte("proposal-rejected") + ActionProposalDropped = []byte("proposal-dropped") + ActionProposalPassed = []byte("proposal-passed") + ActionProposalRejected = []byte("proposal-rejected") + ActionProposalSubmitted = []byte("proposal-submitted") + ActionProposalVote = []byte("proposal-vote") + ActionProposalDeposit = []byte("proposal-deposit") Action = sdk.TagAction Proposer = "proposer"