Transaction Anatomy

How Ladder Script transactions are structured, byte by byte

1. Funding Transaction
A standard Bitcoin input creates a Ladder Script output. This is how coins enter the system.
Version
4 bytes
04000000
Marker
2 B
0002
Input (P2PKH)
~148 bytes
txid + vout + scriptSig + sequence
Output
8 bytes
value only
Conditions Root
32 bytes
root only on the wire
Locktime
4 bytes
00000000
Version
4 bytes, little-endian

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.

Input
~148 bytes for P2PKH

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.

Output (value only)
8 bytes per output

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).

Conditions Root
32 bytes on the wire, one per transaction

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.

0xDFMLSC SPK prefix (1 byte, reconstructed per output on deserialisation)
rootSHA-256 Merkle root of leaves: TaggedHash("LadderLeaf/v1", ...) per leaf, sorted-pair interior with TaggedHash("LadderInternal/v1", ...) (32 bytes)
2. Spending Transaction
An MLSC input spends a Ladder Script output. The witness carries the conditions and proof.
Version
4 B
04000000
Marker
2 B
0002
Input
41 B
txid + vout + empty scriptSig + seq
Output
8 B
value
Conditions Root
32 B
root only on the wire
Witness
variable
ladder witness + MLSC proof
Locktime
4 B

The per-input witness stack has 1, 2, or 3 elements — the count discriminates the spend mode:

1 element — key-path spend

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.

2 elements — script-path, no tweak

[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.

3 elements — script-path with 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.

3. Inside the Ladder Witness
The witness encodes exactly one rung with all its blocks and fields.
Rungs
1 B
01
Blocks
1 B
02
Block 1
header + fields
Block 2
header + fields
Coil
4 B: type + attestation + scheme + output_index
Micro-header encoding

Each block starts with a single byte. Values 0x000x7F 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.

0x00SIG
0x01MULTISIG
0x03CSV
0x0ACTV
0x3EOUTPUT_CHECK
0x3FPQ_BATCH
0x80Escape (full type follows)
0x81Escape + inverted
Typed fields

Every field has a declared type with enforced size. No arbitrary data is possible.

TypeSizeUsed for
PUBKEY1–2,048 BPublic keys (Schnorr 33B, PQ up to 2KB)
SIGNATURE1–50,000 BSignatures (Schnorr 64B, PQ up to 49KB)
HASH25632 BSHA-256 commitments
NUMERIC1–4 BThresholds, timelocks, counts
SCHEME1 BSignature scheme selector
PREIMAGE32 BHash preimage (max 2 per witness)
Coil — 4 bytes total

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_typeUNLOCK (0x01) or UNLOCK_TO (0x02). All other values rejected.
attestationINLINE (0x01). All other values rejected.
schemeSCHNORR (0x01), ECDSA (0x02), FALCON-512/1024, Dilithium3, SPHINCS+
output_index1 byte — which output of the spending tx this rung governs
4. Funding vs Spending
Funding (coins enter)

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.

Spending (coins leave)

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.

5. Verification flow

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