Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(x/gov): add min_stake_to_vote parameter #18186

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

* (x/gov) []() Add `min_stake_to_vote` gov parameter. If specified, it allows chains to set a minimum amount of stake required to vote on proposals.
* (x/staking) [#18142](https://github.com/cosmos/cosmos-sdk/pull/18142) introduce `key_rotation_fee` param to calculate fees while rotating the keys
* (server) [#18110](https://github.com/cosmos/cosmos-sdk/pull/18110) Start gRPC & API server in standalone mode
* (client) [#18101](https://github.com/cosmos/cosmos-sdk/pull/18101) Add a `keyring-default-keyname` in `client.toml` for specifying a default key name, and skip the need to use the `--from` flag when signing transactions.
Expand Down
187 changes: 146 additions & 41 deletions api/cosmos/gov/v1/gov.pulsar.go

Large diffs are not rendered by default.

33 changes: 23 additions & 10 deletions proto/cosmos/gov/v1/gov.proto
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,18 @@ message Params {
// Duration of the voting period.
google.protobuf.Duration voting_period = 3 [(gogoproto.stdduration) = true];

// Minimum percentage of total stake needed to vote for a result to be
// considered valid.
// Minimum percentage of total stake needed to vote for a result to be
// considered valid.
string quorum = 4 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum proportion of Yes votes for proposal to pass. Default value: 0.5.
// Minimum proportion of Yes votes for proposal to pass. Default value: 0.5.
string threshold = 5 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum value of Veto votes to Total votes ratio for proposal to be
// vetoed. Default value: 1/3.
// Minimum value of Veto votes to Total votes ratio for proposal to be
// vetoed. Default value: 1/3.
string veto_threshold = 6 [(cosmos_proto.scalar) = "cosmos.Dec"];

// The ratio representing the proportion of the deposit value that must be paid at proposal submission.
// The ratio representing the proportion of the deposit value that must be paid at proposal submission.
string min_initial_deposit_ratio = 7 [(cosmos_proto.scalar) = "cosmos.Dec"];

// The cancel ratio which will not be returned back to the depositors when a proposal is cancelled.
Expand All @@ -249,16 +249,29 @@ message Params {
// Since: cosmos-sdk 0.50
string expedited_threshold = 11 [(cosmos_proto.scalar) = "cosmos.Dec"];

// Minimum expedited deposit for a proposal to enter voting period.
// Minimum expedited deposit for a proposal to enter voting period.
repeated cosmos.base.v1beta1.Coin expedited_min_deposit = 12
[(gogoproto.nullable) = false, (amino.dont_omitempty) = true];

// burn deposits if a proposal does not meet quorum
// Burn deposits if a proposal does not meet quorum
//
// Since: cosmos-sdk v0.47
bool burn_vote_quorum = 13;

// burn deposits if the proposal does not enter voting period
// Burn deposits if the proposal does not enter voting period
//
// Since: cosmos-sdk v0.47
bool burn_proposal_deposit_prevote = 14;

// burn deposits if quorum with vote type no_veto is met
// Burn deposits if quorum with vote type no_veto is met
//
// Since: cosmos-sdk v0.47
bool burn_vote_veto = 15;

// min_stake_to_vote defines the minimum stake to vote on a proposal
// If the voter has less than min_stake_to_vote, the vote cannot vote.
// If min_stake_to_vote is not set, the vote will be counted regardless of the voter's stake
//
// Since: cosmos-sdk 0.50
cosmos.base.v1beta1.Coin min_stake_to_vote = 16;
}
42 changes: 7 additions & 35 deletions x/gov/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -645,9 +645,10 @@ upon receiving txGovDeposit from sender do

### Vote

Once `ActiveParam.MinDeposit` is reached, voting period starts. From there,
bonded Atom holders are able to send `MsgVote` transactions to cast their
vote on the proposal.
Once `ActiveParam.MinDeposit` is reached, voting period starts.
From there, bonded Atom holders are able to send `MsgVote` transactions to cast their vote on the proposal.
Chains have the ability to limit voting to certain account having a minimum stake.
This parameter is called `MinStakeToVote` and is set to 0 by default.

```protobuf reference
https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/gov/v1/tx.proto#L92-L108
Expand All @@ -661,35 +662,6 @@ https://github.com/cosmos/cosmos-sdk/blob/v0.47.0-rc1/proto/cosmos/gov/v1/tx.pro
Gas cost for this message has to take into account the future tallying of the vote in EndBlocker.
:::

Next is a pseudocode outline of the way `MsgVote` transactions are handled:

```go
// PSEUDOCODE //
// Check if MsgVote is valid. If it is, count vote//

upon receiving txGovVote from sender do
// check if proposal is correctly formatted. Includes fee payment.

if !correctlyFormatted(txGovDeposit)
throw

proposal = load(Proposals, <txGovDeposit.ProposalID|'proposal'>)

if (proposal == nil)
// There is no proposal for this proposalID
throw


if (proposal.CurrentStatus == ProposalStatusActive)


// Sender can vote if
// Proposal is active
// Sender has some bonds

store(Governance, <txGovVote.ProposalID|'addresses'|sender>, txGovVote.Vote) // Voters can vote multiple times. Re-voting overrides previous vote. This is ok because tallying is done once at the end.
```

## Events

The governance module emits the following events:
Expand Down Expand Up @@ -767,11 +739,11 @@ The governance module contains the following parameters:
| expedited_threshold | string (time ns) | "0.667000000000000000" |
| expedited_voting_period | string (time ns) | "86400000000000" (8600s) |
| expedited_min_deposit | array (coins) | [{"denom":"uatom","amount":"50000000"}] |
| burn_proposal_deposit_prevote | bool | false |
| burn_proposal_deposit_prevote | bool | false |
| burn_vote_quorum | bool | false |
| burn_vote_veto | bool | true |
| min_initial_deposit_ratio | string | "0.1" |

| min_initial_deposit_ratio | string | "0.1" |
| min_stake_to_vote | string (coin) | {"denom":"uatom","amount":"10000000"} |

**NOTE**: The governance module contains parameters that are objects unlike other
modules. If only a subset of parameters are desired to be changed, only they need
Expand Down
19 changes: 0 additions & 19 deletions x/gov/codec/doc.go

This file was deleted.

28 changes: 28 additions & 0 deletions x/gov/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,34 @@ func (suite *KeeperTestSuite) TestMsgVote() {
expErr: true,
expErrMsg: longAddressError,
},
"minimum stake not met": {
preRun: func() uint64 {
minStake := sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(1))
params.MinStakeToVote = &minStake
_ = suite.govKeeper.Params.Set(suite.ctx, params)

msg, err := v1.NewMsgSubmitProposal(
[]sdk.Msg{bankMsg},
minDeposit,
proposer.String(),
"",
"Proposal",
"description of proposal",
false,
)
suite.Require().NoError(err)

res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
suite.Require().NoError(err)
suite.Require().NotNil(res.ProposalId)
return res.ProposalId
},
option: v1.VoteOption_VOTE_OPTION_YES,
voter: proposer,
metadata: "",
expErr: false,
expErrMsg: "voter has too not enough staked token to vote",
},
"all good": {
preRun: func() uint64 {
msg, err := v1.NewMsgSubmitProposal(
Expand Down
12 changes: 12 additions & 0 deletions x/gov/keeper/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"cosmossdk.io/collections"
"cosmossdk.io/errors"
"cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
Expand All @@ -14,6 +15,17 @@ import (

// AddVote adds a vote on a specific proposal
func (keeper Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr sdk.AccAddress, options v1.WeightedVoteOptions, metadata string) error {
// Check if voter has enough staked token to vote.
params, err := keeper.Params.Get(ctx)
if err != nil {
return fmt.Errorf("failed to get gov params: %w", err)
}

stakedCoins := sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt()) // todo
if stakedCoins.IsLT(*params.MinStakeToVote) {
return fmt.Errorf("voter has too not enough staked token to vote: minimum %s, got %s", params.MinStakeToVote, stakedCoins)
}

// Check if proposal is in voting period.
inVotingPeriod, err := keeper.VotingPeriodProposals.Has(ctx, proposalID)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions x/gov/migrations/v5/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
//
// Addition of the new proposal expedited parameters that are set to 0 by default.
// Set of default chain constitution.
// Set of default minimum stake to vote.
func MigrateStore(ctx sdk.Context, storeService corestoretypes.KVStoreService, cdc codec.BinaryCodec, constitutionCollection collections.Item[string]) error {
store := storeService.OpenKVStore(ctx)
paramsBz, err := store.Get(ParamsKey)
Expand All @@ -40,6 +41,7 @@ func MigrateStore(ctx sdk.Context, storeService corestoretypes.KVStoreService, c
params.ExpeditedThreshold = defaultParams.ExpeditedThreshold
params.ProposalCancelRatio = defaultParams.ProposalCancelRatio
params.ProposalCancelDest = defaultParams.ProposalCancelDest
params.MinStakeToVote = defaultParams.MinStakeToVote

bz, err := cdc.Marshal(&params)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions x/gov/migrations/v5/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestMigrateStore(t *testing.T) {
require.Equal(t, v1.DefaultParams().ExpeditedMinDeposit, params.ExpeditedMinDeposit)
require.Equal(t, v1.DefaultParams().ExpeditedThreshold, params.ExpeditedThreshold)
require.Equal(t, v1.DefaultParams().ExpeditedVotingPeriod, params.ExpeditedVotingPeriod)
require.Equal(t, v1.DefaultParams().MinStakeToVote, params.MinStakeToVote)

// Check constitution
result, err := constitutionCollection.Get(ctx)
Expand Down
11 changes: 10 additions & 1 deletion x/gov/simulation/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
ExpeditedThreshold = "expedited_threshold"
Veto = "veto"
ProposalCancelRate = "proposal_cancel_rate"
MinStakeToVote = "min_stake_to_vote"

// ExpeditedThreshold must be at least as large as the regular Threshold
// Therefore, we use this break out point in randomization.
Expand Down Expand Up @@ -94,6 +95,11 @@ func GenVeto(r *rand.Rand) sdkmath.LegacyDec {
return sdkmath.LegacyNewDecWithPrec(int64(simulation.RandIntBetween(r, 250, 334)), 3)
}

// GenMinStakeToVote returns randomized MinStakeToVote
func GenMinStakeToVote(r *rand.Rand, bondDenom string) sdk.Coin {
return sdk.NewInt64Coin(bondDenom, int64(simulation.RandIntBetween(r, 0, 1)))
}

// RandomizedGenState generates a random GenesisState for gov
func RandomizedGenState(simState *module.SimulationState) {
startingProposalID := uint64(simState.Rand.Intn(100))
Expand Down Expand Up @@ -131,9 +137,12 @@ func RandomizedGenState(simState *module.SimulationState) {
var veto sdkmath.LegacyDec
simState.AppParams.GetOrGenerate(Veto, &veto, simState.Rand, func(r *rand.Rand) { veto = GenVeto(r) })

var minStakeToVote sdk.Coin
simState.AppParams.GetOrGenerate(MinStakeToVote, &minStakeToVote, simState.Rand, func(r *rand.Rand) { minStakeToVote = GenMinStakeToVote(r, simState.BondDenom) })

govGenesis := v1.NewGenesisState(
startingProposalID,
v1.NewParams(minDeposit, expeditedMinDeposit, depositPeriod, votingPeriod, expeditedVotingPeriod, quorum.String(), threshold.String(), expitedVotingThreshold.String(), veto.String(), minInitialDepositRatio.String(), proposalCancelRate.String(), "", simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0),
v1.NewParams(minDeposit, expeditedMinDeposit, depositPeriod, votingPeriod, expeditedVotingPeriod, quorum.String(), threshold.String(), expitedVotingThreshold.String(), veto.String(), minInitialDepositRatio.String(), proposalCancelRate.String(), "", simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, minStakeToVote),
)

bz, err := json.MarshalIndent(&govGenesis, "", " ")
Expand Down
3 changes: 3 additions & 0 deletions x/gov/simulation/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func TestRandomizedGenState(t *testing.T) {
tallyExpeditedThreshold = "0.545000000000000000"
tallyVetoThreshold = "0.327000000000000000"
minInitialDepositDec = "0.880000000000000000"
minStakeToVote = "0stake"
)

assert.Equal(t, "272stake", govGenesis.Params.MinDeposit[0].String())
Expand All @@ -62,6 +63,8 @@ func TestRandomizedGenState(t *testing.T) {
assert.Equal(t, tallyThreshold, govGenesis.Params.Threshold)
assert.Equal(t, tallyExpeditedThreshold, govGenesis.Params.ExpeditedThreshold)
assert.Equal(t, tallyVetoThreshold, govGenesis.Params.VetoThreshold)
assert.Equal(t, minInitialDepositDec, govGenesis.Params.MinInitialDepositRatio)
assert.Equal(t, minStakeToVote, govGenesis.Params.MinStakeToVote.String())
assert.Equal(t, uint64(0x28), govGenesis.StartingProposalId)
assert.DeepEqual(t, []*v1.Deposit{}, govGenesis.Deposits)
assert.DeepEqual(t, []*v1.Vote{}, govGenesis.Votes)
Expand Down
Loading
Loading