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 }