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