cosmos-sdk/x/staking/simulation/invariants.go

171 lines
5.0 KiB
Go
Raw Normal View History

2018-07-17 11:50:30 -07:00
package simulation
import (
"bytes"
"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
func AllInvariants(k staking.Keeper,
f staking.FeeCollectionKeeper, d staking.DistributionKeeper,
am auth.AccountKeeper) sdk.Invariant {
return func(ctx sdk.Context) error {
err := SupplyInvariants(k, f, d, am)(ctx)
if err != nil {
return err
}
err = NonNegativePowerInvariant(k)(ctx)
if err != nil {
return err
}
err = PositiveDelegationInvariant(k)(ctx)
if err != nil {
return err
}
err = DelegatorSharesInvariant(k)(ctx)
if err != nil {
return err
}
return nil
2018-07-17 11:50:30 -07:00
}
}
Merge PR #3281: Staking Spec Upgrade * remove kv seperation for marshalling * pending * cleanup * cleanup x2 * pending * working * minor refactors * entry structs defined * uncompiled mechanism written * add many compile fixes * code compiles * fix test compile errors * test cover passes * ... * multiple entries fix * ... * more design fix * working * fix test cover bug * Update PENDING.md * update comment around queue completion for redelegations/ubds * basic spec updates * spec folder cleanup * cleanup docs folder cont. * ... * find-replace and folder rename * supplimentary find/replace * pending * supplimentary * pending * few undos, stakingd -> staked * to staking -> to stake * undos * most staking -> most stake * ... * undos * simplestake->simplestaking * ... * pending update * capital letter replacements * ... * working * staking doc updates from rigel/delegation-index branch * spec-spec * spec-spec * LooseTokens -> NotBondedTokens * staking state.md updates * updates to hook and endblock spec * Update docs/gaia/gaiacli.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * comment undo * remove ErrConflictingRedelegation * @cwgoes comments are resolved * further updates to endblock and state * msg json update * working transaction updates * working * complete transaction rewrite * PENDING.md * typo * add todo * address @jackzampolin @cwgoes comments * couple leftover comments, rename * Update x/staking/types/pool.go Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * cwgoes additions * cwgoes suggestions x2
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
func SupplyInvariants(k staking.Keeper,
f staking.FeeCollectionKeeper, d staking.DistributionKeeper, am auth.AccountKeeper) sdk.Invariant {
return func(ctx sdk.Context) error {
pool := k.GetPool(ctx)
2018-07-17 11:50:30 -07:00
loose := sdk.ZeroDec()
bonded := sdk.ZeroDec()
2018-07-17 11:50:30 -07:00
am.IterateAccounts(ctx, func(acc auth.Account) bool {
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 {
for _, entry := range ubd.Entries {
loose = loose.Add(entry.Balance.ToDec())
}
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:
bonded = bonded.Add(validator.GetBondedTokens().ToDec())
case sdk.Unbonding, sdk.Unbonded:
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
loose = loose.Add(f.GetCollectedFees(ctx).AmountOf(k.BondDenom(ctx)).ToDec())
2018-10-18 18:08:25 -07:00
// add community pool
loose = loose.Add(d.GetFeePoolCommunityCoins(ctx).AmountOf(k.BondDenom(ctx)))
2019-01-16 13:38:05 -08:00
// add yet-to-be-withdrawn
loose = loose.Add(d.GetOutstandingRewardsCoins(ctx).AmountOf(k.BondDenom(ctx)))
Merge PR #3281: Staking Spec Upgrade * remove kv seperation for marshalling * pending * cleanup * cleanup x2 * pending * working * minor refactors * entry structs defined * uncompiled mechanism written * add many compile fixes * code compiles * fix test compile errors * test cover passes * ... * multiple entries fix * ... * more design fix * working * fix test cover bug * Update PENDING.md * update comment around queue completion for redelegations/ubds * basic spec updates * spec folder cleanup * cleanup docs folder cont. * ... * find-replace and folder rename * supplimentary find/replace * pending * supplimentary * pending * few undos, stakingd -> staked * to staking -> to stake * undos * most staking -> most stake * ... * undos * simplestake->simplestaking * ... * pending update * capital letter replacements * ... * working * staking doc updates from rigel/delegation-index branch * spec-spec * spec-spec * LooseTokens -> NotBondedTokens * staking state.md updates * updates to hook and endblock spec * Update docs/gaia/gaiacli.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * comment undo * remove ErrConflictingRedelegation * @cwgoes comments are resolved * further updates to endblock and state * msg json update * working transaction updates * working * complete transaction rewrite * PENDING.md * typo * add todo * address @jackzampolin @cwgoes comments * couple leftover comments, rename * Update x/staking/types/pool.go Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * cwgoes additions * cwgoes suggestions x2
2019-01-21 16:52:03 -08:00
// Not-bonded tokens should equal coin supply plus unbonding delegations
// plus tokens on unbonded validators
if !pool.NotBondedTokens.ToDec().Equal(loose) {
return fmt.Errorf("loose token invariance:\n"+
Merge PR #3281: Staking Spec Upgrade * remove kv seperation for marshalling * pending * cleanup * cleanup x2 * pending * working * minor refactors * entry structs defined * uncompiled mechanism written * add many compile fixes * code compiles * fix test compile errors * test cover passes * ... * multiple entries fix * ... * more design fix * working * fix test cover bug * Update PENDING.md * update comment around queue completion for redelegations/ubds * basic spec updates * spec folder cleanup * cleanup docs folder cont. * ... * find-replace and folder rename * supplimentary find/replace * pending * supplimentary * pending * few undos, stakingd -> staked * to staking -> to stake * undos * most staking -> most stake * ... * undos * simplestake->simplestaking * ... * pending update * capital letter replacements * ... * working * staking doc updates from rigel/delegation-index branch * spec-spec * spec-spec * LooseTokens -> NotBondedTokens * staking state.md updates * updates to hook and endblock spec * Update docs/gaia/gaiacli.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * Update docs/gaia/validators/validator-setup.md Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * comment undo * remove ErrConflictingRedelegation * @cwgoes comments are resolved * further updates to endblock and state * msg json update * working transaction updates * working * complete transaction rewrite * PENDING.md * typo * add todo * address @jackzampolin @cwgoes comments * couple leftover comments, rename * Update x/staking/types/pool.go Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * cwgoes additions * cwgoes suggestions x2
2019-01-21 16:52:03 -08:00
"\tpool.NotBondedTokens: %v\n"+
"\tsum of account tokens: %v", pool.NotBondedTokens, loose)
}
2018-07-18 00:42:18 -07:00
// Bonded tokens should equal sum of tokens with bonded validators
if !pool.BondedTokens.ToDec().Equal(bonded) {
return fmt.Errorf("bonded token invariance:\n"+
"\tpool.BondedTokens: %v\n"+
"\tsum of account tokens: %v", pool.BondedTokens, bonded)
}
2018-07-17 11:50:30 -07:00
return nil
2018-07-17 11:50:30 -07:00
}
}
// NonNegativePowerInvariant checks that all stored validators have >= 0 power.
func NonNegativePowerInvariant(k staking.Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
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()))
}
powerKey := keeper.GetValidatorsByPowerIndexKey(validator)
if !bytes.Equal(iterator.Key(), powerKey) {
return fmt.Errorf("power store invariance:\n\tvalidator.Power: %v"+
"\n\tkey should be: %v\n\tkey in store: %v",
validator.GetTendermintPower(), powerKey, iterator.Key())
}
if validator.Tokens.IsNegative() {
return fmt.Errorf("negative tokens for validator: %v", validator)
}
}
iterator.Close()
return nil
2018-07-17 11:50:30 -07:00
}
}
// PositiveDelegationInvariant checks that all stored delegations have > 0 shares.
func PositiveDelegationInvariant(k staking.Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
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
func DelegatorSharesInvariant(k staking.Keeper) sdk.Invariant {
return func(ctx sdk.Context) error {
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
}
}