TeachMeBitcoin

Custom Python P2SH Auditor

From TeachMeBitcoin, the free encyclopedia Reading time: 2 min

Custom Python P2SH Auditor

In this final guide, we will build a Python script that takes a raw P2SH ScriptPubKey and extracts the 20-byte script hash. We will then implement the Base58Check encoding with the P2SH version byte (0x05) to derive the corresponding "3" address.

The P2SH Address Auditor

import hashlib

# Standard Base58 alphabet
BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

def base58_encode(raw_bytes):
    n = int.from_bytes(raw_bytes, 'big')
    res = ""
    while n \u003e 0:
        n, r = divmod(n, 58)
        res = BASE58_ALPHABET[r] + res
    # Handle leading zeros
    pad = 0
    for b in raw_bytes:
        if b == 0: pad += 1
        else: break
    # Version byte 0x05 encodes to '3' in Base58
    return res

def decode_p2sh(script_hex):
    # 1. Check for standard P2SH pattern
    # a9 (HASH160) 14 (PUSH 20) ... 87 (EQUAL)
    if not (script_hex.startswith("a914") and script_hex.endswith("87")):
        print("[ERROR] Not a standard P2SH script!")
        return

    # 2. Extract the 20-byte script hash
    sh_hex = script_hex[4:-2]
    sh_bytes = bytes.fromhex(sh_hex)

    print(f"--- P2SH Script Audit ---")
    print(f"[*] Raw Script: {script_hex}")
    print(f"[*] Script Hash: {sh_hex}")

    # 3. Base58Check Encoding (Version 0x05)
    # Step A: Prepend Version Byte (0x05 for P2SH Mainnet)
    versioned_sh = b'\\x05' + sh_bytes

    # Step B: Double SHA256 for checksum
    first_sha = hashlib.sha256(versioned_sh).digest()
    checksum = hashlib.sha256(first_sha).digest()[:4]

    # Step C: Append Checksum and Encode
    final_payload = versioned_sh + checksum
    address = base58_encode(final_payload)

    print(f"[*] Derived Address: {address}")
    if address.startswith("3"):
        print("[STATUS] Validation Successful: This is a standard P2SH Address.")

# --- Simulation ---

# Case: A real-world P2SH ScriptPubKey
# address: 3Em92CofpTfXqU2iY73G6hA5o6gG7hA5o6
p2sh_script = "a9148f14848d56b0d918901e18d601d51c3a647d6c6e87"
decode_p2sh(p2sh_script)

How to Run the Auditor

  1. Ensure you have Python 3 installed.

  2. Copy the code into a file named p2sh_auditor.py.

  3. Run it using python3 p2sh_auditor.py.

Technical Takeaways

  1. Version Byte Magic: Notice how using 0x05 instead of 0x00 completely changes the first character of the address to a 3. This is how wallets distinguish between P2PKH and P2SH.

  2. Blind Verification: We were able to derive the address without knowing if the underlying script is a multisig, a timelock, or something else. This is the core privacy feature of P2SH.

  3. Soft Fork Integrity: Because P2SH is just OP_HASH160 and OP_EQUAL, it was backward compatible with nodes that didn't know how to "unwrap" and execute the Redeem Script.

Congratulations! You have completed the P2SH (Pay-to-Script-Hash) module. You now understand the gateway to complex Bitcoin smart contracts.

☕ 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!