From 945803d586b83d65547cd16f4cd5994eac2957ea Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 10 Dec 2018 18:50:20 +0800 Subject: [PATCH] Governance Quorum (#3053) --- PENDING.md | 1 + cmd/gaia/cmd/gaiareplay/main.go | 2 +- docs/spec/governance/overview.md | 4 ---- docs/spec/governance/state.md | 1 + x/gov/genesis.go | 1 + x/gov/params.go | 1 + x/gov/tally.go | 10 +++++++++- x/gov/tally_test.go | 26 ++++++++++++++++++++++++++ 8 files changed, 40 insertions(+), 6 deletions(-) diff --git a/PENDING.md b/PENDING.md index af6171e942..95ecba5b56 100644 --- a/PENDING.md +++ b/PENDING.md @@ -31,6 +31,7 @@ FEATURES - [\#2961](https://github.com/cosmos/cosmos-sdk/issues/2961) Add --force flag to gaiacli keys delete command to skip passphrase check and force key deletion unconditionally. * Gaia + - [gov] Added minimum quorum needed for vote to pass * SDK diff --git a/cmd/gaia/cmd/gaiareplay/main.go b/cmd/gaia/cmd/gaiareplay/main.go index 4f52889e2b..cd58640e4b 100644 --- a/cmd/gaia/cmd/gaiareplay/main.go +++ b/cmd/gaia/cmd/gaiareplay/main.go @@ -131,7 +131,7 @@ func run(rootDir string) { _ = proxyApp.Stop() }() - var state tmsm.State = tmsm.LoadState(tmDB) + state := tmsm.LoadState(tmDB) if state.LastBlockHeight == 0 { // Send InitChain msg fmt.Println("Sending InitChain msg") diff --git a/docs/spec/governance/overview.md b/docs/spec/governance/overview.md index a79b951904..e2efe5544a 100644 --- a/docs/spec/governance/overview.md +++ b/docs/spec/governance/overview.md @@ -111,10 +111,6 @@ option that casts a `NoWithVeto` vote.* Quorum is defined as the minimum percentage of voting power that needs to be casted on a proposal for the result to be valid. -In the initial version of the governance module, there will be no quorum -enforced by the protocol. Participation is ensured via the combination of -inheritance and validator's punishment for non-voting. - ### Threshold Threshold is defined as the minimum proportion of `Yes` votes (excluding diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 6c2799fd04..abe2dc41b4 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -24,6 +24,7 @@ type VotingParams struct { ```go type TallyParams struct { + Quorum sdk.Dec // Minimum percentage of stake that needs to vote for a proposal to be considered valid Threshold sdk.Dec // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec // Penalty if validator does not vote diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 341a4253b2..8498e3b5af 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -51,6 +51,7 @@ func DefaultGenesisState() GenesisState { VotingPeriod: time.Duration(172800) * time.Second, }, TallyParams: TallyParams{ + Quorum: sdk.NewDecWithPrec(334, 3), Threshold: sdk.NewDecWithPrec(5, 1), Veto: sdk.NewDecWithPrec(334, 3), GovernancePenalty: sdk.NewDecWithPrec(1, 2), diff --git a/x/gov/params.go b/x/gov/params.go index b9e1efa878..01da184d53 100644 --- a/x/gov/params.go +++ b/x/gov/params.go @@ -14,6 +14,7 @@ type DepositParams struct { // Param around Tallying votes in governance type TallyParams struct { + Quorum sdk.Dec `json:"quorum"` // Minimum percentage of total stake needed to vote for a result to be considered valid Threshold sdk.Dec `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec `json:"governance_penalty"` // Penalty if validator does not vote diff --git a/x/gov/tally.go b/x/gov/tally.go index d66237762e..199370e933 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -93,7 +93,15 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall NoWithVeto: results[OptionNoWithVeto], } - // If no one votes, proposal fails + // If there is no staked coins, the proposal fails + if keeper.vs.TotalPower(ctx).Equal(sdk.ZeroDec()) { + return false, tallyResults + } + // If there is not enough quorum of votes, the proposal fails + if totalVotingPower.Quo(keeper.vs.TotalPower(ctx)).LT(tallyParams.Quorum) { + return false, tallyResults + } + // If no one votes (everyone abstains), proposal fails if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) { return false, tallyResults } diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index b2f6aaf32a..86564ed425 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -59,6 +59,32 @@ func TestTallyNoOneVotes(t *testing.T) { require.True(t, tallyResults.Equals(EmptyTallyResult())) } +func TestTallyNoQuorum(t *testing.T) { + mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10) + mapp.BeginBlock(abci.RequestBeginBlock{}) + ctx := mapp.BaseApp.NewContext(false, abci.Header{}) + stakeHandler := stake.NewHandler(sk) + + valAddrs := make([]sdk.ValAddress, len(addrs[:2])) + for i, addr := range addrs[:2] { + valAddrs[i] = sdk.ValAddress(addr) + } + + createValidators(t, stakeHandler, ctx, valAddrs, []int64{2, 5}) + stake.EndBlocker(ctx, sk) + + proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) + proposalID := proposal.GetProposalID() + proposal.SetStatus(StatusVotingPeriod) + keeper.SetProposal(ctx, proposal) + + err := keeper.AddVote(ctx, proposalID, addrs[0], OptionYes) + require.Nil(t, err) + + passes, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + require.False(t, passes) +} + func TestTallyOnlyValidatorsAllYes(t *testing.T) { mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10) mapp.BeginBlock(abci.RequestBeginBlock{})