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 }