TeachMeBitcoin

52 = OP_2, 53 = OP_3, ae = OP_CHECKMULTISIG

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

3. How to Decode a scriptSig Manually

What Is a scriptSig?

The scriptSig is the unlocking script provided in a transaction input. It supplies the data needed to satisfy the locking conditions in the referenced UTXO's scriptPubKey. In the transaction input structure:

[txid: 32 bytes][vout: 4 bytes][scriptSig length: varint][scriptSig bytes][sequence: 4 bytes]

For SegWit inputs, scriptSig may be empty (for native SegWit) or contain only the redeem script push (for P2SH-wrapped SegWit), with the actual unlocking data moved to the witness field.

scriptSig for P2PKH

A standard P2PKH scriptSig contains two data pushes:

<signature length> <DER-encoded signature + sighash flag>
<pubkey length> <compressed public key>

Example hex:

47
3044022...01  (71-byte DER signature + 0x01 sighash flag)
21
02abcdef...   (33-byte compressed public key)

Decode step by step:

47        → Push next 71 bytes
3044...01 → DER-encoded ECDSA signature with SIGHASH_ALL (0x01) appended
21        → Push next 33 bytes
02abcd... → Compressed public key (02 or 03 prefix)

Parsing DER Signatures

DER encoding of an ECDSA signature has this structure:

30        → Sequence tag
<length>  → Total length of what follows
02        → Integer tag (for R)
<r-len>   → Length of R
<R bytes>
02        → Integer tag (for S)
<s-len>   → Length of S
<S bytes>
<sighash> → Appended sighash type (not part of DER, appended by Bitcoin)
python
def parse_der_signature(sig_hex: str) -> dict:
    data = bytes.fromhex(sig_hex)
    assert data[0] == 0x30, "Not a DER sequence"
    total_len = data[1]
    assert data[2] == 0x02, "Expected integer for R"
    r_len = data[3]
    r = data[4:4+r_len].hex()
    s_start = 4 + r_len
    assert data[s_start] == 0x02, "Expected integer for S"
    s_len = data[s_start + 1]
    s = data[s_start + 2: s_start + 2 + s_len].hex()
    sighash = data[-1]
    sighash_names = {1: "SIGHASH_ALL", 2: "SIGHASH_NONE", 3: "SIGHASH_SINGLE", 0x81: "SIGHASH_ALL|ANYONECANPAY"}
    return {"R": r, "S": s, "sighash": sighash_names.get(sighash, f"0x{sighash:02x}")}

scriptSig for P2SH Multisig

A P2SH 2-of-3 multisig scriptSig:

OP_0
<sig1>
<sig2>
<redeem_script>

The OP_0 at the start is a quirk (a famous off-by-one bug in the original implementation that has become consensus). The redeem script is the serialized multisig locking script:

redeem_script = "52" + "21" + pubkey1 + "21" + pubkey2 + "21" + pubkey3 + "53" + "ae"
# 52 = OP_2, 53 = OP_3, ae = OP_CHECKMULTISIG

Reading scriptSig vs Witness

For P2WPKH and P2WSH native SegWit inputs, the scriptSig is empty (00). All unlocking data is in the witness field. For P2SH-wrapped SegWit (P2SH-P2WPKH), the scriptSig contains only a single push of the redeem script:

16        → Push next 22 bytes
0014{20-byte-hash}  → OP_0 + 20-byte hash (the P2WPKH program)

Pro Tip

When debugging scripts, always start with a high-level disassembly before diving into the stack trace. Tools like bitcoin-cli decodescript are your first line of defense in identifying standard script patterns.

☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!