2019-03-28 16:27:47 -07:00
|
|
|
package keeper
|
2018-10-26 04:42:53 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2019-01-23 03:37:03 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
2019-06-05 10:42:25 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/exported"
|
2018-10-26 04:42:53 -07:00
|
|
|
)
|
|
|
|
|
2019-03-28 16:27:47 -07:00
|
|
|
// register all distribution invariants
|
2019-06-06 13:32:38 -07:00
|
|
|
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
|
2019-05-16 08:25:32 -07:00
|
|
|
ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding",
|
2019-03-28 16:27:47 -07:00
|
|
|
NonNegativeOutstandingInvariant(k))
|
2019-05-16 08:25:32 -07:00
|
|
|
ir.RegisterRoute(types.ModuleName, "can-withdraw",
|
|
|
|
CanWithdrawInvariant(k))
|
|
|
|
ir.RegisterRoute(types.ModuleName, "reference-count",
|
|
|
|
ReferenceCountInvariant(k))
|
2019-06-28 13:11:27 -07:00
|
|
|
ir.RegisterRoute(types.ModuleName, "module-account",
|
|
|
|
ModuleAccountInvariant(k))
|
2019-03-28 16:27:47 -07:00
|
|
|
}
|
|
|
|
|
2018-10-26 04:42:53 -07:00
|
|
|
// AllInvariants runs all invariants of the distribution module
|
2019-05-16 08:25:32 -07:00
|
|
|
func AllInvariants(k Keeper) sdk.Invariant {
|
2019-07-11 03:56:43 -07:00
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
|
|
|
res, stop := CanWithdrawInvariant(k)(ctx)
|
|
|
|
if stop {
|
|
|
|
return res, stop
|
2018-10-26 04:42:53 -07:00
|
|
|
}
|
2019-07-11 03:56:43 -07:00
|
|
|
res, stop = NonNegativeOutstandingInvariant(k)(ctx)
|
|
|
|
if stop {
|
|
|
|
return res, stop
|
2018-11-26 04:21:23 -08:00
|
|
|
}
|
2019-07-11 03:56:43 -07:00
|
|
|
res, stop = ReferenceCountInvariant(k)(ctx)
|
|
|
|
if stop {
|
|
|
|
return res, stop
|
2019-01-23 03:37:03 -08:00
|
|
|
}
|
2019-06-28 13:11:27 -07:00
|
|
|
return ModuleAccountInvariant(k)(ctx)
|
2018-10-26 04:42:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-16 13:38:05 -08:00
|
|
|
// NonNegativeOutstandingInvariant checks that outstanding unwithdrawn fees are never negative
|
2019-03-28 16:27:47 -07:00
|
|
|
func NonNegativeOutstandingInvariant(k Keeper) sdk.Invariant {
|
2019-07-11 03:56:43 -07:00
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
|
|
|
var msg string
|
2019-07-15 09:56:38 -07:00
|
|
|
var count int
|
2019-03-06 10:54:12 -08:00
|
|
|
var outstanding sdk.DecCoins
|
|
|
|
|
2019-07-11 03:56:43 -07:00
|
|
|
k.IterateValidatorOutstandingRewards(ctx, func(addr sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
|
2019-03-06 10:54:12 -08:00
|
|
|
outstanding = rewards
|
|
|
|
if outstanding.IsAnyNegative() {
|
2019-07-15 09:56:38 -07:00
|
|
|
count++
|
2019-07-11 03:56:43 -07:00
|
|
|
msg += fmt.Sprintf("\t%v has negative outstanding coins: %v\n", addr, outstanding)
|
2019-03-06 10:54:12 -08:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
2019-07-15 09:56:38 -07:00
|
|
|
broken := count != 0
|
2019-03-06 10:54:12 -08:00
|
|
|
|
2019-07-11 03:56:43 -07:00
|
|
|
return sdk.FormatInvariant(types.ModuleName, "nonnegative outstanding",
|
2019-08-05 11:21:44 -07:00
|
|
|
fmt.Sprintf("found %d validators with negative outstanding rewards\n%s", count, msg)), broken
|
2018-11-26 04:13:47 -08:00
|
|
|
}
|
|
|
|
}
|
2018-11-26 04:21:23 -08:00
|
|
|
|
|
|
|
// CanWithdrawInvariant checks that current rewards can be completely withdrawn
|
2019-05-16 08:25:32 -07:00
|
|
|
func CanWithdrawInvariant(k Keeper) sdk.Invariant {
|
2019-07-11 03:56:43 -07:00
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
2019-01-16 13:38:05 -08:00
|
|
|
|
|
|
|
// cache, we don't want to write changes
|
2018-11-26 04:21:23 -08:00
|
|
|
ctx, _ = ctx.CacheContext()
|
|
|
|
|
2019-03-06 10:54:12 -08:00
|
|
|
var remaining sdk.DecCoins
|
|
|
|
|
2019-03-06 18:03:15 -08:00
|
|
|
valDelegationAddrs := make(map[string][]sdk.AccAddress)
|
2019-05-16 08:25:32 -07:00
|
|
|
for _, del := range k.stakingKeeper.GetAllSDKDelegations(ctx) {
|
2019-03-06 18:03:15 -08:00
|
|
|
valAddr := del.GetValidatorAddr().String()
|
|
|
|
valDelegationAddrs[valAddr] = append(valDelegationAddrs[valAddr], del.GetDelegatorAddr())
|
|
|
|
}
|
|
|
|
|
2019-03-06 10:54:12 -08:00
|
|
|
// iterate over all validators
|
2019-06-05 10:42:25 -07:00
|
|
|
k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
2019-04-26 16:11:45 -07:00
|
|
|
_, _ = k.WithdrawValidatorCommission(ctx, val.GetOperator())
|
2019-03-06 18:03:15 -08:00
|
|
|
|
|
|
|
delegationAddrs, ok := valDelegationAddrs[val.GetOperator().String()]
|
|
|
|
if ok {
|
|
|
|
for _, delAddr := range delegationAddrs {
|
2019-04-26 16:11:45 -07:00
|
|
|
if _, err := k.WithdrawDelegationRewards(ctx, delAddr, val.GetOperator()); err != nil {
|
2019-03-06 10:54:12 -08:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-06 18:03:15 -08:00
|
|
|
|
2019-03-06 10:54:12 -08:00
|
|
|
remaining = k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
|
|
|
if len(remaining) > 0 && remaining[0].Amount.LT(sdk.ZeroDec()) {
|
|
|
|
return true
|
|
|
|
}
|
2019-03-06 18:03:15 -08:00
|
|
|
|
2018-11-26 04:21:23 -08:00
|
|
|
return false
|
2019-01-16 13:38:05 -08:00
|
|
|
})
|
2018-11-26 04:21:23 -08:00
|
|
|
|
2019-07-11 03:56:43 -07:00
|
|
|
broken := len(remaining) > 0 && remaining[0].Amount.LT(sdk.ZeroDec())
|
|
|
|
return sdk.FormatInvariant(types.ModuleName, "can withdraw",
|
2019-08-05 11:21:44 -07:00
|
|
|
fmt.Sprintf("remaining coins: %v\n", remaining)), broken
|
2019-01-23 03:37:03 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReferenceCountInvariant checks that the number of historical rewards records is correct
|
2019-05-16 08:25:32 -07:00
|
|
|
func ReferenceCountInvariant(k Keeper) sdk.Invariant {
|
2019-07-11 03:56:43 -07:00
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
2019-01-23 03:37:03 -08:00
|
|
|
|
|
|
|
valCount := uint64(0)
|
2019-06-05 10:42:25 -07:00
|
|
|
k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
|
2019-01-23 03:37:03 -08:00
|
|
|
valCount++
|
|
|
|
return false
|
|
|
|
})
|
2019-05-16 08:25:32 -07:00
|
|
|
dels := k.stakingKeeper.GetAllSDKDelegations(ctx)
|
2019-01-23 03:37:03 -08:00
|
|
|
slashCount := uint64(0)
|
2019-02-13 15:01:50 -08:00
|
|
|
k.IterateValidatorSlashEvents(ctx,
|
|
|
|
func(_ sdk.ValAddress, _ uint64, _ types.ValidatorSlashEvent) (stop bool) {
|
|
|
|
slashCount++
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
|
|
|
// one record per validator (last tracked period), one record per
|
|
|
|
// delegation (previous period), one record per slash (previous period)
|
2019-01-23 03:37:03 -08:00
|
|
|
expected := valCount + uint64(len(dels)) + slashCount
|
2019-01-24 13:01:32 -08:00
|
|
|
count := k.GetValidatorHistoricalReferenceCount(ctx)
|
2019-07-11 03:56:43 -07:00
|
|
|
broken := count != expected
|
2019-01-23 03:37:03 -08:00
|
|
|
|
2019-07-15 09:56:38 -07:00
|
|
|
return sdk.FormatInvariant(types.ModuleName, "reference count",
|
|
|
|
fmt.Sprintf("expected historical reference count: %d = %v validators + %v delegations + %v slashes\n"+
|
|
|
|
"total validator historical reference count: %d\n",
|
2019-08-05 11:21:44 -07:00
|
|
|
expected, valCount, len(dels), slashCount, count)), broken
|
2018-11-26 04:21:23 -08:00
|
|
|
}
|
|
|
|
}
|
2019-06-28 13:11:27 -07:00
|
|
|
|
|
|
|
// ModuleAccountInvariant checks that the coins held by the distr ModuleAccount
|
|
|
|
// is consistent with the sum of validator outstanding rewards
|
|
|
|
func ModuleAccountInvariant(k Keeper) sdk.Invariant {
|
2019-07-11 03:56:43 -07:00
|
|
|
return func(ctx sdk.Context) (string, bool) {
|
2019-06-28 13:11:27 -07:00
|
|
|
|
|
|
|
var expectedCoins sdk.DecCoins
|
|
|
|
k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
|
|
|
|
expectedCoins = expectedCoins.Add(rewards)
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
|
|
|
communityPool := k.GetFeePoolCommunityCoins(ctx)
|
|
|
|
expectedInt, _ := expectedCoins.Add(communityPool).TruncateDecimal()
|
|
|
|
|
|
|
|
macc := k.GetDistributionAccount(ctx)
|
|
|
|
|
2019-07-11 03:56:43 -07:00
|
|
|
broken := !macc.GetCoins().IsEqual(expectedInt)
|
2019-07-15 09:56:38 -07:00
|
|
|
return sdk.FormatInvariant(types.ModuleName, "ModuleAccount coins",
|
|
|
|
fmt.Sprintf("\texpected ModuleAccount coins: %s\n"+
|
|
|
|
"\tdistribution ModuleAccount coins: %s\n",
|
2019-08-05 11:21:44 -07:00
|
|
|
expectedInt, macc.GetCoins())), broken
|
2019-06-28 13:11:27 -07:00
|
|
|
}
|
|
|
|
}
|