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 Invertible
P2SH_LEGACY HASH160 + INNER EVAL Inner script must be valid Ladder Script conditions
FieldData TypeSizeSideDescription
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

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.

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
ConditionResult
Missing HASH160 or PREIMAGEERROR
HASH160 wrong size (not 20B)ERROR
HASH160(PREIMAGE) != committed hashUNSATISFIED
PREIMAGE fails to deserialize as Ladder conditionsERROR
Inner conditions evaluation failsUNSATISFIED
Recursion depth > 2ERROR
Inner conditions satisfiedSATISFIED
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" }
  ]
}
  • 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.
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.