TYPE 0x0903 · LEGACY FAMILY
P2SH_LEGACY
Legacy P2SH (pay-to-script-hash) wrapped as a typed Ladder Script block. Inner script must be valid Ladder Script conditions — no arbitrary bytes allowed. Closes the P2SH data-embedding surface.
Legacy InvertibleLadder Diagram
Fields
| Field | Data Type | Size | Side | Description |
|---|---|---|---|---|
| hash160 | HASH160 | 20 B | Conditions | RIPEMD160(SHA256(preimage)) commitment to the inner script. Node-computed — the user provides PREIMAGE (serialised Ladder Script conditions) and the node computes this field automatically. Raw hash input is rejected. |
| preimage | PREIMAGE | var | Witness | Serialised Ladder Script conditions (the inner script) — provided by the user. The node derives HASH160 from this data at creation time. |
| [inner witness] | var | var | Witness | Witness fields required to satisfy the inner conditions |
Wire Format Breakdown
Conditions side (committed in the rung leaf):
0x0903
0
1
HASH160 · 20B
= 26 bytes
Witness side (in input witness):
0x0903
0
n
PREIMAGE · varB
[inner witness fields]
= variable
Total (conditions + witness)
variable
Witness size depends on the inner Ladder Script conditions and their witness fields.
Evaluation Logic
1.
Locate HASH160 and PREIMAGE (or SCRIPT_BODY as a fallback). Either missing → ERROR. HASH160 not exactly 20 B → ERROR
2.
Compute CHash160(PREIMAGE) and
memcmp against the committed HASH160. Mismatch → UNSATISFIED
3.
Recurse into EvalInnerConditions(PREIMAGE, depth+1): deserialize the preimage as a
LadderWitness with SerializationContext::CONDITIONS; failure or empty rungs → ERROR
4.
Recursion depth check:
depth > MAX_LEGACY_INNER_DEPTH (=2) → ERROR (bounds nested P2SH/P2WSH/P2TR_SCRIPT chains).
5.
Witness-stack exact-count guard (audit E-020): the outer block's stack-push fields (PUBKEY/SIGNATURE/NUMERIC/SCHEME) are counted; at least one inner rung's blocks must consume exactly that many witness fields under their implicit layouts. Mismatch → ERROR (closes a ~98 KB attacker-data channel by forbidding both extras and shortfalls).
6.
For each inner rung (OR logic, first-satisfied wins): for each block in that rung, build a combined block with the inner conditions + outer witness fields and dispatch EvalBlock. Any block returns non-SATISFIED → try next rung. All blocks SATISFIED → SATISFIED; no rung satisfies → UNSATISFIED
Return Values
| Condition | Result |
|---|---|
| Missing HASH160 or PREIMAGE | ERROR |
| HASH160 wrong size (not 20B) | ERROR |
| HASH160(PREIMAGE) != committed hash | UNSATISFIED |
| PREIMAGE fails to deserialize as Ladder conditions | ERROR |
| Inner conditions evaluation fails | UNSATISFIED |
| Recursion depth > 2 | ERROR |
| Inner conditions satisfied | SATISFIED |
JSON Wire Format
Conditions (committed in the rung leaf)
{
"type": "P2SH_LEGACY",
"inverted": false,
"fields": [
{ "type": "HASH160", "hex": "89abcd...20 bytes" }
]
}Witness (input)
{
"type": "P2SH_LEGACY",
"inverted": false,
"fields": [
{ "type": "PREIMAGE", "hex": "01020304...serialised Ladder conditions" },
{ "type": "PUBKEY", "hex": "02abc1...33 bytes" },
{ "type": "SIGNATURE", "hex": "e5f6a7...64 bytes" }
]
}Notes
- Inner conditions must deserialize as valid Ladder Script — arbitrary bytes are rejected.
- The HASH160 field is node-computed: the user supplies PREIMAGE (the inner script) and the node derives the hash commitment automatically. Submitting a raw HASH160 value directly is rejected.
- Recursion depth limited to 2 to prevent unbounded nesting.
- Prevents the arbitrary data embedding that plagues Bitcoin P2SH.
- The inner script is only revealed at spend time, preserving script privacy until then.
Use Cases
Anti-Spam Script Wrapping
Wraps legacy P2SH but requires inner script to be valid Ladder conditions. No arbitrary data.
Nested Conditions
Inner Ladder conditions can contain any block type, enabling complex spending policies behind a hash commitment.