github.com/cosmos/cosmos-sdk@v0.50.10/x/distribution/keeper/allocation.go (about)

     1  package keeper
     2  
     3  import (
     4  	"context"
     5  
     6  	abci "github.com/cometbft/cometbft/abci/types"
     7  
     8  	"cosmossdk.io/math"
     9  
    10  	sdk "github.com/cosmos/cosmos-sdk/types"
    11  	"github.com/cosmos/cosmos-sdk/x/distribution/types"
    12  	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
    13  )
    14  
    15  // AllocateTokens performs reward and fee distribution to all validators based
    16  // on the F1 fee distribution specification.
    17  func (k Keeper) AllocateTokens(ctx context.Context, totalPreviousPower int64, bondedVotes []abci.VoteInfo) error {
    18  	// fetch and clear the collected fees for distribution, since this is
    19  	// called in BeginBlock, collected fees will be from the previous block
    20  	// (and distributed to the previous proposer)
    21  	feeCollector := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName)
    22  	feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress())
    23  	feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...)
    24  
    25  	// transfer collected fees to the distribution module account
    26  	err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt)
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	// temporary workaround to keep CanWithdrawInvariant happy
    32  	// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
    33  	feePool, err := k.FeePool.Get(ctx)
    34  	if err != nil {
    35  		return err
    36  	}
    37  
    38  	if totalPreviousPower == 0 {
    39  		feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...)
    40  		return k.FeePool.Set(ctx, feePool)
    41  	}
    42  
    43  	// calculate fraction allocated to validators
    44  	remaining := feesCollected
    45  	communityTax, err := k.GetCommunityTax(ctx)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	voteMultiplier := math.LegacyOneDec().Sub(communityTax)
    51  	feeMultiplier := feesCollected.MulDecTruncate(voteMultiplier)
    52  
    53  	// allocate tokens proportionally to voting power
    54  	//
    55  	// TODO: Consider parallelizing later
    56  	//
    57  	// Ref: https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376
    58  	for _, vote := range bondedVotes {
    59  		validator, err := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)
    60  		if err != nil {
    61  			return err
    62  		}
    63  
    64  		// TODO: Consider micro-slashing for missing votes.
    65  		//
    66  		// Ref: https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701
    67  		powerFraction := math.LegacyNewDec(vote.Validator.Power).QuoTruncate(math.LegacyNewDec(totalPreviousPower))
    68  		reward := feeMultiplier.MulDecTruncate(powerFraction)
    69  
    70  		err = k.AllocateTokensToValidator(ctx, validator, reward)
    71  		if err != nil {
    72  			return err
    73  		}
    74  
    75  		remaining = remaining.Sub(reward)
    76  	}
    77  
    78  	// allocate community funding
    79  	feePool.CommunityPool = feePool.CommunityPool.Add(remaining...)
    80  	return k.FeePool.Set(ctx, feePool)
    81  }
    82  
    83  // AllocateTokensToValidator allocate tokens to a particular validator,
    84  // splitting according to commission.
    85  func (k Keeper) AllocateTokensToValidator(ctx context.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) error {
    86  	// split tokens between validator and delegators according to commission
    87  	commission := tokens.MulDec(val.GetCommission())
    88  	shared := tokens.Sub(commission)
    89  
    90  	valBz, err := k.stakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	// update current commission
    96  	sdkCtx := sdk.UnwrapSDKContext(ctx)
    97  	sdkCtx.EventManager().EmitEvent(
    98  		sdk.NewEvent(
    99  			types.EventTypeCommission,
   100  			sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
   101  			sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator()),
   102  		),
   103  	)
   104  	currentCommission, err := k.GetValidatorAccumulatedCommission(ctx, valBz)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	currentCommission.Commission = currentCommission.Commission.Add(commission...)
   110  	err = k.SetValidatorAccumulatedCommission(ctx, valBz, currentCommission)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	// update current rewards
   116  	currentRewards, err := k.GetValidatorCurrentRewards(ctx, valBz)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	currentRewards.Rewards = currentRewards.Rewards.Add(shared...)
   122  	err = k.SetValidatorCurrentRewards(ctx, valBz, currentRewards)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	// update outstanding rewards
   128  	sdkCtx.EventManager().EmitEvent(
   129  		sdk.NewEvent(
   130  			types.EventTypeRewards,
   131  			sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()),
   132  			sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator()),
   133  		),
   134  	)
   135  
   136  	outstanding, err := k.GetValidatorOutstandingRewards(ctx, valBz)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	outstanding.Rewards = outstanding.Rewards.Add(tokens...)
   142  	return k.SetValidatorOutstandingRewards(ctx, valBz, outstanding)
   143  }