Skip to content

Latest commit

 

History

History
83 lines (55 loc) · 4.29 KB

079.md

File metadata and controls

83 lines (55 loc) · 4.29 KB

Scruffy Gingerbread Cormorant

Medium

State Mutation in Incentive RewardGauges Query Endpoint Leads to Network Inconsistency

Summary

State mutation in a gRPC query endpoint will cause network-wide state divergence for Babylon blockchain as anyone can trigger unintended state changes by simply querying the RewardGauges endpoint, leading to inconsistent state.

Root Cause

In https://github.com/sherlock-audit/2024-12-babylon/blob/main/babylon/x/incentive/keeper/grpc_query.go#L30, the query endpoint RewardGauges calls k.sendAllBtcDelegationTypeToRewardsGauge(ctx, sType, address) which triggers a chain of state mutations. This violates the fundamental Cosmos SDK principle that query endpoints must be read-only. The call path leads to IncrementFinalityProviderPeriod() which writes to the KVStore by updating historical rewards and incrementing period counters.

func (k Keeper) RewardGauges(goCtx context.Context, req *types.QueryRewardGaugesRequest) (*types.QueryRewardGaugesResponse, error) {
    // ...
    for _, sType := range types.GetAllStakeholderTypes() {
        if err := k.sendAllBtcDelegationTypeToRewardsGauge(ctx, sType, address); err != nil {
            return nil, status.Error(codes.Internal, err.Error())
        }
        // ...
    }
    // ...
}

The chain of function calls that lead to state mutations: RewardGaugessendAllBtcDelegationTypeToRewardsGaugesendAllBtcRewardsToGaugebtcDelegationModifiedbtcDelegationModifiedWithPreInitDelIncrementFinalityProviderPeriodsetFinalityProviderHistoricalRewards and setFinalityProviderCurrentRewards

The state mutation results in different reward calculations across nodes because:

  1. Inconsistent Period Incrementation: Nodes that process queries increment finality provider periods, while others do not, causing rewards to be distributed across different period structures.

  2. Different CumulativeRewardsPerSat Values: The critical calculation in IncrementFinalityProviderPeriod() that determines rewards is:

    currentRewardsPerSatWithDecimals := fpCurrentRwd.CurrentRewards.MulInt(types.DecimalAccumulatedRewards)
    currentRewardsPerSat = currentRewardsPerSatWithDecimals.QuoInt(fpCurrentRwd.TotalActiveSat)

    This division operation with truncation produces different results when rewards are split across multiple periods versus accumulated in a single period.

  3. Delegation-Specific Discrepancies: Different delegators with varying amounts of staked satoshis will experience different magnitudes of calculation errors since:

    • Each delegator's reward is calculated by: rewardsWithDecimals := differenceWithDecimals.MulInt(btcDelRwdTracker.TotalActiveSat)
    • The initial error in CumulativeRewardsPerSat is multiplied by each delegator's specific stake amount
    • These individual calculations are then truncated again with rewards := rewardsWithDecimals.QuoInt(types.DecimalAccumulatedRewards)

This creates a system where identical delegators may receive different rewards depending on which node processes their transactions, with the discrepancy varying based on delegation amount and network query patterns.

Internal Pre-conditions

NA

External Pre-conditions

NA

Attack Path

  1. Any user queries the RewardGauges endpoint for a specific delegator address
  2. The query triggers sendAllBtcDelegationTypeToRewardsGauge which processes all delegations for that address
  3. For each delegation, IncrementFinalityProviderPeriod is called which:
    • Calculates the current period's rewards per satoshi
    • Adds this to historical cumulative rewards for the finality provider
    • Stores this updated historical record
    • Creates a new period with zero rewards
    • Updates current period number
  4. Node state diverges as some nodes process these queries and others don't
  5. Subsequent reward calculations use inconsistent data across the network

Impact

The protocol may suffer from consensus failures. When withdrawal transactions are processed, validators will calculate different reward amounts based on their divergent states, resulting in different state hashes after transaction execution. This will cause validators to reject each other's blocks, potentially halting the network.

PoC

No response

Mitigation

No response