TECHNICAL_DOC // KEYS / BECH32
BECH32
Bech32 (BIP 173) is the encoding format for SegWit addresses. It uses a
32-character lowercase alphabet and a powerful BCH polynomial checksum that detects any
error of up to 4 characters. Bech32m (BIP 350) is a small variant used
for Taproot, designed to fix a length-extension weakness when used with witness-version-0-1/">witness version 1+.
BECH32_STRUCTURE
ANATOMY OF A SEGWIT ADDRESS
bc 1 q w508d6qejxtdg4y5r3zarvary0c5xw7k v8f3t4
└┬┘ │ └─────────────┬─────────────┘ └──┬─┘
HRP │ data part checksum
│ (witness version (6 chars)
separator + witness program)
"1"
Components:
HRP : human-readable prefix
"bc"=mainnet, "tb"=testnet, "bcrt"=regtest
separator : the character "1" (cannot appear in HRP)
data : witness version (1 char) + program (5-bit groups)
checksum : 6 characters (30-bit BCH code)
Alphabet (32 chars):
qpzry9x8gf2tvdw0s3jn54khce6mua7l
Excluded: 1, b, i, o (visually ambiguous)
All lowercase. Mixed case is invalid.
Encoding Procedure
PROCESS
Bech32 encodes the witness program as 5-bit groups, then appends a 6-character BCH checksum computed over HRP + data.
Step 1: Convert witness program from 8-bit to 5-bit groups
20-byte hash → 32 × 5-bit values (with padding)
Step 2: Prepend witness version
data = [witness_version] + [5-bit groups]
Step 3: Compute BCH checksum
values = expand_hrp(hrp) + data + [0,0,0,0,0,0]
polymod = bech32_polymod(values) XOR const
const = 1 for Bech32 (witness v0)
const = 0x2bc830a3 for Bech32m (witness v1+)
checksum = 6 × 5-bit values from polymod
Step 4: Encode each 5-bit value via alphabet
output = hrp + "1" + base32_chars(data || checksum)
Result error-detection power:
Any 4-character error: detected with certainty
Any random error: ~10⁻⁹ undetected probability
Bech32 vs Bech32m
VARIANTS
Bech32m (BIP 350) was introduced to fix an insertion-error weakness in original Bech32 when used at lengths matching SegWit v1+ addresses. Witness version determines which variant to use.
Witness version 0 → Bech32 (constant: 1)
P2WPKH: bc1q... (42 chars)
P2WSH: bc1q... (62 chars)
Witness version 1+ → Bech32m (constant: 0x2bc830a3)
P2TR: bc1p... (62 chars)
Detecting which to use:
Decoder reads witness version (first data char):
'q' → version 0 → expect Bech32 checksum
'p' → version 1 → expect Bech32m checksum
Why two variants:
Bech32 has a vulnerability where appending or removing
the character "p" at the end can yield another valid
Bech32 string with the same checksum.
Bech32m's different constant eliminates this.
The two are mutually incompatible by design — preventing
old wallets from sending to misinterpreted v1 outputs.
Address Examples
REAL ADDRESSES
All four SegWit address types use Bech32 family encoding. Witness version distinguishes them.
P2WPKH (Bech32, v0):
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
↑ "q" = witness version 0
20-byte program (HASH160 of pubkey)
P2WSH (Bech32, v0):
bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3
↑ "q" = witness version 0
32-byte program (SHA256 of script)
P2TR (Bech32m, v1):
bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297
↑ "p" = witness version 1
32-byte program (x-only output key)
TERMINOLOGY_INDEX
Separator
The character "1" between HRP and data. Excluded from the 32-char alphabet to avoid ambiguity.
BCH Code
Bose-Chaudhuri-Hocquenghem error-correcting code. 30-bit checksum guarantees detection of 4 errors.
Bech32m
BIP 350 variant for witness version 1+. Different constant prevents insertion-error confusion.
Witness Version
First 5-bit value in the data part. 0 → SegWit v0, 1 → Taproot. Must match Bech32/Bech32m variant.
INTERACTIVE — TRY IT YOURSELF
ENCODING / SEGWIT
Bech32 & Bech32m
SegWit addresses use a fundamentally different encoding: Bech32 (BIP 173). It replaces Base58Check's mixed-case alphabet with a purely lowercase 32-character charset, adds a human-readable part (
bc for mainnet), and uses a BCH polynomial checksum that detects up to 4 substitution errors and all burst errors under 6 characters. Bech32m (BIP 350) fixes a length-extension weakness for Taproot (witness version ≥ 1).
ANATOMY OF A BECH32 ADDRESS
ADDRESS DISSECTOR — HOVER EACH PARTclick a type to decode
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8ihpd
THE BECH32 CHARSET
The charset is
qpzry9x8gf2tvdw0s3jn54khce6mua7l — 32 characters, all lowercase, chosen to minimise OCR errors and confusion between similar-looking characters. The character at index i encodes the 5-bit value i.
CHARSET VISUALIZER — INDEX → CHARACTERvalue 0–31
Why lowercase only? QR codes use uppercase alphanumeric mode which is smaller — a full uppercase Bech32 string in a QR code takes ~30% less space. Wallets typically display uppercase but the checksum is case-insensitive (the case is normalised before validation).
LIVE ENCODER — PUBKEY HASH → BECH32
Encoding takes the 20-byte (P2WPKH) or 32-byte (P2WSH/P2TR) witness program, converts it from 8-bit groups to 5-bit groups (convertbits), prepends the witness version, computes the BCH checksum, and concatenates
HRP + "1" + version + data + checksum.
BECH32 ENCODERhex program → address
v0 P2WPKH (20B)
v0 P2WSH (32B)
v1 P2TR / Taproot (32B)
DECODER — DISSECT ANY BECH32 ADDRESS
BECH32 DECODERpaste any bc1... address
CHECKSUM ERROR DETECTION — CLICK TO CORRUPT
Bech32's BCH checksum is strictly stronger than Base58Check's truncated SHA256d. It can guarantee detection of up to 4 substitution errors and all burst errors ≤5 characters long. Click any character to substitute it — the BCH validator will reject it instantly.
BCH CHECKSUM DEMO — CLICK A CHARACTERany substitution fails
✓ CHECKSUM VALID
BECH32 vs BECH32m — THE LENGTH-EXTENSION BUG
Bech32 has a subtle flaw: if you insert or delete a
q immediately before the final p in the checksum, the checksum remains valid. This means Bech32 cannot detect length-extension for witness programs of lengths other than 20 or 32 bytes — dangerous for future witness versions. BIP 350 introduced Bech32m which changes the constant in the BCH polynomial from 1 to 0x2bc830a3, closing this gap.
BECH32 vs BECH32m COMPARISON
| PROPERTY | BECH32 (BIP 173) | BECH32m (BIP 350) |
|---|---|---|
| BCH CONSTANT | 1 | 0x2bc830a3 |
| WITNESS VERSION | v0 only (P2WPKH, P2WSH) | v1+ (Taproot, future) |
| LENGTH EXTENSION | Vulnerable — q-before-p trick | Fixed |
| CHARSET | Identical: qpzry9x8gf2tvdw0s3jn54khce6mua7l | |
| INTRODUCED | 2017 (BIP 173) | 2021 (BIP 350) |
| ADDRESS EXAMPLE | bc1q... (v0) | bc1p... (v1/Taproot) |
Taproot addresses start with bc1p. The "p" comes from witness version 1 being encoded as the value 1, which maps to 'p' in the charset. If you see a wallet generating
bc1q for Taproot, it's using the wrong encoding variant.