Fixes regression introduced by #2984. Continuiation of #3033 , which didn't fix the simulation issues. (candidate) Complete solution for #3019, 9002 halt bug. From #2984, it isn't sufficient to take the fee pool rewards of a validator. Since we don't track delegator accums (as we do with validator accums), and because onValidatorModified >updateValidatorDistInfoFromPool is also being called upon delegation updates (or at least I believe this is the reason), it is necessary to also withdraw self delegation. TODO: I don't think self-delegation should be required to be modified here... consider using a delegation hook to do the self-delegation withdraw part instead, e.g. splitting the updateValidatorDistInfoFromPool function into two. It might not result in cleaner code, however. Think hard.
163 lines
5.2 KiB
Go
163 lines
5.2 KiB
Go
package keeper
|
|
|
|
import (
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
|
)
|
|
|
|
// check whether a validator has distribution info
|
|
func (k Keeper) HasValidatorDistInfo(ctx sdk.Context,
|
|
operatorAddr sdk.ValAddress) (exists bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
return store.Has(GetValidatorDistInfoKey(operatorAddr))
|
|
}
|
|
|
|
// get the validator distribution info
|
|
func (k Keeper) GetValidatorDistInfo(ctx sdk.Context,
|
|
operatorAddr sdk.ValAddress) (vdi types.ValidatorDistInfo) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
b := store.Get(GetValidatorDistInfoKey(operatorAddr))
|
|
if b == nil {
|
|
panic("Stored validator-distribution info should not have been nil")
|
|
}
|
|
|
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(b, &vdi)
|
|
return
|
|
}
|
|
|
|
// set the validator distribution info
|
|
func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInfo) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
b := k.cdc.MustMarshalBinaryLengthPrefixed(vdi)
|
|
store.Set(GetValidatorDistInfoKey(vdi.OperatorAddr), b)
|
|
}
|
|
|
|
// remove a validator distribution info
|
|
func (k Keeper) RemoveValidatorDistInfo(ctx sdk.Context, valAddr sdk.ValAddress) {
|
|
|
|
// defensive check
|
|
vdi := k.GetValidatorDistInfo(ctx, valAddr)
|
|
if vdi.DelAccum.Accum.IsPositive() {
|
|
panic("Should not delete validator with unwithdrawn delegator accum")
|
|
}
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
store.Delete(GetValidatorDistInfoKey(valAddr))
|
|
}
|
|
|
|
// remove all validator distribution infos
|
|
func (k Keeper) RemoveValidatorDistInfos(ctx sdk.Context) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey)
|
|
defer iter.Close()
|
|
for ; iter.Valid(); iter.Next() {
|
|
store.Delete(iter.Key())
|
|
}
|
|
}
|
|
|
|
// iterate over all the validator distribution infos
|
|
func (k Keeper) IterateValidatorDistInfos(ctx sdk.Context,
|
|
fn func(index int64, distInfo types.ValidatorDistInfo) (stop bool)) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
iter := sdk.KVStorePrefixIterator(store, ValidatorDistInfoKey)
|
|
defer iter.Close()
|
|
index := int64(0)
|
|
for ; iter.Valid(); iter.Next() {
|
|
var vdi types.ValidatorDistInfo
|
|
k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &vdi)
|
|
if fn(index, vdi) {
|
|
return
|
|
}
|
|
index++
|
|
}
|
|
}
|
|
|
|
// Get the calculated accum of a validator at the current block
|
|
// without affecting the state.
|
|
func (k Keeper) GetValidatorAccum(ctx sdk.Context, operatorAddr sdk.ValAddress) (sdk.Dec, sdk.Error) {
|
|
if !k.HasValidatorDistInfo(ctx, operatorAddr) {
|
|
return sdk.Dec{}, types.ErrNoValidatorDistInfo(k.codespace)
|
|
}
|
|
|
|
// withdraw self-delegation
|
|
height := ctx.BlockHeight()
|
|
lastValPower := k.stakeKeeper.GetLastValidatorPower(ctx, operatorAddr)
|
|
valInfo := k.GetValidatorDistInfo(ctx, operatorAddr)
|
|
accum := valInfo.GetValAccum(height, sdk.NewDecFromInt(lastValPower))
|
|
|
|
return accum, nil
|
|
}
|
|
|
|
// takeValidatorFeePoolRewards updates the validator's distribution info
|
|
// from the global fee pool without withdrawing any rewards. This will be called
|
|
// from a onValidatorModified hook.
|
|
func (k Keeper) takeValidatorFeePoolRewards(ctx sdk.Context, operatorAddr sdk.ValAddress) sdk.Error {
|
|
if !k.HasValidatorDistInfo(ctx, operatorAddr) {
|
|
return types.ErrNoValidatorDistInfo(k.codespace)
|
|
}
|
|
accAddr := sdk.AccAddress(operatorAddr.Bytes())
|
|
|
|
// withdraw reward for self-delegation
|
|
if k.HasDelegationDistInfo(ctx, accAddr, operatorAddr) {
|
|
fp, vi, di, withdraw :=
|
|
k.withdrawDelegationReward(ctx, accAddr, operatorAddr)
|
|
k.SetFeePool(ctx, fp)
|
|
k.SetValidatorDistInfo(ctx, vi)
|
|
k.SetDelegationDistInfo(ctx, di)
|
|
k.WithdrawToDelegator(ctx, fp, accAddr, withdraw)
|
|
}
|
|
|
|
// withdrawal validator commission rewards
|
|
valInfo := k.GetValidatorDistInfo(ctx, operatorAddr)
|
|
wc := k.GetWithdrawContext(ctx, operatorAddr)
|
|
valInfo, feePool := valInfo.TakeFeePoolRewards(wc)
|
|
k.SetFeePool(ctx, feePool)
|
|
k.SetValidatorDistInfo(ctx, valInfo)
|
|
return nil
|
|
}
|
|
|
|
// withdrawal all the validator rewards including the commission
|
|
func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.ValAddress) sdk.Error {
|
|
if !k.HasValidatorDistInfo(ctx, operatorAddr) {
|
|
return types.ErrNoValidatorDistInfo(k.codespace)
|
|
}
|
|
|
|
// withdraw self-delegation
|
|
accAddr := sdk.AccAddress(operatorAddr.Bytes())
|
|
withdraw := k.withdrawDelegationRewardsAll(ctx, accAddr)
|
|
|
|
// withdrawal validator commission rewards
|
|
valInfo := k.GetValidatorDistInfo(ctx, operatorAddr)
|
|
wc := k.GetWithdrawContext(ctx, operatorAddr)
|
|
valInfo, feePool, commission := valInfo.WithdrawCommission(wc)
|
|
withdraw = withdraw.Plus(commission)
|
|
k.SetValidatorDistInfo(ctx, valInfo)
|
|
|
|
k.WithdrawToDelegator(ctx, feePool, accAddr, withdraw)
|
|
return nil
|
|
}
|
|
|
|
// get all the validator rewards including the commission
|
|
func (k Keeper) CurrentValidatorRewardsAll(ctx sdk.Context, operatorAddr sdk.ValAddress) (sdk.Coins, sdk.Error) {
|
|
|
|
if !k.HasValidatorDistInfo(ctx, operatorAddr) {
|
|
return sdk.Coins{}, types.ErrNoValidatorDistInfo(k.codespace)
|
|
}
|
|
|
|
// withdraw self-delegation
|
|
accAddr := sdk.AccAddress(operatorAddr.Bytes())
|
|
withdraw := k.CurrentDelegationRewardsAll(ctx, accAddr)
|
|
|
|
// withdrawal validator commission rewards
|
|
valInfo := k.GetValidatorDistInfo(ctx, operatorAddr)
|
|
|
|
wc := k.GetWithdrawContext(ctx, operatorAddr)
|
|
commission := valInfo.CurrentCommissionRewards(wc)
|
|
withdraw = withdraw.Plus(commission)
|
|
truncated, _ := withdraw.TruncateDecimal()
|
|
return truncated, nil
|
|
}
|