github.com/cosmos/cosmos-sdk@v0.50.10/x/bank/keeper/invariants.go (about)

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/cosmos/cosmos-sdk/types"
     7  	"github.com/cosmos/cosmos-sdk/types/query"
     8  	"github.com/cosmos/cosmos-sdk/x/bank/types"
     9  )
    10  
    11  // RegisterInvariants registers the bank module invariants
    12  func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
    13  	ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding", NonnegativeBalanceInvariant(k))
    14  	ir.RegisterRoute(types.ModuleName, "total-supply", TotalSupply(k))
    15  }
    16  
    17  // AllInvariants runs all invariants of the X/bank module.
    18  func AllInvariants(k Keeper) sdk.Invariant {
    19  	return func(ctx sdk.Context) (string, bool) {
    20  		res, stop := NonnegativeBalanceInvariant(k)(ctx)
    21  		if stop {
    22  			return res, stop
    23  		}
    24  		return TotalSupply(k)(ctx)
    25  	}
    26  }
    27  
    28  // NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances
    29  func NonnegativeBalanceInvariant(k ViewKeeper) sdk.Invariant {
    30  	return func(ctx sdk.Context) (string, bool) {
    31  		var (
    32  			msg   string
    33  			count int
    34  		)
    35  
    36  		k.IterateAllBalances(ctx, func(addr sdk.AccAddress, balance sdk.Coin) bool {
    37  			if balance.IsNegative() {
    38  				count++
    39  				msg += fmt.Sprintf("\t%s has a negative balance of %s\n", addr, balance)
    40  			}
    41  
    42  			return false
    43  		})
    44  
    45  		broken := count != 0
    46  
    47  		return sdk.FormatInvariant(
    48  			types.ModuleName, "nonnegative-outstanding",
    49  			fmt.Sprintf("amount of negative balances found %d\n%s", count, msg),
    50  		), broken
    51  	}
    52  }
    53  
    54  // TotalSupply checks that the total supply reflects all the coins held in accounts
    55  func TotalSupply(k Keeper) sdk.Invariant {
    56  	return func(ctx sdk.Context) (string, bool) {
    57  		expectedTotal := sdk.Coins{}
    58  		supply, _, err := k.GetPaginatedTotalSupply(ctx, &query.PageRequest{Limit: query.PaginationMaxLimit})
    59  		if err != nil {
    60  			return sdk.FormatInvariant(types.ModuleName, "query supply",
    61  				fmt.Sprintf("error querying total supply %v", err)), false
    62  		}
    63  
    64  		k.IterateAllBalances(ctx, func(_ sdk.AccAddress, balance sdk.Coin) bool {
    65  			expectedTotal = expectedTotal.Add(balance)
    66  			return false
    67  		})
    68  
    69  		broken := !expectedTotal.Equal(supply)
    70  
    71  		return sdk.FormatInvariant(types.ModuleName, "total supply",
    72  			fmt.Sprintf(
    73  				"\tsum of accounts coins: %v\n"+
    74  					"\tsupply.Total:          %v\n",
    75  				expectedTotal, supply)), broken
    76  	}
    77  }