2019-08-08 12:51:18 -07:00
|
|
|
package keeper
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2019-06-04 11:38:11 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
2019-06-05 10:42:25 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/exported"
|
2018-06-21 17:19:14 -07:00
|
|
|
)
|
|
|
|
|
2019-02-08 18:33:06 -08:00
|
|
|
// TODO: Break into several smaller functions for clarity
|
2019-08-08 12:51:18 -07:00
|
|
|
|
|
|
|
// Tally iterates over the votes and updates the tally of a proposal based on the voting power of the
|
|
|
|
// voters
|
|
|
|
func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes bool, burnDeposits bool, tallyResults types.TallyResult) {
|
|
|
|
results := make(map[types.VoteOption]sdk.Dec)
|
|
|
|
results[types.OptionYes] = sdk.ZeroDec()
|
|
|
|
results[types.OptionAbstain] = sdk.ZeroDec()
|
|
|
|
results[types.OptionNo] = sdk.ZeroDec()
|
|
|
|
results[types.OptionNoWithVeto] = sdk.ZeroDec()
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2018-08-14 17:15:02 -07:00
|
|
|
totalVotingPower := sdk.ZeroDec()
|
2019-08-08 12:51:18 -07:00
|
|
|
currValidators := make(map[string]types.ValidatorGovInfo)
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2019-02-08 18:33:06 -08:00
|
|
|
// fetch all the bonded validators, insert them into currValidators
|
2019-06-05 10:42:25 -07:00
|
|
|
keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator exported.ValidatorI) (stop bool) {
|
2019-08-08 12:51:18 -07:00
|
|
|
currValidators[validator.GetOperator().String()] = types.NewValidatorGovInfo(
|
2019-02-05 21:30:48 -08:00
|
|
|
validator.GetOperator(),
|
|
|
|
validator.GetBondedTokens(),
|
|
|
|
validator.GetDelegatorShares(),
|
|
|
|
sdk.ZeroDec(),
|
2019-08-08 12:51:18 -07:00
|
|
|
types.OptionEmpty,
|
2019-02-05 21:30:48 -08:00
|
|
|
)
|
2019-04-30 09:31:38 -07:00
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
2019-06-04 11:38:11 -07:00
|
|
|
keeper.IterateVotes(ctx, proposal.ProposalID, func(vote types.Vote) bool {
|
2018-06-21 17:19:14 -07:00
|
|
|
// if validator, just record it in the map
|
2018-08-30 21:06:44 -07:00
|
|
|
valAddrStr := sdk.ValAddress(vote.Voter).String()
|
|
|
|
if val, ok := currValidators[valAddrStr]; ok {
|
2018-06-21 17:19:14 -07:00
|
|
|
val.Vote = vote.Option
|
2018-08-30 21:06:44 -07:00
|
|
|
currValidators[valAddrStr] = val
|
2019-10-18 09:38:23 -07:00
|
|
|
}
|
2018-08-30 21:06:44 -07:00
|
|
|
|
2019-10-18 09:38:23 -07:00
|
|
|
// iterate over all delegations from voter, deduct from any delegated-to validators
|
|
|
|
keeper.sk.IterateDelegations(ctx, vote.Voter, func(index int64, delegation exported.DelegationI) (stop bool) {
|
|
|
|
valAddrStr := delegation.GetValidatorAddr().String()
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2019-10-18 09:38:23 -07:00
|
|
|
if val, ok := currValidators[valAddrStr]; ok {
|
|
|
|
// There is no need to handle the special case that validator address equal to voter address.
|
|
|
|
// Because voter's voting power will tally again even if there will deduct voter's voting power from validator.
|
|
|
|
val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares())
|
|
|
|
currValidators[valAddrStr] = val
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2019-10-18 09:38:23 -07:00
|
|
|
delegatorShare := delegation.GetShares().Quo(val.DelegatorShares)
|
|
|
|
votingPower := delegatorShare.MulInt(val.BondedTokens)
|
2018-08-30 21:06:44 -07:00
|
|
|
|
2019-10-18 09:38:23 -07:00
|
|
|
results[vote.Option] = results[vote.Option].Add(votingPower)
|
|
|
|
totalVotingPower = totalVotingPower.Add(votingPower)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
})
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
keeper.deleteVote(ctx, vote.ProposalID, vote.Voter)
|
2019-06-04 11:38:11 -07:00
|
|
|
return false
|
|
|
|
})
|
2018-06-21 17:19:14 -07:00
|
|
|
|
2018-09-25 11:45:20 -07:00
|
|
|
// iterate over the validators again to tally their voting power
|
2018-06-21 17:19:14 -07:00
|
|
|
for _, val := range currValidators {
|
2019-08-08 12:51:18 -07:00
|
|
|
if val.Vote == types.OptionEmpty {
|
2018-06-21 17:19:14 -07:00
|
|
|
continue
|
|
|
|
}
|
2018-08-30 21:06:44 -07:00
|
|
|
|
2019-02-08 18:33:06 -08:00
|
|
|
sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions)
|
|
|
|
fractionAfterDeductions := sharesAfterDeductions.Quo(val.DelegatorShares)
|
|
|
|
votingPower := fractionAfterDeductions.MulInt(val.BondedTokens)
|
2018-06-21 17:19:14 -07:00
|
|
|
|
|
|
|
results[val.Vote] = results[val.Vote].Add(votingPower)
|
|
|
|
totalVotingPower = totalVotingPower.Add(votingPower)
|
|
|
|
}
|
|
|
|
|
2018-11-06 23:33:18 -08:00
|
|
|
tallyParams := keeper.GetTallyParams(ctx)
|
2019-08-08 12:51:18 -07:00
|
|
|
tallyResults = types.NewTallyResultFromMap(results)
|
2018-08-08 03:28:52 -07:00
|
|
|
|
2019-02-08 18:33:06 -08:00
|
|
|
// TODO: Upgrade the spec to cover all of these cases & remove pseudocode.
|
2018-12-10 02:50:20 -08:00
|
|
|
// If there is no staked coins, the proposal fails
|
2019-06-04 15:06:58 -07:00
|
|
|
if keeper.sk.TotalBondedTokens(ctx).IsZero() {
|
2019-05-20 07:13:50 -07:00
|
|
|
return false, false, tallyResults
|
2018-12-10 02:50:20 -08:00
|
|
|
}
|
2019-04-30 09:31:38 -07:00
|
|
|
|
2018-12-10 02:50:20 -08:00
|
|
|
// If there is not enough quorum of votes, the proposal fails
|
2019-06-04 15:06:58 -07:00
|
|
|
percentVoting := totalVotingPower.Quo(keeper.sk.TotalBondedTokens(ctx).ToDec())
|
2019-01-02 12:29:47 -08:00
|
|
|
if percentVoting.LT(tallyParams.Quorum) {
|
2019-05-20 07:13:50 -07:00
|
|
|
return false, true, tallyResults
|
2018-12-10 02:50:20 -08:00
|
|
|
}
|
2019-04-30 09:31:38 -07:00
|
|
|
|
2018-12-10 02:50:20 -08:00
|
|
|
// If no one votes (everyone abstains), proposal fails
|
2019-08-08 12:51:18 -07:00
|
|
|
if totalVotingPower.Sub(results[types.OptionAbstain]).Equal(sdk.ZeroDec()) {
|
2019-05-20 07:13:50 -07:00
|
|
|
return false, false, tallyResults
|
2018-06-21 17:19:14 -07:00
|
|
|
}
|
2019-04-30 09:31:38 -07:00
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
// If more than 1/3 of voters veto, proposal fails
|
2019-08-08 12:51:18 -07:00
|
|
|
if results[types.OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) {
|
2019-05-20 07:13:50 -07:00
|
|
|
return false, true, tallyResults
|
2018-06-21 17:19:14 -07:00
|
|
|
}
|
2019-04-30 09:31:38 -07:00
|
|
|
|
2018-06-21 17:19:14 -07:00
|
|
|
// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
|
2019-08-08 12:51:18 -07:00
|
|
|
if results[types.OptionYes].Quo(totalVotingPower.Sub(results[types.OptionAbstain])).GT(tallyParams.Threshold) {
|
2019-05-20 07:13:50 -07:00
|
|
|
return true, false, tallyResults
|
2018-06-21 17:19:14 -07:00
|
|
|
}
|
2018-08-28 22:22:39 -07:00
|
|
|
|
2019-04-30 09:31:38 -07:00
|
|
|
// If more than 1/2 of non-abstaining voters vote No, proposal fails
|
2019-05-20 07:13:50 -07:00
|
|
|
return false, false, tallyResults
|
2018-06-21 17:19:14 -07:00
|
|
|
}
|