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  }