From eefd7edc125bcb7a22b923a5b242f723ce3392ee Mon Sep 17 00:00:00 2001 From: Giuseppe Natale <12249307+giunatale@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:41:07 +0100 Subject: [PATCH] feat(x/gov): remove ability for validators to add votes (#10) ## Description Closes: #7 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... * [x] Included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title * [ ] Added `!` to the type prefix if API, client, or state breaking change (i.e., requires minor or major version bump) * [x] Targeted the correct branch (see [PR Targeting](https://github.com/atomone-hub/govgen/blob/main/CONTRIBUTING.md#pr-targeting)) * [x] Provided a link to the relevant issue or specification * [x] Followed the guidelines for [building SDK modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/docs/building-modules) * [x] Included the necessary unit and integration [tests](https://github.com/atomone-hub/govgen/blob/main/CONTRIBUTING.md#testing) * [x] Added a changelog entry in `.changelog` (for details, see [contributing guidelines](../../CONTRIBUTING.md#changelog)) * [ ] Included comments for [documenting Go code](https://blog.golang.org/godoc) * [ ] Updated the relevant documentation or specification * [ ] Reviewed "Files changed" and left comments if necessary * [x] Confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... * [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title * [ ] confirmed `!` in the type prefix if API or client breaking change * [ ] confirmed all author checklist items have been addressed * [ ] reviewed state machine logic * [ ] reviewed API design and naming * [ ] reviewed documentation is accurate * [ ] reviewed tests and test coverage --------- Co-authored-by: Thomas Bruyelle --- CHANGELOG.md | 6 +- app/modules.go | 4 +- x/gov/abci_test.go | 16 +-- x/gov/keeper/deposit_test.go | 2 +- x/gov/keeper/grpc_query_test.go | 15 ++- x/gov/keeper/hooks_test.go | 2 +- x/gov/keeper/querier_test.go | 4 +- x/gov/keeper/tally_test.go | 146 +++++++++++++++++++++------- x/gov/keeper/vote.go | 6 ++ x/gov/keeper/vote_test.go | 8 +- x/gov/module.go | 6 +- x/gov/simulation/operations.go | 42 +++++--- x/gov/simulation/operations_test.go | 8 +- x/gov/types/errors.go | 1 + x/gov/types/expected_keepers.go | 2 + 15 files changed, 197 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b3f5b2..5e58055c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,5 +12,9 @@ ### FEATURES -### STATE BREAKING +* Forked `x/gov` module from Cosmos SDK v0.46.16 for custom modifications + ([#4](https://github.com/atomone-hub/govgen/pull/4)). +* Removed ability for validators to vote on proposals + ([#10](https://github.com/atomone-hub/govgen/pull/10)). +### STATE BREAKING diff --git a/app/modules.go b/app/modules.go index 76aee9d7..44fd23fe 100644 --- a/app/modules.go +++ b/app/modules.go @@ -97,7 +97,7 @@ func appModules( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), - gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), + gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), @@ -124,7 +124,7 @@ func simulationModules( bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), + gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), diff --git a/x/gov/abci_test.go b/x/gov/abci_test.go index ffb73ed4..bf962bd5 100644 --- a/x/gov/abci_test.go +++ b/x/gov/abci_test.go @@ -69,7 +69,7 @@ func TestTickExpiredDepositPeriod(t *testing.T) { } func TestTickMultipleExpiredDepositPeriod(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrs := govgenhelpers.AddTestAddrs(app, ctx, 10, valTokens) @@ -146,7 +146,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { } func TestTickPassedDepositPeriod(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrs := govgenhelpers.AddTestAddrs(app, ctx, 10, valTokens) @@ -203,7 +203,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { } func TestTickPassedVotingPeriod(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrs := govgenhelpers.AddTestAddrs(app, ctx, 10, valTokens) @@ -271,7 +271,7 @@ func TestTickPassedVotingPeriod(t *testing.T) { } func TestProposalPassedEndblocker(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrs := govgenhelpers.AddTestAddrs(app, ctx, 10, valTokens) @@ -307,7 +307,7 @@ func TestProposalPassedEndblocker(t *testing.T) { deposits := initialModuleAccCoins.Add(proposal.TotalDeposit...).Add(proposalCoins...) require.True(t, moduleAccCoins.IsEqual(deposits)) - err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)) + err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[1], types.NewNonSplitVoteOption(types.OptionYes)) require.NoError(t, err) newHeader := ctx.BlockHeader() @@ -322,9 +322,9 @@ func TestProposalPassedEndblocker(t *testing.T) { } func TestEndBlockerProposalHandlerFailed(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - addrs := govgenhelpers.AddTestAddrs(app, ctx, 1, valTokens) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 2, valTokens) SortAddresses(addrs) @@ -348,7 +348,7 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) { handleAndCheck(t, gov.NewHandler(app.GovKeeper), ctx, newDepositMsg) - err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[0], types.NewNonSplitVoteOption(types.OptionYes)) + err = app.GovKeeper.AddVote(ctx, proposal.ProposalId, addrs[1], types.NewNonSplitVoteOption(types.OptionYes)) require.NoError(t, err) newHeader := ctx.BlockHeader() diff --git a/x/gov/keeper/deposit_test.go b/x/gov/keeper/deposit_test.go index b04b4563..14ef51a3 100644 --- a/x/gov/keeper/deposit_test.go +++ b/x/gov/keeper/deposit_test.go @@ -12,7 +12,7 @@ import ( ) func TestDeposits(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) TestAddrs := govgenhelpers.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000000)) diff --git a/x/gov/keeper/grpc_query_test.go b/x/gov/keeper/grpc_query_test.go index ecb5f545..d677180a 100644 --- a/x/gov/keeper/grpc_query_test.go +++ b/x/gov/keeper/grpc_query_test.go @@ -10,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func (suite *KeeperTestSuite) TestGRPCQueryProposal() { @@ -337,7 +338,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryVote() { func (suite *KeeperTestSuite) TestGRPCQueryVotes() { app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient - addrs := govgenhelpers.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(30000000)) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 2, sdk.NewInt(30000000)) var ( req *types.QueryVotesRequest @@ -714,7 +715,17 @@ func (suite *KeeperTestSuite) TestGRPCQueryDeposits() { func (suite *KeeperTestSuite) TestGRPCQueryTally() { app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient - addrs, _ := createValidators(suite.T(), ctx, app, []int64{5, 5, 5}) + _, valAddrs := createValidators(suite.T(), ctx, app, []int64{5, 5, 5}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 5) + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + suite.Require().True(found) + + for _, addr := range addrs { + _, err := app.StakingKeeper.Delegate(ctx, addr, delTokens, stakingtypes.Unbonded, val1, true) + suite.Require().NoError(err) + } var ( req *types.QueryTallyResultRequest diff --git a/x/gov/keeper/hooks_test.go b/x/gov/keeper/hooks_test.go index 27a1617e..4d7ff567 100644 --- a/x/gov/keeper/hooks_test.go +++ b/x/gov/keeper/hooks_test.go @@ -46,7 +46,7 @@ func (h *MockGovHooksReceiver) AfterProposalVotingPeriodEnded(ctx sdk.Context, p } func TestHooks(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) minDeposit := app.GovKeeper.GetDepositParams(ctx).MinDeposit diff --git a/x/gov/keeper/querier_test.go b/x/gov/keeper/querier_test.go index fdce0bfa..85c387ae 100644 --- a/x/gov/keeper/querier_test.go +++ b/x/gov/keeper/querier_test.go @@ -145,7 +145,7 @@ func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, quer } func TestQueries(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) legacyQuerierCdc := app.LegacyAmino() querier := keeper.NewQuerier(app.GovKeeper, legacyQuerierCdc) @@ -304,7 +304,7 @@ func TestQueries(t *testing.T) { } func TestPaginatedVotesQuery(t *testing.T) { - app := govgenhelpers.Setup(t) + app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) legacyQuerierCdc := app.LegacyAmino() diff --git a/x/gov/keeper/tally_test.go b/x/gov/keeper/tally_test.go index a76f6c91..37cd6b20 100644 --- a/x/gov/keeper/tally_test.go +++ b/x/gov/keeper/tally_test.go @@ -9,7 +9,6 @@ import ( tmproto "github.com/tendermint/tendermint/proto/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -40,8 +39,7 @@ func TestTallyNoQuorum(t *testing.T) { ctx := app.BaseApp.NewContext(false, tmproto.Header{}) createValidators(t, ctx, app, []int64{2, 5, 0}) - - addrs := govgenhelpers.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000000)) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 1, sdk.NewInt(10000000)) tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -60,11 +58,22 @@ func TestTallyNoQuorum(t *testing.T) { require.True(t, burnDeposits) } -func TestTallyOnlyValidatorsAllYes(t *testing.T) { +func TestTallyAllYes(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - addrs, _ := createValidators(t, ctx, app, []int64{5, 5, 5}) + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 5) + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for _, addr := range addrs { + _, err := app.StakingKeeper.Delegate(ctx, addr, delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } + tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -86,11 +95,22 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) { require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } -func TestTallyOnlyValidators51No(t *testing.T) { +func TestTally51No(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - valAccAddrs, _ := createValidators(t, ctx, app, []int64{5, 6, 0}) + power := []int64{5, 6, 0} + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for i := range addrs { + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power[i]) + _, err := app.StakingKeeper.Delegate(ctx, addrs[i], delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -99,8 +119,8 @@ func TestTallyOnlyValidators51No(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -110,11 +130,22 @@ func TestTallyOnlyValidators51No(t *testing.T) { require.False(t, burnDeposits) } -func TestTallyOnlyValidators51Yes(t *testing.T) { +func TestTally51Yes(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - valAccAddrs, _ := createValidators(t, ctx, app, []int64{5, 6, 0}) + power := []int64{5, 6, 0} + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for i := range addrs { + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power[i]) + _, err := app.StakingKeeper.Delegate(ctx, addrs[i], delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -123,8 +154,8 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionNo))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -135,11 +166,22 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } -func TestTallyOnlyValidatorsVetoed(t *testing.T) { +func TestTallyVetoed(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - valAccAddrs, _ := createValidators(t, ctx, app, []int64{6, 6, 7}) + power := []int64{6, 6, 7} + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for i := range addrs { + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power[i]) + _, err := app.StakingKeeper.Delegate(ctx, addrs[i], delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -148,9 +190,9 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionNoWithVeto))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionNoWithVeto))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -161,11 +203,22 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) { require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } -func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { +func TestTallyAbstainPasses(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - valAccAddrs, _ := createValidators(t, ctx, app, []int64{6, 6, 7}) + power := []int64{6, 6, 7} + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for i := range addrs { + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power[i]) + _, err := app.StakingKeeper.Delegate(ctx, addrs[i], delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -174,9 +227,9 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionNo))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionYes))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -187,11 +240,22 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } -func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { +func TestTallyAbstainFails(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - valAccAddrs, _ := createValidators(t, ctx, app, []int64{6, 6, 7}) + power := []int64{6, 6, 7} + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for i := range addrs { + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power[i]) + _, err := app.StakingKeeper.Delegate(ctx, addrs[i], delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -200,9 +264,9 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddrs[2], types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[2], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -213,12 +277,22 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } -func TestTallyOnlyValidatorsNonVoter(t *testing.T) { +func TestTallyNonVoter(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - valAccAddrs, _ := createValidators(t, ctx, app, []int64{5, 6, 7}) - valAccAddr1, valAccAddr2 := valAccAddrs[0], valAccAddrs[1] + power := []int64{6, 6, 7} + _, valAddrs := createValidators(t, ctx, app, []int64{1, 1, 1}) + addrs := govgenhelpers.AddTestAddrs(app, ctx, 3, sdk.NewInt(50000000)) + + val1, found := app.StakingKeeper.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + + for i := range addrs { + delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power[i]) + _, err := app.StakingKeeper.Delegate(ctx, addrs[i], delTokens, stakingtypes.Unbonded, val1, true) + require.NoError(t, err) + } tp := TestProposal proposal, err := app.GovKeeper.SubmitProposal(ctx, tp) @@ -227,8 +301,8 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { proposal.Status = types.StatusVotingPeriod app.GovKeeper.SetProposal(ctx, proposal) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr1, types.NewNonSplitVoteOption(types.OptionYes))) - require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, valAccAddr2, types.NewNonSplitVoteOption(types.OptionNo))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionYes))) + require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[1], types.NewNonSplitVoteOption(types.OptionNo))) proposal, ok := app.GovKeeper.GetProposal(ctx, proposalID) require.True(t, ok) @@ -239,6 +313,10 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } +/* +// NOTE: these tests are disabled as voting for validators is not available and therefore +// all features around voting and delegations will not work + func TestTallyDelgatorOverride(t *testing.T) { app := govgenhelpers.SetupNoValset(false) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) @@ -473,3 +551,5 @@ func TestTallyValidatorMultipleDelegations(t *testing.T) { require.True(t, tallyResults.Equals(expectedTallyResult)) } + +*/ diff --git a/x/gov/keeper/vote.go b/x/gov/keeper/vote.go index 72f984fe..e12cc6d2 100644 --- a/x/gov/keeper/vote.go +++ b/x/gov/keeper/vote.go @@ -19,6 +19,12 @@ func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.A return sdkerrors.Wrapf(types.ErrInactiveProposal, "%d", proposalID) } + // if account is validator it cannot vote + valAddr := sdk.ValAddress(voterAddr.Bytes()) + if _, found := keeper.sk.GetValidator(ctx, valAddr); found { + return types.ErrValidatorCannotVote + } + for _, option := range options { if !types.ValidWeightedVoteOption(option) { return sdkerrors.Wrap(types.ErrInvalidVote, option.String()) diff --git a/x/gov/keeper/vote_test.go b/x/gov/keeper/vote_test.go index 21558702..07832b6f 100644 --- a/x/gov/keeper/vote_test.go +++ b/x/gov/keeper/vote_test.go @@ -12,7 +12,7 @@ import ( ) func TestVotes(t *testing.T) { - app := govgenhelpers.SetupNoValset(false) + app := govgenhelpers.Setup(t) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrs := govgenhelpers.AddTestAddrsIncremental(app, ctx, 5, sdk.NewInt(30000000)) @@ -32,6 +32,12 @@ func TestVotes(t *testing.T) { require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(invalidOption)), "invalid option") + // Test vote from validator + vals := app.StakingKeeper.GetAllValidators(ctx) + valAddr, err := sdk.ValAddressFromBech32(vals[0].OperatorAddress) + require.NoError(t, err) + require.Error(t, app.GovKeeper.AddVote(ctx, proposalID, sdk.AccAddress(valAddr.Bytes()), types.NewNonSplitVoteOption(types.OptionAbstain)), "voting for validators is disabled") + // Test first vote require.NoError(t, app.GovKeeper.AddVote(ctx, proposalID, addrs[0], types.NewNonSplitVoteOption(types.OptionAbstain))) vote, found := app.GovKeeper.GetVote(ctx, proposalID, addrs[0]) diff --git a/x/gov/module.go b/x/gov/module.go index 52c4a2ed..e01c20d7 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -119,15 +119,17 @@ type AppModule struct { keeper keeper.Keeper accountKeeper types.AccountKeeper bankKeeper types.BankKeeper + stakingKeeper types.StakingKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper) AppModule { +func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, accountKeeper: ak, bankKeeper: bk, + stakingKeeper: sk, } } @@ -215,6 +217,6 @@ func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { return simulation.WeightedOperations( simState.AppParams, simState.Cdc, - am.accountKeeper, am.bankKeeper, am.keeper, simState.Contents, + am.accountKeeper, am.bankKeeper, am.stakingKeeper, am.keeper, simState.Contents, ) } diff --git a/x/gov/simulation/operations.go b/x/gov/simulation/operations.go index ddc36af8..f3ae6211 100644 --- a/x/gov/simulation/operations.go +++ b/x/gov/simulation/operations.go @@ -29,7 +29,8 @@ const ( // WeightedOperations returns all the operations from the module with their respective weights func WeightedOperations( appParams simtypes.AppParams, cdc codec.JSONCodec, ak types.AccountKeeper, - bk types.BankKeeper, k keeper.Keeper, wContents []simtypes.WeightedProposalContent, + bk types.BankKeeper, sk types.StakingKeeper, k keeper.Keeper, + wContents []simtypes.WeightedProposalContent, ) simulation.WeightedOperations { var ( weightMsgDeposit int @@ -68,7 +69,7 @@ func WeightedOperations( wProposalOps, simulation.NewWeightedOperation( weight, - SimulateMsgSubmitProposal(ak, bk, k, wContent.ContentSimulatorFn()), + SimulateMsgSubmitProposal(ak, bk, sk, k, wContent.ContentSimulatorFn()), ), ) } @@ -80,11 +81,11 @@ func WeightedOperations( ), simulation.NewWeightedOperation( weightMsgVote, - SimulateMsgVote(ak, bk, k), + SimulateMsgVote(ak, bk, sk, k), ), simulation.NewWeightedOperation( weightMsgVoteWeighted, - SimulateMsgVoteWeighted(ak, bk, k), + SimulateMsgVoteWeighted(ak, bk, sk, k), ), } @@ -95,7 +96,8 @@ func WeightedOperations( // voting on the proposal, and subsequently slashing the proposal. It is implemented using // future operations. func SimulateMsgSubmitProposal( - ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, contentSim simtypes.ContentSimulatorFn, + ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, + k keeper.Keeper, contentSim simtypes.ContentSimulatorFn, ) simtypes.Operation { // The states are: // column 1: All validators vote @@ -200,7 +202,7 @@ func SimulateMsgSubmitProposal( whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second) fops[i] = simtypes.FutureOperation{ BlockTime: whenVote, - Op: operationSimulateMsgVote(ak, bk, k, accs[whoVotes[i]], int64(proposalID)), + Op: operationSimulateMsgVote(ak, bk, sk, k, accs[whoVotes[i]], int64(proposalID)), } } @@ -260,12 +262,12 @@ func SimulateMsgDeposit(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Ke } // SimulateMsgVote generates a MsgVote with random values. -func SimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { - return operationSimulateMsgVote(ak, bk, k, simtypes.Account{}, -1) +func SimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, k keeper.Keeper) simtypes.Operation { + return operationSimulateMsgVote(ak, bk, sk, k, simtypes.Account{}, -1) } -func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, - simAccount simtypes.Account, proposalIDInt int64, +func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, + k keeper.Keeper, simAccount simtypes.Account, proposalIDInt int64, ) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, @@ -288,6 +290,12 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee proposalID = uint64(proposalIDInt) } + // if account is validator it cannot vote + valAddr := sdk.ValAddress(simAccount.Address.Bytes()) + if _, found := sk.GetValidator(ctx, valAddr); found { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgVote, "account is a validator"), nil, nil + } + option := randomVotingOption(r) msg := types.NewMsgVote(simAccount.Address, proposalID, option) @@ -314,12 +322,12 @@ func operationSimulateMsgVote(ak types.AccountKeeper, bk types.BankKeeper, k kee } // SimulateMsgVoteWeighted generates a MsgVoteWeighted with random values. -func SimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { - return operationSimulateMsgVoteWeighted(ak, bk, k, simtypes.Account{}, -1) +func SimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, k keeper.Keeper) simtypes.Operation { + return operationSimulateMsgVoteWeighted(ak, bk, sk, k, simtypes.Account{}, -1) } -func operationSimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, - simAccount simtypes.Account, proposalIDInt int64, +func operationSimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, + k keeper.Keeper, simAccount simtypes.Account, proposalIDInt int64, ) simtypes.Operation { return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, @@ -342,6 +350,12 @@ func operationSimulateMsgVoteWeighted(ak types.AccountKeeper, bk types.BankKeepe proposalID = uint64(proposalIDInt) } + // if account is validator it cannot vote + valAddr := sdk.ValAddress(simAccount.Address.Bytes()) + if _, found := sk.GetValidator(ctx, valAddr); found { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgVote, "account is a validator"), nil, nil + } + options := randomWeightedVotingOptions(r) msg := types.NewMsgVoteWeighted(simAccount.Address, proposalID, options) diff --git a/x/gov/simulation/operations_test.go b/x/gov/simulation/operations_test.go index 17c09faa..b8ba6ae4 100644 --- a/x/gov/simulation/operations_test.go +++ b/x/gov/simulation/operations_test.go @@ -61,7 +61,7 @@ func TestWeightedOperations(t *testing.T) { appParams := make(simtypes.AppParams) weightesOps := simulation.WeightedOperations(appParams, cdc, app.AccountKeeper, - app.BankKeeper, app.GovKeeper, mockWeightedProposalContent(3), + app.BankKeeper, app.StakingKeeper, app.GovKeeper, mockWeightedProposalContent(3), ) // setup 3 accounts @@ -107,7 +107,7 @@ func TestSimulateMsgSubmitProposal(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) // execute operation - op := simulation.SimulateMsgSubmitProposal(app.AccountKeeper, app.BankKeeper, app.GovKeeper, MockWeightedProposalContent{3}.ContentSimulatorFn()) + op := simulation.SimulateMsgSubmitProposal(app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GovKeeper, MockWeightedProposalContent{3}.ContentSimulatorFn()) operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") require.NoError(t, err) @@ -192,7 +192,7 @@ func TestSimulateMsgVote(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash, Time: blockTime}}) // execute operation - op := simulation.SimulateMsgVote(app.AccountKeeper, app.BankKeeper, app.GovKeeper) + op := simulation.SimulateMsgVote(app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GovKeeper) operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") require.NoError(t, err) @@ -234,7 +234,7 @@ func TestSimulateMsgVoteWeighted(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash, Time: blockTime}}) // execute operation - op := simulation.SimulateMsgVoteWeighted(app.AccountKeeper, app.BankKeeper, app.GovKeeper) + op := simulation.SimulateMsgVoteWeighted(app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.GovKeeper) operationMsg, _, err := op(r, app.BaseApp, ctx, accounts, "") require.NoError(t, err) diff --git a/x/gov/types/errors.go b/x/gov/types/errors.go index 49fbf2ed..7172de2e 100644 --- a/x/gov/types/errors.go +++ b/x/gov/types/errors.go @@ -14,4 +14,5 @@ var ( ErrInvalidVote = sdkerrors.Register(ModuleName, 70, "invalid vote option") ErrInvalidGenesis = sdkerrors.Register(ModuleName, 80, "invalid genesis state") ErrNoProposalHandlerExists = sdkerrors.Register(ModuleName, 90, "no handler exists for proposal type") + ErrValidatorCannotVote = sdkerrors.Register(ModuleName, 100, "voting for validators is disabled") ) diff --git a/x/gov/types/expected_keepers.go b/x/gov/types/expected_keepers.go index a6e52164..863bc73d 100644 --- a/x/gov/types/expected_keepers.go +++ b/x/gov/types/expected_keepers.go @@ -24,6 +24,8 @@ type StakingKeeper interface { ctx sdk.Context, delegator sdk.AccAddress, fn func(index int64, delegation stakingtypes.DelegationI) (stop bool), ) + // GetValidator gets a single validator + GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool) } // AccountKeeper defines the expected account keeper (noalias)