github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/distribution/keeper/allocation.go (about) 1 package keeper 2 3 import ( 4 "fmt" 5 6 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 7 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 8 9 "github.com/fibonacci-chain/fbc/x/distribution/types" 10 "github.com/fibonacci-chain/fbc/x/staking/exported" 11 stakingexported "github.com/fibonacci-chain/fbc/x/staking/exported" 12 ) 13 14 var ( 15 valPortion = sdk.NewDecWithPrec(25, 2) 16 votePortion = sdk.NewDecWithPrec(75, 2) 17 ) 18 19 // AllocateTokens allocates fees from fee_collector 20 // 1. 25% rewards to validators, equally. 21 // 2. 75% rewards to validators and candidates, by shares' weight 22 func (k Keeper) AllocateTokens(ctx sdk.Context, totalPreviousPower int64, 23 previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo) { 24 logger := k.Logger(ctx) 25 // fetch and clear the collected fees for distribution, since this is 26 // called in BeginBlock, collected fees will be from the previous block 27 // (and distributed to the previous proposer) 28 feeCollector := k.supplyKeeper.GetModuleAccount(ctx, k.feeCollectorName) 29 feesCollected := feeCollector.GetCoins() 30 31 if feesCollected.Empty() { 32 logger.Debug("No fee to distributed.") 33 return 34 } 35 logger.Debug("AllocateTokens", "TotalFee", feesCollected.String()) 36 37 // transfer collected fees to the distribution module account 38 err := k.supplyKeeper.SendCoinsFromModuleToModule(ctx, k.feeCollectorName, types.ModuleName, feesCollected) 39 if err != nil { 40 panic(err) 41 } 42 43 feePool := k.GetFeePool(ctx) 44 if totalPreviousPower == 0 { 45 feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected...) 46 k.SetFeePool(ctx, feePool) 47 logger.Debug("totalPreviousPower is zero, send fees to community pool", "fees", feesCollected) 48 return 49 } 50 51 preProposerVal := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer) 52 if preProposerVal == nil { 53 // previous proposer can be unknown if say, the unbonding period is 1 block, so 54 // e.g. a validator undelegates at block X, it's removed entirely by 55 // block X+1's endblock, then X+2 we need to refer to the previous 56 // proposer for X+1, but we've forgotten about them. 57 logger.Error(fmt.Sprintf( 58 "WARNING: Cannot find the entity of previous proposer validator %s.\n"+ 59 "This should happen only if the proposer unbonded completely within a single block, "+ 60 "which generally should not happen except in exceptional circumstances (or fuzz testing). "+ 61 "We recommend you investigate immediately.", previousProposer.String())) 62 } 63 64 feesToVals := feesCollected.MulDecTruncate(sdk.OneDec().Sub(k.GetCommunityTax(ctx))) 65 feeByEqual, feeByVote := feesToVals.MulDecTruncate(valPortion), feesToVals.MulDecTruncate(votePortion) 66 feesToCommunity := feesCollected.Sub(feeByEqual.Add(feeByVote...)) 67 remainByEqual := k.allocateByEqual(ctx, feeByEqual, previousVotes) //allocate rewards equally between validators 68 remainByShare := k.allocateByShares(ctx, feeByVote) //allocate rewards by shares 69 feesToCommunity = feesToCommunity.Add(remainByEqual.Add(remainByShare...)...) 70 71 // allocate community funding 72 if !feesToCommunity.IsZero() { 73 feePool.CommunityPool = feePool.CommunityPool.Add(feesToCommunity...) 74 k.SetFeePool(ctx, feePool) 75 logger.Debug("Send fees to community pool", "community_pool", feesToCommunity) 76 } 77 } 78 79 func (k Keeper) allocateByEqual(ctx sdk.Context, rewards sdk.SysCoins, previousVotes []abci.VoteInfo) sdk.SysCoins { 80 logger := k.Logger(ctx) 81 82 //count the total sum of the unJailed val 83 var validators []stakingexported.ValidatorI 84 for _, vote := range previousVotes { 85 validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address) 86 if validator != nil { 87 if validator.IsJailed() { 88 logger.Debug(fmt.Sprintf("validator %s is jailed, not allowed to get reward by equal", validator.GetOperator())) 89 } else { 90 validators = append(validators, validator) 91 } 92 } 93 } 94 95 //calculate the proportion of every valid validator 96 powerFraction := sdk.NewDec(1).QuoTruncate(sdk.NewDec(int64(len(validators)))) 97 98 //beginning allocating rewards equally 99 remaining := rewards 100 reward := rewards.MulDecTruncate(powerFraction) 101 for _, val := range validators { 102 k.AllocateTokensToValidator(ctx, val, reward) 103 logger.Debug("allocate by equal", val.GetOperator(), reward.String()) 104 remaining = remaining.Sub(reward) 105 } 106 return remaining 107 } 108 109 func (k Keeper) allocateByShares(ctx sdk.Context, rewards sdk.SysCoins) sdk.SysCoins { 110 logger := k.Logger(ctx) 111 112 //allocate tokens proportionally by votes to validators and candidates 113 var validators []stakingexported.ValidatorI 114 k.stakingKeeper.IterateValidators(ctx, func(index int64, validator stakingexported.ValidatorI) (stop bool) { 115 if validator != nil { 116 if validator.IsJailed() { 117 logger.Debug("validator is jailed, not allowed to get reward by shares weight", "validator", validator.GetOperator()) 118 } else { 119 validators = append(validators, validator) 120 } 121 } 122 return false 123 }) 124 125 //calculate total Shares-Weight 126 var totalVotes = sdk.NewDec(0) 127 sum := len(validators) 128 for i := 0; i < sum; i++ { 129 totalVotes = totalVotes.Add(validators[i].GetDelegatorShares()) 130 } 131 132 //beginning allocating rewards 133 remaining := rewards 134 for _, val := range validators { 135 powerFraction := val.GetDelegatorShares().QuoTruncate(totalVotes) 136 reward := rewards.MulDecTruncate(powerFraction) 137 k.AllocateTokensToValidator(ctx, val, reward) 138 logger.Debug("allocate by shares", val.GetOperator(), reward) 139 remaining = remaining.Sub(reward) 140 } 141 return remaining 142 } 143 144 // AllocateTokensToValidator allocate tokens to a particular validator, splitting according to commissions 145 func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val exported.ValidatorI, tokens sdk.SysCoins) { 146 if k.CheckDistributionProposalValid(ctx) { 147 k.allocateTokensToValidatorForDistributionProposal(ctx, val, tokens) 148 return 149 } 150 151 // split tokens between validator and delegators according to commissions 152 // commissions is always 1.0, so tokens.MulDec(val.GetCommission()) = tokens 153 // only update current commissions 154 commission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator()) 155 commission = commission.Add(tokens...) 156 k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), commission) 157 ctx.EventManager().EmitEvent( 158 sdk.NewEvent( 159 types.EventTypeCommission, 160 sdk.NewAttribute(sdk.AttributeKeyAmount, tokens.String()), 161 sdk.NewAttribute(types.AttributeKeyValidator, val.GetOperator().String()), 162 ), 163 ) 164 }