-
Notifications
You must be signed in to change notification settings - Fork 45
/
invariants.go
149 lines (125 loc) · 5.06 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
package keeper
import (
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/desmos-labs/desmos/x/staging/posts/types"
)
// RegisterInvariants registers all posts invariants
func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper) {
ir.RegisterRoute(types.ModuleName, "valid-post",
ValidPostsInvariant(keeper))
ir.RegisterRoute(types.ModuleName, "comments-date",
ValidCommentsDateInvariant(keeper))
ir.RegisterRoute(types.ModuleName, "post-reactions",
ValidPostForReactionsInvariant(keeper))
ir.RegisterRoute(types.ModuleName, "poll-user-answers",
ValidPollForUserAnswersInvariant(keeper))
}
// AllInvariants runs all invariants of the module
func AllInvariants(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
if res, stop := ValidPostsInvariant(k)(ctx); stop {
return res, stop
}
if res, stop := ValidCommentsDateInvariant(k)(ctx); stop {
return res, stop
}
if res, stop := ValidPollForUserAnswersInvariant(k)(ctx); stop {
return res, stop
}
if res, stop := ValidPostForReactionsInvariant(k)(ctx); stop {
return res, stop
}
return "Every invariant condition is fulfilled correctly", true
}
}
//____________________________________________________________________________
// formatOutputIDs concatenate the ids given into a unique string
func formatOutputIDs(ids []string) (outputIDs string) {
return strings.Join(ids, "\n")
}
// ValidPostsInvariant checks that the all posts are valid
func ValidPostsInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var invalidPostIDs []string
k.IteratePosts(ctx, func(_ int64, post types.Post) (stop bool) {
if k.ValidatePost(ctx, post) != nil {
invalidPostIDs = append(invalidPostIDs, post.PostID)
}
return false
})
return sdk.FormatInvariant(types.ModuleName, "invalid posts IDs",
fmt.Sprintf("The following posts are invalid:\n %s", formatOutputIDs(invalidPostIDs)),
), invalidPostIDs != nil
}
}
//____________________________________________________________________________
// ValidCommentsDateInvariant checks that comments creation date is always greater than parent creation date
func ValidCommentsDateInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var invalidCommentsIDs []string
k.IteratePosts(ctx, func(_ int64, post types.Post) (stop bool) {
if types.IsValidPostID(post.ParentID) {
parentPost, _ := k.GetPost(ctx, post.ParentID)
if post.Created.Before(parentPost.Created) {
invalidCommentsIDs = append(invalidCommentsIDs, post.PostID)
}
}
return false
})
return sdk.FormatInvariant(types.ModuleName, "comments dates before parent post date",
fmt.Sprintf("The following post IDs referred to posts which are invalid comments "+
"having creation date before parent post creation date:\n %s",
formatOutputIDs(invalidCommentsIDs)),
), invalidCommentsIDs != nil
}
}
//____________________________________________________________________________
// formatOutputReactions concatenate the reactions given into a unique string
func formatOutputReactions(reactions []types.PostReaction) (outputReactions string) {
for _, reaction := range reactions {
outputReactions += reaction.String() + "\n"
}
return outputReactions
}
// ValidPostForReactionsInvariant checks that the post related to the reactions is valid and exists
func ValidPostForReactionsInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var invalidReactions []types.PostReaction
reactions := k.GetAllPostReactions(ctx)
for _, reaction := range reactions {
if !k.DoesPostExist(ctx, reaction.PostID) {
invalidReactions = append(invalidReactions, reaction)
}
}
return sdk.FormatInvariant(types.ModuleName, "posts reactions refers to non existing posts",
fmt.Sprintf("The following reactions refer to posts that do not exist:\n %s",
formatOutputReactions(invalidReactions)),
), invalidReactions != nil
}
}
//____________________________________________________________________________
// formatOutputUserAnswers concatenate the user answers given into a unique string
func formatOutputUserAnswers(userAnswers []types.UserAnswer) (outputAnswers string) {
for _, answer := range userAnswers {
outputAnswers += answer.String() + "\n"
}
return outputAnswers
}
// ValidPollForUserAnswersInvariant check that the user answers are referred to a valid post's poll
func ValidPollForUserAnswersInvariant(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
var invalidUserAnswers []types.UserAnswer
answers := k.GetAllUserAnswers(ctx)
for _, entry := range answers {
if post, found := k.GetPost(ctx, entry.PostID); !found || (found && post.PollData == nil) {
invalidUserAnswers = append(invalidUserAnswers, answers...)
}
}
return sdk.FormatInvariant(types.ModuleName, "poll answers refers to posts without poll",
fmt.Sprintf("The following answers refer to a post that either does not exist or has no poll associated to it:\n %s",
formatOutputUserAnswers(invalidUserAnswers)),
), invalidUserAnswers != nil
}
}