github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/gov/keeper/tally.go (about) 1 package keeper 2 3 import ( 4 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 5 "github.com/fibonacci-chain/fbc/x/gov/types" 6 "github.com/fibonacci-chain/fbc/x/staking/exported" 7 ) 8 9 // validatorGovInfo used for tallying 10 type validatorGovInfo struct { 11 Address sdk.ValAddress // address of the validator operator 12 BondedTokens sdk.Int // Power of a Validator 13 DelegatorShares sdk.Dec // Total outstanding delegator shares 14 DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently 15 Vote types.VoteOption // Vote of the validator 16 } 17 18 func newValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares, 19 delegatorDeductions sdk.Dec, vote types.VoteOption) validatorGovInfo { 20 21 return validatorGovInfo{ 22 Address: address, 23 BondedTokens: bondedTokens, 24 DelegatorShares: delegatorShares, 25 DelegatorDeductions: delegatorDeductions, 26 Vote: vote, 27 } 28 } 29 30 func tallyDelegatorVotes( 31 ctx sdk.Context, keeper Keeper, currValidators map[string]validatorGovInfo, proposalID uint64, 32 voteP *types.Vote, voterPower, totalVotedPower *sdk.Dec, results map[types.VoteOption]sdk.Dec, 33 ) { 34 // iterate over all the votes 35 votesIterator := keeper.GetVotes(ctx, proposalID) 36 if voteP != nil { 37 votesIterator = append(votesIterator, *voteP) 38 } 39 for i := 0; i < len(votesIterator); i++ { 40 vote := votesIterator[i] 41 42 // if validator, just record it in the map 43 // if delegator tally voting power 44 valAddrStr := sdk.ValAddress(vote.Voter).String() 45 if val, ok := currValidators[valAddrStr]; ok { 46 val.Vote = vote.Option 47 currValidators[valAddrStr] = val 48 } else { 49 // iterate over all delegations from voter, deduct from any delegated-to validators 50 delegation := keeper.sk.Delegator(ctx, vote.Voter) 51 if delegation == nil { 52 continue 53 } 54 for _, val := range delegation.GetShareAddedValidatorAddresses() { 55 valAddrStr := val.String() 56 if valInfo, ok := currValidators[valAddrStr]; ok { 57 valInfo.DelegatorDeductions = valInfo.DelegatorDeductions.Add(delegation.GetLastAddedShares()) 58 currValidators[valAddrStr] = valInfo 59 60 votedPower := delegation.GetLastAddedShares() 61 // calculate vote power of delegator for voterPowerRate 62 if voteP != nil && vote.Voter.Equals(voteP.Voter) { 63 voterPower.Add(votedPower) 64 } 65 results[vote.Option] = results[vote.Option].Add(votedPower) 66 *totalVotedPower = totalVotedPower.Add(votedPower) 67 } 68 } 69 } 70 } 71 } 72 73 func tallyValidatorVotes( 74 currValidators map[string]validatorGovInfo, voteP *types.Vote, voterPower, 75 totalPower, totalVotedPower *sdk.Dec, results map[types.VoteOption]sdk.Dec, 76 ) { 77 // iterate over the validators again to tally their voting power 78 for key, val := range currValidators { 79 // calculate all vote power of current validators including delegated for voterPowerRate 80 *totalPower = totalPower.Add(val.DelegatorShares) 81 if val.Vote == types.OptionEmpty { 82 continue 83 } 84 85 valValidVotedPower := val.DelegatorShares.Sub(val.DelegatorDeductions) 86 if voteP != nil && sdk.ValAddress(voteP.Voter).String() == key { 87 // calculate vote power of validator after deduction for voterPowerRate 88 *voterPower = voterPower.Add(valValidVotedPower) 89 } 90 results[val.Vote] = results[val.Vote].Add(valValidVotedPower) 91 *totalVotedPower = totalVotedPower.Add(valValidVotedPower) 92 } 93 } 94 95 func preTally( 96 ctx sdk.Context, keeper Keeper, proposal types.Proposal, voteP *types.Vote, 97 ) (results map[types.VoteOption]sdk.Dec, totalVotedPower sdk.Dec, voterPowerRate sdk.Dec) { 98 results = make(map[types.VoteOption]sdk.Dec) 99 results[types.OptionYes] = sdk.ZeroDec() 100 results[types.OptionAbstain] = sdk.ZeroDec() 101 results[types.OptionNo] = sdk.ZeroDec() 102 results[types.OptionNoWithVeto] = sdk.ZeroDec() 103 104 totalVotedPower = sdk.ZeroDec() 105 totalPower := sdk.ZeroDec() 106 voterPower := sdk.ZeroDec() 107 currValidators := make(map[string]validatorGovInfo) 108 109 // fetch all the current validators except candidate, insert them into currValidators 110 keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator exported.ValidatorI) (stop bool) { 111 currValidators[validator.GetOperator().String()] = newValidatorGovInfo( 112 validator.GetOperator(), 113 validator.GetBondedTokens(), 114 validator.GetDelegatorShares(), 115 sdk.ZeroDec(), 116 types.OptionEmpty, 117 ) 118 119 return false 120 }) 121 122 tallyDelegatorVotes(ctx, keeper, currValidators, proposal.ProposalID, 123 voteP, &voterPower, &totalVotedPower, results) 124 125 tallyValidatorVotes(currValidators, voteP, &voterPower, &totalPower, &totalVotedPower, results) 126 if totalPower.GT(sdk.ZeroDec()) { 127 voterPowerRate = voterPower.Quo(totalPower) 128 } else { 129 voterPowerRate = sdk.ZeroDec() 130 } 131 132 return results, totalVotedPower, voterPowerRate 133 } 134 135 // tally and return status before voting period end time 136 func tallyStatusInVotePeriod( 137 ctx sdk.Context, keeper Keeper, tallyResults types.TallyResult, 138 ) (types.ProposalStatus, bool) { 139 tallyParams := keeper.GetTallyParams(ctx) 140 totalPower := tallyResults.TotalPower 141 // TODO: Upgrade the spec to cover all of these cases & remove pseudocode. 142 // If there is no staked coins, the proposal fails 143 if totalPower.IsZero() { 144 return types.StatusRejected, false 145 } 146 // If no one votes (everyone abstains), proposal fails 147 if totalPower.Sub(tallyResults.Abstain).Equal(sdk.ZeroDec()) { 148 return types.StatusRejected, false 149 } 150 // If more than 1/3 of voters veto, proposal fails 151 if tallyResults.NoWithVeto.Quo(totalPower).GT(tallyParams.Veto) { 152 return types.StatusRejected, true 153 } 154 // If more than or equal to 1/2 of non-abstain vote not Yes, proposal fails 155 if tallyResults.NoWithVeto.Add(tallyResults.No).Quo(totalPower.Sub(tallyResults.Abstain)). 156 GTE(tallyParams.Threshold) { 157 return types.StatusRejected, false 158 } 159 // If more than 2/3 of totalPower vote Yes, proposal passes 160 if tallyResults.Yes.Quo(totalPower).GT(tallyParams.YesInVotePeriod) { 161 return types.StatusPassed, false 162 } 163 164 return types.StatusVotingPeriod, false 165 } 166 167 // tally and return status expire voting period end time 168 func tallyStatusExpireVotePeriod( 169 ctx sdk.Context, keeper Keeper, tallyResults types.TallyResult, 170 ) (types.ProposalStatus, bool) { 171 tallyParams := keeper.GetTallyParams(ctx) 172 totalVoted := tallyResults.TotalVotedPower 173 totalPower := tallyResults.TotalPower 174 // TODO: Upgrade the spec to cover all of these cases & remove pseudo code. 175 // If there is no staked coins, the proposal fails 176 if totalPower.IsZero() { 177 return types.StatusRejected, false 178 } 179 // If there is not enough quorum of votes, the proposal fails 180 percentVoting := totalVoted.Quo(totalPower) 181 if percentVoting.LT(tallyParams.Quorum) { 182 return types.StatusRejected, true 183 } 184 // If no one votes (everyone abstains), proposal fails 185 if totalVoted.Sub(tallyResults.Abstain).Equal(sdk.ZeroDec()) { 186 return types.StatusRejected, false 187 } 188 // If more than 1/3 of voters veto, proposal fails 189 if tallyResults.NoWithVeto.Quo(totalVoted).GT(tallyParams.Veto) { 190 return types.StatusRejected, true 191 } 192 // If more than 1/2 of non-abstaining voters vote Yes, proposal passes 193 if tallyResults.Yes.Quo(totalVoted.Sub(tallyResults.Abstain)).GT(tallyParams.Threshold) { 194 return types.StatusPassed, false 195 } 196 // If more than 1/2 of non-abstaining voters vote No, proposal fails 197 198 return types.StatusRejected, false 199 } 200 201 // Tally counts the votes for proposal 202 func Tally(ctx sdk.Context, keeper Keeper, proposal types.Proposal, isExpireVoteEndTime bool, 203 ) (types.ProposalStatus, bool, types.TallyResult) { 204 results, totalVotedPower, _ := preTally(ctx, keeper, proposal, nil) 205 tallyResults := types.NewTallyResultFromMap(results) 206 tallyResults.TotalPower = keeper.totalPower(ctx) 207 tallyResults.TotalVotedPower = totalVotedPower 208 209 if isExpireVoteEndTime { 210 status, distribute := tallyStatusExpireVotePeriod(ctx, keeper, tallyResults) 211 return status, distribute, tallyResults 212 } 213 status, distribute := tallyStatusInVotePeriod(ctx, keeper, tallyResults) 214 return status, distribute, tallyResults 215 }