package keeper import ( "cosmossdk.io/math" abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // AllocateTokens performs reward and fee distribution to all validators based // on the F1 fee distribution specification. func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64, bondedVotes []abci.VoteInfo) { // fetch and clear the collected fees for distribution, since this is // called in BeginBlock, collected fees will be from the previous block // (and distributed to the previous proposer) feeCollector := k.authKeeper.GetModuleAccount(ctx, k.feeCollectorName) feesCollectedInt := k.bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress()) feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...) // transfer collected fees to the distribution module account err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollectedInt) if err != nil { panic(err) } // temporary workaround to keep CanWithdrawInvariant happy // general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634 feePool := k.GetFeePool(ctx) if totalPreviousPower == 0 { feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...) k.SetFeePool(ctx, feePool) return } // calculate fraction allocated to validators remaining := feesCollected communityTax := k.GetCommunityTax(ctx) voteMultiplier := math.LegacyOneDec().Sub(communityTax) feeMultiplier := feesCollected.MulDecTruncate(voteMultiplier) // allocate tokens proportionally to voting power // // TODO: Consider parallelizing later // // Ref: https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376 for _, vote := range bondedVotes { validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address) // TODO: Consider micro-slashing for missing votes. // // Ref: https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701 powerFraction := math.LegacyNewDec(vote.Validator.Power).QuoTruncate(math.LegacyNewDec(totalPreviousPower)) reward := feeMultiplier.MulDecTruncate(powerFraction) k.AllocateTokensToValidator(ctx, validator, reward) remaining = remaining.Sub(reward) } // allocate community funding feePool.CommunityPool = feePool.CommunityPool.Add(remaining...) k.SetFeePool(ctx, feePool) } // AllocateTokensToValidator allocate tokens to a particular validator, // splitting according to commission. func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val stakingtypes.ValidatorI, tokens sdk.DecCoins) { // split tokens between validator and delegators according to commission commission := tokens.MulDec(val.GetCommission()) shared := tokens.Sub(commission) // update current commission ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeCommission, sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()), sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), ), ) currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) currentCommission.Commission = currentCommission.Commission.Add(commission...) k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission) // update current rewards currentRewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator()) currentRewards.Rewards = currentRewards.Rewards.Add(shared...) k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards) // update outstanding rewards ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeRewards, sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()), sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), ), ) outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator()) outstanding.Rewards = outstanding.Rewards.Add(tokens...) k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding) }