From 66ee994ce4b5951a373131f0ee5bb0436f507ab4 Mon Sep 17 00:00:00 2001 From: MD Aleem <72057206+aleem1314@users.noreply.github.com> Date: Thu, 3 Jun 2021 13:06:08 +0530 Subject: [PATCH] fix: x/gov deposits querier (Initial Deposit) (#9288) * copied from old PR * fix errors * add test * Update x/gov/client/utils/query.go Co-authored-by: Robert Zaremba * fix tests * fix failing test * add test * update test * fix tests * fix deposit query * fix test * update tests * add more tests * address lint error * address lint error * review changes Co-authored-by: Robert Zaremba --- CHANGELOG.md | 1 + x/gov/client/cli/query.go | 26 ++-- x/gov/client/testutil/cli_test.go | 12 ++ x/gov/client/testutil/deposits.go | 193 ++++++++++++++++++++++++++++++ x/gov/client/testutil/helpers.go | 12 ++ x/gov/client/utils/query.go | 68 ++++++++++- 6 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 x/gov/client/testutil/deposits.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bc5902a837e..fe9eee18f626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ if input key is empty, or input data contains empty key. ### Bug Fixes +* (x/gov) [\#8813](https://github.com/cosmos/cosmos-sdk/pull/8813) fix `GET /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits` to include initial deposit * (gRPC) [\#8945](https://github.com/cosmos/cosmos-sdk/pull/8945) gRPC reflection now works correctly. * (keyring) [#\8635](https://github.com/cosmos/cosmos-sdk/issues/8635) Remove hardcoded default passphrase value on `NewMnemonic` * (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index 68e1347c0b02..34d4665a99d4 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -366,7 +366,7 @@ $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk // check to see if the proposal is in the store ctx := cmd.Context() - _, err = queryClient.Proposal( + proposalRes, err := queryClient.Proposal( ctx, &types.QueryProposalRequest{ProposalId: proposalID}, ) @@ -379,25 +379,27 @@ $ %s query gov deposit 1 cosmos1skjwj5whet0lpe65qaq4rpq03hjxlwd9nf39lk return err } - res, err := queryClient.Deposit( - ctx, - &types.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]}, - ) - if err != nil { - return err - } - - deposit := res.GetDeposit() - if deposit.Empty() { + var deposit types.Deposit + propStatus := proposalRes.Proposal.Status + if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) { params := types.NewQueryDepositParams(proposalID, depositorAddr) resByTxQuery, err := gcutils.QueryDepositByTxQuery(clientCtx, params) if err != nil { return err } clientCtx.JSONCodec.MustUnmarshalJSON(resByTxQuery, &deposit) + return clientCtx.PrintProto(&deposit) + } + + res, err := queryClient.Deposit( + ctx, + &types.QueryDepositRequest{ProposalId: proposalID, Depositor: args[1]}, + ) + if err != nil { + return err } - return clientCtx.PrintProto(&deposit) + return clientCtx.PrintProto(&res.Deposit) }, } diff --git a/x/gov/client/testutil/cli_test.go b/x/gov/client/testutil/cli_test.go index dd36a6af2d3e..713c33f12182 100644 --- a/x/gov/client/testutil/cli_test.go +++ b/x/gov/client/testutil/cli_test.go @@ -4,9 +4,13 @@ package testutil import ( "testing" + "time" "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -14,4 +18,12 @@ func TestIntegrationTestSuite(t *testing.T) { cfg := network.DefaultConfig() cfg.NumValidators = 1 suite.Run(t, NewIntegrationTestSuite(cfg)) + + genesisState := types.DefaultGenesisState() + genesisState.DepositParams = types.NewDepositParams(sdk.NewCoins(sdk.NewCoin(cfg.BondDenom, types.DefaultMinDepositTokens)), time.Duration(15)*time.Second) + genesisState.VotingParams = types.NewVotingParams(time.Duration(5) * time.Second) + bz, err := cfg.Codec.MarshalJSON(genesisState) + require.NoError(t, err) + cfg.GenesisState["gov"] = bz + suite.Run(t, NewDepositTestSuite(cfg)) } diff --git a/x/gov/client/testutil/deposits.go b/x/gov/client/testutil/deposits.go new file mode 100644 index 000000000000..57c3c9363330 --- /dev/null +++ b/x/gov/client/testutil/deposits.go @@ -0,0 +1,193 @@ +package testutil + +import ( + "fmt" + "time" + + clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" + "github.com/cosmos/cosmos-sdk/testutil/network" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/suite" + tmcli "github.com/tendermint/tendermint/libs/cli" +) + +type DepositTestSuite struct { + suite.Suite + + cfg network.Config + network *network.Network + fees string +} + +func NewDepositTestSuite(cfg network.Config) *DepositTestSuite { + return &DepositTestSuite{cfg: cfg} +} + +func (s *DepositTestSuite) SetupSuite() { + s.T().Log("setting up test suite") + + s.network = network.New(s.T(), s.cfg) + + _, err := s.network.WaitForHeight(1) + s.Require().NoError(err) + s.fees = sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(20))).String() + +} + +func (s *DepositTestSuite) TearDownSuite() { + s.T().Log("tearing down test suite") + s.network.Cleanup() +} + +func (s *DepositTestSuite) TestQueryDepositsInitialDeposit() { + val := s.network.Validators[0] + clientCtx := val.ClientCtx + initialDeposit := sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens.Sub(sdk.NewInt(20))).String() + + // create a proposal with deposit + _, err := MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 1", "Where is the title!?", types.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, initialDeposit)) + s.Require().NoError(err) + + // deposit more amount + _, err = MsgDeposit(clientCtx, val.Address.String(), "1", sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(50)).String()) + s.Require().NoError(err) + + // waiting for voting period to end + time.Sleep(20 * time.Second) + + // query deposit & verify initial deposit + deposit := s.queryDeposit(val, "1", false) + s.Require().Equal(deposit.Amount.String(), initialDeposit) + + // query deposits + deposits := s.queryDeposits(val, "1", false) + s.Require().Equal(len(deposits), 2) + // verify initial deposit + s.Require().Equal(deposits[0].Amount.String(), initialDeposit) +} + +func (s *DepositTestSuite) TestQueryDepositsWithoutInitialDeposit() { + val := s.network.Validators[0] + clientCtx := val.ClientCtx + + // create a proposal without deposit + _, err := MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 2", "Where is the title!?", types.ProposalTypeText) + s.Require().NoError(err) + + // deposit amount + _, err = MsgDeposit(clientCtx, val.Address.String(), "2", sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens.Add(sdk.NewInt(50))).String()) + s.Require().NoError(err) + + // waiting for voting period to end + time.Sleep(20 * time.Second) + + // query deposit + deposit := s.queryDeposit(val, "2", false) + s.Require().Equal(deposit.Amount.String(), sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens.Add(sdk.NewInt(50))).String()) + + // query deposits + deposits := s.queryDeposits(val, "2", false) + s.Require().Equal(len(deposits), 1) + // verify initial deposit + s.Require().Equal(deposits[0].Amount.String(), sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens.Add(sdk.NewInt(50))).String()) +} + +func (s *DepositTestSuite) TestQueryProposalNotEnoughDeposits() { + val := s.network.Validators[0] + clientCtx := val.ClientCtx + initialDeposit := sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens.Sub(sdk.NewInt(2000))).String() + + // create a proposal with deposit + _, err := MsgSubmitProposal(val.ClientCtx, val.Address.String(), + "Text Proposal 3", "Where is the title!?", types.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, initialDeposit)) + s.Require().NoError(err) + + // query proposal + args := []string{"3", fmt.Sprintf("--%s=json", tmcli.OutputFlag)} + cmd := cli.GetCmdQueryProposal() + _, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + s.Require().NoError(err) + + // waiting for deposit period to end + time.Sleep(20 * time.Second) + + // query proposal + _, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + s.Require().Error(err) + s.Require().Contains(err.Error(), "proposal 3 doesn't exist") +} + +func (s *DepositTestSuite) TestRejectedProposalDeposits() { + val := s.network.Validators[0] + clientCtx := val.ClientCtx + initialDeposit := sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens) + + // create a proposal with deposit + _, err := MsgSubmitProposal(clientCtx, val.Address.String(), + "Text Proposal 4", "Where is the title!?", types.ProposalTypeText, + fmt.Sprintf("--%s=%s", cli.FlagDeposit, initialDeposit)) + s.Require().NoError(err) + + // query deposits + var deposits types.QueryDepositsResponse + args := []string{"4", fmt.Sprintf("--%s=json", tmcli.OutputFlag)} + cmd := cli.GetCmdQueryDeposits() + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(out.Bytes(), &deposits)) + s.Require().Equal(len(deposits.Deposits), 1) + // verify initial deposit + s.Require().Equal(deposits.Deposits[0].Amount.String(), sdk.NewCoin(s.cfg.BondDenom, types.DefaultMinDepositTokens).String()) + + // vote + _, err = MsgVote(clientCtx, val.Address.String(), "4", "no") + s.Require().NoError(err) + + time.Sleep(20 * time.Second) + + args = []string{"4", fmt.Sprintf("--%s=json", tmcli.OutputFlag)} + cmd = cli.GetCmdQueryProposal() + _, err = clitestutil.ExecTestCLICmd(clientCtx, cmd, args) + s.Require().NoError(err) + + // query deposits + depositsRes := s.queryDeposits(val, "4", false) + s.Require().Equal(len(depositsRes), 1) + // verify initial deposit + s.Require().Equal(depositsRes[0].Amount.String(), initialDeposit.String()) + +} + +func (s *DepositTestSuite) queryDeposits(val *network.Validator, proposalID string, exceptErr bool) types.Deposits { + args := []string{proposalID, fmt.Sprintf("--%s=json", tmcli.OutputFlag)} + var depositsRes types.Deposits + cmd := cli.GetCmdQueryDeposits() + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + if exceptErr { + s.Require().Error(err) + return nil + } + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(out.Bytes(), &depositsRes)) + return depositsRes +} + +func (s *DepositTestSuite) queryDeposit(val *network.Validator, proposalID string, exceptErr bool) *types.Deposit { + args := []string{proposalID, val.Address.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)} + var depositRes types.Deposit + cmd := cli.GetCmdQueryDeposit() + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) + if exceptErr { + s.Require().Error(err) + return nil + } + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.LegacyAmino.UnmarshalJSON(out.Bytes(), &depositRes)) + return &depositRes +} diff --git a/x/gov/client/testutil/helpers.go b/x/gov/client/testutil/helpers.go index ce9d5de725a5..e16ac0e98e83 100644 --- a/x/gov/client/testutil/helpers.go +++ b/x/gov/client/testutil/helpers.go @@ -43,3 +43,15 @@ func MsgVote(clientCtx client.Context, from, id, vote string, extraArgs ...strin return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdWeightedVote(), args) } + +func MsgDeposit(clientCtx client.Context, from, id, deposit string, extraArgs ...string) (testutil.BufferWriter, error) { + args := append([]string{ + id, + deposit, + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + }, commonArgs...) + + args = append(args, extraArgs...) + + return clitestutil.ExecTestCLICmd(clientCtx, govcli.NewCmdDeposit(), args) +} diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go index 0538cfc72d65..977b5adc699f 100644 --- a/x/gov/client/utils/query.go +++ b/x/gov/client/utils/query.go @@ -5,6 +5,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -37,6 +38,18 @@ func (p Proposer) String() string { // NOTE: SearchTxs is used to facilitate the txs query which does not currently // support configurable pagination. func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposalParams) ([]byte, error) { + var deposits []types.Deposit + + // initial deposit was submitted with proposal, so must be queried separately + initialDeposit, err := queryInitialDepositByTxQuery(clientCtx, params.ProposalID) + if err != nil { + return nil, err + } + + if !initialDeposit.Amount.IsZero() { + deposits = append(deposits, initialDeposit) + } + searchResult, err := combineEvents( clientCtx, defaultPage, // Query legacy Msgs event action @@ -54,8 +67,6 @@ func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposal return nil, err } - var deposits []types.Deposit - for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { if depMsg, ok := msg.(*types.MsgDeposit); ok { @@ -226,6 +237,22 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) // QueryDepositByTxQuery will query for a single deposit via a direct txs tags // query. func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositParams) ([]byte, error) { + + // initial deposit was submitted with proposal, so must be queried separately + initialDeposit, err := queryInitialDepositByTxQuery(clientCtx, params.ProposalID) + if err != nil { + return nil, err + } + + if !initialDeposit.Amount.IsZero() { + bz, err := clientCtx.JSONCodec.MarshalJSON(&initialDeposit) + if err != nil { + return nil, err + } + + return bz, nil + } + searchResult, err := combineEvents( clientCtx, defaultPage, // Query legacy Msgs event action @@ -338,3 +365,40 @@ func combineEvents(clientCtx client.Context, page int, eventGroups ...[]string) return &sdk.SearchTxsResult{Txs: allTxs}, nil } + +// queryInitialDepositByTxQuery will query for a initial deposit of a governance proposal by +// ID. +func queryInitialDepositByTxQuery(clientCtx client.Context, proposalID uint64) (types.Deposit, error) { + searchResult, err := combineEvents( + clientCtx, defaultPage, + // Query legacy Msgs event action + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal), + fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), + }, + // Query proto Msgs event action + []string{ + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgSubmitProposal{})), + fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), + }, + ) + + if err != nil { + return types.Deposit{}, err + } + + for _, info := range searchResult.Txs { + for _, msg := range info.GetTx().GetMsgs() { + // there should only be a single proposal under the given conditions + if subMsg, ok := msg.(*types.MsgSubmitProposal); ok { + return types.Deposit{ + ProposalId: proposalID, + Depositor: subMsg.Proposer, + Amount: subMsg.InitialDeposit, + }, nil + } + } + } + + return types.Deposit{}, sdkerrors.ErrNotFound.Wrapf("failed to find the initial deposit for proposalID %d", proposalID) +}