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 }