The BTC light client module is essentially a BTC light client that maintains the canonical header chain of Bitcoin.
Babylon chain needs to know about different events happening on Bitcoin chain. To make it possible in a secure way, Babylon needs to know the current state of the Bitcoin chain i.e., what is the canonical chain of the Bitcoin network.
The Babylon chain needs to learn and validate a number of events that had happened on the Bitcoin chain. Those events are:
New checkpoint event
- Bitcoin Timestamping protocol requires checkpoints on Bitcoin to be reported back to Babylon. To do it securely, each checkpoint must be reported back along with the inclusion proof of transactions which carry this checkpoint.New BTC delegation event
- Bitcoin Staking protocol requires staking transactions to be deep enough in the Bitcoin chain. Thus, the staking transactions also must be accompanied by the inclusion proof.
To properly validate those inclusion proofs, the Babylon chain needs to know the current state of BTC chain i.e., what is current canonical chain recognized by BTC.
Babylon maintains a BTC light client so that it can verify the inclusion of various Bitcoin transactions.
In a high-level overview, the Babylon BTC light client starts from some base BTC header existing on the BTC network and allows extending this header by applying the same rules as a normal BTC node.
The base BTC header must:
- Be deep enough in the BTC chain so that it will never be reverted by the BTC network.
- Be at a height of BTC difficulty adjustment boundary. This is required, to properly validate all future difficulty adjustments.
The base BTC header is defined in the genesis module.
The Babylon BTC light client module stores only BTC headers from the canonical chain, and does not store the headers on the forks. The BTC canonical chain can only be extended by processing valid MsgInsertHeaders messages.
If a better fork is encountered:
- The current chain is rolled back to the parent of the received fork.
- The chain is extend with new headers from the fork.
A better fork is defined as the fork with higher total difficulty, summing the difficulties for each block in the fork.
The BTC light client module maintains the following KV stores.
The parameter storage maintains the BTC light client module's
parameters. The BTC light client module's parameters are represented as a Params
object defined as follows:
// Params defines the parameters for the module.
message Params {
option (gogoproto.equal) = true;
// List of addresses which are allowed to insert headers to btc light client
// if the list is empty, any address can insert headers
repeated string insert_headers_allow_list = 1;
}
In a nutshell, insert_headers_allow_list
makes it possible to set up
restrictions about who is able to update the BTC light client module state.
If insert_headers_allow_list
is not empty, only addresses in the list can send
MsgInsertHeaders
messages.
The Headers storage maintains all headers on the canonical
chain of Bitcoin.
The key is the header height in the BTC chain, and the value is a BTCHeaderInfo
object
which contains the BTC header along with some metadata.
// BTCHeaderInfo is a structure that contains all relevant information about a
// BTC header
// - Full header bytes
// - Header hash for easy retrieval
// - Height of the header in the BTC chain
// - Total work spent on the header. This is the sum of the work corresponding
// to the header Bits field and the total work of the header.
message BTCHeaderInfo {
bytes header = 1
[ (gogoproto.customtype) =
"github.com/babylonchain/babylon/types.BTCHeaderBytes" ];
bytes hash = 2
[ (gogoproto.customtype) =
"github.com/babylonchain/babylon/types.BTCHeaderHashBytes" ];
uint64 height = 3;
bytes work = 4
[ (gogoproto.customtype) = "cosmossdk.io/math.Uint" ];
}
The HashToHeight storage maintains an index in which the key is the BTC header hash, and the value is the BTC header's height.
This index enables efficient lookup of BTC headers by their hash. This is useful in many situations, notably when receiving a potential chain extension which does not point to the current BTC chain tip.
MsgInsertHeaders
is the main message processed by the BTC light client module.
Its purpose is to update the state of the BTC chain as viewed by Babylon chain.
The handler of this message is defined at x/btclightclient/keeper/msg_server.go.
The message contains a list of BTC headers encoded in Bitcoin format.
message MsgInsertHeaders {
option (cosmos.msg.v1.signer) = "signer";
string signer = 1;
repeated bytes headers = 2
[ (gogoproto.customtype) =
"github.com/babylonchain/babylon/types.BTCHeaderBytes" ];
}
Upon receiving a MsgInsertHeaders
message, a Babylon node applies the following
verification rules. This is a subset of the BTC
protocol rules:
- The
headers
list must not be empty. - The headers in the list must be connected by parent-child relationships i.e.
the header at position
i + 1
, must have itsPrevBlock
field set to header'si
hash. - The first header of the list must point to a header already maintained by the BTC light client module.
- Each header must be correctly encoded.
- Each header in the list must have valid proof of work and difficulty.
- Each header in the list must have a
Timestamp
that is greater than the median of last 11 ancestors. - If the first header of the list does not point to the current tip of the chain maintained by the BTC light client, it means that the message contains a fork. For the fork to be valid, the forked chain must be better than the current chain maintained by the BTC light client. The fork is better when its total work is greater than the work of current the chain.
All those rules are the same rules which are applied by BTC nodes when receiving headers from the BTC network.
Processing of the message is atomic, so, if just one header in the list is invalid, the state of the BTC light client module won't be updated.
In case of receiving a valid chain extension, the chain maintained by the BTC light client module will be extended, and the received headers will be saved in the storage.
In case of receiving a valid and better fork, i.e., the first header of the headers
list
does not point to the current BTC chain tip, and the fork's total work is larger than
the current BTC chain's total work, the chain maintained by the BTC light client will
be rolled back to the header that is the fork's header, and it will then be
extended with the headers received in headers
.
The MsgUpdateParams
message is used for updating the module parameters for the
BTC light client module. It can only be executed via a governance proposal.
// MsgUpdateParams defines a message for updating btc light client module parameters.
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";
// authority is the address of the governance account.
// just FYI: cosmos.AddressString marks that this field should use type alias
// for AddressString instead of string, but the functionality is not yet implemented
// in cosmos-proto
string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// params defines the btc light client parameters to update.
//
// NOTE: All parameters must be supplied.
Params params = 2 [(gogoproto.nullable) = false];
}
The BTC light client module exposes a set of hooks to inform other modules about the updates to the maintained Bitcoin light client:
type BTCLightClientHooks interface {
AfterBTCRollBack(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled back
AfterBTCRollForward(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled forward
AfterBTCHeaderInserted(ctx context.Context, headerInfo *BTCHeaderInfo) // Must be called after a header is inserted
}
The BTC light client module exposes a set of events about the updates to the maintained Bitcoin best chain:
// The header included in the event is the block in the history
// of the current mainchain to which we are rolling back to.
// In other words, there is one rollback event emitted per re-org, to the
// greatest common ancestor of the old and the new fork.
message EventBTCRollBack { BTCHeaderInfo header = 1; }
// EventBTCRollForward is emitted on Msg/InsertHeader
// The header included in the event is the one the main chain is extended with.
// In the event of a reorg, each block on the new fork that comes after
// the greatest common ancestor will have a corresponding roll forward event.
message EventBTCRollForward { BTCHeaderInfo header = 1; }
// EventBTCHeaderInserted is emitted on Msg/InsertHeader
// The header included in the event is the one that was added to the
// on chain BTC storage.
message EventBTCHeaderInserted { BTCHeaderInfo header = 1; }