github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/distribution/keeper/allocation.go (about)

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     7  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
     8  
     9  	"github.com/fibonacci-chain/fbc/x/distribution/types"
    10  	"github.com/fibonacci-chain/fbc/x/staking/exported"
    11  	stakingexported "github.com/fibonacci-chain/fbc/x/staking/exported"
    12  )
    13  
    14  var (
    15  	valPortion  = sdk.NewDecWithPrec(25, 2)
    16  	votePortion = sdk.NewDecWithPrec(75, 2)
    17  )
    18  
    19  // AllocateTokens allocates fees from fee_collector
    20  // 1. 25% rewards to validators, equally.
    21  // 2. 75% rewards to validators and candidates, by shares' weight
    22  func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64,
    23  	previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo) {
    24  	logger := k.Logger(ctx)
    25  	// fetch and clear the collected fees for distribution, since this is
    26  	// called in BeginBlock, collected fees will be from the previous block
    27  	// (and distributed to the previous proposer)
    28  	feeCollector := k.supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName)
    29  	feesCollected := feeCollector.GetCoins()
    30  
    31  	if feesCollected.Empty() {
    32  		logger.Debug("No fee to distributed.")
    33  		return
    34  	}
    35  	logger.Debug("AllocateTokens", "TotalFee", feesCollected.String())
    36  
    37  	// transfer collected fees to the distribution module account
    38  	err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollected)
    39  	if err != nil {
    40  		panic(err)
    41  	}
    42  
    43  	feePool := k.GetFeePool(ctx)
    44  	if totalPreviousPower == 0 {
    45  		feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...)
    46  		k.SetFeePool(ctx, feePool)
    47  		logger.Debug("totalPreviousPower is zero, send fees to community pool", "fees", feesCollected)
    48  		return
    49  	}
    50  
    51  	preProposerVal := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer)
    52  	if preProposerVal == nil {
    53  		// previous proposer can be unknown if say, the unbonding period is 1 block, so
    54  		// e.g. a validator undelegates at block X, it's removed entirely by
    55  		// block X+1's endblock, then X+2 we need to refer to the previous
    56  		// proposer for X+1, but we've forgotten about them.
    57  		logger.Error(fmt.Sprintf(
    58  			"WARNING: Cannot find the entity of previous proposer validator %s.\n"+
    59  				"This should happen only if the proposer unbonded completely within a single block, "+
    60  				"which generally should not happen except in exceptional circumstances (or fuzz testing). "+
    61  				"We recommend you investigate immediately.", previousProposer.String()))
    62  	}
    63  
    64  	feesToVals := feesCollected.MulDecTruncate(sdk.OneDec().Sub(k.GetCommunityTax(ctx)))
    65  	feeByEqual, feeByVote := feesToVals.MulDecTruncate(valPortion), feesToVals.MulDecTruncate(votePortion)
    66  	feesToCommunity := feesCollected.Sub(feeByEqual.Add(feeByVote...))
    67  	remainByEqual := k.allocateByEqual(ctx, feeByEqual, previousVotes) //allocate rewards equally between validators
    68  	remainByShare := k.allocateByShares(ctx, feeByVote)                //allocate rewards by shares
    69  	feesToCommunity = feesToCommunity.Add(remainByEqual.Add(remainByShare...)...)
    70  
    71  	// allocate community funding
    72  	if !feesToCommunity.IsZero() {
    73  		feePool.CommunityPool = feePool.CommunityPool.Add(feesToCommunity...)
    74  		k.SetFeePool(ctx, feePool)
    75  		logger.Debug("Send fees to community pool", "community_pool", feesToCommunity)
    76  	}
    77  }
    78  
    79  func (k Keeper) allocateByEqual(ctx sdk.Context, rewards sdk.SysCoins, previousVotes []abci.VoteInfo) sdk.SysCoins {
    80  	logger := k.Logger(ctx)
    81  
    82  	//count the total sum of the unJailed val
    83  	var validators []stakingexported.ValidatorI
    84  	for _, vote := range previousVotes {
    85  		validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)
    86  		if validator != nil {
    87  			if validator.IsJailed() {
    88  				logger.Debug(fmt.Sprintf("validator %s is jailed, not allowed to get reward by equal", validator.GetOperator()))
    89  			} else {
    90  				validators = append(validators, validator)
    91  			}
    92  		}
    93  	}
    94  
    95  	//calculate the proportion of every valid validator
    96  	powerFraction := sdk.NewDec(1).QuoTruncate(sdk.NewDec(int64(len(validators))))
    97  
    98  	//beginning allocating rewards equally
    99  	remaining := rewards
   100  	reward := rewards.MulDecTruncate(powerFraction)
   101  	for _, val := range validators {
   102  		k.AllocateTokensToValidator(ctx, val, reward)
   103  		logger.Debug("allocate by equal", val.GetOperator(), reward.String())
   104  		remaining = remaining.Sub(reward)
   105  	}
   106  	return remaining
   107  }
   108  
   109  func (k Keeper) allocateByShares(ctx sdk.Context, rewards sdk.SysCoins) sdk.SysCoins {
   110  	logger := k.Logger(ctx)
   111  
   112  	//allocate tokens proportionally by votes to validators and candidates
   113  	var validators []stakingexported.ValidatorI
   114  	k.stakingKeeper.IterateValidators(ctx, func(index int64, validator stakingexported.ValidatorI) (stop bool) {
   115  		if validator != nil {
   116  			if validator.IsJailed() {
   117  				logger.Debug("validator is jailed, not allowed to get reward by shares weight", "validator", validator.GetOperator())
   118  			} else {
   119  				validators = append(validators, validator)
   120  			}
   121  		}
   122  		return false
   123  	})
   124  
   125  	//calculate total Shares-Weight
   126  	var totalVotes = sdk.NewDec(0)
   127  	sum := len(validators)
   128  	for i := 0; i < sum; i++ {
   129  		totalVotes = totalVotes.Add(validators[i].GetDelegatorShares())
   130  	}
   131  
   132  	//beginning allocating rewards
   133  	remaining := rewards
   134  	for _, val := range validators {
   135  		powerFraction := val.GetDelegatorShares().QuoTruncate(totalVotes)
   136  		reward := rewards.MulDecTruncate(powerFraction)
   137  		k.AllocateTokensToValidator(ctx, val, reward)
   138  		logger.Debug("allocate by shares", val.GetOperator(), reward)
   139  		remaining = remaining.Sub(reward)
   140  	}
   141  	return remaining
   142  }
   143  
   144  // AllocateTokensToValidator allocate tokens to a particular validator, splitting according to commissions
   145  func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val exported.ValidatorI, tokens sdk.SysCoins) {
   146  	if k.CheckDistributionProposalValid(ctx) {
   147  		k.allocateTokensToValidatorForDistributionProposal(ctx, val, tokens)
   148  		return
   149  	}
   150  
   151  	// split tokens between validator and delegators according to commissions
   152  	// commissions is always 1.0, so tokens.MulDec(val.GetCommission()) = tokens
   153  	// only update current commissions
   154  	commission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator())
   155  	commission = commission.Add(tokens...)
   156  	k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), commission)
   157  	ctx.EventManager().EmitEvent(
   158  		sdk.NewEvent(
   159  			types.EventTypeCommission,
   160  			sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()),
   161  			sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()),
   162  		),
   163  	)
   164  }