---
# System prepended metadata

title: Regenerating the Bitlayer BitVM Transaction Graph for Verification
tags: [BitVM]

---

# Regenerating the Bitlayer BitVM Transaction Graph for Verification

While constructing a BitVM bridge is a complex process for the average user, verifying transactions is crucial for ensuring the safety of their deposits. To empower users with this capability, we will soon provide tools and essential information for regenerating the deposit address, the key scripts and transactions involved in the challenge process and the entire transaction graph. This article will elaborate on how to construct them.

![image](https://hackmd.io/_uploads/rJ-88dYdll.png)
> this figure can be also found [here](https://bitvmbridge.bitlayer.org/mint/cb6c6712-5037-4ac3-a8cb-cf73425b7c56/graph)

## Identities
The transaction graph needs Operator, Attesting committee and Challenger work together to complete the whole process, such as Happytake path (ends with `Happytake` transaction), Unhappytake path (ends with `Unhappytake` transaction) and Disprove path (ends with `Disprove` transaction). Besides, Three different identities are needed for Operator:
- Operator signing key: This key is managed by the Operator and is associated with a Bitcoin wallet. It holds the assets needed to cover protocol fees and collateral. For security, this key should be hosted in a secure service like a hardware security module (HSM) to protect its associated assets.
- Operator temporary key: Each transaction graph instance of each Operator will generate a dedicated key during the pre-signing phase, with an associated Bitcoin wallet that contains no assets. This key is used to sign transactions for reimbursement purposes, such as `HappyTake`, `Challenge`, `PreAssert`, `Assert` and `UnhappyTake` 
- Fund receiver address: A separate address designated to receive reimbursement funds. This design simplifies UTXO management by keeping the spending and receiving wallets distinct, as the Operator does not need to own the private key for the fund receiver address

## Verifying Deposit Address

In the figure above, user issues a `Pegin` transaction, which deposits their Bitcoin into a P2TR (Pay-to-Taproot) deposit address. The deposit address is critically important; if it isn't the address our bridge has defined, their Bitcoin is at risk of being stolen.

![image](https://hackmd.io/_uploads/Hktdv6ZYxg.png)

The deposit address is a P2TR (Pay-to-Taproot) address. It contains two distinct leaf scripts. The first is:
```
OP_PUSHBYTES_32
<attesting_committee_xoly_pubkey>
OP_CHECKSIGVERIFY 
OP_TRUE
```
Here, `attesting_committee_xoly_pubkey` is the aggregated public key of every attester involved in the transaction graph. This script can only be spent if a `HappyTake` or `UnhappyTake` transaction is accompanied by the corresponding aggregated signature.
the second is: 
```
OP_PUSHBYTES_32
<pubkey1>
OP_CHECKSIG
OP_PUSHBYTES_32
<pubkey2>
OP_CHECKSIGADD
// OP_PUSHBYTES_32 <pubkey> OP_CHECKSIGADD repeat N times if the total number of Security Council members is N + 1
<threshold>
OP_GREATERTHANOREQUAL
```
As our bridge is in its early stages, we have implemented an emergency upgrade path. This allows us to move deposited funds to a safe address in the event of an unexpected situation, ensuring the security of user assets.

We only want to enable script-path spending and completely disable key-path spending to prevent any unexpected fund transfers. To achieve this, we use the unspendable internal public key `50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0`,  which is also known as a Nothing Up My Sleeve (NUMS) point. You can find more information about this point [here](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs)

## Verifying Optimistic Path
If the Operator is honest, they can reclaim their deposit and staked funds via the `HappyTake` transaction. This is considered the optimistic path in our design and consists of three transactions: `Pegin`, `Kickoff`, and `HappyTake`.
The `Kickoff` transaction has two distinct outputs:
- P2WPKH Script: The first output uses a standard Pay-to-Witness-Public-Key-Hash (P2WPKH) script, which can only be spent by the Operator's temporary key. Since P2WPKH is a widely used and well-understood Bitcoin script, a detailed introduction is not necessary here.
- The second output is a Taproot address looks like:
![image](https://hackmd.io/_uploads/S1H8S6-Klg.png)


It contains three leaf scripts:
- the first timeout script (bottom left): This script is for the Challenger. If the Operator fails to issue the `PreAssert` transaction within the given assert window, the Challenger can spend this output using a pre-signed signature from the Attesting Committee.
- the second timeout script (upper right): This script is for the Operator. If the Challenger fails to issue the `Challenge` transaction within the given challenge window, the Operator can spend this output using a pre-signed signature from the Attesting Committee. This action completes the optimistic path.
- the check script(upper left): This script is for the Operator. If a Challenger issues a `Challenge` transaction within the given challenge window and the Operator decides to respond, this script allows the Operator to spend the output and issue the `PreAssert` transaction to proceed with the challenge resolution.

## Verifying Challenge Process

For this type of optimistic challenge protocol, the ability to slash malicious operators is just as important as the deposit address. To ensure they can defend their funds, users need to be able to regenerate these key scripts. This capability allows them to reveal proof and stop any malicious operator from initiating a fraudulent reimbursement.

The assert scripts are crucial for revealing the shared values of the bridge proof, which they commit using a Winternitz One-Time Signature. By providing the Winternitz public key and a commitment script template, users can regenerate the P2TR address for `PreAssert`'s assert scripts. The Taptree for the fund address and bitcommitment address looks like this:
![image](https://hackmd.io/_uploads/SJ4qDaZtxl.png)

In practice, committing these shared values requires a considerable number of outputs, specifically around 400 transaction outputs (scripts). For clarity, the diagram above simplifies this by depicting the P2TR addresses of two types of output. 
- Fund address: This address is primarily used to protect the operator's staked funds. The first is an alternative script designed for a time-based challenge, allowing an honest challenger to slash the Operator's funds if the proof is not revealed within the specified time limit. The second is an alternative script designed for a time-based mechanism, allowing operator to receive their dust funds if the proof is not revealed within the specified time limit and it is slashed by a honest challenger. The `PreAssert` transaction contains only one output with fund address.
- Bitcommitment address: This address is primarily used for revealing the shared values. The first script serves as a Bitcoin commitment, enabling the Operator to publicly reveal the shared values. The second is a script that allows the operator to claim their dust funds after the slashing, identical to the second script in the Fund address Taptree. The PreAssert transaction contains approximately 400 outputs with bitcommitment address.

Once the shared values are revealed, a challenger can use them to disprove a malicious operator's claims by issuing a `Disprove` transaction. This is why the second output of the `Assert` transaction is a P2TR address that consolidates all disprove scripts, using the Winternitz public key and the Groth16 verifier.
![image](https://hackmd.io/_uploads/B1l2waZKxx.png)

(The Disprove P2TR address is comprised of all the slash scripts generated by the `get_all_slash_scripts` function. Actually there are a lot of leaf scripts to construct the whole taptree, but  for simplicity we only show two of them)


To generate the Assert and Disprove scripts, we will provide the following functions:

``` Rust
impl ZKPVerifier for RawGroth16Verifier {
    // config: including certain fixed parameters, 
    // such as the information corresponding to the deposited funds
    // winternitz_pubkey: the Winternitz public key corresponding 
    // to the transaction graph
    fn new_with_bcommits(
        config: Self::Config,
        winternitz_pubkey: Vec<u8>,
    ) -> Result<Self, VerifierError>; 
    
    // A list of scripts for all committed shared values
    fn get_assert_script(&self) -> Result<Vec<Vec<BitcoinVerifierScritpt>>, Self::Error>;
    
    /// Return all slash scripts
    fn get_all_slash_scripts(&self) -> Result<Vec<BitcoinVerifierScritpt>, Self::Error>;

}
```
To allow for the regeneration of the Assert and Disprove scripts, all necessary information will be stored in open and accessible storage like S3, making it available for everyone.


## Transaction Graph Validator
Ultimately, our goal is to make every detail of the entire transaction graph easily verifiable by anyone. To achieve this, we will provide all the necessary tools, source code, documentation, and information to regenerate the graph.

The functions to do this will be structured as follows:
```Rust

impl TransactionManager {

    // bridge_verifier: The ZKPVerifier is shown above
    // pegin_input: User's deposit UTXO info
    // lock_inputs: The UTXO that operator has staked
    // operator: Operator's address, who issues the kickoff transaction, must have a sufficient balance to cover transaction fees and the collateral.
    // operator_tmp_address: A temporary address for this transaction's workflow. It is specifically designed to not hold assets, serving as a placeholder or intermediary account.
    // attesting_committee:  The members of the committee for this transaction graph
    // security_council: The members of the security council for this transaction graph
    // threshold: The minimum number of committee members required to perform an emergency upgrade
    // fund_receiver: The beneficiary's address, which is the final destination for the funds in this reimbursement
    // params: The key parameters, such as tx fees and expiration window 
    pub fn new<V: ZKPVerifier>(
        bridge_verifier: &V,
        pegin_input: UTXO,
        lock_inputs: Vec<UTXO>,
        operator: Address,
        operator_tmp_address: Address,
        attesting_committee: XOnlyPublicKey,
        security_council: Vec<XOnlyPublicKey>,
        threshold: usize,
        fund_receiver: Address,
        params: &Params,
    ) -> Result<Self, TransactionBuildError>;
}
```

This transparent design empowers users and developers alike. With access to these well-defined functions and the public data they rely on, anyone can independently verify the integrity of the bridge. This level of transparency is not just a feature; it is the fundamental principle underpinning the security and trustworthiness of our solution. We believe this approach is essential for building a truly reliable and permissionless system.






