github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/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/libs/cosmos-sdk/x/gov/types"
     6  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/exported"
     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 exported.ValidatorI) (stop bool) {
    25  		currValidators[validator.GetOperator().String()] = types.NewValidatorGovInfo(
    26  			validator.GetOperator(),
    27  			validator.GetBondedTokens(),
    28  			validator.GetDelegatorShares(),
    29  			sdk.ZeroDec(),
    30  			types.OptionEmpty,
    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  		valAddrStr := sdk.ValAddress(vote.Voter).String()
    39  		if val, ok := currValidators[valAddrStr]; ok {
    40  			val.Vote = vote.Option
    41  			currValidators[valAddrStr] = val
    42  		}
    43  
    44  		// iterate over all delegations from voter, deduct from any delegated-to validators
    45  		keeper.sk.IterateDelegations(ctx, vote.Voter, func(index int64, delegation exported.DelegationI) (stop bool) {
    46  			valAddrStr := delegation.GetValidatorAddr().String()
    47  
    48  			if val, ok := currValidators[valAddrStr]; ok {
    49  				// There is no need to handle the special case that validator address equal to voter address.
    50  				// Because voter's voting power will tally again even if there will deduct voter's voting power from validator.
    51  				val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares())
    52  				currValidators[valAddrStr] = val
    53  
    54  				delegatorShare := delegation.GetShares().Quo(val.DelegatorShares)
    55  				votingPower := delegatorShare.MulInt(val.BondedTokens)
    56  
    57  				results[vote.Option] = results[vote.Option].Add(votingPower)
    58  				totalVotingPower = totalVotingPower.Add(votingPower)
    59  			}
    60  
    61  			return false
    62  		})
    63  
    64  		keeper.deleteVote(ctx, vote.ProposalID, vote.Voter)
    65  		return false
    66  	})
    67  
    68  	// iterate over the validators again to tally their voting power
    69  	for _, val := range currValidators {
    70  		if val.Vote == types.OptionEmpty {
    71  			continue
    72  		}
    73  
    74  		sharesAfterDeductions := val.DelegatorShares.Sub(val.DelegatorDeductions)
    75  		fractionAfterDeductions := sharesAfterDeductions.Quo(val.DelegatorShares)
    76  		votingPower := fractionAfterDeductions.MulInt(val.BondedTokens)
    77  
    78  		results[val.Vote] = results[val.Vote].Add(votingPower)
    79  		totalVotingPower = totalVotingPower.Add(votingPower)
    80  	}
    81  
    82  	tallyParams := keeper.GetTallyParams(ctx)
    83  	tallyResults = types.NewTallyResultFromMap(results)
    84  
    85  	// TODO: Upgrade the spec to cover all of these cases & remove pseudocode.
    86  	// If there is no staked coins, the proposal fails
    87  	if keeper.sk.TotalBondedTokens(ctx).IsZero() {
    88  		return false, false, tallyResults
    89  	}
    90  
    91  	// If there is not enough quorum of votes, the proposal fails
    92  	percentVoting := totalVotingPower.Quo(keeper.sk.TotalBondedTokens(ctx))
    93  	if percentVoting.LT(tallyParams.Quorum) {
    94  		return false, true, tallyResults
    95  	}
    96  
    97  	// If no one votes (everyone abstains), proposal fails
    98  	if totalVotingPower.Sub(results[types.OptionAbstain]).Equal(sdk.ZeroDec()) {
    99  		return false, false, tallyResults
   100  	}
   101  
   102  	// If more than 1/3 of voters veto, proposal fails
   103  	if results[types.OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) {
   104  		return false, true, tallyResults
   105  	}
   106  
   107  	// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
   108  	if results[types.OptionYes].Quo(totalVotingPower.Sub(results[types.OptionAbstain])).GT(tallyParams.Threshold) {
   109  		return true, false, tallyResults
   110  	}
   111  
   112  	// If more than 1/2 of non-abstaining voters vote No, proposal fails
   113  	return false, false, tallyResults
   114  }