github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/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/x/staking/exported"
     9  	"github.com/fibonacci-chain/fbc/x/staking/types"
    10  )
    11  
    12  // RegisterInvariantsCustom registers all staking invariants for fbexchain
    13  func RegisterInvariantsCustom(ir sdk.InvariantRegistry, k Keeper) {
    14  
    15  	ir.RegisterRoute(types.ModuleName, "module-accounts",
    16  		ModuleAccountInvariantsCustom(k))
    17  	ir.RegisterRoute(types.ModuleName, "nonnegative-power",
    18  		NonNegativePowerInvariantCustom(k))
    19  	ir.RegisterRoute(types.ModuleName, "positive-delegator",
    20  		PositiveDelegatorInvariant(k))
    21  	ir.RegisterRoute(types.ModuleName, "delegator-add-shares",
    22  		DelegatorAddSharesInvariant(k))
    23  }
    24  
    25  // DelegatorAddSharesInvariant checks whether all the shares which persist
    26  // in the store add up to the correct total shares amount stored on each existing validator
    27  func DelegatorAddSharesInvariant(k Keeper) sdk.Invariant {
    28  	return func(ctx sdk.Context) (string, bool) {
    29  		var msg string
    30  		var broken bool
    31  
    32  		validators := k.GetAllValidators(ctx)
    33  		for _, validator := range validators {
    34  
    35  			valTotalShares := validator.GetDelegatorShares()
    36  
    37  			var totalShares sdk.Dec
    38  			if validator.MinSelfDelegation.Equal(sdk.ZeroDec()) && validator.Jailed {
    39  				totalShares = sdk.ZeroDec()
    40  			} else {
    41  				totalShares = k.getSharesFromDefaultMinSelfDelegation()
    42  			}
    43  
    44  			allShares := k.GetValidatorAllShares(ctx, validator.GetOperator())
    45  			for _, shares := range allShares {
    46  				totalShares = totalShares.Add(shares.Shares)
    47  			}
    48  
    49  			if !valTotalShares.Equal(totalShares) {
    50  				broken = true
    51  				msg += fmt.Sprintf("broken delegator shares invariance:\n"+
    52  					"\tvalidator.DelegatorShares: %v\n"+
    53  					"\tsum of Shares. Shares and min self delegation: %v\n", valTotalShares, totalShares)
    54  			}
    55  		}
    56  		return sdk.FormatInvariant(types.ModuleName, "delegator shares", msg), broken
    57  	}
    58  }
    59  
    60  // PositiveDelegatorInvariant checks that all tokens delegated by delegator are greater than 0
    61  func PositiveDelegatorInvariant(k Keeper) sdk.Invariant {
    62  	return func(ctx sdk.Context) (string, bool) {
    63  		var msg string
    64  		var count int
    65  
    66  		k.IterateDelegator(ctx, func(index int64, delegator types.Delegator) bool {
    67  			if delegator.Tokens.IsNegative() {
    68  				count++
    69  				msg += fmt.Sprintf("\tdelegation with negative tokens: %+v\n", delegator)
    70  			}
    71  
    72  			if delegator.Tokens.IsZero() {
    73  				count++
    74  				msg += fmt.Sprintf("\tdelegation with zero tokens: %+v\n", delegator)
    75  			}
    76  
    77  			return false
    78  		})
    79  
    80  		broken := count != 0
    81  
    82  		return sdk.FormatInvariant(types.ModuleName, "positive tokens of delegator", fmt.Sprintf(
    83  			"%d invalid tokens of delegator found\n%s", count, msg)), broken
    84  	}
    85  }
    86  
    87  // NonNegativePowerInvariantCustom checks that all stored validators have nonnegative power
    88  func NonNegativePowerInvariantCustom(k Keeper) sdk.Invariant {
    89  	return func(ctx sdk.Context) (string, bool) {
    90  		var msg string
    91  		var broken bool
    92  
    93  		iterator := k.ValidatorsPowerStoreIterator(ctx)
    94  
    95  		for ; iterator.Valid(); iterator.Next() {
    96  			validator, found := k.GetValidator(ctx, iterator.Value())
    97  			if !found {
    98  				panic(fmt.Sprintf("validator record not found for address: %X\n", iterator.Value()))
    99  			}
   100  
   101  			powerKey := types.GetValidatorsByPowerIndexKey(validator)
   102  
   103  			if !bytes.Equal(iterator.Key(), powerKey) {
   104  				broken = true
   105  				msg += fmt.Sprintf("power store invariance:\n\tvalidator.Power: %v"+
   106  					"\n\tkey should be: %v\n\tkey in store: %v\n",
   107  					validator.ConsensusPowerByShares(), powerKey, iterator.Key())
   108  			}
   109  
   110  			if validator.DelegatorShares.IsNegative() {
   111  				broken = true
   112  				msg += fmt.Sprintf("\tnegative shares for validator: %v\n", validator)
   113  			}
   114  		}
   115  		iterator.Close()
   116  		return sdk.FormatInvariant(types.ModuleName, "nonnegative power",
   117  			fmt.Sprintf("found invalid validator powers\n%s", msg)), broken
   118  	}
   119  }
   120  
   121  // ModuleAccountInvariantsCustom check invariants for module account
   122  func ModuleAccountInvariantsCustom(k Keeper) sdk.Invariant {
   123  	return func(ctx sdk.Context) (string, bool) {
   124  		bonded := sdk.ZeroDec()
   125  		notBonded := sdk.ZeroDec()
   126  		bondedPool := k.GetBondedPool(ctx)
   127  		notBondedPool := k.GetNotBondedPool(ctx)
   128  		bondDenom := k.BondDenom(ctx)
   129  
   130  		k.IterateValidators(ctx, func(index int64, validator exported.ValidatorI) bool {
   131  			bonded = bonded.Add(validator.GetMinSelfDelegation())
   132  			return false
   133  		})
   134  
   135  		k.IterateDelegator(ctx, func(index int64, delegator types.Delegator) bool {
   136  			bonded = bonded.Add(delegator.Tokens)
   137  			return false
   138  		})
   139  
   140  		k.IterateUndelegationInfo(ctx, func(_ int64, undelegationInfo types.UndelegationInfo) bool {
   141  			notBonded = notBonded.Add(undelegationInfo.Quantity)
   142  			return false
   143  		})
   144  
   145  		poolBonded := bondedPool.GetCoins().AmountOf(bondDenom)
   146  		poolNotBonded := notBondedPool.GetCoins().AmountOf(bondDenom)
   147  		broken := !poolBonded.Equal(bonded) || !poolNotBonded.Equal(notBonded)
   148  
   149  		// Bonded tokens should be equal to the sum of delegators' tokens
   150  		// Not-bonded tokens should be equal to the sum of undelegation infos' tokens
   151  		return sdk.FormatInvariant(types.ModuleName, "bonded and not bonded module account coins", fmt.Sprintf(
   152  			"\tPool's bonded tokens: %v\n"+
   153  				"\tsum of bonded tokens: %v\n"+
   154  				"not bonded token invariance:\n"+
   155  				"\tPool's not bonded tokens: %v\n"+
   156  				"\tsum of not bonded tokens: %v\n"+
   157  				"module accounts total (bonded + not bonded):\n"+
   158  				"\tModule Accounts' tokens: %v\n"+
   159  				"\tsum tokens:              %v\n",
   160  			poolBonded, bonded, poolNotBonded, notBonded, poolBonded.Add(poolNotBonded), bonded.Add(notBonded))), broken
   161  	}
   162  }