diff --git a/x/bank/keeper/invariants.go b/x/bank/keeper/invariants.go index 12ebf782a..3cb88efad 100644 --- a/x/bank/keeper/invariants.go +++ b/x/bank/keeper/invariants.go @@ -8,53 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// RegisterInvariants registers the bank module invariants -func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding", NonnegativeBalanceInvariant(k)) - ir.RegisterRoute(types.ModuleName, "total-supply", TotalSupply(k)) -} - -// AllInvariants runs all invariants of the X/bank module. -func AllInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - return TotalSupply(k)(ctx) - } -} - -// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances -func NonnegativeBalanceInvariant(k ViewKeeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var ( - msg string - count int - ) - - k.IterateAllBalances(ctx, func(addr sdk.AccAddress, balance sdk.Coin) bool { - if balance.IsNegative() { - count++ - msg += fmt.Sprintf("\t%s has a negative balance of %s\n", addr, balance) - } - - return false - }) - k.IterateAllWeiBalances(ctx, func(addr sdk.AccAddress, balance sdk.Int) bool { - if balance.IsNegative() { - count++ - msg += fmt.Sprintf("\t%s has a negative wei balance of %s\n", addr, balance) - } - - return false - }) - - broken := count != 0 - - return sdk.FormatInvariant( - types.ModuleName, "nonnegative-outstanding", - fmt.Sprintf("amount of negative balances found %d\n%s", count, msg), - ), broken - } -} - // TotalSupply checks that the total supply reflects all the coins held in accounts func TotalSupply(k Keeper) sdk.Invariant { return func(ctx sdk.Context) (string, bool) { diff --git a/x/bank/module.go b/x/bank/module.go index 4a9aabe50..b1b7fe906 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -134,9 +134,7 @@ func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, accountKeeper types.Acc func (AppModule) Name() string { return types.ModuleName } // RegisterInvariants registers the bank module invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - keeper.RegisterInvariants(ir, am.keeper) -} +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // Route returns the message routing key for the bank module. func (am AppModule) Route() sdk.Route { diff --git a/x/distribution/keeper/invariants.go b/x/distribution/keeper/invariants.go deleted file mode 100644 index 0a23d36be..000000000 --- a/x/distribution/keeper/invariants.go +++ /dev/null @@ -1,162 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/distribution/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// register all distribution invariants -func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding", - NonNegativeOutstandingInvariant(k)) - ir.RegisterRoute(types.ModuleName, "can-withdraw", - CanWithdrawInvariant(k)) - ir.RegisterRoute(types.ModuleName, "reference-count", - ReferenceCountInvariant(k)) - ir.RegisterRoute(types.ModuleName, "module-account", - ModuleAccountInvariant(k)) -} - -// AllInvariants runs all invariants of the distribution module -func AllInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - res, stop := CanWithdrawInvariant(k)(ctx) - if stop { - return res, stop - } - res, stop = NonNegativeOutstandingInvariant(k)(ctx) - if stop { - return res, stop - } - res, stop = ReferenceCountInvariant(k)(ctx) - if stop { - return res, stop - } - return ModuleAccountInvariant(k)(ctx) - } -} - -// NonNegativeOutstandingInvariant checks that outstanding unwithdrawn fees are never negative -func NonNegativeOutstandingInvariant(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var msg string - var count int - var outstanding sdk.DecCoins - - k.IterateValidatorOutstandingRewards(ctx, func(addr sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { - outstanding = rewards.GetRewards() - if outstanding.IsAnyNegative() { - count++ - msg += fmt.Sprintf("\t%v has negative outstanding coins: %v\n", addr, outstanding) - } - return false - }) - broken := count != 0 - - return sdk.FormatInvariant(types.ModuleName, "nonnegative outstanding", - fmt.Sprintf("found %d validators with negative outstanding rewards\n%s", count, msg)), broken - } -} - -// CanWithdrawInvariant checks that current rewards can be completely withdrawn -func CanWithdrawInvariant(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - - // cache, we don't want to write changes - ctx, _ = ctx.CacheContext() - - var remaining sdk.DecCoins - - valDelegationAddrs := make(map[string][]sdk.AccAddress) - for _, del := range k.stakingKeeper.GetAllSDKDelegations(ctx) { - valAddr := del.GetValidatorAddr().String() - valDelegationAddrs[valAddr] = append(valDelegationAddrs[valAddr], del.GetDelegatorAddr()) - } - - // iterate over all validators - k.stakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { - _, _ = k.WithdrawValidatorCommission(ctx, val.GetOperator()) - - delegationAddrs, ok := valDelegationAddrs[val.GetOperator().String()] - if ok { - for _, delAddr := range delegationAddrs { - if _, err := k.WithdrawDelegationRewards(ctx, delAddr, val.GetOperator()); err != nil { - panic(err) - } - } - } - - remaining = k.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) - if len(remaining) > 0 && remaining[0].Amount.IsNegative() { - return true - } - - return false - }) - - broken := len(remaining) > 0 && remaining[0].Amount.IsNegative() - return sdk.FormatInvariant(types.ModuleName, "can withdraw", - fmt.Sprintf("remaining coins: %v\n", remaining)), broken - } -} - -// ReferenceCountInvariant checks that the number of historical rewards records is correct -func ReferenceCountInvariant(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - - valCount := uint64(0) - k.stakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { - valCount++ - return false - }) - dels := k.stakingKeeper.GetAllSDKDelegations(ctx) - slashCount := uint64(0) - 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) - expected := valCount + uint64(len(dels)) + slashCount - count := k.GetValidatorHistoricalReferenceCount(ctx) - broken := count != expected - - 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", - expected, valCount, len(dels), slashCount, count)), broken - } -} - -// 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 { - return func(ctx sdk.Context) (string, bool) { - - var expectedCoins sdk.DecCoins - k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) { - expectedCoins = expectedCoins.Add(rewards.Rewards...) - return false - }) - - communityPool := k.GetFeePoolCommunityCoins(ctx) - expectedInt, _ := expectedCoins.Add(communityPool...).TruncateDecimal() - - macc := k.GetDistributionAccount(ctx) - balances := k.bankKeeper.GetAllBalances(ctx, macc.GetAddress()) - - broken := !balances.IsEqual(expectedInt) - return sdk.FormatInvariant( - types.ModuleName, "ModuleAccount coins", - fmt.Sprintf("\texpected ModuleAccount coins: %s\n"+ - "\tdistribution ModuleAccount coins: %s\n", - expectedInt, balances, - ), - ), broken - } -} diff --git a/x/distribution/module.go b/x/distribution/module.go index 2fcc4601b..33fd2add2 100644 --- a/x/distribution/module.go +++ b/x/distribution/module.go @@ -127,9 +127,7 @@ func (AppModule) Name() string { } // RegisterInvariants registers the distribution module invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - keeper.RegisterInvariants(ir, am.keeper) -} +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // Route returns the message routing key for the distribution module. func (am AppModule) Route() sdk.Route { diff --git a/x/gov/module.go b/x/gov/module.go index be10664ef..21622b3f6 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -142,9 +142,7 @@ func (AppModule) Name() string { } // RegisterInvariants registers module invariants -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - keeper.RegisterInvariants(ir, am.keeper, am.bankKeeper) -} +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // Route returns the message routing key for the gov module. func (am AppModule) Route() sdk.Route { diff --git a/x/staking/keeper/invariants.go b/x/staking/keeper/invariants.go deleted file mode 100644 index 0b412c6a4..000000000 --- a/x/staking/keeper/invariants.go +++ /dev/null @@ -1,198 +0,0 @@ -package keeper - -import ( - "bytes" - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -// RegisterInvariants registers all staking invariants -func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, "module-accounts", - ModuleAccountInvariants(k)) - ir.RegisterRoute(types.ModuleName, "nonnegative-power", - NonNegativePowerInvariant(k)) - ir.RegisterRoute(types.ModuleName, "positive-delegation", - PositiveDelegationInvariant(k)) - ir.RegisterRoute(types.ModuleName, "delegator-shares", - DelegatorSharesInvariant(k)) -} - -// AllInvariants runs all invariants of the staking module. -func AllInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - res, stop := ModuleAccountInvariants(k)(ctx) - if stop { - return res, stop - } - - res, stop = NonNegativePowerInvariant(k)(ctx) - if stop { - return res, stop - } - - res, stop = PositiveDelegationInvariant(k)(ctx) - if stop { - return res, stop - } - - return DelegatorSharesInvariant(k)(ctx) - } -} - -// ModuleAccountInvariants checks that the bonded and notBonded ModuleAccounts pools -// reflects the tokens actively bonded and not bonded -func ModuleAccountInvariants(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - bonded := sdk.ZeroInt() - notBonded := sdk.ZeroInt() - bondedPool := k.GetBondedPool(ctx) - notBondedPool := k.GetNotBondedPool(ctx) - bondDenom := k.BondDenom(ctx) - - k.IterateValidators(ctx, func(_ int64, validator types.ValidatorI) bool { - switch validator.GetStatus() { - case types.Bonded: - bonded = bonded.Add(validator.GetTokens()) - case types.Unbonding, types.Unbonded: - notBonded = notBonded.Add(validator.GetTokens()) - default: - panic("invalid validator status") - } - return false - }) - - k.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) bool { - for _, entry := range ubd.Entries { - notBonded = notBonded.Add(entry.Balance) - } - return false - }) - - poolBonded := k.bankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom) - poolNotBonded := k.bankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom) - broken := !poolBonded.Amount.Equal(bonded) || !poolNotBonded.Amount.Equal(notBonded) - - // Bonded tokens should equal sum of tokens with bonded validators - // Not-bonded tokens should equal unbonding delegations plus tokens on unbonded validators - return sdk.FormatInvariant(types.ModuleName, "bonded and not bonded module account coins", fmt.Sprintf( - "\tPool's bonded tokens: %v\n"+ - "\tsum of bonded tokens: %v\n"+ - "not bonded token invariance:\n"+ - "\tPool's not bonded tokens: %v\n"+ - "\tsum of not bonded tokens: %v\n"+ - "module accounts total (bonded + not bonded):\n"+ - "\tModule Accounts' tokens: %v\n"+ - "\tsum tokens: %v\n", - poolBonded, bonded, poolNotBonded, notBonded, poolBonded.Add(poolNotBonded), bonded.Add(notBonded))), broken - } -} - -// NonNegativePowerInvariant checks that all stored validators have >= 0 power. -func NonNegativePowerInvariant(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var ( - msg string - broken bool - ) - - iterator := k.ValidatorsPowerStoreIterator(ctx) - defer iterator.Close() - 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 := types.GetValidatorsByPowerIndexKey(validator, k.PowerReduction(ctx)) - - if !bytes.Equal(iterator.Key(), powerKey) { - broken = true - msg += fmt.Sprintf("power store invariance:\n\tvalidator.Power: %v"+ - "\n\tkey should be: %v\n\tkey in store: %v\n", - validator.GetConsensusPower(k.PowerReduction(ctx)), powerKey, iterator.Key()) - } - - if validator.Tokens.IsNegative() { - broken = true - msg += fmt.Sprintf("\tnegative tokens for validator: %v\n", validator) - } - } - - return sdk.FormatInvariant(types.ModuleName, "nonnegative power", fmt.Sprintf("found invalid validator powers\n%s", msg)), broken - } -} - -// PositiveDelegationInvariant checks that all stored delegations have > 0 shares. -func PositiveDelegationInvariant(k Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var ( - msg string - count int - ) - - delegations := k.GetAllDelegations(ctx) - for _, delegation := range delegations { - if delegation.Shares.IsNegative() { - count++ - - msg += fmt.Sprintf("\tdelegation with negative shares: %+v\n", delegation) - } - - if delegation.Shares.IsZero() { - count++ - - msg += fmt.Sprintf("\tdelegation with zero shares: %+v\n", delegation) - } - } - - broken := count != 0 - - return sdk.FormatInvariant(types.ModuleName, "positive delegations", fmt.Sprintf( - "%d invalid delegations found\n%s", count, msg)), broken - } -} - -// 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 Keeper) sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var ( - msg string - broken bool - ) - - validators := k.GetAllValidators(ctx) - validatorsDelegationShares := map[string]sdk.Dec{} - - // initialize a map: validator -> its delegation shares - for _, validator := range validators { - validatorsDelegationShares[validator.GetOperator().String()] = sdk.ZeroDec() - } - - // iterate through all the delegations to calculate the total delegation shares for each validator - delegations := k.GetAllDelegations(ctx) - for _, delegation := range delegations { - delegationValidatorAddr := delegation.GetValidatorAddr().String() - validatorDelegationShares := validatorsDelegationShares[delegationValidatorAddr] - validatorsDelegationShares[delegationValidatorAddr] = validatorDelegationShares.Add(delegation.Shares) - } - - // for each validator, check if its total delegation shares calculated from the step above equals to its expected delegation shares - for _, validator := range validators { - expValTotalDelShares := validator.GetDelegatorShares() - calculatedValTotalDelShares := validatorsDelegationShares[validator.GetOperator().String()] - if !calculatedValTotalDelShares.Equal(expValTotalDelShares) { - broken = true - msg += fmt.Sprintf("broken delegator shares invariance:\n"+ - "\tvalidator.DelegatorShares: %v\n"+ - "\tsum of Delegator.Shares: %v\n", expValTotalDelShares, calculatedValTotalDelShares) - } - } - - return sdk.FormatInvariant(types.ModuleName, "delegator shares", msg), broken - } -} diff --git a/x/staking/module.go b/x/staking/module.go index c5b81823d..4716d5d0f 100644 --- a/x/staking/module.go +++ b/x/staking/module.go @@ -124,9 +124,7 @@ func (AppModule) Name() string { } // RegisterInvariants registers the staking module invariants. -func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) { - keeper.RegisterInvariants(ir, am.keeper) -} +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} // Route returns the message routing key for the staking module. func (am AppModule) Route() sdk.Route {