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