github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/distribution/keeper/invariants.go (about)

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     7  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/distribution/types"
     8  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/exported"
     9  )
    10  
    11  // register all distribution invariants
    12  func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) {
    13  	ir.RegisterRoute(types.ModuleName, "nonnegative-outstanding",
    14  		NonNegativeOutstandingInvariant(k))
    15  	ir.RegisterRoute(types.ModuleName, "can-withdraw",
    16  		CanWithdrawInvariant(k))
    17  	ir.RegisterRoute(types.ModuleName, "reference-count",
    18  		ReferenceCountInvariant(k))
    19  	ir.RegisterRoute(types.ModuleName, "module-account",
    20  		ModuleAccountInvariant(k))
    21  }
    22  
    23  // AllInvariants runs all invariants of the distribution module
    24  func AllInvariants(k Keeper) sdk.Invariant {
    25  	return func(ctx sdk.Context) (string, bool) {
    26  		res, stop := CanWithdrawInvariant(k)(ctx)
    27  		if stop {
    28  			return res, stop
    29  		}
    30  		res, stop = NonNegativeOutstandingInvariant(k)(ctx)
    31  		if stop {
    32  			return res, stop
    33  		}
    34  		res, stop = ReferenceCountInvariant(k)(ctx)
    35  		if stop {
    36  			return res, stop
    37  		}
    38  		return ModuleAccountInvariant(k)(ctx)
    39  	}
    40  }
    41  
    42  // NonNegativeOutstandingInvariant checks that outstanding unwithdrawn fees are never negative
    43  func NonNegativeOutstandingInvariant(k Keeper) sdk.Invariant {
    44  	return func(ctx sdk.Context) (string, bool) {
    45  		var msg string
    46  		var count int
    47  		var outstanding sdk.DecCoins
    48  
    49  		k.IterateValidatorOutstandingRewards(ctx, func(addr sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
    50  			outstanding = rewards
    51  			if outstanding.IsAnyNegative() {
    52  				count++
    53  				msg += fmt.Sprintf("\t%v has negative outstanding coins: %v\n", addr, outstanding)
    54  			}
    55  			return false
    56  		})
    57  		broken := count != 0
    58  
    59  		return sdk.FormatInvariant(types.ModuleName, "nonnegative outstanding",
    60  			fmt.Sprintf("found %d validators with negative outstanding rewards\n%s", count, msg)), broken
    61  	}
    62  }
    63  
    64  // CanWithdrawInvariant checks that current rewards can be completely withdrawn
    65  func CanWithdrawInvariant(k Keeper) sdk.Invariant {
    66  	return func(ctx sdk.Context) (string, bool) {
    67  
    68  		// cache, we don't want to write changes
    69  		ctx, _ = ctx.CacheContext()
    70  
    71  		var remaining sdk.DecCoins
    72  
    73  		valDelegationAddrs := make(map[string][]sdk.AccAddress)
    74  		for _, del := range k.stakingKeeper.GetAllSDKDelegations(ctx) {
    75  			valAddr := del.GetValidatorAddr().String()
    76  			valDelegationAddrs[valAddr] = append(valDelegationAddrs[valAddr], del.GetDelegatorAddr())
    77  		}
    78  
    79  		// iterate over all validators
    80  		k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
    81  			_, _ = k.WithdrawValidatorCommission(ctx, val.GetOperator())
    82  
    83  			delegationAddrs, ok := valDelegationAddrs[val.GetOperator().String()]
    84  			if ok {
    85  				for _, delAddr := range delegationAddrs {
    86  					if _, err := k.WithdrawDelegationRewards(ctx, delAddr, val.GetOperator()); err != nil {
    87  						panic(err)
    88  					}
    89  				}
    90  			}
    91  
    92  			remaining = k.GetValidatorOutstandingRewards(ctx, val.GetOperator())
    93  			if len(remaining) > 0 && remaining[0].Amount.IsNegative() {
    94  				return true
    95  			}
    96  
    97  			return false
    98  		})
    99  
   100  		broken := len(remaining) > 0 && remaining[0].Amount.IsNegative()
   101  		return sdk.FormatInvariant(types.ModuleName, "can withdraw",
   102  			fmt.Sprintf("remaining coins: %v\n", remaining)), broken
   103  	}
   104  }
   105  
   106  // ReferenceCountInvariant checks that the number of historical rewards records is correct
   107  func ReferenceCountInvariant(k Keeper) sdk.Invariant {
   108  	return func(ctx sdk.Context) (string, bool) {
   109  
   110  		valCount := uint64(0)
   111  		k.stakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) {
   112  			valCount++
   113  			return false
   114  		})
   115  		dels := k.stakingKeeper.GetAllSDKDelegations(ctx)
   116  		slashCount := uint64(0)
   117  		k.IterateValidatorSlashEvents(ctx,
   118  			func(_ sdk.ValAddress, _ uint64, _ types.ValidatorSlashEvent) (stop bool) {
   119  				slashCount++
   120  				return false
   121  			})
   122  
   123  		// one record per validator (last tracked period), one record per
   124  		// delegation (previous period), one record per slash (previous period)
   125  		expected := valCount + uint64(len(dels)) + slashCount
   126  		count := k.GetValidatorHistoricalReferenceCount(ctx)
   127  		broken := count != expected
   128  
   129  		return sdk.FormatInvariant(types.ModuleName, "reference count",
   130  			fmt.Sprintf("expected historical reference count: %d = %v validators + %v delegations + %v slashes\n"+
   131  				"total validator historical reference count: %d\n",
   132  				expected, valCount, len(dels), slashCount, count)), broken
   133  	}
   134  }
   135  
   136  // ModuleAccountInvariant checks that the coins held by the distr ModuleAccount
   137  // is consistent with the sum of validator outstanding rewards
   138  func ModuleAccountInvariant(k Keeper) sdk.Invariant {
   139  	return func(ctx sdk.Context) (string, bool) {
   140  
   141  		var expectedCoins sdk.DecCoins
   142  		k.IterateValidatorOutstandingRewards(ctx, func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
   143  			expectedCoins = expectedCoins.Add(rewards...)
   144  			return false
   145  		})
   146  
   147  		communityPool := k.GetFeePoolCommunityCoins(ctx)
   148  		expectedInt, _ := expectedCoins.Add(communityPool...).TruncateDecimal()
   149  
   150  		macc := k.GetDistributionAccount(ctx)
   151  
   152  		broken := !macc.GetCoins().IsEqual(expectedInt)
   153  		return sdk.FormatInvariant(types.ModuleName, "ModuleAccount coins",
   154  			fmt.Sprintf("\texpected ModuleAccount coins:     %s\n"+
   155  				"\tdistribution ModuleAccount coins: %s\n",
   156  				expectedInt, macc.GetCoins())), broken
   157  	}
   158  }