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

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
     7  
     8  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     9  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/distribution/types"
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/exported"
    11  )
    12  
    13  // AllocateTokens handles distribution of the collected fees
    14  func (k Keeper) AllocateTokens(
    15  	ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64,
    16  	previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo,
    17  ) {
    18  
    19  	logger := k.Logger(ctx)
    20  
    21  	// fetch and clear the collected fees for distribution, since this is
    22  	// called in BeginBlock, collected fees will be from the previous block
    23  	// (and distributed to the previous proposer)
    24  	feeCollector := k.supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName)
    25  	feesCollectedInt := feeCollector.GetCoins()
    26  	feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...)
    27  
    28  	// transfer collected fees to the distribution module account
    29  	err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt)
    30  	if err != nil {
    31  		panic(err)
    32  	}
    33  
    34  	// temporary workaround to keep CanWithdrawInvariant happy
    35  	// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
    36  	feePool := k.GetFeePool(ctx)
    37  	if totalPreviousPower == 0 {
    38  		feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...)
    39  		k.SetFeePool(ctx, feePool)
    40  		return
    41  	}
    42  
    43  	// calculate fraction votes
    44  	previousFractionVotes := sdk.NewDec(sumPreviousPrecommitPower).Quo(sdk.NewDec(totalPreviousPower))
    45  
    46  	// calculate previous proposer reward
    47  	baseProposerReward := k.GetBaseProposerReward(ctx)
    48  	bonusProposerReward := k.GetBonusProposerReward(ctx)
    49  	proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes))
    50  	proposerReward := feesCollected.MulDecTruncate(proposerMultiplier)
    51  
    52  	// pay previous proposer
    53  	remaining := feesCollected
    54  	proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer)
    55  
    56  	if proposerValidator != nil {
    57  		ctx.EventManager().EmitEvent(
    58  			sdk.NewEvent(
    59  				types.EventTypeProposerReward,
    60  				sdk.NewAttribute(sdk.AttributeKeyAmount, proposerReward.String()),
    61  				sdk.NewAttribute(types.AttributeKeyValidator, proposerValidator.GetOperator().String()),
    62  			),
    63  		)
    64  
    65  		k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward)
    66  		remaining = remaining.Sub(proposerReward)
    67  	} else {
    68  		// previous proposer can be unknown if say, the unbonding period is 1 block, so
    69  		// e.g. a validator undelegates at block X, it's removed entirely by
    70  		// block X+1's endblock, then X+2 we need to refer to the previous
    71  		// proposer for X+1, but we've forgotten about them.
    72  		logger.Error(fmt.Sprintf(
    73  			"WARNING: Attempt to allocate proposer rewards to unknown proposer %s. "+
    74  				"This should happen only if the proposer unbonded completely within a single block, "+
    75  				"which generally should not happen except in exceptional circumstances (or fuzz testing). "+
    76  				"We recommend you investigate immediately.",
    77  			previousProposer.String()))
    78  	}
    79  
    80  	// calculate fraction allocated to validators
    81  	communityTax := k.GetCommunityTax(ctx)
    82  	voteMultiplier := sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax)
    83  
    84  	// allocate tokens proportionally to voting power
    85  	// TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376
    86  	for _, vote := range previousVotes {
    87  		validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)
    88  
    89  		// TODO consider microslashing for missing votes.
    90  		// ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701
    91  		powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPreviousPower))
    92  		reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction)
    93  		k.AllocateTokensToValidator(ctx, validator, reward)
    94  		remaining = remaining.Sub(reward)
    95  	}
    96  
    97  	// allocate community funding
    98  	feePool.CommunityPool = feePool.CommunityPool.Add(remaining...)
    99  	k.SetFeePool(ctx, feePool)
   100  }
   101  
   102  // AllocateTokensToValidator allocate tokens to a particular validator, splitting according to commission
   103  func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val exported.ValidatorI, tokens sdk.DecCoins) {
   104  	// split tokens between validator and delegators according to commission
   105  	commission := tokens.MulDec(val.GetCommission())
   106  	shared := tokens.Sub(commission)
   107  
   108  	// update current commission
   109  	ctx.EventManager().EmitEvent(
   110  		sdk.NewEvent(
   111  			types.EventTypeCommission,
   112  			sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
   113  			sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()),
   114  		),
   115  	)
   116  	currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator())
   117  	currentCommission = currentCommission.Add(commission...)
   118  	k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission)
   119  
   120  	// update current rewards
   121  	currentRewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator())
   122  	currentRewards.Rewards = currentRewards.Rewards.Add(shared...)
   123  	k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards)
   124  
   125  	// update outstanding rewards
   126  	ctx.EventManager().EmitEvent(
   127  		sdk.NewEvent(
   128  			types.EventTypeRewards,
   129  			sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()),
   130  			sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()),
   131  		),
   132  	)
   133  	outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
   134  	outstanding = outstanding.Add(tokens...)
   135  	k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding)
   136  }