Assessment reports>Babylon Chain>Threat Model>Babylon modules

Babylon modules

x/btccheckpoint

This module is used to track checkpoints in Bitcoin.

MsgInsertBTCSpvProof

The MsgInsertBTCSpvProof message is used to submit information regarding Babylon checkpoints that have been submitted to the Bitcoin ledger. The structure of the message is as follows:

type MsgInsertBTCSpvProof struct {
	Submitter string         `protobuf:"bytes,1,opt,name=submitter,proto3" json:"submitter,omitempty"`
	Proofs    []*BTCSpvProof `protobuf:"bytes,2,rep,name=proofs,proto3" json:"proofs,omitempty"`
}

type BTCSpvProof struct {
	// Valid bitcoin transaction containing OP_RETURN opcode.
	BtcTransaction []byte `protobuf:"bytes,1,opt,name=btc_transaction,json=btcTransaction,proto3" json:"btc_transaction,omitempty"`
	// Index of transaction within the block. Index is needed to determine if
	// currently hashed node is left or right.
	BtcTransactionIndex uint32 `protobuf:"varint,2,opt,name=btc_transaction_index,json=btcTransactionIndex,proto3" json:"btc_transaction_index,omitempty"`
	// List of concatenated intermediate merkle tree nodes, without root node and
	// leaf node against which we calculate the proof. Each node has 32 byte
	// length. Example proof can look like: 32_bytes_of_node1 || 32_bytes_of_node2
	// ||  32_bytes_of_node3 so the length of the proof will always be divisible
	// by 32.
	MerkleNodes []byte `protobuf:"bytes,3,opt,name=merkle_nodes,json=merkleNodes,proto3" json:"merkle_nodes,omitempty"`
	// Valid btc header which confirms btc_transaction.
	// Should have exactly 80 bytes
	ConfirmingBtcHeader *github_com_babylonchain_babylon_types.BTCHeaderBytes `protobuf:"bytes,4,opt,name=confirming_btc_header,json=confirmingBtcHeader,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderBytes" json:"confirming_btc_header,omitempty"`
}

Each valid checkpoint submission consists of two instances of the BTCSpvProof structure, which tracks the following:

  1. BtcTransaction — the BTC transaction itself, which must contain an OP_RETURN

  2. BtcTransactionIndex — the index of the transaction in the BTC block

  3. MerkleNodes — a list of Merkle tree nodes used to calculate a Merkle proof that can be used to verify the existence of this transaction in this BTC block

  4. ConfirmingBtcHeader — The block header of the BTC block, used in conjunction with MerkleNodes to confirm that the BtcTransaction exists in this block

The x/btclightclient module is used when handling this message. The light client module keeps track of the state of the Bitcoin ledger and can thus be used to verify that the block denoted by ConfirmingBtcHeader is indeed part of the Bitcoin chain.

The x/checkpointing module is also used when handling this message. The checkpointing module understands the rules that make a checkpoint valid. These rules are not visible to the x/btccheckpoint module, and thus the checkpointing module is consulted to verify that the checkpoint follows the rules correctly. See the x/checkpointing module section for more details.

The MsgInsertBTCSpvProof message handler otherwise only validates the following:

  1. The new submission must not be a duplicate of a previous submission.

  2. Both transactions must contain the OP_RETURN opcode.

  3. All the data in the message must be in the correct format (the transactions, the header, the proofs, etc.).

  4. The Merkle proof provided must be valid and prove that the transactions exist in the specified BTC block.

  5. The previous epoch must have valid submissions that are deeper (i.e., more confirmations) than the currently submitted checkpoint in the current epoch.

EndBlocker

The EndBlocker function has logic that first checks if the light client was updated during the current block execution. An update in this context is the light client's view of the BTC chain either rolling forward (i.e., maintaining the current tip) or rolling backwards (i.e., a new fork with more work has been established).

Each time the light client is updated, the checkpoint module will check the status of all checkpoint submissions and update them accordingly. This will then determine whether the checkpoints are confirmed, finalized, or abandoned.

When checkpoints are finalized, the x/incentives module is used to reward submitters accordingly.

x/btclightclient

This module maintains a chain of Bitcoin blocks and their headers. These are then used to identify the canonical Bitcoin chain, which can be used to verify the inclusion of checkpoints.

MsgInsertHeaders

This MsgInsertHeaders message is used to submit new BTC block headers to Babylon. The structure of the message is as follows:

type MsgInsertHeaders struct {
	Signer  string                                                 `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	Headers []github_com_babylonchain_babylon_types.BTCHeaderBytes `protobuf:"bytes,2,rep,name=headers,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderBytes" json:"headers,omitempty"`
}

Only specific reporters can submit BTC headers, although if there are no reporters set, then anyone can submit BTC headers.

There are certain validations done on the headers:

  1. The headers being submitted must form a chain.

  2. The headers must extend from the current tip of chain (tracked by the light client).

  3. If the headers do not extend from the tip of chain, then they are for a fork. The light-client state is rolled back to this fork only if it conforms to the following rules:

    • The parent hash of the very first header must be known to the light client.

    • The amount of work done for this fork must be larger than the work done at the current tip of chain.

x/btcstaking

This module is responsible for maintaining information about finality providers and BTC delegations under them.

MsgCreateFinalityProvider

The MsgCreateFinalityProvider message is used to create a finality provider. The structure of the message is as follows:

type MsgCreateFinalityProvider struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// description defines the description terms for the finality provider
	Description *types.Description `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
	// commission defines the commission rate of the finality provider
	Commission *cosmossdk_io_math.LegacyDec `protobuf:"bytes,3,opt,name=commission,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"commission,omitempty"`
	// babylon_pk is the Babylon secp256k1 PK of this finality provider
	BabylonPk *secp256k1.PubKey `protobuf:"bytes,4,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"`
	// btc_pk is the Bitcoin secp256k1 PK of this finality provider
	// the PK follows encoding in BIP-340 spec
	BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,5,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"`
	// pop is the proof of possession of babylon_pk and btc_pk
	Pop *ProofOfPossession `protobuf:"bytes,6,opt,name=pop,proto3" json:"pop,omitempty"`
	// master_pub_rand is the master public randomness of the finality provider
	// encoded as a base58 string
	MasterPubRand string `protobuf:"bytes,7,opt,name=master_pub_rand,json=masterPubRand,proto3" json:"master_pub_rand,omitempty"`
}

A few things are validated when adding a finality provider:

  1. There must not be a duplicate finality provider already added.

  2. The BtcPk and BabylonPk are verified to be owned by the Signer. This is done by verifying the Pop (proof of possession), specifically by validating that a message has been signed by both these keys.

  3. The Commission rate must be within a set limit.

  4. The MasterPubRand number must be a valid random number that corresponds to a public Bitcoin hierarchical deterministic extended key. The btcd library is used for this. See BIP-0032.

If all these conditions are met, the finality provider is added.

MsgEditFinalityProvider

The MsgEditFinalityProvider message is used to edit an already added finality provider. Only the commission rate and description can be updated. The structure of the message is as follows:

type MsgEditFinalityProvider struct {
	// NOTE: this signer needs to correspond to babylon_pk of the finality provider
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// btc_pk is the Bitcoin secp256k1 PK of the finality provider to be edited
	BtcPk []byte `protobuf:"bytes,2,opt,name=btc_pk,json=btcPk,proto3" json:"btc_pk,omitempty"`
	// description defines the updated description terms for the finality provider
	Description *types.Description `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
	// commission defines the updated commission rate of the finality provider
	Commission *cosmossdk_io_math.LegacyDec `protobuf:"bytes,4,opt,name=commission,proto3,customtype=cosmossdk.io/math.LegacyDec" json:"commission,omitempty"`
}

The commission rate must be within a set limit.

MsgCreateBTCDelegation

The MsgCreateBTCDelegation message is used to delegate BTC to a finality provider. The structure of this message is massive, so we have removed all developer-added comments from the following excerpt:

type MsgCreateBTCDelegation struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	BabylonPk *secp256k1.PubKey `protobuf:"bytes,2,opt,name=babylon_pk,json=babylonPk,proto3" json:"babylon_pk,omitempty"`
	Pop *ProofOfPossession `protobuf:"bytes,3,opt,name=pop,proto3" json:"pop,omitempty"`
	BtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,4,opt,name=btc_pk,json=btcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"btc_pk,omitempty"`
	FpBtcPkList []github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,5,rep,name=fp_btc_pk_list,json=fpBtcPkList,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk_list,omitempty"`
	StakingTime uint32 `protobuf:"varint,6,opt,name=staking_time,json=stakingTime,proto3" json:"staking_time,omitempty"`
	StakingValue int64 `protobuf:"varint,7,opt,name=staking_value,json=stakingValue,proto3" json:"staking_value,omitempty"`
	StakingTx *types1.TransactionInfo `protobuf:"bytes,8,opt,name=staking_tx,json=stakingTx,proto3" json:"staking_tx,omitempty"`
	SlashingTx *BTCSlashingTx `protobuf:"bytes,9,opt,name=slashing_tx,json=slashingTx,proto3,customtype=BTCSlashingTx" json:"slashing_tx,omitempty"`
	DelegatorSlashingSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,10,opt,name=delegator_slashing_sig,json=delegatorSlashingSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_slashing_sig,omitempty"`
	UnbondingTime uint32 `protobuf:"varint,11,opt,name=unbonding_time,json=unbondingTime,proto3" json:"unbonding_time,omitempty"`
	UnbondingTx []byte `protobuf:"bytes,12,opt,name=unbonding_tx,json=unbondingTx,proto3" json:"unbonding_tx,omitempty"`
	UnbondingValue int64 `protobuf:"varint,13,opt,name=unbonding_value,json=unbondingValue,proto3" json:"unbonding_value,omitempty"`
	UnbondingSlashingTx *BTCSlashingTx `protobuf:"bytes,14,opt,name=unbonding_slashing_tx,json=unbondingSlashingTx,proto3,customtype=BTCSlashingTx" json:"unbonding_slashing_tx,omitempty"`
	DelegatorUnbondingSlashingSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,15,opt,name=delegator_unbonding_slashing_sig,json=delegatorUnbondingSlashingSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"delegator_unbonding_slashing_sig,omitempty"`
}

There are extensive checks here prior to a delegation being successful.

  1. The UnbondingTime must be greater than a preset minimum.

  2. Both BabylonPk and BtcPk are checked for proof of possession against the Pop, similar to MsgCreateFinalityProvider.

  3. Ensure all finality providers in FpBtcPkList are known to Babylon (i.e., have been created before).

  4. Ensure none of the finality providers are currently slashed and that the epoch they were created in is finalized.

  5. The StakingTx must be unique (i.e., there must not be a delegation using this staking TX already).

  6. The StakingValue must match the output of the StakingTx.

  7. The btclightclient module must be aware of the block that the StakingTx claims to be in.

  8. The StakingTx must be k confirmations deep (this is configurable).

  9. The StakingTx time lock must have more than a certain amount of BTC blocks left.

  10. The StakingTx is then verified to actually be included in the BTC block that it claims to be in.

  11. The SlashingTx is verified to be consistent with the StakingTx. These checks boil down to the following:

    • It must have exactly one input, which points to the staking transaction.

    • It is nonreplaceable (see replace-by-fee).

    • The lock time must be zero.

    • It must have exactly two outputs — one that pays to the slashing address and one that pays to an unspendable taproot address, which effectively locks the funds.

    • It must have a transaction fee larger than a preset minimum.

  12. The DelegatorSlashingSig is verified to be a valid signature that signs the StakingTx's slashing path.

  13. The UnbondingTx's first input is verified to be pointing to the StakingTx, similar to the SlashingTx.

  14. The StakingTx's output index must match the UnbondingTx's input's output index. This seems mostly to be a sanity check because the previous check is enough here.

  15. The UnbondingTx is then verified to be consistent with the UnbondingSlashingTx. This is the same verification done on SlashingTx.

  16. The DelegatorUnbondingSlashingSig is verified to be a valid signature that signs the UnbondingTx's slashing path.

  17. The UnbondingTx's output is verified to be greater than zero and greater than a set percentage of the StakingTx's output value.

Once all of these extensive checks pass, the BTC delegation is successfully created and stored.

MsgAddCovenantSigs

The MsgAddCovenantSigs message is used by covenant members to add covenant signatures to BTC delegations. Without enough covenant signatures to reach quorum, the BTC delegations can never become active.

The structure of the message is as follows:

type MsgAddCovenantSigs struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// pk is the BTC public key of the covenant member
	Pk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=pk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"pk,omitempty"`
	// staking_tx_hash is the hash of the staking tx.
	// It uniquely identifies a BTC delegation
	StakingTxHash string `protobuf:"bytes,3,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"`
	// sigs is a list of adaptor signatures of the covenant
	// the order of sigs should respect the order of finality providers
	// of the corresponding delegation
	SlashingTxSigs [][]byte `protobuf:"bytes,4,rep,name=slashing_tx_sigs,json=slashingTxSigs,proto3" json:"slashing_tx_sigs,omitempty"`
	// unbonding_tx_sig is the signature of the covenant on the unbonding tx submitted to babylon
	// the signature follows encoding in BIP-340 spec
	UnbondingTxSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,5,opt,name=unbonding_tx_sig,json=unbondingTxSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"unbonding_tx_sig,omitempty"`
	// slashing_unbonding_tx_sigs is a list of adaptor signatures of the covenant
	// on slashing tx corresponding to unbonding tx submitted to babylon
	// the order of sigs should respect the order of finality providers
	// of the corresponding delegation
	SlashingUnbondingTxSigs [][]byte `protobuf:"bytes,6,rep,name=slashing_unbonding_tx_sigs,json=slashingUnbondingTxSigs,proto3" json:"slashing_unbonding_tx_sigs,omitempty"`
}

The checks performed here are as follows:

  1. The Pk must be a covenant PK. This is stored in the module parameters.

  2. The BTC delegation specified by StakingTxHash must not already be signed by this Pk.

  3. The BTC delegation specified by StakingTxHash must not have already reached quorum.

  4. The BTC delegation specified by StakingTxHash must not have already expired.

  5. The number of SlashingTxSigs must match the number of finality providers on the BTC delegation.

  6. The number of SlashingUnbondingTxSigs must match the number of finality providers on the BTC delegation.

  7. Each signature is verified over the BTC delegation's staking TX's slashing path.

  8. Each signature is verified over the BTC delegation's unbonding TX's slashing path.

Once these checks succeed, the covenant signatures are added to the BTC delegation specified by StakingTxHash.

MsgBTCUndelegate

The MsgBTCUndelegate message is used to trigger unbonding on an active BTC delegation. The structure of the message is as follows:

type MsgBTCUndelegate struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// staking_tx_hash is the hash of the staking tx.
	// It uniquely identifies a BTC delegation
	StakingTxHash string `protobuf:"bytes,2,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"`
	// unbonding_tx_sig is the signature of the staker on the unbonding tx submitted to babylon
	// the signature follows encoding in BIP-340 spec
	UnbondingTxSig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,3,opt,name=unbonding_tx_sig,json=unbondingTxSig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"unbonding_tx_sig,omitempty"`
}

There are only two main checks performed here:

  1. The BTC delegation specified by StakingTxHash must be in the active state.

  2. The UnbondingTxSig must be a valid signature for the unbonding transaction.

If both of these checks pass, then the BTC delegation is unbonded.

MsgSelectiveSlashingEvidence

The MsgSelectiveSlashingEvidence message is used to handle evidence of selective slashing that is triggered by a finality provider. The structure of the message is as follows:

type MsgSelectiveSlashingEvidence struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// staking_tx_hash is the hash of the staking tx.
	// It uniquely identifies a BTC delegation
	StakingTxHash string `protobuf:"bytes,2,opt,name=staking_tx_hash,json=stakingTxHash,proto3" json:"staking_tx_hash,omitempty"`
	// recovered_fp_btc_sk is the BTC SK of the finality provider who
	// launches the selective slashing offence. The SK is recovered by
	// using a covenant adaptor signature and the corresponding Schnorr
	// signature
	RecoveredFpBtcSk []byte `protobuf:"bytes,3,opt,name=recovered_fp_btc_sk,json=recoveredFpBtcSk,proto3" json:"recovered_fp_btc_sk,omitempty"`
}

Selective slashing in this case is when a finality provider becomes adversarial and decides to selectively slash a BTC delegation incorrectly. The covenant can then decide to slash the finality provider using this message.

BeginBlocker

The BeginBlocker function for the x/btcstaking module does two main things:

  • Index the BTC height. For every Babylon block, the BeginBlocker function maps the Babylon block height to the current tip BTC block height.

  • Update voting power distribution. For every Babylon block, there are voting power changes based on new finality providers being created, BTC delegations being added and unbonded, and finality providers being slashed. These voting power changes are recorded and saved in the BeginBlocker function at the start of every block.

x/checkpointing

This module is responsible for maintaining the status of Babylon Bitcoin checkpoints. It handles requests for registering validators with BLS keys and constructing checkpoints out of BLS signatures provided by these validators.

MsgWrappedCreateValidator

The MsgWrappedCreateValidator message is a wrapped version of the Cosmos SDK's x/staking module's MsgCreateValidator message. This wrapped version performs an extra BLS key registration and has the following structure:

type MsgWrappedCreateValidator struct {
	Key                *BlsKey                   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
	MsgCreateValidator *types.MsgCreateValidator `protobuf:"bytes,2,opt,name=msg_create_validator,json=msgCreateValidator,proto3" json:"msg_create_validator,omitempty"`
}

Refer to the x/staking module in the Cosmos SDK to see how that message handler works. The BLS key registration has the following checks:

  1. The same validator must not be able to register multiple BLS public keys.

  2. Two different validators must not be able to register the same BLS public key.

If these two checks pass, then a validator is successfully added using the x/staking message handler. This message handler is triggered by the x/epoching module's EndBlocker later on.

Checkpoint checks

The x/btccheckpoint module delegates a lot of the checkpoint verification to the x/checkpointing module. These checks are done through the VerifyCheckpoint() function.

First, the checkpoint structure is as follows:

// RawCheckpoint wraps the BLS multi sig with metadata
type RawCheckpoint struct {
	// epoch_num defines the epoch number the raw checkpoint is for
	EpochNum uint64 `protobuf:"varint,1,opt,name=epoch_num,json=epochNum,proto3" json:"epoch_num,omitempty"`
	// block_hash defines the 'BlockID.Hash', which is the hash of
	// the block that individual BLS sigs are signed on
	BlockHash *BlockHash `protobuf:"bytes,2,opt,name=block_hash,json=blockHash,proto3,customtype=BlockHash" json:"block_hash,omitempty"`
	// bitmap defines the bitmap that indicates the signers of the BLS multi sig
	Bitmap []byte `protobuf:"bytes,3,opt,name=bitmap,proto3" json:"bitmap,omitempty"`
	// bls_multi_sig defines the multi sig that is aggregated from individual BLS
	// sigs
	BlsMultiSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,4,opt,name=bls_multi_sig,json=blsMultiSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_multi_sig,omitempty"`
}

The checks on this are as follows:

  1. The BlockHash must be a byte array of size 32 and must not be nil.

  2. The BlsMultiSig must be a byte array of size 48 and must not be nil.

  3. If this checkpoint is the same as the locally stored checkpoint and the local checkpoint's status is not waiting for BLS signatures to be accumulated, then the checkpoint is saved as is.

  4. Otherwise, the validator set for the current epoch is fetched, and it ensures that the BlsMultiSig is a valid signature and that enough voting power has been accumulated.

  5. If the above check passes but the BlockHash does not match the current local checkpoint, then the checkpoint is conflicting and rejected. This indicates the existence of a fork.

BeginBlocker

The BeginBlocker function for the x/checkpointing module is really simple — if the current Babylon block is the first block of a new epoch, then it initializes the new epoch's validator set and stores it in the blockchain.

x/epoching

The x/epoching module handles epoched staking. This reduces the frequency of needing to update the validator set (which otherwise updates every block in Cosmos).

This module introduces five messages that are wrapped versions of messages in the Cosmos SDK's x/staking module:

  1. MsgDelegate for delegating coins from a delegator to a validator

  2. MsgBeginRedelegate for redelegating coins from a delegator and source validator to a destination validator

  3. MsgUndelegate for undelegating from a delegator and a validator

  4. MsgCancelUnbondingDelegation for canceling an unbonding delegation of a delegator

The module prevents these messages from being used directly using an AnteHandler. However, this AnteHandler is not sufficient to prevent these messages from being used. See Finding ref for more details.

BeginBlocker

The BeginBlocker function for this module handles a few things. Note that all of these are only done if the current block is the first block of a new epoch.

  1. It increments the epoch counter.

  2. It records the block hash of the last block of the previous epoch.

  3. It initializes the message queue for the new epoch.

  4. It resets the slashed voting power for the new epoch.

  5. It initializes the new validator set.

EndBlocker

The EndBlocker function for this module handles a few things. Note that all of these are only done if the current block is the last block of the current epoch.

  1. It records the block-header time of this last epoch block.

  2. It handles all queued messages. These are the wrapped versions of the staking-related messages discussed above, which are only intended to be handled once per epoch.

  3. It commits any validator-set changes to the store so the BeginBlocker for the next block (i.e., the first block of the new epoch) can take them into account.

x/finality

The x/finality module maintains finality of blocks and handles slashing of finality providers that equivocate (i.e., vote for finality on two blocks at the same height).

MsgAddFinalitySig

The MsgAddFinalitySig message is used to add a finality vote on a specific block. The structure of the message is as follows:

type MsgAddFinalitySig struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// fp_btc_pk is the BTC PK of the finality provider that casts this vote
	FpBtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=fp_btc_pk,json=fpBtcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk,omitempty"`
	// block_height is the height of the voted block
	BlockHeight uint64 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"`
	// pub_rand is the public randomness committed at this height
	PubRand *github_com_babylonchain_babylon_types.SchnorrPubRand `protobuf:"bytes,4,opt,name=pub_rand,json=pubRand,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrPubRand" json:"pub_rand,omitempty"`
	// proof is the proof that the given public randomness is committed under the commitment
	Proof *crypto.Proof `protobuf:"bytes,5,opt,name=proof,proto3" json:"proof,omitempty"`
	// block_app_hash is the AppHash of the voted block
	BlockAppHash []byte `protobuf:"bytes,6,opt,name=block_app_hash,json=blockAppHash,proto3" json:"block_app_hash,omitempty"`
	// finality_sig is the finality signature to this block
	// where finality signature is an EOTS signature, i.e.,
	// the `s` in a Schnorr signature `(r, s)`
	// `r` is the public randomness that is already committed by the finality provider
	FinalitySig *github_com_babylonchain_babylon_types.SchnorrEOTSSig `protobuf:"bytes,7,opt,name=finality_sig,json=finalitySig,proto3,customtype=github.com/babylonchain/babylon/types.SchnorrEOTSSig" json:"finality_sig,omitempty"`
}

In order to add a finality vote, the following checks must pass:

  1. The finality provider denoted by FpBtcPk must exist and have voting power in the current block height.

  2. The finality provider must not have cast a vote for this block height already.

  3. The public randomness committment denoted by PubRand must match the committed public randomness at the current block height. This is verified using the FinalitySig signature.

  4. The finality provider must not be voting for a known forked block. Otherwise, they are slashed.

  5. The finality provider must not have already voted for a fork in the current block height. If they have (and they are now voting for the nonforked block), then they are slashed again.

The fifth step might seem redundant since slashing in Step 4 is supposed to remove the voting power for the finality provider. However, the voting power is actually removed in the BeginBlock function of the next block (which is where the slash event is handled), which is why Step 5 is still necessary.

MsgCommitPubRandList

The MsgCommitPubRandList is used by a finality provider to commit a list of EOTS public randomness. See the MsgAddFinalitySig message to see how these are used.

The structure of the message is as follows:

type MsgCommitPubRandList struct {
	Signer string `protobuf:"bytes,1,opt,name=signer,proto3" json:"signer,omitempty"`
	// fp_btc_pk is the BTC PK of the finality provider that commits the public randomness
	FpBtcPk *github_com_babylonchain_babylon_types.BIP340PubKey `protobuf:"bytes,2,opt,name=fp_btc_pk,json=fpBtcPk,proto3,customtype=github.com/babylonchain/babylon/types.BIP340PubKey" json:"fp_btc_pk,omitempty"`
	// start_height is the start block height of the list of public randomness
	StartHeight uint64 `protobuf:"varint,3,opt,name=start_height,json=startHeight,proto3" json:"start_height,omitempty"`
	// num_pub_rand is the number of public randomness committed
	NumPubRand uint64 `protobuf:"varint,4,opt,name=num_pub_rand,json=numPubRand,proto3" json:"num_pub_rand,omitempty"`
	// commitment is the commitment of these public randomness
	// currently it's the root of the Merkle tree that includes these public randomness
	Commitment []byte `protobuf:"bytes,5,opt,name=commitment,proto3" json:"commitment,omitempty"`
	// sig is the signature on (start_height || num_pub_rand || commitment) signed by
	// SK corresponding to fp_btc_pk. This prevents others to commit public
	// randomness on behalf of fp_btc_pk
	// TODO: another option is to restrict signer to correspond to fp_btc_pk. This restricts
	// the tx submitter to be the holder of fp_btc_pk. Decide this later
	Sig *github_com_babylonchain_babylon_types.BIP340Signature `protobuf:"bytes,6,opt,name=sig,proto3,customtype=github.com/babylonchain/babylon/types.BIP340Signature" json:"sig,omitempty"`
}

The message handler has these checks:

  1. The NumPubRand must be greater than the minimum number of public randomness required. This is a chain parameter.

  2. The finality provider specified by FpBtcPk must be valid and registered.

  3. The Sig must be valid and verified over the Commitment.

  4. If this is the first time this finality provider is committing public randomness, then nothing else needs to be done. Otherwise, the previous public randomness needs to be checked to ensure that the StartHeight is higher than the ending height of the previous randomness.

EndBlocker

The EndBlocker function for the x/finality module essentially checks if there are enough votes for blocks that are to be finalized. If there are, the blocks are set as finalized and stored. If there are not enough votes, the blocks are left unfinalized and the process continues on for each Babylon block.

x/incentive

The x/incentive module handles distributing rewards to the stakeholders. The stakeholders in this context are the following:

  • Submitters — These stakeholders submit Babylon checkpoints to Bitcoin as Bitcoin transactions.

  • Reporters — These stakeholders automatically report Bitcoin headers and information about submitted Babylon checkpoints back to Babylon using Babylon transactions.

  • Finality Providers — These stakeholders participate in voting for finality on blocks.

  • BTC delegators — These stakeholders delegate their BTC to finality providers.

MsgWithdrawReward

The MsgWithdrawReward message can be used by the aforementioned stakeholders to withdraw any accrued reward. The structure of this message is as follows:

// MsgWithdrawReward defines a message for withdrawing reward of a stakeholder.
type MsgWithdrawReward struct {
	// {submitter, reporter, finality_provider, btc_delegation}
	Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
	// address is the address of the stakeholder in bech32 string
	// signer of this msg has to be this address
	Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"`
}

The module keeps track of rewards using gauges. Each stakeholder type gets its own reward gauge, which has the following structure:

// RewardGauge is an object that stores rewards distributed to a BTC staking/timestamping stakeholder
// code adapted from https://github.com/osmosis-labs/osmosis/blob/v18.0.0/proto/osmosis/incentives/gauge.proto
type RewardGauge struct {
	// coins are coins that have been in the gauge
	// Can have multiple coin denoms
	Coins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=coins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"coins"`
	// withdrawn_coins are coins that have been withdrawn by the stakeholder already
	WithdrawnCoins github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=withdrawn_coins,json=withdrawnCoins,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"withdrawn_coins"`
}

Each stakeholder can use the MsgWithdrawReward message to withdraw their share of the rewards (assuming there are any rewards accrued).

BeginBlocker

The BeginBlocker function is short and simple — it takes a portion of coins in the fee-collector account and distributes them out to the Bitcoin stakers through a rewards gauge.

x/monitor

The x/monitor module is responsible for maintaining certain monitoring-related functionality. These include the following:

  1. Monitoring when checkpoints are received at specific block heights

  2. Monitoring the x/btclightclient height at the end of an epoch

  3. Updating the x/btclightclient height at the end of an epoch

The module otherwise does not have any messages, begin blockers, or end blockers.

x/zoneconcierge

The x/zoneconcierge module is responsible for generating BTC time stamps of headers from other POS blockchains. This allows these other POS chains to integrate with Babylon, because forking these other POS blockchains will be just as hard as forking Bitcoin.

The module receives IBC packets containing the POS blockchain's headers. It uses these headers to construct a BTCTimestamp structure, which is shown below:

type BTCTimestamp struct {
	// header is the last CZ header in the finalized Babylon epoch
	Header *IndexedHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`
	// btc_headers is BTC headers between
	// - the block AFTER the common ancestor of BTC tip at epoch `lastFinalizedEpoch-1` and BTC tip at epoch `lastFinalizedEpoch`
	// - BTC tip at epoch `lastFinalizedEpoch`
	// where `lastFinalizedEpoch` is the last finalised epoch in Babylon
	BtcHeaders []*types.BTCHeaderInfo `protobuf:"bytes,2,rep,name=btc_headers,json=btcHeaders,proto3" json:"btc_headers,omitempty"`
	// epoch_info is the metadata of the sealed epoch
	EpochInfo *types1.Epoch `protobuf:"bytes,3,opt,name=epoch_info,json=epochInfo,proto3" json:"epoch_info,omitempty"`
	// raw_checkpoint is the raw checkpoint that seals this epoch
	RawCheckpoint *types2.RawCheckpoint `protobuf:"bytes,4,opt,name=raw_checkpoint,json=rawCheckpoint,proto3" json:"raw_checkpoint,omitempty"`
	// btc_submission_key is position of two BTC txs that include the raw checkpoint of this epoch
	BtcSubmissionKey *types3.SubmissionKey `protobuf:"bytes,5,opt,name=btc_submission_key,json=btcSubmissionKey,proto3" json:"btc_submission_key,omitempty"`
	//
	//Proofs that the header is finalized
	Proof *ProofFinalizedChainInfo `protobuf:"bytes,6,opt,name=proof,proto3" json:"proof,omitempty"`
}

This BTCTimestamp structure is then sent to other Cosmos zones (i.e., other POS chains). This is essentially a way for the other POS chains to checkpoint themselves to Babylon, as Babylon then knows that the block headers from those POS blockchains are finalized with the same degree of security as Bitcoin.

Zellic © 2025Back to top ↑