cosmos-sdk/x/distribution/keeper/allocation.go

107 lines
4.4 KiB
Go
Raw Normal View History

2018-09-04 23:41:17 -07:00
package keeper
2018-09-11 10:35:47 -07:00
import (
"fmt"
2019-01-16 13:38:05 -08:00
abci "github.com/tendermint/tendermint/abci/types"
2018-09-11 10:35:47 -07:00
sdk "github.com/cosmos/cosmos-sdk/types"
)
2018-09-04 23:41:17 -07:00
2019-01-16 13:38:05 -08:00
// allocate fees handles distribution of the collected fees
2019-03-14 11:13:15 -07:00
func (k Keeper) AllocateTokens(ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64,
previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo) {
logger := ctx.Logger().With("module", "x/distribution")
2019-03-14 11:13:15 -07:00
// 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)
2019-01-16 13:38:05 -08:00
feesCollectedInt := k.feeCollectionKeeper.GetCollectedFees(ctx)
feesCollected := sdk.NewDecCoins(feesCollectedInt)
k.feeCollectionKeeper.ClearCollectedFees(ctx)
2018-09-11 10:35:47 -07:00
2019-01-16 13:38:05 -08:00
// temporary workaround to keep CanWithdrawInvariant happy
// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
2019-03-14 11:13:15 -07:00
feePool := k.GetFeePool(ctx)
if totalPreviousPower == 0 {
feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected)
k.SetFeePool(ctx, feePool)
return
}
2019-01-16 13:38:05 -08:00
// calculate fraction votes
2019-03-14 11:13:15 -07:00
previousFractionVotes := sdk.NewDec(sumPreviousPrecommitPower).Quo(sdk.NewDec(totalPreviousPower))
2019-01-16 13:38:05 -08:00
2019-03-14 11:13:15 -07:00
// calculate previous proposer reward
2018-10-15 13:12:42 -07:00
baseProposerReward := k.GetBaseProposerReward(ctx)
bonusProposerReward := k.GetBonusProposerReward(ctx)
2019-03-14 11:13:15 -07:00
proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes))
proposerReward := feesCollected.MulDecTruncate(proposerMultiplier)
2018-09-04 23:41:17 -07:00
2019-03-14 11:13:15 -07:00
// pay previous proposer
remaining := feesCollected
2019-03-14 11:13:15 -07:00
proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer)
if proposerValidator != nil {
k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward)
remaining = remaining.Sub(proposerReward)
} else {
2019-03-14 11:13:15 -07:00
// previous proposer can be unknown if say, the unbonding period is 1 block, so
// e.g. a validator undelegates at block X, it's removed entirely by
// block X+1's endblock, then X+2 we need to refer to the previous
// proposer for X+1, but we've forgotten about them.
logger.Error(fmt.Sprintf(
"WARNING: Attempt to allocate proposer rewards to unknown proposer %s. "+
"This should happen only if the proposer unbonded completely within a single block, "+
"which generally should not happen except in exceptional circumstances (or fuzz testing). "+
"We recommend you investigate immediately.",
2019-03-14 11:13:15 -07:00
previousProposer.String()))
}
2018-09-04 23:41:17 -07:00
2019-01-16 13:38:05 -08:00
// calculate fraction allocated to validators
2018-09-18 21:42:05 -07:00
communityTax := k.GetCommunityTax(ctx)
2019-01-16 13:38:05 -08:00
voteMultiplier := sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax)
2018-09-04 23:41:17 -07:00
2019-01-16 13:38:05 -08:00
// allocate tokens proportionally to voting power
// TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376
2019-03-14 11:13:15 -07:00
for _, vote := range previousVotes {
2019-01-16 13:38:05 -08:00
validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)
2018-09-04 23:41:17 -07:00
// TODO consider microslashing for missing votes.
2019-01-16 13:38:05 -08:00
// ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701
2019-03-14 11:13:15 -07:00
powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPreviousPower))
reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction)
2019-01-16 13:38:05 -08:00
k.AllocateTokensToValidator(ctx, validator, reward)
remaining = remaining.Sub(reward)
2019-01-16 13:38:05 -08:00
}
// allocate community funding
feePool.CommunityPool = feePool.CommunityPool.Add(remaining)
2018-09-19 19:13:12 -07:00
k.SetFeePool(ctx, feePool)
2018-09-11 10:35:47 -07:00
2019-01-16 13:38:05 -08:00
}
// allocate tokens to a particular validator, splitting according to commission
func (k Keeper) AllocateTokensToValidator(ctx sdk.Context, val sdk.Validator, tokens sdk.DecCoins) {
2019-01-16 13:38:05 -08:00
// split tokens between validator and delegators according to commission
commission := tokens.MulDec(val.GetCommission())
shared := tokens.Sub(commission)
2019-01-16 13:38:05 -08:00
// update current commission
currentCommission := k.GetValidatorAccumulatedCommission(ctx, val.GetOperator())
currentCommission = currentCommission.Add(commission)
2019-01-16 13:38:05 -08:00
k.SetValidatorAccumulatedCommission(ctx, val.GetOperator(), currentCommission)
// update current rewards
currentRewards := k.GetValidatorCurrentRewards(ctx, val.GetOperator())
currentRewards.Rewards = currentRewards.Rewards.Add(shared)
2019-01-16 13:38:05 -08:00
k.SetValidatorCurrentRewards(ctx, val.GetOperator(), currentRewards)
// update outstanding rewards
outstanding := k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
outstanding = outstanding.Add(tokens)
k.SetValidatorOutstandingRewards(ctx, val.GetOperator(), outstanding)
2018-09-04 23:41:17 -07:00
}