forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tally.go
119 lines (99 loc) · 3.96 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
package gov
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// validatorGovInfo used for tallying
type validatorGovInfo struct {
Address sdk.ValAddress // address of the validator operator
Power sdk.Dec // Power of a Validator
DelegatorShares sdk.Dec // Total outstanding delegator shares
Minus sdk.Dec // Minus of validator, used to compute validator's voting power
Vote VoteOption // Vote of the validator
}
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()
results[OptionNo] = sdk.ZeroDec()
results[OptionNoWithVeto] = sdk.ZeroDec()
totalVotingPower := sdk.ZeroDec()
currValidators := make(map[string]validatorGovInfo)
keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) {
currValidators[validator.GetOperator().String()] = validatorGovInfo{
Address: validator.GetOperator(),
Power: validator.GetPower(),
DelegatorShares: validator.GetDelegatorShares(),
Minus: sdk.ZeroDec(),
Vote: OptionEmpty,
}
return false
})
// iterate over all the votes
votesIterator := keeper.GetVotes(ctx, proposal.GetProposalID())
defer votesIterator.Close()
for ; votesIterator.Valid(); votesIterator.Next() {
vote := &Vote{}
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), vote)
// if validator, just record it in the map
// if delegator tally voting power
valAddrStr := sdk.ValAddress(vote.Voter).String()
if val, ok := currValidators[valAddrStr]; ok {
val.Vote = vote.Option
currValidators[valAddrStr] = val
} else {
keeper.ds.IterateDelegations(ctx, vote.Voter, func(index int64, delegation sdk.Delegation) (stop bool) {
valAddrStr := delegation.GetValidatorAddr().String()
if val, ok := currValidators[valAddrStr]; ok {
val.Minus = val.Minus.Add(delegation.GetShares())
currValidators[valAddrStr] = val
delegatorShare := delegation.GetShares().Quo(val.DelegatorShares)
votingPower := val.Power.Mul(delegatorShare)
results[vote.Option] = results[vote.Option].Add(votingPower)
totalVotingPower = totalVotingPower.Add(votingPower)
}
return false
})
}
keeper.deleteVote(ctx, vote.ProposalID, vote.Voter)
}
// iterate over the validators again to tally their voting power
for _, val := range currValidators {
if val.Vote == OptionEmpty {
continue
}
sharesAfterMinus := val.DelegatorShares.Sub(val.Minus)
percentAfterMinus := sharesAfterMinus.Quo(val.DelegatorShares)
votingPower := val.Power.Mul(percentAfterMinus)
results[val.Vote] = results[val.Vote].Add(votingPower)
totalVotingPower = totalVotingPower.Add(votingPower)
}
tallyParams := keeper.GetTallyParams(ctx)
tallyResults = TallyResult{
Yes: results[OptionYes],
Abstain: results[OptionAbstain],
No: results[OptionNo],
NoWithVeto: results[OptionNoWithVeto],
}
// 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
}
// If more than 1/3 of voters veto, proposal fails
if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) {
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(tallyParams.Threshold) {
return true, tallyResults
}
// If more than 1/2 of non-abstaining voters vote No, proposal fails
return false, tallyResults
}