diff --git a/PENDING.md b/PENDING.md index f4f816b2647e..1b9238796cb2 100644 --- a/PENDING.md +++ b/PENDING.md @@ -36,6 +36,7 @@ BREAKING CHANGES * `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub` * [x/stake] [#1013] TendermintUpdates now uses transient store * [x/gov] [#2195] Governance uses BFT Time + * [x/gov] \#2256 Removed slashing for governance non-voting validators * SDK * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 0d2927d7a5e2..5b577cec3918 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -28,7 +28,6 @@ type TallyingProcedure struct { Threshold sdk.Dec // Minimum propotion 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 - GracePeriod time.Time // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply } ``` @@ -192,14 +191,10 @@ And the pseudocode for the `ProposalProcessingQueue`: tallyingProcedure = load(GlobalParams, 'TallyingProcedure') - // Slash validators that did not vote, or update tally if they voted + // Update tally if validator voted they voted for each validator in validators - if (validator.bondTime < CurrentTime - tallyingProcedure.GracePeriod) - // only slash if validator entered validator set before grace period - if (!tmpValMap(validator).HasVoted) - slash validator by tallyingProcedure.GovernancePenalty - else - proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) + if tmpValMap(validator).HasVoted + proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 4cbd1d758821..27eff15f6443 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake" abci "github.com/tendermint/tendermint/abci/types" ) @@ -194,62 +193,3 @@ func TestTickPassedVotingPeriod(t *testing.T) { require.Equal(t, StatusRejected, keeper.GetProposal(ctx, proposalID).GetStatus()) require.True(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult())) } - -func TestSlashing(t *testing.T) { - mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10) - SortAddresses(addrs) - mapp.BeginBlock(abci.RequestBeginBlock{}) - ctx := mapp.BaseApp.NewContext(false, abci.Header{}) - govHandler := NewHandler(keeper) - stakeHandler := stake.NewHandler(sk) - - valAddrs := make([]sdk.ValAddress, len(addrs[:3])) - for i, addr := range addrs[:3] { - valAddrs[i] = sdk.ValAddress(addr) - } - - createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7}) - - initTotalPower := keeper.ds.GetValidatorSet().TotalPower(ctx) - val0Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[0])).GetPower().Quo(initTotalPower) - val1Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[1])).GetPower().Quo(initTotalPower) - val2Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[2])).GetPower().Quo(initTotalPower) - - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 15)}) - - res := govHandler(ctx, newProposalMsg) - require.True(t, res.IsOK()) - var proposalID int64 - keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) - - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - - require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) - - newVoteMsg := NewMsgVote(addrs[0], proposalID, OptionYes) - res = govHandler(ctx, newVoteMsg) - require.True(t, res.IsOK()) - - EndBlocker(ctx, keeper) - - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) - ctx = ctx.WithBlockHeader(newHeader) - - require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) - - EndBlocker(ctx, keeper) - - require.False(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult())) - - endTotalPower := keeper.ds.GetValidatorSet().TotalPower(ctx) - val0End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[0])).GetPower().Quo(endTotalPower) - val1End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[1])).GetPower().Quo(endTotalPower) - val2End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[2])).GetPower().Quo(endTotalPower) - - require.True(t, val0End.GTE(val0Initial)) - require.True(t, val1End.LT(val1Initial)) - require.True(t, val2End.LT(val2Initial)) -} diff --git a/x/gov/handler.go b/x/gov/handler.go index 00eaf3744020..3f8cd312b695 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -134,7 +134,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { continue } - passes, tallyResults, nonVotingVals := tally(ctx, keeper, activeProposal) + passes, tallyResults := tally(ctx, keeper, activeProposal) proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(activeProposal.GetProposalID()) var action []byte if passes { @@ -152,18 +152,6 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) - for _, valAddr := range nonVotingVals { - val := keeper.ds.GetValidatorSet().Validator(ctx, valAddr) - keeper.ds.GetValidatorSet().Slash(ctx, - val.GetConsAddr(), - ctx.BlockHeight(), - val.GetPower().RoundInt64(), - keeper.GetTallyingProcedure(ctx).GovernancePenalty) - - logger.Info(fmt.Sprintf("validator %s failed to vote on proposal %d; slashing", - val.GetOperator(), activeProposal.GetProposalID())) - } - resTags.AppendTag(tags.Action, action) resTags.AppendTag(tags.ProposalID, proposalIDBytes) } diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 44380fe91af3..606f73a9ca49 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -220,7 +220,7 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke } else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { tallyResult = proposal.GetTallyResult() } else { - _, tallyResult, _ = tally(ctx, keeper, proposal) + _, tallyResult = tally(ctx, keeper, proposal) } bz, err2 := codec.MarshalJSONIndent(keeper.cdc, tallyResult) diff --git a/x/gov/tally.go b/x/gov/tally.go index 55fd81da24a4..c5751258a00a 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -13,7 +13,7 @@ type validatorGovInfo struct { Vote VoteOption // Vote of the validator } -func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult, nonVoting []sdk.ValAddress) { +func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult) { results := make(map[VoteOption]sdk.Dec) results[OptionYes] = sdk.ZeroDec() results[OptionAbstain] = sdk.ZeroDec() @@ -70,12 +70,9 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall keeper.deleteVote(ctx, vote.ProposalID, vote.Voter) } - // iterate over the validators again to tally their voting power and see - // who didn't vote - nonVoting = []sdk.ValAddress{} + // iterate over the validators again to tally their voting power for _, val := range currValidators { if val.Vote == OptionEmpty { - nonVoting = append(nonVoting, val.Address) continue } @@ -98,19 +95,17 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall // If no one votes, proposal fails if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) { - return false, tallyResults, nonVoting + return false, tallyResults } // If more than 1/3 of voters veto, proposal fails if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyingProcedure.Veto) { - return false, tallyResults, nonVoting + return false, tallyResults } // If more than 1/2 of non-abstaining voters vote Yes, proposal passes if results[OptionYes].Quo(totalVotingPower.Sub(results[OptionAbstain])).GT(tallyingProcedure.Threshold) { - return true, tallyResults, nonVoting + return true, tallyResults } // If more than 1/2 of non-abstaining voters vote No, proposal fails - SortValAddresses(nonVoting) - - return false, tallyResults, nonVoting + return false, tallyResults } diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index d8e3e290ca91..28fe0cb5990e 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -51,7 +51,7 @@ func TestTallyNoOneVotes(t *testing.T) { proposal.SetStatus(StatusVotingPeriod) keeper.SetProposal(ctx, proposal) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.True(t, tallyResults.Equals(EmptyTallyResult())) @@ -80,7 +80,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[1], OptionYes) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -109,7 +109,7 @@ func TestTallyOnlyValidators51No(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[1], OptionNo) require.Nil(t, err) - passes, _, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) } @@ -139,7 +139,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -170,7 +170,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNoWithVeto) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -201,7 +201,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionYes) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -232,7 +232,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -261,11 +261,9 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, nonVoting := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) - require.Equal(t, 1, len(nonVoting)) - require.Equal(t, sdk.ValAddress(addrs[0]), nonVoting[0]) require.False(t, tallyResults.Equals(EmptyTallyResult())) } @@ -299,7 +297,7 @@ func TestTallyDelgatorOverride(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[3], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -333,10 +331,9 @@ func TestTallyDelgatorInherit(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionYes) require.Nil(t, err) - passes, tallyResults, nonVoting := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) - require.Equal(t, 0, len(nonVoting)) require.False(t, tallyResults.Equals(EmptyTallyResult())) } @@ -372,7 +369,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[3], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -417,7 +414,7 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -458,7 +455,7 @@ func TestTallyJailedValidator(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult()))