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