CKB-side Validation on Bitcoin UTXO Commitment

Series on Bitcoin Commitment

This post is my thoughts on implementing a minimal product to validate the states bound to Bitcoin UTXO via commitments.

Bitcoin Commitment

  • Bind states to Bitcoin UTXOs.
  • States constitute DAGs.

CKB-side Validation on Bitcoin UTXO Commitment - Drawing Commitment.excalidraw

The example above uses the first output OP_RETURN to tag a merkle root hash. The leaves of the mekle tree are hashes of states bound to the remaining outputs.

The DAG between states is derived from the bitcoin tx graph. Protocol like RGB does not use tx graph to avoid tx tracing, but it is not a concern to understand the core principle of UXO commitments.

CKB-side Validation on Bitcoin UTXO Commitment - Drawing DAG.excalidraw

DAG Validation

To verify a state, we need the state itself and all its ancestors.

CKB-side Validation on Bitcoin UTXO Commitment - Drawing State History.excalidraw

Then we validate that:

  1. Root states are valid.
  2. State transitions are valid
  3. State bindings and the DAG structure matches the Bitcoin chain.

We don’t know the rules to validate root states and state transitions yet. Let’s add a genesis state. Here I add the hash of the genesis state to all the state nodes

CKB-side Validation on Bitcoin UTXO Commitment - Drawing Genesis State.excalidraw

Map to CKB

We can leverage CKB and its VM to validate the states.

First, we define Rules to be:

  "code_hash": Hash(RISC-V Elf Binary),
  "type": "data2",
  "args": "0x..."

Yes, I use a CKB script structure. This script will be used as the type script.

Each state is mapped to a CKB cell, where

  • lock: any
  • type: the script set as Rules in the genesis state.
  • data: Data part in the state (excluding H(G) ).
  • capacity: any

For Root states. We create a type script group that has no inputs, and has the state mapped CKB cell as the only output.

For non-root states, we create one script group for each transaction. The outputs are CKB cells for states that having sibling relationship in the DAG, and the inputs are their parents.

CKB-side Validation on Bitcoin UTXO Commitment - Drawing CKB Script Groups.excalidraw

Users can run these script groups locally to validate, or complete these script groups into full CKB txs and commit them to the CKB chain. A CKB cell can be used as a proof of a state, if its type script and the data match the state, and it is created in a script group matching the state transitions. If the cell is in the CKB chain, users do not need to verify the history when they trust the CKB chain. Also notice that, anyone can commit the txs to CKB as a shortcut poof, and one state in Bitcoin may have many matched CKB cells. Strict 1 to 1 mapping requires careful protocol design, refer to RGB++ for one of such protocols.


From my perspective, comparing a Merkle root to the approach of using a hash of concatenated outputs for commitments reveals no significant differences under typical circumstances. The distinction becomes notable only in scenarios where a transaction contains a substantial number of outputs, such as 100. In such cases, a Merkle proof offers a more compact representation than the entirety of the outputs for a transaction, thereby reducing the burden on client-side validation.

For genesis transaction and L2toL1 transaction, I think at least one CKB tx input included in commitment is needed, otherwise double-spending is possible.

You are right. It seems we always need all siblings to validate the states, and there’s no scenes we only need one state in a commitment in this PoC.

Yes. Also there’s no limitation on who can create these CKB cells. It can be a problem to L1toL2 either.

L1toL2 is ok, just unlock RGB_lock to locks bind to outputs in commitment, so some outputs have lock script, mean them are L1toL2 outputs, outputs without lockscript are pure L1 outputs.

After rethink this issue, I think Merkle Tree is useless, because user need full transaction to check its validity. Using one leaf is not enough, for example, one person could transfer 1000USD to 20 * 100USD outputs, that’s invalid, but user cannot check it by merkle inclusion proof.

Adding sum of leaves’ amount to internal node may fix it, but for a Turing-complete protocol, instead of payment protocol, this type of tree cannot fix all type of this issue.

Another related post 1-on-1 Binding Between Bitcoin UTXO and CKB SUDT Cell - English / CKB Development & Technical Discussion - Nervos Talk