github.com/cosmos/cosmos-sdk@v0.50.10/x/group/keeper/invariants.go (about) 1 package keeper 2 3 import ( 4 "fmt" 5 "math" 6 "sort" 7 8 "golang.org/x/exp/maps" 9 10 storetypes "cosmossdk.io/store/types" 11 12 sdk "github.com/cosmos/cosmos-sdk/types" 13 "github.com/cosmos/cosmos-sdk/x/group" 14 "github.com/cosmos/cosmos-sdk/x/group/errors" 15 groupmath "github.com/cosmos/cosmos-sdk/x/group/internal/math" 16 "github.com/cosmos/cosmos-sdk/x/group/internal/orm" 17 ) 18 19 const weightInvariant = "Group-TotalWeight" 20 21 // RegisterInvariants registers all group invariants. 22 func RegisterInvariants(ir sdk.InvariantRegistry, keeper Keeper) { 23 ir.RegisterRoute(group.ModuleName, weightInvariant, GroupTotalWeightInvariant(keeper)) 24 } 25 26 // GroupTotalWeightInvariant checks that group's TotalWeight must be equal to the sum of its members. 27 func GroupTotalWeightInvariant(keeper Keeper) sdk.Invariant { 28 return func(ctx sdk.Context) (string, bool) { 29 msg, broken := GroupTotalWeightInvariantHelper(ctx, keeper.key, keeper.groupTable, keeper.groupMemberByGroupIndex) 30 return sdk.FormatInvariant(group.ModuleName, weightInvariant, msg), broken 31 } 32 } 33 34 func GroupTotalWeightInvariantHelper(ctx sdk.Context, key storetypes.StoreKey, groupTable orm.AutoUInt64Table, groupMemberByGroupIndex orm.Index) (string, bool) { 35 var msg string 36 var broken bool 37 38 groupIt, err := groupTable.PrefixScan(ctx.KVStore(key), 1, math.MaxUint64) 39 if err != nil { 40 msg += fmt.Sprintf("PrefixScan failure on group table\n%v\n", err) 41 return msg, broken 42 } 43 defer groupIt.Close() 44 45 groups := make(map[uint64]group.GroupInfo) 46 for { 47 var groupInfo group.GroupInfo 48 _, err = groupIt.LoadNext(&groupInfo) 49 if errors.ErrORMIteratorDone.Is(err) { 50 break 51 } 52 if err != nil { 53 msg += fmt.Sprintf("LoadNext failure on group table iterator\n%v\n", err) 54 return msg, broken 55 } 56 57 groups[groupInfo.Id] = groupInfo 58 } 59 60 groupByIDs := maps.Keys(groups) 61 sort.Slice(groupByIDs, func(i, j int) bool { 62 return groupByIDs[i] < groupByIDs[j] 63 }) 64 for _, groupID := range groupByIDs { 65 groupInfo := groups[groupID] 66 membersWeight, err := groupmath.NewNonNegativeDecFromString("0") 67 if err != nil { 68 msg += fmt.Sprintf("error while parsing positive dec zero for group member\n%v\n", err) 69 return msg, broken 70 } 71 72 memIt, err := groupMemberByGroupIndex.Get(ctx.KVStore(key), groupInfo.Id) 73 if err != nil { 74 msg += fmt.Sprintf("error while returning group member iterator for group with ID %d\n%v\n", groupInfo.Id, err) 75 return msg, broken 76 } 77 defer memIt.Close() 78 79 for { 80 var groupMember group.GroupMember 81 _, err = memIt.LoadNext(&groupMember) 82 if errors.ErrORMIteratorDone.Is(err) { 83 break 84 } 85 if err != nil { 86 msg += fmt.Sprintf("LoadNext failure on member table iterator\n%v\n", err) 87 return msg, broken 88 } 89 90 curMemWeight, err := groupmath.NewPositiveDecFromString(groupMember.GetMember().GetWeight()) 91 if err != nil { 92 msg += fmt.Sprintf("error while parsing non-nengative decimal for group member %s\n%v\n", groupMember.Member.Address, err) 93 return msg, broken 94 } 95 96 membersWeight, err = groupmath.Add(membersWeight, curMemWeight) 97 if err != nil { 98 msg += fmt.Sprintf("decimal addition error while adding group member voting weight to total voting weight\n%v\n", err) 99 return msg, broken 100 } 101 } 102 103 groupWeight, err := groupmath.NewNonNegativeDecFromString(groupInfo.GetTotalWeight()) 104 if err != nil { 105 msg += fmt.Sprintf("error while parsing non-nengative decimal for group with ID %d\n%v\n", groupInfo.Id, err) 106 return msg, broken 107 } 108 109 if groupWeight.Cmp(membersWeight) != 0 { 110 broken = true 111 msg += fmt.Sprintf("group's TotalWeight must be equal to the sum of its members' weights\ngroup weight: %s\nSum of group members weights: %s\n", groupWeight.String(), membersWeight.String()) 112 break 113 } 114 } 115 116 return msg, broken 117 }