TeachMeBitcoin

Custom Python P2WSH Auditor

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

Custom Python P2WSH Auditor

In this final guide, we will build a Python script that parses a Native SegWit Multisig (P2WSH) ScriptPubKey. The script will extract the 32-byte SHA256 hash and convert it into a human-readable bc1q address. Note that P2WSH addresses are much longer than P2WPKH addresses.

The P2WSH Address Auditor

# Bech32 constants
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"

def bech32_polymod(values):
    generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
    chk = 1
    for value in values:
        top = chk \u003e\u003e 25
        chk = (chk \u0026 0x1ffffff) \u003c\u003c 5 ^ value
        for i in range(5):
            chk ^= generator[i] if ((top \u003e\u003e i) \u0026 1) else 0
    return chk

def bech32_hrp_expand(hrp):
    return [ord(x) \u003e\u003e 5 for x in hrp] + [0] + [ord(x) \u0026 31 for x in hrp]

def convertbits(data, frombits, tobits, pad=True):
    acc = 0
    bits = 0
    ret = []
    maxv = (1 \u003c\u003c tobits) - 1
    for value in data:
        acc = (acc \u003c\u003c frombits) | value
        bits += frombits
        while bits \u003e= tobits:
            bits -= tobits
            ret.append((acc \u003e\u003e bits) \u0026 maxv)
    if pad:
        if bits:
            ret.append((acc \u003c\u003c (tobits - bits)) \u0026 maxv)
    return ret

def audit_p2wsh(script_hex):
    # 1. Identify the Pattern (00 20 [32-byte hash])
    if not (script_hex.startswith("0020") and len(script_hex) == 68):
        print("[ERROR] Not a standard P2WSH ScriptPubKey!")
        return

    sh_hex = script_hex[4:]
    sh_bytes = list(bytes.fromhex(sh_hex))

    print(f"--- P2WSH Native Auditor ---")
    print(f"[*] ScriptPubKey: {script_hex}")
    print(f"[*] Script Hash:  {sh_hex}")

    # 2. Convert bytes to 5-bit groups for Bech32
    # The '0' at the start is the Witness Version
    data = [0] + convertbits(sh_bytes, 8, 5)

    # 3. Calculate Checksum (BIP 173)
    hrp = "bc"
    combined = bech32_hrp_expand(hrp) + data
    mod = bech32_polymod(combined + [0, 0, 0, 0, 0, 0]) ^ 1
    checksum = [(mod \u003e\u003e 5 * (5 - i)) \u0026 31 for i in range(6)]

    # 4. Final Encoding
    address = hrp + "1" + "".join([CHARSET[d] for d in data + checksum])
    print(f"[*] Derived Address: {address}")
    print(f"[*] Length Check: {len(address)} characters (Expected: 62)")

# --- Simulation ---

# Case: A standard Native Multisig output
# (A real hash would be SHA256 of the multisig script)
p2wsh_script = "0020aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
audit_p2wsh(p2wsh_script)

How to Run the Auditor

  1. Ensure you have Python 3 installed.

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

  3. Run it using python3 p2wsh_auditor.py.

Technical Takeaways

  1. Bit Packing Complexity: Notice that because the hash is 32 bytes (256 bits), it doesn't divide evenly into 5-bit chunks (it requires 52 chunks + 4 bits padding). This is why the address is exactly 62 characters.

  2. Visual Distinction: The long bc1q addresses are the hallmark of multisig security. If you see an address that is roughly 1.5x longer than a standard SegWit address, you are likely looking at a multisig vault.

  3. Optimal Scaling: By using Native P2WSH, the sender only pays for 34 bytes of script, no matter how many keys are actually protecting the funds.

Congratulations! You have completed the P2WSH (Native SegWit Multisig) module. You now understand the highest form of collaborative custody in Bitcoin.

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