Assessment reports>Nibiru>High findings>Distributing zero coins causes chain halt
Category: Coding Mistakes

Distributing zero coins causes chain halt

High Severity
High Impact
High Likelihood

Description

The oracle module uses an AfterEpochEnd hook, which allocates rewards for validators. This hook is inside the BeginBlocker.

func (h Hooks) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, _ uint64) {
        [...]
		balances := h.bankKeeper.GetAllBalances(ctx, account.GetAddress())
		for _, balance := range balances {
			validatorFees := balance.Amount.ToDec().Mul(params.ValidatorFeeRatio).TruncateInt()
			rest := balance.Amount.Sub(validatorFees)
			totalValidatorFees = append(totalValidatorFees, sdk.NewCoin(balance.Denom, validatorFees))
			totalRest = append(totalRest, sdk.NewCoin(balance.Denom, rest))
		}

        [...]

		err = h.k.AllocateRewards(
			ctx,
			perptypes.FeePoolModuleAccount,
			totalValidatorFees,
			1,
		)
		if err != nil {
			panic(err)
		}

The issue here is that validatorFees could be zero for very small positions. This means AllocateRewards could be called with one or more coins with a zero amount.

Impact

The AllocateRewards function in turn calls bankKeeper.SendCoinsFromModuleToModule, which will fail if any of the coins have a nonpositive amount.

func (coins Coins) Validate() error {
        [...]
		if err := ValidateDenom(coins[0].Denom); err != nil {
			return err
		}
		if !coins[0].IsPositive() {
			return fmt.Errorf("coin %s amount is not positive", coins[0])
		}

Since the AfterEpochEnd hook is inside the BeginBlocker, this will cause the chain to halt.

Recommendations

If the final value of totalValidatorFees is not greater than zero then the call to h.k.AllocateRewards should not be made.

Remediation

This issue has been acknowledged by Nibiru, and a fix was implemented in commit c430556a.

Zellic © 2023Back to top ↑