From 804baa70f442c5d23c70bf0bdb934a4fb1659790 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 3 Aug 2018 12:55:00 -0700 Subject: [PATCH] added querier to gov module --- baseapp/baseapp.go | 14 ++- baseapp/queryrouter.go | 10 +-- types/queryable.go | 5 +- x/gov/keeper.go | 60 +++++++++++++ x/gov/queryable.go | 189 +++++++++++++++++++++++++++++++++-------- 5 files changed, 230 insertions(+), 48 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 22f5927909..8f08d2abfc 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -393,10 +393,18 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { // "/custom" prefix for keeper queries - queryable := app.queryrouter.Route(path[1]) + querier := app.queryrouter.Route(path[1]) ctx := app.checkState.ctx - res, err := queryable.Query(ctx, path[2:], req) - return + resBytes, err := querier(ctx, path[2:], req) + if err != nil { + return abci.ResponseQuery{ + Code: uint32(err.ABCICode()), + } + } + return abci.ResponseQuery{ + Code: uint32(sdk.ABCICodeOK), + Value: resBytes, + } } // BeginBlock implements the ABCI application interface. diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go index 0a6b4dfb19..ade071f1f7 100644 --- a/baseapp/queryrouter.go +++ b/baseapp/queryrouter.go @@ -6,14 +6,14 @@ import ( // QueryRouter provides queryables for each query path. type QueryRouter interface { - AddRoute(r string, h sdk.CustomQueryable) (rtr QueryRouter) - Route(path string) (h sdk.CustomQueryable) + AddRoute(r string, h sdk.Querier) (rtr QueryRouter) + Route(path string) (h sdk.Querier) } // map a transaction type to a handler and an initgenesis function type queryroute struct { r string - h sdk.CustomQueryable + h sdk.Querier } type queryrouter struct { @@ -30,7 +30,7 @@ func NewQueryRouter() *queryrouter { } // AddRoute - TODO add description -func (rtr *queryrouter) AddRoute(r string, h sdk.CustomQueryable) QueryRouter { +func (rtr *queryrouter) AddRoute(r string, h sdk.Querier) QueryRouter { if !isAlphaNumeric(r) { panic("route expressions can only contain alphanumeric characters") } @@ -41,7 +41,7 @@ func (rtr *queryrouter) AddRoute(r string, h sdk.CustomQueryable) QueryRouter { // Route - TODO add description // TODO handle expressive matches. -func (rtr *queryrouter) Route(path string) (h sdk.CustomQueryable) { +func (rtr *queryrouter) Route(path string) (h sdk.Querier) { for _, route := range rtr.routes { if route.r == path { return route.h diff --git a/types/queryable.go b/types/queryable.go index 9ad36b8d28..9223332bc8 100644 --- a/types/queryable.go +++ b/types/queryable.go @@ -2,6 +2,5 @@ package types import abci "github.com/tendermint/tendermint/abci/types" -type CustomQueryable interface { - Query(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error) -} +// Type for querier functions on keepers to implement to handle custom queries +type Querier = func(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 8a23ad2485..45af4b471d 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -108,6 +108,52 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) { store.Delete(KeyProposal(proposal.GetProposalID())) } +// nolint: gocyclo +// Get Proposal from store by ProposalID +func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal { + + maxProposalID, err := keeper.peekCurrentProposalID(ctx) + if err != nil { + return nil + } + + matchingProposals := []Proposal{} + + if numLatest <= 0 { + numLatest = maxProposalID + } + + for proposalID := maxProposalID - numLatest; proposalID < maxProposalID; proposalID++ { + if voterAddr != nil && len(voterAddr) != 0 { + _, found := keeper.GetVote(ctx, proposalID, voterAddr) + if !found { + continue + } + } + + if depositerAddr != nil && len(depositerAddr) != 0 { + _, found := keeper.GetDeposit(ctx, proposalID, depositerAddr) + if !found { + continue + } + } + + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + continue + } + + if validProposalStatus(status) { + if proposal.GetStatus() != status { + continue + } + } + + matchingProposals = append(matchingProposals, proposal) + } + return matchingProposals +} + func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk.Error { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -131,6 +177,7 @@ func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) { return } +// Gets the next available ProposalID and increments it func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -143,6 +190,19 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd return proposalID, nil } +// Peeks the next available ProposalID without incrementing it +func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { + store := ctx.KVStore(keeper.storeKey) + bz := store.Get(KeyNextProposalID) + if bz == nil { + return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") + } + keeper.cdc.MustUnmarshalBinary(bz, &proposalID) + bz = keeper.cdc.MustMarshalBinary(proposalID + 1) + store.Set(KeyNextProposalID, bz) + return proposalID, nil +} + func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { proposal.SetVotingStartBlock(ctx.BlockHeight()) proposal.SetStatus(StatusVotingPeriod) diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 059d0d1dbb..c0e66d0bf7 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -5,52 +5,167 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) -type Querier struct { - keeper Keeper -} - -func NewQuerier(keeper Keeper) { - return Querier{ - keeper: keeper, +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case "proposal": + return queryProposal(ctx, path[1:], req, keeper) + case "deposit": + return queryDeposit(ctx, path[1:], req, keeper) + case "vote": + return queryVote(ctx, path[1:], req, keeper) + case "deposits": + return queryDeposits(ctx, path[1:], req, keeper) + case "votes": + return queryVotes(ctx, path[1:], req, keeper) + case "proposals": + return queryProposals(ctx, path[1:], req, keeper) + case "tally": + return queryTally(ctx, path[1:], req, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown gov query endpoint") + } } } -func (keeper Keeper) Query(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - switch path[0] { - case "tally": - return QueryTally(ctx, path[1:], req) - case "proposal": - return handleMsgSubmitProposal(ctx, keeper, msg) - case MsgVote: - return handleMsgVote(ctx, keeper, msg) - default: - errMsg := "Unrecognized gov msg type" - return sdk.ErrUnknownRequest(errMsg).Result() - } +// Params for query 'custom/gov/proposal' +type QueryProposalParams struct { + ProposalID int64 } -func QueryProposal(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - var proposalID int64 - err := keeper.cdc.UnmarshalBinary(req.Data, proposalID) - if err != nil { - return []byte{}, sdk.ErrUnknownRequest() +func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") } - proposal := keeper.GetProposal(ctx, proposalID) + + proposal := keeper.GetProposal(ctx, params.ProposalID) if proposal == nil { - return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + return []byte{}, ErrUnknownProposal(DefaultCodespace, params.ProposalID) } return keeper.cdc.MustMarshalBinary(proposal), nil } -func QueryTally(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - var proposalID int64 - err := keeper.cdc.UnmarshalBinary(req.Data, proposalID) - if err != nil { - return []byte{}, sdk.ErrUnknownRequest() - } - proposal := keeper.GetProposal(ctx, proposalID) - if proposal == nil { - return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) - } - passes, _ := tally(ctx, keeper, proposal) +// Params for query 'custom/gov/deposit' +type QueryDepositParams struct { + ProposalID int64 + Depositer sdk.AccAddress +} + +func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryDepositParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, params) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) + return keeper.cdc.MustMarshalBinary(deposit), nil +} + +// Params for query 'custom/gov/vote' +type QueryVoteParams struct { + ProposalID int64 + Voter sdk.AccAddress +} + +func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryVoteParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) + return keeper.cdc.MustMarshalBinary(vote), nil +} + +// Params for query 'custom/gov/deposits' +type QueryDepositsParams struct { + ProposalID int64 +} + +func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryDepositParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + var deposits []Deposit + depositsIterator := keeper.GetDeposits(ctx, params.ProposalID) + for ; depositsIterator.Valid(); depositsIterator.Next() { + deposit := Deposit{} + keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + deposits = append(deposits, deposit) + } + + return keeper.cdc.MustMarshalBinary(deposits), nil +} + +// Params for query 'custom/gov/votes' +type QueryVotesParams struct { + ProposalID int64 +} + +func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryVotesParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + var votes []Vote + votesIterator := keeper.GetVotes(ctx, params.ProposalID) + for ; votesIterator.Valid(); votesIterator.Next() { + vote := Vote{} + keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + votes = append(votes, vote) + } + + return keeper.cdc.MustMarshalBinary(votes), nil +} + +// Params for query 'custom/gov/proposals' +type QueryProposalsParams struct { + Voter sdk.AccAddress + Depositer sdk.AccAddress + ProposalStatus ProposalStatus + NumLatestProposals int64 +} + +func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalsParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) + + bz := keeper.cdc.MustMarshalBinary(proposals) + return bz, nil +} + +// Params for query 'custom/gov/tally' +type QueryTallyParams struct { + ProposalID int64 +} + +func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // TODO: Dependant on #1914 + + // var proposalID int64 + // err2 := keeper.cdc.UnmarshalBinary(req.Data, proposalID) + // if err2 != nil { + // return []byte{}, sdk.ErrUnknownRequest() + // } + + // proposal := keeper.GetProposal(ctx, proposalID) + // if proposal == nil { + // return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + // } + // _, tallyResult, _ := tally(ctx, keeper, proposal) + return nil, nil }