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  }