TeachMeBitcoin

Custom Python P2WPKH Auditor

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

Custom Python P2WPKH Auditor

In this final guide, we will build a Python script that parses a Native SegWit (P2WPKH) ScriptPubKey. The script will extract the 20-byte hash and convert it into a human-readable bc1q address using a simplified Bech32 implementation.

The P2WPKH 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_p2wpkh(script_hex):
    # 1. Identify the Pattern (00 14 [20-byte hash])
    if not (script_hex.startswith("0014") and len(script_hex) == 44):
        print("[ERROR] Not a standard P2WPKH ScriptPubKey!")
        return

    pkh_hex = script_hex[4:]
    pkh_bytes = list(bytes.fromhex(pkh_hex))

    print(f"--- Native SegWit Audit ---")
    print(f"[*] ScriptPubKey: {script_hex}")
    print(f"[*] Key Hash:     {pkh_hex}")

    # 2. Convert bytes to 5-bit groups for Bech32
    data = [0] + convertbits(pkh_bytes, 8, 5) # 0 is the witness version

    # 3. Calculate Checksum
    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}")

# --- Simulation ---

# Case: A real-world bc1q address ScriptPubKey
# address: bc1qypmlegh4p9824mlegvlegvlegvlegvlegvleg
# (simplified test case hash)
p2wpkh_script = "001462e907b15cbf27d5425399ebf6f0fb50ebb88f18"
audit_p2wpkh(p2wpkh_script)

How to Run the Auditor

  1. Ensure you have Python 3 installed.

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

  3. Run it using python3 p2wpkh_auditor.py.

Technical Takeaways

  1. Version Zero: Notice how the first byte of the data (0) corresponds to the 'q' in the address. If this were a Version 1 (Taproot) address, the byte would be 1 and the address would start with bc1p.

  2. Bit Packing: Bech32 is more complex than Base58 because it requires converting 8-bit bytes into 5-bit chunks. This allows the alphabet to stay small and safe.

  3. Modern Footprint: At 22 bytes, P2WPKH is the smallest non-custom locking script in Bitcoin history. It represents the pinnacle of single-signature optimization.

Congratulations! You have completed the P2WPKH (Native SegWit) module. You now understand the modern standard for personal Bitcoin wallets.

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