TeachMeBitcoin

Custom Python P2TR Auditor

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

Custom Python P2TR Auditor

In this final guide, we will build a Python script that parses a Taproot (P2TR) ScriptPubKey. The script will extract the 32-byte public key and convert it into a human-readable bc1p address using the Bech32m encoding algorithm (BIP 350).

The P2TR Address Auditor

# Bech32m constants
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
BECH32M_CONST = 0x2be810b1

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_p2tr(script_hex):
    # 1. Identify the Pattern (51 20 [32-byte public key])
    if not (script_hex.startswith("5120") and len(script_hex) == 68):
        print("[ERROR] Not a standard P2TR ScriptPubKey!")
        return

    pubkey_hex = script_hex[4:]
    pubkey_bytes = list(bytes.fromhex(pubkey_hex))

    print(f"--- Taproot Native Auditor ---")
    print(f"[*] ScriptPubKey: {script_hex}")
    print(f"[*] Output Key:   {pubkey_hex}")

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

    # 3. Calculate Checksum (Bech32m - BIP 350)
    hrp = "bc"
    combined = bech32_hrp_expand(hrp) + data
    mod = bech32_polymod(combined + [0, 0, 0, 0, 0, 0]) ^ BECH32M_CONST
    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 standard Taproot output
# Address: bc1p5d7rx9... (example)
p2tr_script = "5120aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
audit_p2tr(p2tr_script)

How to Run the Auditor

  1. Ensure you have Python 3 installed.

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

  3. Run it using python3 p2tr_auditor.py.

Technical Takeaways

  1. Bech32m vs Bech32: The only difference in the code is the BECH32M_CONST. This constant prevents users from accidentally sending funds to a Taproot address using an old Bech32 wallet, which could cause a loss of funds.

  2. No RIPEMD160: Notice we didn't perform any hashing. The script directly uses the 32-byte key. This makes the address derivation simpler but requires the user to know the public key X-coordinate beforehand.

  3. Witness Version 1: The first character after the 1 is always p for mainnet Taproot. This is because 1 (version) maps to p in the Bech32 alphabet.

Congratulations! You have completed the P2TR (Pay-to-Taproot) module. You are now at the cutting edge of Bitcoin technical scripting!

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