Block QTL, decoded

How a time-locked spend is sealed, step by step, using the frozen WO-09 example. Every value below comes from e46/golden/wo09-golden.json and was re-checked independently.

The idea in one line: you first publish a sealed fingerprint of your spend (the commitment), wait at least 6 blocks, then reveal the spend. Nobody can see your key or signature while you wait, so there’s nothing to copy or race.

The golden file holds several hashes, but they are not all roots

  serialized transaction (8,020 bytes)
  ├── base (96 bytes) ─────────────► txid ─────────────┐
  └── witnesses (7,924 bytes) ─────► SHAKE256(witnesses)├──► commitment C ──► event leaf ──► event_root ──► header
                       └── salt (last 32 bytes) ────────┘      (not a root)     0x03‖C‖nonce   (a root)

1. Split the transaction

The serialized transaction is 8,020 bytes: 96 bytes of base (inputs, outputs, lock_time) plus 7,924 bytes of witnesses (public key, signature and salt).

2. Hash the base: the txid

txid  0ea950747e07d178e0dd3712d04daac19506876f353b66164ad2e3d64c8bdcfc

It names the transaction’s inputs and outputs. It leaves the witnesses out, so it is not the full-transaction hash (that’s the wtxid).

3. Hash the witnesses on their own

SHAKE256(witnesses)  00315abd9fcb5205d67c11ad7d74c8efc308d1cf2d0c46355c87d97d954ac2d2

One 32-byte summary of the public key, the signature and the salt.

4. Build the commitment C

Label + txid + witness hash + salt, hashed together with SHAKE256:

C = SHAKE256("E46-commit\0" ‖ txid ‖ SHAKE256(witnesses) ‖ salt)
C   7fc741bb12d0e8a72560ff4695a785f63a352d892f03b305622fec792e2d3706

C binds the payment and its signatures together. C is not a Merkle root; it’s a single sealed fingerprint.

5. Make the commitment event

The event leaf is 0x03 ‖ C ‖ nonce. The golden example uses nonce 0 only to freeze the encoding; it doesn’t claim that nonce meets the real work threshold (Z_commit = 18 leading zero bits, about a second of laptop work).

6. Into the block’s event_root

Once a block has its full, sorted list of events (Rads and Block QTL commitments), their leaves are hashed into the event Merkle root, and that root goes into the block header. The golden case freezes one event’s encoding, not a whole block’s event_root.

Three different structures, not one

Structure

What it fingerprints

Where it lives

Block QTL commitment C

one spend: its txid, witnesses and salt

inside an event leaf

Transaction Merkle root (merkle_root)

the txids of every transaction in the block

the block header

Event Merkle root (event_root)

every Rad and commitment event in the block

the block header

And a fourth, separate value: the coinbase’s witness commitment, which fingerprints the block’s wtxids (decision #23).

The rules around it

  • Wait: the reveal is valid only if C is at least 6 and at most 2,016 blocks deep (about 15 minutes to 3.5 days). Tested at the edges: 5 rejected, 6 accepted, 2,016 accepted, 2,017 rejected.

  • One salt per reveal (decision #24): a reveal that spends several Block QTL coins uses one commitment, and every one of those inputs carries the same 32-byte salt.

  • Fresh salt every time (wallet rule): wallets, including light and Android wallets, draw a new random 32-byte salt from the operating system for each unlock. Nodes keep no list of used salts.

  • Spam limit: at most 1,000 commitment events per block, and each needs its small proof of work.