cosmos-sdk/x/distribution/keeper/allocation.go
Jacob Gadikian cb31043d35
chore: math lib update and type fixes (#12791)
## Description

Upgrades the math library in the repo root and find/replaces many usages of sdk.Dec, sdk.ZeroDec, sdk.OneDec, sdk.NewDec with their legacy-ified-math-lib replacements.

Note for review: I assume that I did not find 100% of usages

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-08-02 11:16:40 +00:00

139 lines
5.7 KiB
Go

package keeper
import (
"fmt"
"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 handles distribution of the collected fees
// bondedVotes is a list of (validator address, validator voted on last block flag) for all
// validators in the bonded set.
func (k Keeper) AllocateTokens(
ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64,
previousProposer sdk.ConsAddress, bondedVotes []abci.VoteInfo,
) {
logger := k.Logger(ctx)
// 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 votes
previousFractionVotes := math.LegacyNewDec(sumPreviousPrecommitPower).Quo(math.LegacyNewDec(totalPreviousPower))
// calculate previous proposer reward
baseProposerReward := k.GetBaseProposerReward(ctx)
bonusProposerReward := k.GetBonusProposerReward(ctx)
proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes))
proposerReward := feesCollected.MulDecTruncate(proposerMultiplier)
// pay previous proposer
remaining := feesCollected
proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer)
if proposerValidator != nil {
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeProposerReward,
sdk.NewAttribute(sdk.AttributeKeyAmount, proposerReward.String()),
sdk.NewAttribute(types.AttributeKeyValidator, proposerValidator.GetOperator().String()),
),
)
k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward)
remaining = remaining.Sub(proposerReward)
} else {
// 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.",
previousProposer.String()))
}
// calculate fraction allocated to validators
communityTax := k.GetCommunityTax(ctx)
voteMultiplier := math.LegacyOneDec().Sub(proposerMultiplier).Sub(communityTax)
// 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 microslashing 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 := feesCollected.MulDecTruncate(voteMultiplier).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)
}