-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathtally.go
127 lines (103 loc) · 4.58 KB
/
tally.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package keeper
import (
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// TODO: Break into several smaller functions for clarity
// 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 v1.Proposal) (passes bool, burnDeposits bool, tallyResults v1.TallyResult) {
results := make(map[v1.VoteOption]sdk.Dec)
results[v1.OptionYes] = math.LegacyZeroDec()
results[v1.OptionAbstain] = math.LegacyZeroDec()
results[v1.OptionNo] = math.LegacyZeroDec()
results[v1.OptionNoWithVeto] = math.LegacyZeroDec()
totalVotingPower := math.LegacyZeroDec()
currValidators := make(map[string]v1.ValidatorGovInfo)
// fetch all the bonded validators, insert them into currValidators
keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
currValidators[validator.GetOperator().String()] = v1.NewValidatorGovInfo(
validator.GetOperator(),
validator.GetBondedTokens(),
validator.GetDelegatorShares(),
math.LegacyZeroDec(),
v1.WeightedVoteOptions{},
)
return false
})
keeper.IterateVotes(ctx, proposal.Id, func(vote v1.Vote) bool {
// if validator, just record it in the map
voter := sdk.MustAccAddressFromBech32(vote.Voter)
valAddrStr := sdk.ValAddress(voter.Bytes()).String()
if val, ok := currValidators[valAddrStr]; ok {
val.Vote = vote.Options
currValidators[valAddrStr] = val
}
// iterate over all delegations from voter, deduct from any delegated-to validators
keeper.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) {
valAddrStr := delegation.GetValidatorAddr().String()
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 be deduction of voter's voting power from validator.
val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares())
currValidators[valAddrStr] = val
// delegation shares * bonded / total shares
votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
for _, option := range vote.Options {
weight, _ := sdk.NewDecFromStr(option.Weight)
subPower := votingPower.Mul(weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower)
}
return false
})
keeper.deleteVote(ctx, vote.ProposalId, voter)
return false
})
// iterate over the validators again to tally their voting power
for _, val := range currValidators {
if len(val.Vote) == 0 {
continue
}
sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions)
votingPower := sharesAfterDeductions.MulInt(val.BondedTokens).Quo(val.DelegatorShares)
for _, option := range val.Vote {
weight, _ := sdk.NewDecFromStr(option.Weight)
subPower := votingPower.Mul(weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower)
}
tallyParams := keeper.GetParams(ctx)
tallyResults = v1.NewTallyResultFromMap(results)
// TODO: Upgrade the spec to cover all of these cases & remove pseudocode.
// If there is no staked coins, the proposal fails
if keeper.sk.TotalBondedTokens(ctx).IsZero() {
return false, false, tallyResults
}
// If there is not enough quorum of votes, the proposal fails
percentVoting := totalVotingPower.Quo(sdk.NewDecFromInt(keeper.sk.TotalBondedTokens(ctx)))
quorum, _ := sdk.NewDecFromStr(tallyParams.Quorum)
if percentVoting.LT(quorum) {
return false, false, tallyResults
}
// If no one votes (everyone abstains), proposal fails
if totalVotingPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) {
return false, false, tallyResults
}
// If more than 1/3 of voters veto, proposal fails
vetoThreshold, _ := sdk.NewDecFromStr(tallyParams.VetoThreshold)
if results[v1.OptionNoWithVeto].Quo(totalVotingPower).GT(vetoThreshold) {
return false, true, tallyResults
}
// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
threshold, _ := sdk.NewDecFromStr(tallyParams.Threshold)
if results[v1.OptionYes].Quo(totalVotingPower.Sub(results[v1.OptionAbstain])).GT(threshold) {
return true, false, tallyResults
}
// If more than 1/2 of non-abstaining voters vote No, proposal fails
return false, false, tallyResults
}