/
invariants.go
89 lines (77 loc) · 3.21 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
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/zenchainprotocol/zenchain-node/x/distribution/types"
"github.com/zenchainprotocol/zenchain-node/x/staking/exported"
)
// RegisterInvariants registers all distribution invariants
func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
ir.RegisterRoute(types.ModuleName, "nonnegative-commission", NonNegativeCommissionsInvariant(k))
ir.RegisterRoute(types.ModuleName, "can-withdraw", CanWithdrawInvariant(k))
ir.RegisterRoute(types.ModuleName, "module-account", ModuleAccountInvariant(k))
}
// NonNegativeCommissionsInvariant checks that accumulated commissions unwithdrawned fees are never negative
func NonNegativeCommissionsInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var msg string
var count int
var commission sdk.SysCoins
k.IterateValidatorAccumulatedCommissions(ctx,
func(addr sdk.ValAddress, c types.ValidatorAccumulatedCommission) (stop bool) {
commission = c
if commission.IsAnyNegative() {
count++
msg += fmt.Sprintf("\t%v has negative accumulated commission coins: %v\n", addr, commission)
}
return false
})
broken := count != 0
return sdk.FormatInvariant(types.ModuleName, "nonnegative accumulated commission",
fmt.Sprintf("found %d validators with negative accumulated commission\n%s", count, msg)), broken
}
}
// CanWithdrawInvariant checks that current commission can be completely withdrawn
func CanWithdrawInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var msg string
var count int
// cache, we don't want to write changes
ctx, _ = ctx.CacheContext()
// iterate over all validators
k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
valAddr := val.GetOperator()
accumCommission := k.GetValidatorAccumulatedCommission(ctx, valAddr)
if accumCommission.IsZero() {
return false
}
if _, err := k.WithdrawValidatorCommission(ctx, valAddr); err != nil {
count++
msg += fmt.Sprintf("\t%v failed to withdraw accumulated commission coins: %v. error: %v\n",
valAddr, accumCommission, err)
}
return false
})
broken := count != 0
return sdk.FormatInvariant(types.ModuleName, "withdraw commission", msg), broken
}
}
// ModuleAccountInvariant checks that the coins held by the distr ModuleAccount
// is consistent with the sum of accumulated commissions
func ModuleAccountInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var accumulatedCommission sdk.SysCoins
k.IterateValidatorAccumulatedCommissions(ctx,
func(_ sdk.ValAddress, commission types.ValidatorAccumulatedCommission) (stop bool) {
accumulatedCommission = accumulatedCommission.Add(commission...)
return false
})
communityPool := k.GetFeePoolCommunityCoins(ctx)
macc := k.GetDistributionAccount(ctx)
broken := !macc.GetCoins().IsEqual(communityPool.Add(accumulatedCommission...))
return sdk.FormatInvariant(types.ModuleName, "ModuleAccount coins",
fmt.Sprintf("\texpected distribution ModuleAccount coins: %s\n"+
"\tacutal distribution ModuleAccount coins: %s\n",
accumulatedCommission, macc.GetCoins())), broken
}
}