2018-10-26 04:42:53 -07:00
|
|
|
package simulation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
2019-01-23 03:37:03 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/distribution/types"
|
2018-10-26 04:42:53 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// AllInvariants runs all invariants of the distribution module
|
2019-02-13 15:01:50 -08:00
|
|
|
func AllInvariants(d distr.Keeper, stk types.StakingKeeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2019-01-16 13:38:05 -08:00
|
|
|
err := CanWithdrawInvariant(d, stk)(ctx)
|
2018-10-26 04:42:53 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-16 13:38:05 -08:00
|
|
|
err = NonNegativeOutstandingInvariant(d)(ctx)
|
2018-11-26 04:21:23 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-23 03:37:03 -08:00
|
|
|
err = ReferenceCountInvariant(d, stk)(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-10-26 04:42:53 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-16 13:38:05 -08:00
|
|
|
// NonNegativeOutstandingInvariant checks that outstanding unwithdrawn fees are never negative
|
2019-02-13 15:01:50 -08:00
|
|
|
func NonNegativeOutstandingInvariant(k distr.Keeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2019-03-06 10:54:12 -08:00
|
|
|
|
|
|
|
var outstanding sdk.DecCoins
|
|
|
|
|
|
|
|
k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
|
|
|
|
outstanding = rewards
|
|
|
|
if outstanding.IsAnyNegative() {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
2019-02-15 07:33:23 -08:00
|
|
|
if outstanding.IsAnyNegative() {
|
2019-01-23 03:37:03 -08:00
|
|
|
return fmt.Errorf("negative outstanding coins: %v", outstanding)
|
2018-11-26 04:13:47 -08:00
|
|
|
}
|
2019-03-06 10:54:12 -08:00
|
|
|
|
2018-11-26 04:13:47 -08:00
|
|
|
return nil
|
2019-03-06 10:54:12 -08:00
|
|
|
|
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-02-13 15:01:50 -08:00
|
|
|
func CanWithdrawInvariant(k distr.Keeper, sk types.StakingKeeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
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
|
|
|
|
|
|
|
|
// iterate over all validators
|
2019-01-16 13:38:05 -08:00
|
|
|
sk.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) {
|
|
|
|
_ = k.WithdrawValidatorCommission(ctx, val.GetOperator())
|
2019-03-06 10:54:12 -08:00
|
|
|
// TODO fetch delegations just for the validator, requires sdk.ValidatorSet change
|
|
|
|
// iterate over all current delegations, withdraw rewards
|
|
|
|
dels := sk.GetAllSDKDelegations(ctx)
|
|
|
|
for _, delegation := range dels {
|
|
|
|
if delegation.GetValidatorAddr().String() == val.GetOperator().String() {
|
|
|
|
err := k.WithdrawDelegationRewards(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr())
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
remaining = k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
|
|
|
|
if len(remaining) > 0 && remaining[0].Amount.LT(sdk.ZeroDec()) {
|
|
|
|
return true
|
|
|
|
}
|
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-01-16 13:38:05 -08:00
|
|
|
if len(remaining) > 0 && remaining[0].Amount.LT(sdk.ZeroDec()) {
|
2019-01-23 03:37:03 -08:00
|
|
|
return fmt.Errorf("negative remaining coins: %v", remaining)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReferenceCountInvariant checks that the number of historical rewards records is correct
|
2019-02-13 15:01:50 -08:00
|
|
|
func ReferenceCountInvariant(k distr.Keeper, sk types.StakingKeeper) sdk.Invariant {
|
2019-01-23 03:37:03 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
|
|
|
|
|
|
|
valCount := uint64(0)
|
|
|
|
sk.IterateValidators(ctx, func(_ int64, val sdk.Validator) (stop bool) {
|
|
|
|
valCount++
|
|
|
|
return false
|
|
|
|
})
|
2019-02-13 15:01:50 -08:00
|
|
|
dels := sk.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-01-23 03:37:03 -08:00
|
|
|
|
|
|
|
if count != expected {
|
2019-02-13 15:01:50 -08:00
|
|
|
return fmt.Errorf("unexpected number of historical rewards records: "+
|
|
|
|
"expected %v (%v vals + %v dels + %v slashes), got %v",
|
|
|
|
expected, valCount, len(dels), slashCount, count)
|
2018-11-26 04:21:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|