github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/staking/keeper/invariants.go (about)

     1  package keeper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     8  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/exported"
     9  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/types"
    10  )
    11  
    12  // RegisterInvariants registers all staking invariants
    13  func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
    14  
    15  	ir.RegisterRoute(types.ModuleName, "module-accounts",
    16  		ModuleAccountInvariants(k))
    17  	ir.RegisterRoute(types.ModuleName, "nonnegative-power",
    18  		NonNegativePowerInvariant(k))
    19  	ir.RegisterRoute(types.ModuleName, "positive-delegation",
    20  		PositiveDelegationInvariant(k))
    21  	ir.RegisterRoute(types.ModuleName, "delegator-shares",
    22  		DelegatorSharesInvariant(k))
    23  }
    24  
    25  // AllInvariants runs all invariants of the staking module.
    26  func AllInvariants(k Keeper) sdk.Invariant {
    27  
    28  	return func(ctx sdk.Context) (string, bool) {
    29  		res, stop := ModuleAccountInvariants(k)(ctx)
    30  		if stop {
    31  			return res, stop
    32  		}
    33  
    34  		res, stop = NonNegativePowerInvariant(k)(ctx)
    35  		if stop {
    36  			return res, stop
    37  		}
    38  
    39  		res, stop = PositiveDelegationInvariant(k)(ctx)
    40  		if stop {
    41  			return res, stop
    42  		}
    43  
    44  		return DelegatorSharesInvariant(k)(ctx)
    45  	}
    46  }
    47  
    48  // ModuleAccountInvariants checks that the bonded and notBonded ModuleAccounts pools
    49  // reflects the tokens actively bonded and not bonded
    50  func ModuleAccountInvariants(k Keeper) sdk.Invariant {
    51  	return func(ctx sdk.Context) (string, bool) {
    52  		bonded := sdk.ZeroInt()
    53  		notBonded := sdk.ZeroInt()
    54  		bondedPool := k.GetBondedPool(ctx)
    55  		notBondedPool := k.GetNotBondedPool(ctx)
    56  		bondDenom := k.BondDenom(ctx)
    57  
    58  		k.IterateValidators(ctx, func(_ int64, validator exported.ValidatorI) bool {
    59  			switch validator.GetStatus() {
    60  			//case sdk.Bonded:
    61  			//bonded = bonded.Add(validator.GetTokens())
    62  			//case sdk.Unbonding, sdk.Unbonded:
    63  			//	notBonded = notBonded.Add(validator.GetTokens())
    64  			default:
    65  				panic("invalid validator status")
    66  			}
    67  			return false
    68  		})
    69  
    70  		k.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) bool {
    71  			for _, entry := range ubd.Entries {
    72  				notBonded = notBonded.Add(entry.Balance)
    73  			}
    74  			return false
    75  		})
    76  
    77  		poolBonded := bondedPool.GetCoins().AmountOf(bondDenom)
    78  		poolNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom)
    79  		broken := !poolBonded.Equal(bonded.ToDec()) || !poolNotBonded.Equal(notBonded.ToDec())
    80  
    81  		// Bonded tokens should equal sum of tokens with bonded validators
    82  		// Not-bonded tokens should equal unbonding delegations	plus tokens on unbonded validators
    83  		return sdk.FormatInvariant(types.ModuleName, "bonded and not bonded module account coins", fmt.Sprintf(
    84  			"\tPool's bonded tokens: %v\n"+
    85  				"\tsum of bonded tokens: %v\n"+
    86  				"not bonded token invariance:\n"+
    87  				"\tPool's not bonded tokens: %v\n"+
    88  				"\tsum of not bonded tokens: %v\n"+
    89  				"module accounts total (bonded + not bonded):\n"+
    90  				"\tModule Accounts' tokens: %v\n"+
    91  				"\tsum tokens:              %v\n",
    92  			poolBonded, bonded, poolNotBonded, notBonded, poolBonded.Add(poolNotBonded), bonded.Add(notBonded))), broken
    93  	}
    94  }
    95  
    96  // NonNegativePowerInvariant checks that all stored validators have >= 0 power.
    97  func NonNegativePowerInvariant(k Keeper) sdk.Invariant {
    98  	return func(ctx sdk.Context) (string, bool) {
    99  		var msg string
   100  		var broken bool
   101  
   102  		iterator := k.ValidatorsPowerStoreIterator(ctx)
   103  
   104  		for ; iterator.Valid(); iterator.Next() {
   105  			validator, found := k.GetValidator(ctx, iterator.Value())
   106  			if !found {
   107  				panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value()))
   108  			}
   109  
   110  			powerKey := types.GetValidatorsByPowerIndexKey(validator)
   111  
   112  			if !bytes.Equal(iterator.Key(), powerKey) {
   113  				broken = true
   114  				msg += fmt.Sprintf("power store invariance:\n\tvalidator.Power: %v"+
   115  					"\n\tkey should be: %v\n\tkey in store: %v\n",
   116  					validator.GetConsensusPower(), powerKey, iterator.Key())
   117  			}
   118  
   119  			if validator.Tokens.IsNegative() {
   120  				broken = true
   121  				msg += fmt.Sprintf("\tnegative tokens for validator: %v\n", validator)
   122  			}
   123  		}
   124  		iterator.Close()
   125  		return sdk.FormatInvariant(types.ModuleName, "nonnegative power", fmt.Sprintf("found invalid validator powers\n%s", msg)), broken
   126  	}
   127  }
   128  
   129  // PositiveDelegationInvariant checks that all stored delegations have > 0 shares.
   130  func PositiveDelegationInvariant(k Keeper) sdk.Invariant {
   131  	return func(ctx sdk.Context) (string, bool) {
   132  		var msg string
   133  		var count int
   134  
   135  		delegations := k.GetAllDelegations(ctx)
   136  		for _, delegation := range delegations {
   137  			if delegation.Shares.IsNegative() {
   138  				count++
   139  				msg += fmt.Sprintf("\tdelegation with negative shares: %+v\n", delegation)
   140  			}
   141  			if delegation.Shares.IsZero() {
   142  				count++
   143  				msg += fmt.Sprintf("\tdelegation with zero shares: %+v\n", delegation)
   144  			}
   145  		}
   146  		broken := count != 0
   147  
   148  		return sdk.FormatInvariant(types.ModuleName, "positive delegations", fmt.Sprintf(
   149  			"%d invalid delegations found\n%s", count, msg)), broken
   150  	}
   151  }
   152  
   153  // DelegatorSharesInvariant checks whether all the delegator shares which persist
   154  // in the delegator object add up to the correct total delegator shares
   155  // amount stored in each validator.
   156  func DelegatorSharesInvariant(k Keeper) sdk.Invariant {
   157  	return func(ctx sdk.Context) (string, bool) {
   158  		var msg string
   159  		var broken bool
   160  
   161  		validators := k.GetAllValidators(ctx)
   162  		for _, validator := range validators {
   163  
   164  			valTotalDelShares := validator.GetDelegatorShares()
   165  
   166  			totalDelShares := sdk.ZeroDec()
   167  			delegations := k.GetValidatorDelegations(ctx, validator.GetOperator())
   168  			for _, delegation := range delegations {
   169  				totalDelShares = totalDelShares.Add(delegation.Shares)
   170  			}
   171  
   172  			if !valTotalDelShares.Equal(totalDelShares) {
   173  				broken = true
   174  				msg += fmt.Sprintf("broken delegator shares invariance:\n"+
   175  					"\tvalidator.DelegatorShares: %v\n"+
   176  					"\tsum of Delegator.Shares: %v\n", valTotalDelShares, totalDelShares)
   177  			}
   178  		}
   179  		return sdk.FormatInvariant(types.ModuleName, "delegator shares", msg), broken
   180  	}
   181  }