2018-07-17 11:50:30 -07:00
|
|
|
package simulation
|
|
|
|
|
|
|
|
import (
|
2018-11-04 20:44:43 -08:00
|
|
|
"bytes"
|
2018-09-09 08:34:09 -07:00
|
|
|
"fmt"
|
2018-07-17 11:50:30 -07:00
|
|
|
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
2019-01-11 12:08:01 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
2018-07-17 11:50:30 -07:00
|
|
|
)
|
|
|
|
|
2019-01-11 12:08:01 -08:00
|
|
|
// AllInvariants runs all invariants of the staking module.
|
2018-07-17 11:50:30 -07:00
|
|
|
// Currently: total supply, positive power
|
2019-02-13 15:01:50 -08:00
|
|
|
func AllInvariants(k staking.Keeper,
|
|
|
|
f staking.FeeCollectionKeeper, d staking.DistributionKeeper,
|
|
|
|
am auth.AccountKeeper) sdk.Invariant {
|
2018-10-26 04:42:53 -07:00
|
|
|
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2019-02-13 15:01:50 -08:00
|
|
|
err := SupplyInvariants(k, f, d, am)(ctx)
|
2018-09-09 08:34:09 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-11-04 20:44:43 -08:00
|
|
|
|
2018-12-07 16:04:52 -08:00
|
|
|
err = NonNegativePowerInvariant(k)(ctx)
|
2018-09-09 08:34:09 -07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-11-04 20:44:43 -08:00
|
|
|
|
2018-11-29 07:17:10 -08:00
|
|
|
err = PositiveDelegationInvariant(k)(ctx)
|
2018-11-26 04:13:47 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-29 07:17:10 -08:00
|
|
|
err = DelegatorSharesInvariant(k)(ctx)
|
2018-11-26 04:13:47 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-29 07:17:10 -08:00
|
|
|
return nil
|
2018-07-17 11:50:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-21 16:52:03 -08:00
|
|
|
// SupplyInvariants checks that the total supply reflects all held not-bonded tokens, bonded tokens, and unbonding delegations
|
2018-08-31 15:22:37 -07:00
|
|
|
// nolint: unparam
|
2019-02-13 15:01:50 -08:00
|
|
|
func SupplyInvariants(k staking.Keeper,
|
|
|
|
f staking.FeeCollectionKeeper, d staking.DistributionKeeper, am auth.AccountKeeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2018-09-27 06:52:30 -07:00
|
|
|
pool := k.GetPool(ctx)
|
2018-07-17 11:50:30 -07:00
|
|
|
|
2018-10-18 12:58:57 -07:00
|
|
|
loose := sdk.ZeroDec()
|
2018-08-14 17:15:02 -07:00
|
|
|
bonded := sdk.ZeroDec()
|
2018-07-17 11:50:30 -07:00
|
|
|
am.IterateAccounts(ctx, func(acc auth.Account) bool {
|
2019-02-21 00:05:31 -08:00
|
|
|
loose = loose.Add(acc.GetCoins().AmountOf(k.BondDenom(ctx)).ToDec())
|
2018-07-17 11:50:30 -07:00
|
|
|
return false
|
|
|
|
})
|
2019-01-11 12:08:01 -08:00
|
|
|
k.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) bool {
|
2019-01-16 02:35:18 -08:00
|
|
|
for _, entry := range ubd.Entries {
|
2019-02-21 00:05:31 -08:00
|
|
|
loose = loose.Add(entry.Balance.ToDec())
|
2019-01-16 02:35:18 -08:00
|
|
|
}
|
2018-07-17 22:37:38 -07:00
|
|
|
return false
|
|
|
|
})
|
2018-07-17 11:50:30 -07:00
|
|
|
k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool {
|
|
|
|
switch validator.GetStatus() {
|
|
|
|
case sdk.Bonded:
|
2019-02-21 00:05:31 -08:00
|
|
|
bonded = bonded.Add(validator.GetBondedTokens().ToDec())
|
2019-01-02 12:29:47 -08:00
|
|
|
case sdk.Unbonding, sdk.Unbonded:
|
2019-02-21 00:05:31 -08:00
|
|
|
loose = loose.Add(validator.GetTokens().ToDec())
|
2018-07-17 11:50:30 -07:00
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
2018-07-18 00:42:18 -07:00
|
|
|
|
2018-10-18 18:08:25 -07:00
|
|
|
// add outstanding fees
|
2019-02-21 00:05:31 -08:00
|
|
|
loose = loose.Add(f.GetCollectedFees(ctx).AmountOf(k.BondDenom(ctx)).ToDec())
|
2018-10-18 18:08:25 -07:00
|
|
|
|
2018-10-18 12:58:57 -07:00
|
|
|
// add community pool
|
2019-02-13 15:01:50 -08:00
|
|
|
loose = loose.Add(d.GetFeePoolCommunityCoins(ctx).AmountOf(k.BondDenom(ctx)))
|
2018-10-18 12:58:57 -07:00
|
|
|
|
2019-01-16 13:38:05 -08:00
|
|
|
// add yet-to-be-withdrawn
|
2019-02-13 15:01:50 -08:00
|
|
|
loose = loose.Add(d.GetOutstandingRewardsCoins(ctx).AmountOf(k.BondDenom(ctx)))
|
2018-10-18 12:58:57 -07:00
|
|
|
|
2019-01-21 16:52:03 -08:00
|
|
|
// Not-bonded tokens should equal coin supply plus unbonding delegations
|
2018-10-26 04:42:53 -07:00
|
|
|
// plus tokens on unbonded validators
|
2019-02-21 00:05:31 -08:00
|
|
|
if !pool.NotBondedTokens.ToDec().Equal(loose) {
|
2019-01-02 12:29:47 -08:00
|
|
|
return fmt.Errorf("loose token invariance:\n"+
|
2019-01-21 16:52:03 -08:00
|
|
|
"\tpool.NotBondedTokens: %v\n"+
|
|
|
|
"\tsum of account tokens: %v", pool.NotBondedTokens, loose)
|
2018-09-27 06:52:30 -07:00
|
|
|
}
|
2018-07-18 00:42:18 -07:00
|
|
|
|
|
|
|
// Bonded tokens should equal sum of tokens with bonded validators
|
2019-02-21 00:05:31 -08:00
|
|
|
if !pool.BondedTokens.ToDec().Equal(bonded) {
|
2019-01-02 12:29:47 -08:00
|
|
|
return fmt.Errorf("bonded token invariance:\n"+
|
|
|
|
"\tpool.BondedTokens: %v\n"+
|
|
|
|
"\tsum of account tokens: %v", pool.BondedTokens, bonded)
|
2018-09-27 06:52:30 -07:00
|
|
|
}
|
2018-07-17 11:50:30 -07:00
|
|
|
|
2018-09-09 08:34:09 -07:00
|
|
|
return nil
|
2018-07-17 11:50:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 16:04:52 -08:00
|
|
|
// NonNegativePowerInvariant checks that all stored validators have >= 0 power.
|
2019-02-13 15:01:50 -08:00
|
|
|
func NonNegativePowerInvariant(k staking.Keeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2018-11-04 20:44:43 -08:00
|
|
|
iterator := k.ValidatorsPowerStoreIterator(ctx)
|
|
|
|
|
|
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
|
|
validator, found := k.GetValidator(ctx, iterator.Value())
|
|
|
|
if !found {
|
|
|
|
panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value()))
|
2018-09-09 08:34:09 -07:00
|
|
|
}
|
2018-11-04 20:44:43 -08:00
|
|
|
|
2018-12-07 16:04:52 -08:00
|
|
|
powerKey := keeper.GetValidatorsByPowerIndexKey(validator)
|
2018-11-04 20:44:43 -08:00
|
|
|
|
|
|
|
if !bytes.Equal(iterator.Key(), powerKey) {
|
|
|
|
return fmt.Errorf("power store invariance:\n\tvalidator.Power: %v"+
|
2019-02-05 21:30:48 -08:00
|
|
|
"\n\tkey should be: %v\n\tkey in store: %v",
|
|
|
|
validator.GetTendermintPower(), powerKey, iterator.Key())
|
2018-11-04 20:44:43 -08:00
|
|
|
}
|
2018-12-07 16:04:52 -08:00
|
|
|
|
2019-01-02 12:29:47 -08:00
|
|
|
if validator.Tokens.IsNegative() {
|
2018-12-07 16:04:52 -08:00
|
|
|
return fmt.Errorf("negative tokens for validator: %v", validator)
|
|
|
|
}
|
2018-11-04 20:44:43 -08:00
|
|
|
}
|
|
|
|
iterator.Close()
|
|
|
|
return nil
|
2018-07-17 11:50:30 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-26 04:13:47 -08:00
|
|
|
// PositiveDelegationInvariant checks that all stored delegations have > 0 shares.
|
2019-02-13 15:01:50 -08:00
|
|
|
func PositiveDelegationInvariant(k staking.Keeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2018-11-26 04:13:47 -08:00
|
|
|
delegations := k.GetAllDelegations(ctx)
|
|
|
|
for _, delegation := range delegations {
|
|
|
|
if delegation.Shares.IsNegative() {
|
|
|
|
return fmt.Errorf("delegation with negative shares: %+v", delegation)
|
|
|
|
}
|
|
|
|
if delegation.Shares.IsZero() {
|
|
|
|
return fmt.Errorf("delegation with zero shares: %+v", delegation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// DelegatorSharesInvariant checks whether all the delegator shares which persist
|
|
|
|
// in the delegator object add up to the correct total delegator shares
|
|
|
|
// amount stored in each validator
|
2019-02-13 15:01:50 -08:00
|
|
|
func DelegatorSharesInvariant(k staking.Keeper) sdk.Invariant {
|
2018-11-29 07:17:10 -08:00
|
|
|
return func(ctx sdk.Context) error {
|
2018-11-26 04:13:47 -08:00
|
|
|
validators := k.GetAllValidators(ctx)
|
|
|
|
for _, validator := range validators {
|
|
|
|
|
|
|
|
valTotalDelShares := validator.GetDelegatorShares()
|
|
|
|
|
|
|
|
totalDelShares := sdk.ZeroDec()
|
|
|
|
delegations := k.GetValidatorDelegations(ctx, validator.GetOperator())
|
|
|
|
for _, delegation := range delegations {
|
|
|
|
totalDelShares = totalDelShares.Add(delegation.Shares)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !valTotalDelShares.Equal(totalDelShares) {
|
|
|
|
return fmt.Errorf("broken delegator shares invariance:\n"+
|
|
|
|
"\tvalidator.DelegatorShares: %v\n"+
|
|
|
|
"\tsum of Delegator.Shares: %v", valTotalDelShares, totalDelShares)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|