feat: v1beta1 gov query server (#11029)

This commit is contained in:
Callum Waters 2022-02-01 19:25:50 +01:00 committed by GitHub
parent 4e8172d1a1
commit df36a6fe5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 577 additions and 6 deletions

View File

@ -124,6 +124,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints.
* [\#9995](https://github.com/cosmos/cosmos-sdk/pull/9995) Increased gas cost for creating proposals.
* [\#11029](https://github.com/cosmos/cosmos-sdk/pull/11029) The deprecated Vote Option field is removed in gov v1beta2 and nil in v1beta1. Use Options instead.
* [\#11013](https://github.com/cosmos/cosmos-sdk/pull/) The `tx gov submit-proposal` command has changed syntax to support the new Msg-based gov proposals. To access the old CLI command, please use `tx gov submit-legacy-proposal`.
### CLI Breaking Changes

View File

@ -9,7 +9,9 @@ import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/gov/migrations/v046"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2"
)
@ -277,3 +279,178 @@ func (q Keeper) TallyResult(c context.Context, req *v1beta2.QueryTallyResultRequ
return &v1beta2.QueryTallyResultResponse{Tally: &tallyResult}, nil
}
var _ v1beta1.QueryServer = legacyQueryServer{}
type legacyQueryServer struct {
keeper Keeper
}
func NewLegacyQueryServer(k Keeper) v1beta1.QueryServer {
return &legacyQueryServer{keeper: k}
}
func (q legacyQueryServer) Proposal(c context.Context, req *v1beta1.QueryProposalRequest) (*v1beta1.QueryProposalResponse, error) {
resp, err := q.keeper.Proposal(c, &v1beta2.QueryProposalRequest{
ProposalId: req.ProposalId,
})
if err != nil {
return nil, err
}
proposal, err := v046.ConvertToLegacyProposal(*resp.Proposal)
if err != nil {
return nil, err
}
return &v1beta1.QueryProposalResponse{Proposal: proposal}, nil
}
func (q legacyQueryServer) Proposals(c context.Context, req *v1beta1.QueryProposalsRequest) (*v1beta1.QueryProposalsResponse, error) {
resp, err := q.keeper.Proposals(c, &v1beta2.QueryProposalsRequest{
ProposalStatus: v1beta2.ProposalStatus(req.ProposalStatus),
Voter: req.Voter,
Depositor: req.Depositor,
Pagination: req.Pagination,
})
if err != nil {
return nil, err
}
legacyProposals := make([]v1beta1.Proposal, len(resp.Proposals))
for idx, proposal := range resp.Proposals {
legacyProposals[idx], err = v046.ConvertToLegacyProposal(*proposal)
if err != nil {
return nil, err
}
}
return &v1beta1.QueryProposalsResponse{
Proposals: legacyProposals,
Pagination: resp.Pagination,
}, nil
}
func (q legacyQueryServer) Vote(c context.Context, req *v1beta1.QueryVoteRequest) (*v1beta1.QueryVoteResponse, error) {
resp, err := q.keeper.Vote(c, &v1beta2.QueryVoteRequest{
ProposalId: req.ProposalId,
Voter: req.Voter,
})
if err != nil {
return nil, err
}
vote, err := v046.ConvertToLegacyVote(*resp.Vote)
if err != nil {
return nil, err
}
return &v1beta1.QueryVoteResponse{Vote: vote}, nil
}
func (q legacyQueryServer) Votes(c context.Context, req *v1beta1.QueryVotesRequest) (*v1beta1.QueryVotesResponse, error) {
resp, err := q.keeper.Votes(c, &v1beta2.QueryVotesRequest{
ProposalId: req.ProposalId,
Pagination: req.Pagination,
})
if err != nil {
return nil, err
}
votes := make([]v1beta1.Vote, len(resp.Votes))
for i, v := range resp.Votes {
votes[i], err = v046.ConvertToLegacyVote(*v)
if err != nil {
return nil, err
}
}
return &v1beta1.QueryVotesResponse{
Votes: votes,
Pagination: resp.Pagination,
}, nil
}
func (q legacyQueryServer) Params(c context.Context, req *v1beta1.QueryParamsRequest) (*v1beta1.QueryParamsResponse, error) {
resp, err := q.keeper.Params(c, &v1beta2.QueryParamsRequest{
ParamsType: req.ParamsType,
})
if err != nil {
return nil, err
}
response := &v1beta1.QueryParamsResponse{}
if resp.DepositParams != nil {
minDeposit := sdk.NewCoins(resp.DepositParams.MinDeposit...)
response.DepositParams = v1beta1.NewDepositParams(minDeposit, *resp.DepositParams.MaxDepositPeriod)
}
if resp.VotingParams != nil {
response.VotingParams = v1beta1.NewVotingParams(*resp.VotingParams.VotingPeriod)
}
if resp.TallyParams != nil {
quorum, err := sdk.NewDecFromStr(resp.TallyParams.Quorum)
if err != nil {
return nil, err
}
threshold, err := sdk.NewDecFromStr(resp.TallyParams.Threshold)
if err != nil {
return nil, err
}
vetoThreshold, err := sdk.NewDecFromStr(resp.TallyParams.VetoThreshold)
if err != nil {
return nil, err
}
response.TallyParams = v1beta1.NewTallyParams(quorum, threshold, vetoThreshold)
}
return response, nil
}
func (q legacyQueryServer) Deposit(c context.Context, req *v1beta1.QueryDepositRequest) (*v1beta1.QueryDepositResponse, error) {
resp, err := q.keeper.Deposit(c, &v1beta2.QueryDepositRequest{
ProposalId: req.ProposalId,
Depositor: req.Depositor,
})
if err != nil {
return nil, err
}
deposit := v046.ConvertToLegacyDeposit(resp.Deposit)
return &v1beta1.QueryDepositResponse{Deposit: deposit}, nil
}
func (q legacyQueryServer) Deposits(c context.Context, req *v1beta1.QueryDepositsRequest) (*v1beta1.QueryDepositsResponse, error) {
resp, err := q.keeper.Deposits(c, &v1beta2.QueryDepositsRequest{
ProposalId: req.ProposalId,
Pagination: req.Pagination,
})
if err != nil {
return nil, err
}
deposits := make([]v1beta1.Deposit, len(resp.Deposits))
for idx, deposit := range resp.Deposits {
deposits[idx] = v046.ConvertToLegacyDeposit(deposit)
}
return &v1beta1.QueryDepositsResponse{Deposits: deposits, Pagination: resp.Pagination}, nil
}
func (q legacyQueryServer) TallyResult(c context.Context, req *v1beta1.QueryTallyResultRequest) (*v1beta1.QueryTallyResultResponse, error) {
resp, err := q.keeper.TallyResult(c, &v1beta2.QueryTallyResultRequest{
ProposalId: req.ProposalId,
})
if err != nil {
return nil, err
}
tally, err := v046.ConvertToLegacyTallyResult(resp.Tally)
if err != nil {
return nil, err
}
return &v1beta1.QueryTallyResultResponse{Tally: tally}, nil
}

View File

@ -7,6 +7,7 @@ import (
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/cosmos-sdk/x/gov/migrations/v046"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2"
)
@ -85,6 +86,81 @@ func (suite *KeeperTestSuite) TestGRPCQueryProposal() {
}
}
func (suite *KeeperTestSuite) TestLegacyGRPCQueryProposal() {
app, ctx, queryClient := suite.app, suite.ctx, suite.legacyQueryClient
var (
req *v1beta1.QueryProposalRequest
expProposal v1beta1.Proposal
)
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = &v1beta1.QueryProposalRequest{}
},
false,
},
{
"non existing proposal request",
func() {
req = &v1beta1.QueryProposalRequest{ProposalId: 3}
},
false,
},
{
"zero proposal id request",
func() {
req = &v1beta1.QueryProposalRequest{ProposalId: 0}
},
false,
},
{
"valid request",
func() {
req = &v1beta1.QueryProposalRequest{ProposalId: 1}
testProposal := v1beta1.NewTextProposal("Proposal", "testing proposal")
msgContent, err := v1beta2.NewLegacyContent(testProposal, govAcct.String())
suite.Require().NoError(err)
submittedProposal, err := app.GovKeeper.SubmitProposal(ctx, []sdk.Msg{msgContent}, nil)
suite.Require().NoError(err)
suite.Require().NotEmpty(submittedProposal)
expProposal, err = v046.ConvertToLegacyProposal(submittedProposal)
suite.Require().NoError(err)
},
true,
},
}
for _, testCase := range testCases {
suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() {
testCase.malleate()
proposalRes, err := queryClient.Proposal(gocontext.Background(), req)
if testCase.expPass {
suite.Require().NoError(err)
// Instead of using MashalJSON, we could compare .String() output too.
// https://github.com/cosmos/cosmos-sdk/issues/10965
expJSON, err := suite.app.AppCodec().MarshalJSON(&expProposal)
suite.Require().NoError(err)
actualJSON, err := suite.app.AppCodec().MarshalJSON(&proposalRes.Proposal)
suite.Require().NoError(err)
suite.Require().Equal(expJSON, actualJSON)
} else {
suite.Require().Error(err)
suite.Require().Nil(proposalRes)
}
})
}
}
func (suite *KeeperTestSuite) TestGRPCQueryProposals() {
app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs
@ -451,6 +527,107 @@ func (suite *KeeperTestSuite) TestGRPCQueryVotes() {
}
}
func (suite *KeeperTestSuite) TestLegacyGRPCQueryVotes() {
app, ctx, queryClient := suite.app, suite.ctx, suite.legacyQueryClient
addrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(30000000))
var (
req *v1beta1.QueryVotesRequest
expRes *v1beta1.QueryVotesResponse
proposal v1beta2.Proposal
votes v1beta1.Votes
)
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = &v1beta1.QueryVotesRequest{}
},
false,
},
{
"zero proposal id request",
func() {
req = &v1beta1.QueryVotesRequest{
ProposalId: 0,
}
},
false,
},
{
"non existed proposals",
func() {
req = &v1beta1.QueryVotesRequest{
ProposalId: 2,
}
},
true,
},
{
"create a proposal and get votes",
func() {
var err error
proposal, err = app.GovKeeper.SubmitProposal(ctx, TestProposal, nil)
suite.Require().NoError(err)
req = &v1beta1.QueryVotesRequest{
ProposalId: proposal.ProposalId,
}
},
true,
},
{
"request after adding 2 votes",
func() {
proposal.Status = v1beta2.StatusVotingPeriod
app.GovKeeper.SetProposal(ctx, proposal)
votes = []v1beta1.Vote{
{ProposalId: proposal.ProposalId, Voter: addrs[0].String(), Options: v1beta1.NewNonSplitVoteOption(v1beta1.OptionAbstain)},
{ProposalId: proposal.ProposalId, Voter: addrs[1].String(), Options: v1beta1.NewNonSplitVoteOption(v1beta1.OptionYes)},
}
accAddr1, err1 := sdk.AccAddressFromBech32(votes[0].Voter)
accAddr2, err2 := sdk.AccAddressFromBech32(votes[1].Voter)
suite.Require().NoError(err1)
suite.Require().NoError(err2)
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr1, v1beta2.NewNonSplitVoteOption(v1beta2.OptionAbstain)))
suite.Require().NoError(app.GovKeeper.AddVote(ctx, proposal.ProposalId, accAddr2, v1beta2.NewNonSplitVoteOption(v1beta2.OptionYes)))
req = &v1beta1.QueryVotesRequest{
ProposalId: proposal.ProposalId,
}
expRes = &v1beta1.QueryVotesResponse{
Votes: votes,
}
},
true,
},
}
for _, testCase := range testCases {
suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() {
testCase.malleate()
votes, err := queryClient.Votes(gocontext.Background(), req)
if testCase.expPass {
suite.Require().NoError(err)
suite.Require().Equal(expRes.GetVotes(), votes.GetVotes())
} else {
suite.Require().Error(err)
suite.Require().Nil(votes)
}
})
}
}
func (suite *KeeperTestSuite) TestGRPCQueryParams() {
queryClient := suite.queryClient
@ -533,6 +710,96 @@ func (suite *KeeperTestSuite) TestGRPCQueryParams() {
}
}
func (suite *KeeperTestSuite) TestLegacyGRPCQueryParams() {
queryClient := suite.legacyQueryClient
var (
req *v1beta1.QueryParamsRequest
expRes *v1beta1.QueryParamsResponse
)
defaultTallyParams := v1beta1.TallyParams{
Quorum: sdk.NewDec(0),
Threshold: sdk.NewDec(0),
VetoThreshold: sdk.NewDec(0),
}
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = &v1beta1.QueryParamsRequest{}
},
false,
},
{
"deposit params request",
func() {
req = &v1beta1.QueryParamsRequest{ParamsType: v1beta1.ParamDeposit}
depositParams := v1beta1.DefaultDepositParams()
expRes = &v1beta1.QueryParamsResponse{
DepositParams: depositParams,
TallyParams: defaultTallyParams,
}
},
true,
},
{
"voting params request",
func() {
req = &v1beta1.QueryParamsRequest{ParamsType: v1beta1.ParamVoting}
votingParams := v1beta1.DefaultVotingParams()
expRes = &v1beta1.QueryParamsResponse{
VotingParams: votingParams,
TallyParams: defaultTallyParams,
}
},
true,
},
{
"tally params request",
func() {
req = &v1beta1.QueryParamsRequest{ParamsType: v1beta1.ParamTallying}
tallyParams := v1beta1.DefaultTallyParams()
expRes = &v1beta1.QueryParamsResponse{
TallyParams: tallyParams,
}
},
true,
},
{
"invalid request",
func() {
req = &v1beta1.QueryParamsRequest{ParamsType: "wrongPath"}
expRes = &v1beta1.QueryParamsResponse{}
},
false,
},
}
for _, testCase := range testCases {
suite.Run(fmt.Sprintf("Case %s", testCase.msg), func() {
testCase.malleate()
params, err := queryClient.Params(gocontext.Background(), req)
if testCase.expPass {
suite.Require().NoError(err)
suite.Require().Equal(expRes.GetDepositParams(), params.GetDepositParams())
suite.Require().Equal(expRes.GetVotingParams(), params.GetVotingParams())
suite.Require().Equal(expRes.GetTallyParams(), params.GetTallyParams())
} else {
suite.Require().Error(err)
suite.Require().Nil(params)
}
})
}
}
func (suite *KeeperTestSuite) TestGRPCQueryDeposit() {
app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs

View File

@ -10,7 +10,9 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
)
@ -18,10 +20,11 @@ import (
type KeeperTestSuite struct {
suite.Suite
app *simapp.SimApp
ctx sdk.Context
queryClient v1beta2.QueryClient
addrs []sdk.AccAddress
app *simapp.SimApp
ctx sdk.Context
queryClient v1beta2.QueryClient
legacyQueryClient v1beta1.QueryClient
addrs []sdk.AccAddress
}
func (suite *KeeperTestSuite) SetupTest() {
@ -38,11 +41,15 @@ func (suite *KeeperTestSuite) SetupTest() {
queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
v1beta2.RegisterQueryServer(queryHelper, app.GovKeeper)
legacyQueryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
v1beta1.RegisterQueryServer(legacyQueryHelper, keeper.NewLegacyQueryServer(app.GovKeeper))
queryClient := v1beta2.NewQueryClient(queryHelper)
legacyQueryClient := v1beta1.NewQueryClient(legacyQueryHelper)
suite.app = app
suite.ctx = ctx
suite.queryClient = queryClient
suite.legacyQueryClient = legacyQueryClient
suite.addrs = simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(30000000))
}

View File

@ -0,0 +1,119 @@
package v046
import (
"fmt"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2"
)
// ConvertToLegacyProposal takes a new proposal and attempts to convert it to the
// legacy proposal format. This conversion is best effort. New proposal types that
// don't have a legacy message will return a "nil" content.
func ConvertToLegacyProposal(proposal v1beta2.Proposal) (v1beta1.Proposal, error) {
var err error
legacyProposal := v1beta1.Proposal{
ProposalId: proposal.ProposalId,
Status: v1beta1.ProposalStatus(proposal.Status),
TotalDeposit: types.NewCoins(proposal.TotalDeposit...),
}
legacyProposal.FinalTallyResult, err = ConvertToLegacyTallyResult(proposal.FinalTallyResult)
if err != nil {
return v1beta1.Proposal{}, err
}
if proposal.VotingStartTime != nil {
legacyProposal.VotingStartTime = *proposal.VotingStartTime
}
if proposal.VotingEndTime != nil {
legacyProposal.VotingEndTime = *proposal.VotingEndTime
}
if proposal.SubmitTime != nil {
legacyProposal.SubmitTime = *proposal.SubmitTime
}
if proposal.DepositEndTime != nil {
legacyProposal.DepositEndTime = *proposal.DepositEndTime
}
msgs, err := proposal.GetMsgs()
if err != nil {
return v1beta1.Proposal{}, err
}
for _, msg := range msgs {
if legacyMsg, ok := msg.(*v1beta2.MsgExecLegacyContent); ok {
// check that the content struct can be unmarshalled
_, err := v1beta2.LegacyContentFromMessage(legacyMsg)
if err != nil {
return v1beta1.Proposal{}, err
}
legacyProposal.Content = legacyMsg.Content
}
}
return legacyProposal, nil
}
func ConvertToLegacyTallyResult(tally *v1beta2.TallyResult) (v1beta1.TallyResult, error) {
yes, ok := types.NewIntFromString(tally.Yes)
if !ok {
return v1beta1.TallyResult{}, fmt.Errorf("unable to convert yes tally string (%s) to int", tally.Yes)
}
no, ok := types.NewIntFromString(tally.No)
if !ok {
return v1beta1.TallyResult{}, fmt.Errorf("unable to convert no tally string (%s) to int", tally.No)
}
veto, ok := types.NewIntFromString(tally.NoWithVeto)
if !ok {
return v1beta1.TallyResult{}, fmt.Errorf("unable to convert no with veto tally string (%s) to int", tally.NoWithVeto)
}
abstain, ok := types.NewIntFromString(tally.Abstain)
if !ok {
return v1beta1.TallyResult{}, fmt.Errorf("unable to convert abstain tally string (%s) to int", tally.Abstain)
}
return v1beta1.TallyResult{
Yes: yes,
No: no,
NoWithVeto: veto,
Abstain: abstain,
}, nil
}
func ConvertToLegacyVote(vote v1beta2.Vote) (v1beta1.Vote, error) {
options, err := ConvertToLegacyVoteOptions(vote.Options)
if err != nil {
return v1beta1.Vote{}, err
}
return v1beta1.Vote{
ProposalId: vote.ProposalId,
Voter: vote.Voter,
Options: options,
}, nil
}
func ConvertToLegacyVoteOptions(voteOptions []*v1beta2.WeightedVoteOption) ([]v1beta1.WeightedVoteOption, error) {
options := make([]v1beta1.WeightedVoteOption, len(voteOptions))
for i, option := range voteOptions {
weight, err := types.NewDecFromStr(option.Weight)
if err != nil {
return options, err
}
options[i] = v1beta1.WeightedVoteOption{
Option: v1beta1.VoteOption(option.Option),
Weight: weight,
}
}
return options, nil
}
func ConvertToLegacyDeposit(deposit *v1beta2.Deposit) v1beta1.Deposit {
return v1beta1.Deposit{
ProposalId: deposit.ProposalId,
Depositor: deposit.Depositor,
Amount: types.NewCoins(deposit.Amount...),
}
}

View File

@ -160,8 +160,8 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
v1beta1.RegisterMsgServer(cfg.MsgServer(), keeper.NewLegacyMsgServerImpl(am.accountKeeper.GetModuleAddress(types.ModuleName).String(), msgServer))
v1beta2.RegisterMsgServer(cfg.MsgServer(), msgServer)
// TODO Register v1beta1 query server.
// https://github.com/cosmos/cosmos-sdk/issues/10951
legacyQueryServer := keeper.NewLegacyQueryServer(am.keeper)
v1beta1.RegisterQueryServer(cfg.QueryServer(), legacyQueryServer)
v1beta2.RegisterQueryServer(cfg.QueryServer(), am.keeper)
m := keeper.NewMigrator(am.keeper)