52 = OP_2, 53 = OP_3, ae = OP_CHECKMULTISIG
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.
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: