From 4de3fd3fa2a71ad43c3293ba1349f19c6e2632fa Mon Sep 17 00:00:00 2001 From: son trinh Date: Fri, 6 Sep 2024 00:15:01 +0700 Subject: [PATCH] refactor(x/gov): Audit gov changes (#21454) (cherry picked from commit 0c10dd0cc1f5c0c1b32139995347acc73019c9a4) --- x/gov/README.md | 14 +-- x/gov/client/utils/query_test.go | 192 ++++++++++++++++++++++++++++++- x/gov/common_test.go | 13 --- x/gov/keeper/deposit.go | 16 +-- x/gov/keeper/proposal.go | 39 +++++-- x/gov/keeper/proposal_test.go | 57 ++++----- x/gov/keeper/tally.go | 6 +- x/gov/keeper/vote.go | 4 +- x/gov/types/v1/params.go | 102 ++++++++++++++++ x/gov/types/v1/proposal.go | 2 +- x/gov/types/v1beta1/router.go | 2 - 11 files changed, 357 insertions(+), 90 deletions(-) delete mode 100644 x/gov/common_test.go diff --git a/x/gov/README.md b/x/gov/README.md index 872b6daa68c5..d3d0112966a3 100644 --- a/x/gov/README.md +++ b/x/gov/README.md @@ -2204,8 +2204,6 @@ Example Output: The `params` endpoint allows users to query all parameters for the `gov` module. - - Using legacy v1beta1: ```bash @@ -2225,16 +2223,6 @@ Example Output: "voting_params": { "voting_period": "172800s" }, - "deposit_params": { - "min_deposit": [ - ], - "max_deposit_period": "0s" - }, - "tally_params": { - "quorum": "0.000000000000000000", - "threshold": "0.000000000000000000", - "veto_threshold": "0.000000000000000000" - } } ``` @@ -2270,6 +2258,8 @@ Example Output: } ``` +Note: `params_type` are deprecated in v1 since all params are stored in Params. + #### deposits The `deposits` endpoint allows users to query a deposit for a given proposal from a given depositor. diff --git a/x/gov/client/utils/query_test.go b/x/gov/client/utils/query_test.go index be2ffb621994..590f52c9b351 100644 --- a/x/gov/client/utils/query_test.go +++ b/x/gov/client/utils/query_test.go @@ -2,6 +2,9 @@ package utils_test import ( "context" + "fmt" + "strconv" + "strings" "testing" "github.com/cometbft/cometbft/rpc/client/mock" @@ -13,6 +16,7 @@ import ( "cosmossdk.io/x/gov" "cosmossdk.io/x/gov/client/utils" v1 "cosmossdk.io/x/gov/types/v1" + "cosmossdk.io/x/gov/types/v1beta1" "github.com/cosmos/cosmos-sdk/client" codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil" @@ -24,9 +28,12 @@ type TxSearchMock struct { txConfig client.TxConfig mock.Client txs []cmttypes.Tx + + // use for filter tx with query conditions + msgsSet [][]sdk.Msg } -func (mock TxSearchMock) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) { +func (mock TxSearchMock) TxSearch(ctx context.Context, query string, _ bool, page, perPage *int, _ string) (*coretypes.ResultTxSearch, error) { if page == nil { *page = 0 } @@ -40,8 +47,11 @@ func (mock TxSearchMock) TxSearch(ctx context.Context, query string, prove bool, // nil result with nil error crashes utils.QueryTxsByEvents return &coretypes.ResultTxSearch{}, nil } + txs, err := mock.filterTxs(query, start, end) + if err != nil { + return nil, err + } - txs := mock.txs[start:end] rst := &coretypes.ResultTxSearch{Txs: make([]*coretypes.ResultTx, len(txs)), TotalCount: len(txs)} for i := range txs { rst.Txs[i] = &coretypes.ResultTx{Tx: txs[i]} @@ -54,6 +64,74 @@ func (mock TxSearchMock) Block(ctx context.Context, height *int64) (*coretypes.R return &coretypes.ResultBlock{Block: &cmttypes.Block{}}, nil } +// mock applying the query string in TxSearch +func (mock TxSearchMock) filterTxs(query string, start, end int) ([]cmttypes.Tx, error) { + filterTxs := []cmttypes.Tx{} + proposalIdStr, senderAddr := getQueryAttributes(query) + if senderAddr != "" { + proposalId, err := strconv.ParseUint(proposalIdStr, 10, 64) + if err != nil { + return nil, err + } + + for i, msgs := range mock.msgsSet { + for _, msg := range msgs { + if voteMsg, ok := msg.(*v1beta1.MsgVote); ok { + if voteMsg.Voter == senderAddr && voteMsg.ProposalId == proposalId { + filterTxs = append(filterTxs, mock.txs[i]) + continue + } + } + + if voteMsg, ok := msg.(*v1.MsgVote); ok { + if voteMsg.Voter == senderAddr && voteMsg.ProposalId == proposalId { + filterTxs = append(filterTxs, mock.txs[i]) + continue + } + } + + if voteWeightedMsg, ok := msg.(*v1beta1.MsgVoteWeighted); ok { + if voteWeightedMsg.Voter == senderAddr && voteWeightedMsg.ProposalId == proposalId { + filterTxs = append(filterTxs, mock.txs[i]) + continue + } + } + + if voteWeightedMsg, ok := msg.(*v1.MsgVoteWeighted); ok { + if voteWeightedMsg.Voter == senderAddr && voteWeightedMsg.ProposalId == proposalId { + filterTxs = append(filterTxs, mock.txs[i]) + continue + } + } + } + } + } else { + filterTxs = append(filterTxs, mock.txs...) + } + + if len(filterTxs) < end { + return filterTxs, nil + } + + return filterTxs[start:end], nil +} + +// getQueryAttributes extracts value from query string +func getQueryAttributes(q string) (proposalId, senderAddr string) { + splitStr := strings.Split(q, " OR ") + if len(splitStr) >= 2 { + keySender := strings.Trim(splitStr[1], ")") + senderAddr = strings.Trim(strings.Split(keySender, "=")[1], "'") + + keyProposal := strings.Split(q, " AND ")[0] + proposalId = strings.Trim(strings.Split(keyProposal, "=")[1], "'") + } else { + proposalId = strings.Trim(strings.Split(splitStr[0], "=")[1], "'") + } + + return proposalId, senderAddr +} + func TestGetPaginatedVotes(t *testing.T) { cdcOpts := codectestutil.CodecOptions{} encCfg := moduletestutil.MakeTestEncodingConfig(cdcOpts, gov.AppModule{}) @@ -178,3 +256,113 @@ func TestGetPaginatedVotes(t *testing.T) { }) } } + +func TestGetSingleVote(t *testing.T) { + cdcOpts := codectestutil.CodecOptions{} + encCfg := moduletestutil.MakeTestEncodingConfig(cdcOpts, gov.AppModule{}) + + type testCase struct { + description string + msgs [][]sdk.Msg + votes []v1.Vote + expErr string + } + acc1 := make(sdk.AccAddress, 20) + acc1[0] = 1 + acc1Str, err := cdcOpts.GetAddressCodec().BytesToString(acc1) + require.NoError(t, err) + acc2 := make(sdk.AccAddress, 20) + acc2[0] = 2 + acc2Str, err := cdcOpts.GetAddressCodec().BytesToString(acc2) + require.NoError(t, err) + acc1Msgs := []sdk.Msg{ + v1.NewMsgVote(acc1Str, 0, v1.OptionYes, ""), + v1.NewMsgVote(acc1Str, 0, v1.OptionYes, ""), + v1.NewMsgDeposit(acc1Str, 0, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10)))), // should be ignored + } + acc2Msgs := []sdk.Msg{ + v1.NewMsgVote(acc2Str, 0, v1.OptionYes, ""), + v1.NewMsgVoteWeighted(acc2Str, 0, v1.NewNonSplitVoteOption(v1.OptionYes), ""), + v1beta1.NewMsgVoteWeighted(acc2Str, 0, v1beta1.NewNonSplitVoteOption(v1beta1.OptionYes)), + } + for _, tc := range []testCase{ + { + description: "no vote found: no msgVote", + msgs: [][]sdk.Msg{ + acc1Msgs[:1], + }, + votes: []v1.Vote{}, + expErr: "did not vote on proposalID", + }, + { + description: "no vote found: wrong proposal ID", + msgs: [][]sdk.Msg{ + acc1Msgs[:1], + }, + votes: []v1.Vote{}, + expErr: "did not vote on proposalID", + }, + { + description: "query 2 voter vote", + msgs: [][]sdk.Msg{ + acc1Msgs, + acc2Msgs[:1], + }, + votes: []v1.Vote{ + v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""), + v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""), + }, + }, + { + description: "query 2 voter vote with v1beta1", + msgs: [][]sdk.Msg{ + acc1Msgs, + acc2Msgs[2:], + }, + votes: []v1.Vote{ + v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""), + v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""), + }, + }, + } { + tc := tc + + t.Run(tc.description, func(t *testing.T) { + marshaled := make([]cmttypes.Tx, len(tc.msgs)) + cli := TxSearchMock{txs: marshaled, txConfig: encCfg.TxConfig, msgsSet: tc.msgs} + clientCtx := client.Context{}. + WithLegacyAmino(encCfg.Amino). + WithClient(cli). + WithTxConfig(encCfg.TxConfig). + WithAddressCodec(cdcOpts.GetAddressCodec()). + WithCodec(encCfg.Codec) + + for i := range tc.msgs { + txBuilder := clientCtx.TxConfig.NewTxBuilder() + err := txBuilder.SetMsgs(tc.msgs[i]...) + require.NoError(t, err) + + tx, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + require.NoError(t, err) + marshaled[i] = tx + } + + // testing query single vote + for i, v := range tc.votes { + accAddr, err := clientCtx.AddressCodec.StringToBytes(v.Voter) + require.NoError(t, err) + voteParams := utils.QueryVoteParams{ProposalID: 0, Voter: accAddr} + voteData, err := utils.QueryVoteByTxQuery(clientCtx, voteParams) + if tc.expErr != "" { + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), tc.expErr)) + continue + } + require.NoError(t, err) + vote := v1.Vote{} + require.NoError(t, clientCtx.Codec.UnmarshalJSON(voteData, &vote)) + require.Equal(t, v, vote, fmt.Sprintf("vote should be equal at entry %v", i)) + } + }) + } +} diff --git a/x/gov/common_test.go b/x/gov/common_test.go deleted file mode 100644 index 5aeaf19cfaf0..000000000000 --- a/x/gov/common_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package gov_test - -import ( - "cosmossdk.io/math" - "cosmossdk.io/x/gov/types/v1beta1" - stakingtypes "cosmossdk.io/x/staking/types" -) - -var ( - TestProposal = v1beta1.NewTextProposal("Test", "description") - TestDescription = stakingtypes.NewDescription("T", "E", "S", "T", "Z") - TestCommissionRates = stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()) -) diff --git a/x/gov/keeper/deposit.go b/x/gov/keeper/deposit.go index 6759ac1407c3..e3ac7138479c 100644 --- a/x/gov/keeper/deposit.go +++ b/x/gov/keeper/deposit.go @@ -157,6 +157,11 @@ func (k Keeper) AddDeposit(ctx context.Context, proposalID uint64, depositorAddr activatedVotingPeriod = true } + addr, err := k.authKeeper.AddressCodec().BytesToString(depositorAddr) + if err != nil { + return false, err + } + // Add or update deposit object deposit, err := k.Deposits.Get(ctx, collections.Join(proposalID, depositorAddr)) switch { @@ -165,10 +170,6 @@ func (k Keeper) AddDeposit(ctx context.Context, proposalID uint64, depositorAddr deposit.Amount = sdk.NewCoins(deposit.Amount...).Add(depositAmount...) case errors.IsOf(err, collections.ErrNotFound): // deposit doesn't exist - addr, err := k.authKeeper.AddressCodec().BytesToString(depositorAddr) - if err != nil { - return false, err - } deposit = v1.NewDeposit(proposalID, addr, depositAmount) default: // failed to get deposit @@ -181,14 +182,9 @@ func (k Keeper) AddDeposit(ctx context.Context, proposalID uint64, depositorAddr return false, err } - depositorStrAddr, err := k.authKeeper.AddressCodec().BytesToString(depositorAddr) - if err != nil { - return false, err - } - if err := k.EventService.EventManager(ctx).EmitKV( types.EventTypeProposalDeposit, - event.NewAttribute(types.AttributeKeyDepositor, depositorStrAddr), + event.NewAttribute(types.AttributeKeyDepositor, addr), event.NewAttribute(sdk.AttributeKeyAmount, depositAmount.String()), event.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)), ); err != nil { diff --git a/x/gov/keeper/proposal.go b/x/gov/keeper/proposal.go index d095d7053ae6..c68ef10c165f 100644 --- a/x/gov/keeper/proposal.go +++ b/x/gov/keeper/proposal.go @@ -27,11 +27,15 @@ func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata return v1.Proposal{}, err } + proposerAddr, err := k.authKeeper.AddressCodec().BytesToString(proposer) + if err != nil { + return v1.Proposal{}, err + } + // additional checks per proposal types switch proposalType { case v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC: - proposerStr, _ := k.authKeeper.AddressCodec().BytesToString(proposer) - if len(params.OptimisticAuthorizedAddresses) > 0 && !slices.Contains(params.OptimisticAuthorizedAddresses, proposerStr) { + if len(params.OptimisticAuthorizedAddresses) > 0 && !slices.Contains(params.OptimisticAuthorizedAddresses, proposerAddr) { return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposer, "proposer is not authorized to submit optimistic proposal") } case v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE: @@ -43,20 +47,35 @@ func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata msgs := make([]string, 0, len(messages)) // will hold a string slice of all Msg type URLs. // Loop through all messages and confirm that each has a handler and the gov module account as the only signer + var currentMessagedBasedParams *v1.MessageBasedParams for _, msg := range messages { msgs = append(msgs, sdk.MsgTypeURL(msg)) // check if any of the message has message based params - hasMessagedBasedParams, err := k.MessageBasedParams.Has(ctx, sdk.MsgTypeURL(msg)) + hasMessagedBasedParams := true + messagedBasedParams, err := k.MessageBasedParams.Get(ctx, sdk.MsgTypeURL(msg)) if err != nil { - return v1.Proposal{}, err + if !errorsmod.IsOf(err, collections.ErrNotFound) { + return v1.Proposal{}, err + } + + hasMessagedBasedParams = false } if hasMessagedBasedParams { - // TODO(@julienrbrt), in the future, we can check if all messages have the same params - // and if so, we can allow the proposal. - if len(messages) > 1 { - return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposalMsg, "cannot submit multiple messages proposal with message based params") + // set initial value for currentMessagedBasedParams + if currentMessagedBasedParams == nil { + currentMessagedBasedParams = &messagedBasedParams + } + + // check if newly fetched messagedBasedParams is different from the previous fetched params + isEqual, err := currentMessagedBasedParams.Equal(&messagedBasedParams) + if err != nil { + return v1.Proposal{}, err + } + + if len(messages) > 1 && !isEqual { + return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposalMsg, "cannot submit multiple messages proposal with different message based params") } if proposalType != v1.ProposalType_PROPOSAL_TYPE_STANDARD { @@ -131,10 +150,6 @@ func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata return v1.Proposal{}, err } - proposerAddr, err := k.authKeeper.AddressCodec().BytesToString(proposer) - if err != nil { - return v1.Proposal{}, err - } submitTime := k.HeaderService.HeaderInfo(ctx).Time proposal, err := v1.NewProposal(messages, proposalID, submitTime, submitTime.Add(*params.MaxDepositPeriod), metadata, title, summary, proposerAddr, proposalType) if err != nil { diff --git a/x/gov/keeper/proposal_test.go b/x/gov/keeper/proposal_test.go index eb5e7b41abe0..d4c8e49b04a0 100644 --- a/x/gov/keeper/proposal_test.go +++ b/x/gov/keeper/proposal_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "errors" "fmt" "strings" "testing" @@ -18,38 +19,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO(tip): remove this -func (suite *KeeperTestSuite) TestDeleteProposal() { - testCases := map[string]struct { - proposalType v1.ProposalType - }{ - "unspecified proposal type": {}, - "regular proposal": { - proposalType: v1.ProposalType_PROPOSAL_TYPE_STANDARD, - }, - "expedited proposal": { - proposalType: v1.ProposalType_PROPOSAL_TYPE_EXPEDITED, - }, - } - - for _, tc := range testCases { - // delete non-existing proposal - suite.Require().ErrorIs(suite.govKeeper.DeleteProposal(suite.ctx, 10), collections.ErrNotFound) - - tp := TestProposal - proposal, err := suite.govKeeper.SubmitProposal(suite.ctx, tp, "", "test", "summary", suite.addrs[0], tc.proposalType) - suite.Require().NoError(err) - proposalID := proposal.Id - err = suite.govKeeper.Proposals.Set(suite.ctx, proposal.Id, proposal) - suite.Require().NoError(err) - - suite.Require().NotPanics(func() { - err := suite.govKeeper.DeleteProposal(suite.ctx, proposalID) - suite.Require().NoError(err) - }, "") - } -} - func (suite *KeeperTestSuite) TestActivateVotingPeriod() { testCases := []struct { name string @@ -147,6 +116,24 @@ func (suite *KeeperTestSuite) TestSubmitProposal() { }) suite.Require().NoError(err) + // add 1 more messageBasedParams with the same value as above + err = suite.govKeeper.MessageBasedParams.Set(suite.ctx, sdk.MsgTypeURL(&v1.MsgSudoExec{}), v1.MessageBasedParams{ + VotingPeriod: func() *time.Duration { t := time.Hour * 24 * 7; return &t }(), + Quorum: "0.4", + Threshold: "0.50", + VetoThreshold: "0.66", + }) + suite.Require().NoError(err) + + // add 1 more messageBasedParams with different value as above + err = suite.govKeeper.MessageBasedParams.Set(suite.ctx, sdk.MsgTypeURL(&v1.MsgCancelProposal{}), v1.MessageBasedParams{ + VotingPeriod: func() *time.Duration { t := time.Hour * 24 * 7; return &t }(), + Quorum: "0.2", + Threshold: "0.4", + VetoThreshold: "0.7", + }) + suite.Require().NoError(err) + testCases := []struct { msgs []sdk.Msg metadata string @@ -156,6 +143,12 @@ func (suite *KeeperTestSuite) TestSubmitProposal() { {legacyProposal(&tp, govAcct), "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, nil}, // normal proposal with msg with custom params {[]sdk.Msg{&v1.MsgUpdateParams{Authority: govAcct}}, "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, nil}, + // normal proposal with 2 identical msgs with custom params + {[]sdk.Msg{&v1.MsgUpdateParams{Authority: govAcct}, &v1.MsgUpdateParams{Authority: govAcct}}, "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, nil}, + // normal proposal with 2 msgs with custom params shared the same value + {[]sdk.Msg{&v1.MsgUpdateParams{Authority: govAcct}, &v1.MsgSudoExec{Authority: govAcct}}, "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, nil}, + // normal proposal with 2 msgs with different custom params + {[]sdk.Msg{&v1.MsgUpdateParams{Authority: govAcct}, &v1.MsgCancelProposal{}}, "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, errors.New("cannot submit multiple messages proposal with different message based params")}, {legacyProposal(&tp, govAcct), "", v1.ProposalType_PROPOSAL_TYPE_EXPEDITED, nil}, {nil, "", v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE, nil}, // Keeper does not check the validity of title and description, no error diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go index cc0790c27031..193895793caa 100644 --- a/x/gov/keeper/tally.go +++ b/x/gov/keeper/tally.go @@ -98,7 +98,7 @@ func (k Keeper) tallyStandard(ctx context.Context, proposal v1.Proposal, totalVo } // If no one votes (everyone abstains), proposal fails - if totalVoterPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) { + if totalVoterPower.Equal(results[v1.OptionAbstain]) { return false, false, tallyResults, nil } @@ -142,7 +142,7 @@ func (k Keeper) tallyExpedited(totalVoterPower math.LegacyDec, totalBonded math. } // If no one votes (everyone abstains), proposal fails - if totalVoterPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) { + if totalVoterPower.Equal(results[v1.OptionAbstain]) { return false, false, tallyResults, nil } @@ -160,7 +160,6 @@ func (k Keeper) tallyExpedited(totalVoterPower math.LegacyDec, totalBonded math. // If more than 2/3 of non-abstaining voters vote Yes, proposal passes threshold, _ := math.LegacyNewDecFromStr(params.GetExpeditedThreshold()) - if results[v1.OptionYes].Quo(totalVoterPower.Sub(results[v1.OptionAbstain])).GT(threshold) { return true, false, tallyResults, nil } @@ -206,7 +205,6 @@ func (k Keeper) tallyMultipleChoice(totalVoterPower math.LegacyDec, totalBonded } // a multiple choice proposal always passes unless it was spam or quorum was not reached. - return true, false, tallyResults, nil } diff --git a/x/gov/keeper/vote.go b/x/gov/keeper/vote.go index 25d11e514722..53156e6d8626 100644 --- a/x/gov/keeper/vote.go +++ b/x/gov/keeper/vote.go @@ -57,9 +57,9 @@ func (k Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr sdk.Ac } // verify votes only on existing votes - if proposalOptionsStr.OptionOne == "" && option.Option == v1.OptionOne { // should never trigger option one is always mandatory + if proposalOptionsStr.OptionOne == "" && option.Option == v1.OptionOne { return errors.Wrap(types.ErrInvalidVote, "invalid vote option") - } else if proposalOptionsStr.OptionTwo == "" && option.Option == v1.OptionTwo { // should never trigger option two is always mandatory + } else if proposalOptionsStr.OptionTwo == "" && option.Option == v1.OptionTwo { return errors.Wrap(types.ErrInvalidVote, "invalid vote option") } else if proposalOptionsStr.OptionThree == "" && option.Option == v1.OptionThree { return errors.Wrap(types.ErrInvalidVote, "invalid vote option") diff --git a/x/gov/types/v1/params.go b/x/gov/types/v1/params.go index 0bddd772fb3f..4407b9daf29f 100644 --- a/x/gov/types/v1/params.go +++ b/x/gov/types/v1/params.go @@ -5,6 +5,7 @@ import ( "time" "cosmossdk.io/core/address" + "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -332,3 +333,104 @@ func (p MessageBasedParams) ValidateBasic() error { return nil } + +func (p MessageBasedParams) Equal(params *MessageBasedParams) (bool, error) { + if p.VotingPeriod != nil && params.VotingPeriod != nil { + if p.VotingPeriod.Seconds() != params.VotingPeriod.Seconds() { + return false, nil + } + } else if p.VotingPeriod == nil && params.VotingPeriod != nil || + p.VotingPeriod != nil && params.VotingPeriod == nil { + return false, nil + } + + quorum1, err := sdkmath.LegacyNewDecFromStr(p.Quorum) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid quorum string: %w", err) + } + + quorum1 = sdkmath.LegacyZeroDec() + } + + quorum2, err := sdkmath.LegacyNewDecFromStr(params.Quorum) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid compared quorum string: %w", err) + } + + quorum2 = sdkmath.LegacyZeroDec() + } + + if !quorum1.Equal(quorum2) { + return false, nil + } + + yesQuorum1, err := sdkmath.LegacyNewDecFromStr(p.YesQuorum) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid yes quorum string: %w", err) + } + + yesQuorum1 = sdkmath.LegacyZeroDec() + } + + yesQuorum2, err := sdkmath.LegacyNewDecFromStr(params.YesQuorum) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid compared yes quorum string: %w", err) + } + + yesQuorum2 = sdkmath.LegacyZeroDec() + } + + if !yesQuorum1.Equal(yesQuorum2) { + return false, nil + } + + threshold1, err := sdkmath.LegacyNewDecFromStr(p.Threshold) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid vote threshold string: %w", err) + } + + threshold1 = sdkmath.LegacyZeroDec() + } + + threshold2, err := sdkmath.LegacyNewDecFromStr(params.Threshold) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid compared vote threshold string: %w", err) + } + + threshold2 = sdkmath.LegacyZeroDec() + } + + if !threshold1.Equal(threshold2) { + return false, nil + } + + vetoThreshold1, err := sdkmath.LegacyNewDecFromStr(p.VetoThreshold) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid veto threshold string: %w", err) + } + + vetoThreshold1 = sdkmath.LegacyZeroDec() + } + + vetoThreshold2, err := sdkmath.LegacyNewDecFromStr(params.VetoThreshold) + if err != nil { + if !errors.IsOf(err, sdkmath.ErrLegacyEmptyDecimalStr) { + return false, fmt.Errorf("invalid compared veto threshold string: %w", err) + } + + vetoThreshold2 = sdkmath.LegacyZeroDec() + } + + if !vetoThreshold1.Equal(vetoThreshold2) { + return false, nil + } + + return true, nil +} diff --git a/x/gov/types/v1/proposal.go b/x/gov/types/v1/proposal.go index e9a901934c58..9428131eaace 100644 --- a/x/gov/types/v1/proposal.go +++ b/x/gov/types/v1/proposal.go @@ -68,7 +68,7 @@ func (p Proposal) GetMsgs() ([]sdk.Msg, error) { // the proposal is expedited. Otherwise, returns the regular min deposit from // gov params. func (p Proposal) GetMinDepositFromParams(params Params) sdk.Coins { - if p.Expedited { + if p.ProposalType == ProposalType_PROPOSAL_TYPE_EXPEDITED { return params.ExpeditedMinDeposit } return params.MinDeposit diff --git a/x/gov/types/v1beta1/router.go b/x/gov/types/v1beta1/router.go index 41e7eaaacd13..0c5bfb7d22fd 100644 --- a/x/gov/types/v1beta1/router.go +++ b/x/gov/types/v1beta1/router.go @@ -9,8 +9,6 @@ import ( var _ Router = (*router)(nil) // Router implements a governance Handler router. -// -// TODO: Use generic router (ref #3976). type Router interface { AddRoute(r string, h Handler) (rtr Router) HasRoute(r string) bool