github.com/Finschia/finschia-sdk@v0.48.1/x/distribution/keeper/allocation.go (about)

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