TeachMeBitcoin

How Script Type is Detected by Wallets

From TeachMeBitcoin, the free encyclopedia Reading time: 2 min

11. How Script Type is Detected by Wallets

Overview

Bitcoin wallets and nodes identify script types through pattern matching on the scriptPubKey. This detection determines how to generate addresses, decode UTXOs for balance calculation, construct spending transactions, and display information to users. The detection logic has evolved alongside Bitcoin's script types.

Detection Algorithm

Wallets inspect the scriptPubKey byte sequence, typically using template matching:

def classify_script(script: bytes) -> str:
    n = len(script)

    # P2PKH: OP_DUP OP_HASH160 PUSH20 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG
    if (n == 25 and script[0] == 0x76 and script[1] == 0xa9
            and script[2] == 0x14 and script[23] == 0x88 and script[24] == 0xac):
        return "P2PKH"

    # P2SH: OP_HASH160 PUSH20 <20 bytes> OP_EQUAL
    if (n == 23 and script[0] == 0xa9 and script[1] == 0x14 and script[22] == 0x87):
        return "P2SH"

    # P2WPKH: OP_0 PUSH20 <20 bytes>
    if n == 22 and script[0] == 0x00 and script[1] == 0x14:
        return "P2WPKH"

    # P2WSH: OP_0 PUSH32 <32 bytes>
    if n == 34 and script[0] == 0x00 and script[1] == 0x20:
        return "P2WSH"

    # P2TR: OP_1 PUSH32 <32 bytes>
    if n == 34 and script[0] == 0x51 and script[1] == 0x20:
        return "P2TR"

    # P2PK: PUSH33 <33 bytes> OP_CHECKSIG (compressed)
    if n == 35 and script[0] == 0x21 and script[34] == 0xac:
        return "P2PK (compressed)"

    # P2PK: PUSH65 <65 bytes> OP_CHECKSIG (uncompressed)
    if n == 67 and script[0] == 0x41 and script[66] == 0xac:
        return "P2PK (uncompressed)"

    # OP_RETURN: data carrier
    if script[0] == 0x6a:
        return "OP_RETURN (unspendable)"

    return "UNKNOWN / NON-STANDARD"

Address Encoding by Type

P2PKH   → Base58Check, version byte 0x00 → "1..." addresses
P2SH    → Base58Check, version byte 0x05 → "3..." addresses
P2WPKH  → Bech32, HRP "bc", version 0   → "bc1q..." (20-byte program)
P2WSH   → Bech32, HRP "bc", version 0   → "bc1q..." (32-byte program)
P2TR    → Bech32m, HRP "bc", version 1  → "bc1p..." addresses

Note that P2WPKH and P2WSH both use Bech32 with bc1q prefix; they're distinguished by the length of the decoded data (20 vs 32 bytes). P2TR uses Bech32m (BIP 350), a modified checksum scheme.

Descriptor Wallets

Modern wallets use output descriptors (BIP 380–386) to formally describe which scripts they manage:

pkh(<key>)                    → P2PKH
sh(multi(2,<k1>,<k2>,<k3>))  → P2SH 2-of-3 multisig
wpkh(<key>)                   → P2WPKH
wsh(multi(2,<k1>,<k2>,<k3>)) → P2WSH multisig
sh(wpkh(<key>))               → P2SH-P2WPKH
tr(<key>)                     → P2TR key path only
tr(<key>,{pk(<k2>),multi_a(2,<k1>,<k2>,<k3>)})  → P2TR with script tree
☕ 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!