Module: finality

Description

Babylon's BTC staking protocol introduces an additional finality round for blocks generated in CometBFT. Participants in this round are called finality providers (FPs), and their voting power is derived from delegated staked BTC.

The finality module is responsible for the following functions:

  • Handling extractable--one-time--signature (EOTS) public-randomness commit requests from FPs

  • Processing finality-vote submission requests from FPs

  • Managing the finalization status of blocks

  • Identifying sluggish FPs who vote slowly

  • Maintaining and managing evidence of equivocation (double-signing) by FPs

Messages

MsgResumeFinalityProposal

The MsgResumeFinalityProposal message can only be executed through a governance proposal. It is used to verify FPs' participation in finality voting at a specific block height and to penalize those who have not participated.

When a Babylon node receives MsgResumeFinalityProposal, it checks whether the specified FPs, identified by their public keys, have participated in finality voting at the given block height. FPs that have not participated are jailed, and their jail duration is set according to JailDuration from the finality module, starting from the current block time. If an FP has previously missed blocks, its missed block count is reset.

If these checks pass, the message performs the following steps:

  • Set the voting power of the specified FPs to zero from the given block height up to the current block height.

  • Mark these FPs as jailed in the VotingPowerDistCache of the finality module.

  • Iterate over all blocks from the most recently finalized block up to the current block height. If a block has accumulated more than two-thirds of the total votes, it is marked finalized and its finalization status is updated.

MsgAddFinalitySig

The MsgAddFinalitySig message is submitted by FPs to provide EOTSs for a specific block during the finality voting process.

When a Babylon node receives MsgAddFinalitySig, it checks the following conditions:

  • All required fields (FpBtcPk, PubRand, Proof, FinalitySig, and BlockAppHash) are not nil and have the correct length.

  • The target block height is not before the BTC staking activation block height (retrieved from the finality module).

  • The target block height has been indexed in the finality module’s EndBlocker.

  • The target block belongs to the most recently finalized epoch and is not from an earlier epoch.

  • The target block is not already finalized.

  • The FP (identified by FpBtcPk) is registered, not jailed or slashed, and has nonzero voting power.

  • The FP has not already submitted a signature for the same block height.

If these checks pass, the system

  1. retrieves the public randomness committed via MsgCommitPubRandList that matches the target block height;

  2. verifies the finality EOTS signature using the committed public randomness; and

  3. checks whether the provided BlockAppHash differs from the stored AppHash for the indexed block. If it differs, this is considered a fork vote, and the system generates and stores equivocation evidence for potential future slashing.

If the same message with identical parameters is submitted again, the system checks for any existing equivocation evidence. If it exists, the FP is slashed. Otherwise, if the block is not a fork block, the vote is recorded in the store.

MsgCommitPubRandList

The MsgCommitPubRandList message allows an FP to commit a Merkle tree containing a list of EOTS public-randomness values. This message is typically submitted by the FP's program.

When a Babylon node receives MsgCommitPubRandList, it checks the following conditions:

  • The submitted number of EOTS public-randomness values is at least MinPubRand (defined in the finality module parameters).

  • The FP is correctly registered on the Babylon chain.

  • The submitted public randomness does not duplicate any previous commits.

  • The newly committed public randomness does not overlap with the block range already covered by the FP’s prior commitments.

  • The FP has signed the public randomness with a valid Schnorr signature.

If all conditions are met, the EOTS public randomness is stored on the Babylon chain along with the current epoch number.

MsgUnjailFinalityProvider

The MsgUnjailFinalityProvider message is used by a jailed FP to request unjailing.

When a Babylon node receives MsgUnjailFinalityProvider, it checks the following conditions:

  • The sender is the same as the FP requesting unjailing.

  • The FP is currently jailed.

  • The jail period has ended based on the current block time.

If these checks pass, the FP is unjailed.

MsgUpdateParams

The MsgUpdateParams message updates the parameters of the finality module. This message can only be executed via a governance proposal.

ABCI++ handlers

BeginBlocker

At the beginning of each block, the finality module does the following:

  • Retrieves the voting-power distribution from the previous block

  • Processes events from the prior block that affected FPs (e.g., becoming active, unbonded, expired, jailed, slashed, or unjailed)

  • Invokes the processRewardTracker function from the incentive module to track and allocate rewards

After event processing, the module calculates the cumulative rewards for FPs by

  • dividing the current total rewards by the total staked Satoshi (CurrentRewards / TotalActiveSat) to determine per-Satoshi rewards,

  • storing these reward values as historical data, and

  • distributing delegation rewards proportionally to FPs based on their delegation ratios.

For providers in ACTIVE, UNBONDED, or EXPIRED states, the module updates the total staked Satoshi accordingly. If a provider has been slashed, all pending rewards are settled and sent to the gauge, and the provider is excluded from future rewards. The active FP list is updated according to the current block height, reflecting any changes in delegation and staking power.

To maintain an optimal number of active providers, the module sorts FPs by voting power and keeps only the top MaxActiveFinalityProviders. The voting power of these retained providers is then updated, and events related to activated or deactivated providers are emitted. Finally, the module updates FinalityProviderSigningInfo for each provider, tracking their voting participation and ensuring timely detection of nonparticipation.

EndBlocker

At the end of each block, the finality module does the following:

  • It indexes the current block height along with its AppHash and finalization status to maintain a record of finalized and pending blocks.

  • It checks all nonfinalized blocks to determine if they have reached at least two-thirds of the total voting power; if so, it marks them as finalized in the system.

  • It verifies if any FPs have failed to vote within FinalitySigTimeout. If a provider consistently fails to vote for longer than SignedBlocksWindow, the provider is jailed for a duration defined by JailDuration.

  • It triggers the RewardBTCStaking function from the incentive module, for newly finalized blocks, to distribute fees collected in the fee collector. Providers receive their share of rewards minus their commission, with the remainder given to delegators.

  • It cleans up old data by removing the voting-power--distribution cache for blocks that no longer need referencing, optimizing storage and processing for future blocks.

Test coverage

The x/finality package has low test coverage (11.6%), while the keeper module is well-tested (77.6%), and the types module is mostly untested (1.8%).

ok      github.com/babylonlabs-io/babylon/x/finality    1.472s  coverage: 11.6% of statements
ok      github.com/babylonlabs-io/babylon/x/finality/keeper     47.864s coverage: 77.6% of statements
ok      github.com/babylonlabs-io/babylon/x/finality/types      2.173s  coverage: 1.8% of statements

Attack surface

The finality module exposes several messages, which if incorrectly implemented have the potential of incorrectly affecting the voting power of FPs, allowing FPs to miss blocks and ensuring the liveliness of FPs. Other potential issues that could arise are from incorrect state management in EndBlockers; these could affect voting-power issues when dealing with edge cases.

Zellic © 2025Back to top ↑