Transaction Anatomy
How Ladder Script transactions are structured, byte by byte
Transaction version 4 signals a RUNG_TX — the new transaction type — which uses the TX_MLSC wire format on the wire. Nodes without Ladder Script treat it as a standard transaction (soft fork compatibility). The version routes validation to VerifyRungTx instead of VerifyScript.
The funding input is a standard Bitcoin input: P2PKH, P2WPKH, P2TR, or any existing type. The wallet signs it normally. Ladder Script only governs the output side.
Each output is exactly 8 bytes: the value in satoshis. There is no per-output script. Conditions are defined once per transaction via the shared conditions_root (PLC model).
A 32-byte Merkle root shared across all outputs. The root defines a condition tree (PLC model) where each rung's coil carries an output_index declaring which output it governs — the binding is committed in the leaf hash, not a separate wrapper. On deserialisation, each vout[i].scriptPubKey is reconstructed as 0xDF || conditions_root (33 bytes) so all downstream code that touches scriptPubKeys sees a normal-looking SPK. The root is also written to a synthetic UTXO entry at (txid, MLSC_ROOT_VOUT = 0xFFFFFFFF) so per-coin chainstate cost drops to 3 bytes — the root is recovered from the synthetic entry at spend time.
| 0xDF | MLSC SPK prefix (1 byte, reconstructed per output on deserialisation) |
| root | SHA-256 Merkle root of leaves: TaggedHash("LadderLeaf/v1", ...) per leaf, sorted-pair interior with TaggedHash("LadderInternal/v1", ...) (32 bytes) |
The per-input witness stack has 1, 2, or 3 elements — the count discriminates the spend mode:
A single 64-byte Schnorr signature against the conditions_root interpreted as an x-only public key. No conditions revealed. The smallest spend Ladder Script supports — same shape as a Taproot key-path spend.
[LadderWitness, MLSCProof]. LadderWitness = the serialised rung with all blocks and their witness fields (signatures, pubkeys, preimages), plus coil metadata and relay definitions. MLSCProof = the Merkle proof linking the revealed conditions to the conditions_root: rung index, revealed conditions, sibling hashes. Used when the conditions_root was committed without an internal-pubkey tweak.
[LadderWitness, MLSCProof, internal_pubkey (32 bytes)]. The third element is the x-only internal pubkey used to tweak the merkle_root into the conditions_root via TaggedHash("LadderTweak/v1", P || merkle_root). The verifier reconstructs the raw merkle_root from the proof, re-applies the tweak, and asserts equality with the spent output's conditions_root.
After the per-input witness stacks, the TX_MLSC wire format (flag byte 0x02) carries two tx-level fields: qabi_block (empty for non-QABIO txs; the serialised QABIO batch state otherwise) and aggregated_sig (empty for non-QABIO txs; the coordinator's FALCON-512 signature, variable up to 666 bytes, for QABIO batches). These let a single FALCON sig authorise an N-input batch — see QABIO.
Each block starts with a single byte. Values 0x00–0x7F are micro-header slots (128 total), currently populated up to 0x3F = PQ_BATCH; unused slots are reserved for future block types. A populated micro-header encodes the block type via a lookup table; fields use the implicit layout, so no field count or type bytes are needed. Values 0x80/0x81 are escape codes for full 2-byte type + explicit fields.
| 0x00 | SIG |
| 0x01 | MULTISIG |
| 0x03 | CSV |
| 0x0A | CTV |
| 0x3E | OUTPUT_CHECK |
| 0x3F | PQ_BATCH |
| 0x80 | Escape (full type follows) |
| 0x81 | Escape + inverted |
Every field has a declared type with enforced size. No arbitrary data is possible.
| Type | Size | Used for |
|---|---|---|
| PUBKEY | 1–2,048 B | Public keys (Schnorr 33B, PQ up to 2KB) |
| SIGNATURE | 1–50,000 B | Signatures (Schnorr 64B, PQ up to 49KB) |
| HASH256 | 32 B | SHA-256 commitments |
| NUMERIC | 1–4 B | Thresholds, timelocks, counts |
| SCHEME | 1 B | Signature scheme selector |
| PREIMAGE | 32 B | Hash preimage (max 2 per witness) |
Defines what happens when a rung is satisfied. The wire form is exactly 4 bytes; the structural template that goes into each rung's leaf hash commits all four. (Two earlier fields, address_hash and rung_destinations, were dropped pre-v1 as unbound spender channels — wallets that need destination metadata track it locally.)
| coil_type | UNLOCK (0x01) or UNLOCK_TO (0x02). All other values rejected. |
| attestation | INLINE (0x01). All other values rejected. |
| scheme | SCHNORR (0x01), ECDSA (0x02), FALCON-512/1024, Dilithium3, SPHINCS+ |
| output_index | 1 byte — which output of the spending tx this rung governs |
Input: any standard Bitcoin output
Output: 8 bytes (value only) per output on the wire
Conditions root: 32-byte Merkle root (one per tx)
Witness: standard Bitcoin witness for the input
The funding wallet needs the Ladder Script library (or the engine/RPC) to compute the conditions_root from the desired conditions. Each output is just 8 bytes on the wire; the 32-byte shared root is carried once per transaction; per-coin chainstate cost drops to 3 bytes after compression.
Input: references an MLSC output
Output: another MLSC output (8 bytes value only)
Witness: LadderWitness (revealed rung) + MLSCProof (Merkle path)
The node fetches the conditions_root from the synthetic UTXO entry of the creating tx, verifies the Merkle proof, merges conditions with witness, and evaluates the ladder.
1. Node sees tx version 4 (RUNG_TX), input spending an MLSC output (0xDF SPK)
2. Routes to rung::VerifyRungTx
3. Tx-level CheckRungTxLevel runs once per tx: validates output structure (every output MLSC, max 1 DATA_RETURN, dust threshold) and per-tx preimage-field cap, plus cross-input invariants (PQ_BATCH cache, QABIO output binding) when applicable
4. Looks up the synthetic UTXO entry at (creating_txid, MLSC_ROOT_VOUT) to recover the 32-byte conditions_root
5. Deserialises witness stack[0] as LadderWitness
6. Deserialises witness stack[1] as MLSCProof
7. Extracts pubkeys from witness blocks (merkle_pub_key)
8. VerifyMLSCProof rebuilds the leaf array from revealed data + proof hashes, computes the Merkle root, compares to conditions_root
9. MergeConditionsAndWitness combines the revealed rung with the witness fields (signatures, pubkeys, preimages)
10. EvalLadder evaluates relays first (cached), then dispatches each block in the revealed rung to its evaluator (AND within rung, OR across rungs)
11. If the rung returns SATISFIED, the input is valid