§ 04 · Keys & addresses
Bech32m
The checksum that catches your typos
On this page 7 sections
Bech32mBech32m: The format Pearl addresses are written in: a prefix (prl), the separator 1, data in a 32-character alphabet, and a 6-character checksum that catches typos. Same algorithm as Bitcoin’s bc1p addresses. is the format every Pearl address is written in. It turns bytes into short, case-insensitive text and adds a checksum strong enough that a typo almost never gets through.
You don’t need to know any of this to use Pearl. But if you’ve ever wondered how a wallet can tell, instantly and offline, that you mistyped one character out of 63, this page computes it by hand.
Try it
Decode an address, then break it. Change any single character and watch the checksum fail.
What is the alphabet?
Bech32 uses 32 characters, one for each 5-bit value from 0 to 31:
| Value | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Char | q | p | z | r | y | 9 | x | 8 | g | f | 2 | t | v | d | w | 0 |
| Value | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| Char | s | 3 | j | n | 5 | 4 | k | h | c | e | 6 | m | u | a | 7 | l |
The order looks random on purpose. It was chosen so that characters people confuse (in handwriting and in common fonts) differ by as few bits as possible, which the checksum then catches best. The digit 1 and the letters b, i and o are left out entirely.
That’s why the version character of a Taproot address is p: p is 1 in this table. P2MR addresses use z, which is 2.
How is an address encoded?
Here is every step for the address of private key 1. (A public, unsafe key: don’t send coins to it.) Its 32-byte program is the tweaked key Q:
da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21
-
Write the program as bits
da 47 10 96…becomes11011010 01000111 00010000 10010110 …, 256 bits in all. -
Regroup into fives
11011 01001 00011 10001 00001 …gives 27, 9, 3, 17, 1, … 256 bits make 51 full groups plus 1 leftover bit, which is padded with four zeros: 52 values. -
Put the version in front
Witness version 1 goes first: 1, 27, 9, 3, 17, … (53 values). -
Expand the prefix
Each letter of “prl” is split into its top 3 bits and bottom 5 bits, with a 0 between the halves: p = 112, r = 114, l = 108 give3 3 3 0 16 18 12. -
Run the polymod
Feed the expanded prefix, the 53 values and six zeros through the BCH polymod (below). Result:0x199b14b2. -
XOR with the bech32m constant
0x199b14b2XOR0x2bc830a3=0x32532411. -
Split into six checksum values
0x32532411as six 5-bit numbers: 25, 5, 6, 9, 0, 17. -
Write everything as characters
prefixprl, separator1, versionp, programmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5ss, checksume9xfq3.
Result: prl1pmfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5sse9xfq3, 63 characters.
All 59 data values of this address
Version, then 52 program values, then 6 checksum values:
p=1
m=27 f=9 r=3 3=17 p=1 9=5 j=18 0=15 0=15 p=1 f=9 x=6 j=18
h=23 0=15 z=2 m=27 g=8 p=1 9=5 9=5 y=4 8=7 z=2 f=9 t=11
m=27 d=13 3=17 s=16 5=20 p=1 m=27 e=25 d=13 q=0 h=23 y=4 p=1
t=11 w=14 y=4 6=26 l=31 m=27 8=7 7=30 h=23 f=9 5=20 s=16 s=16
e=25 9=5 x=6 f=9 q=0 3=17 The last program value, 16 (s), is 10000: the final bit of the 32 bytes, a 1, followed by four zero bits of padding. A decoder checks that the padding is zero.
How is an address checked?
The receiving wallet runs the same polymod over the expanded prefix and all 59 data characters, checksum included. A valid bech32m string always gives exactly the constant:
| Address | Polymod result | Verdict |
|---|---|---|
prl1pmfr3…hf5sse9xfq3 |
0x2bc830a3 |
valid bech32m |
prl1pmfr3…hf5sse9xfq4 (last character changed) |
0x2bc830a7 |
invalid |
prl1pmfr3…hf5ssvek99n (bech32 checksum) |
0x00000001 |
valid bech32, rejected by Pearl |
The wallet never needs to know what the address “should” be. The checksum is self-checking, which is what lets a wallet refuse a typo before any coins move.
Make a guess
Could a single typo ever turn one valid Pearl address into another valid Pearl address?
Think of your answer first, then check it.
Answer No.
Bech32’s code is guaranteed to detect any error affecting up to 4 characters (BIP-173). Changing one character always breaks the checksum. Only a large, unlucky scramble has any chance of passing, and that chance is below one in a billion.
How does the polymod work?
It’s the remainder of a polynomial division, done with shifts and XORs. Each 5-bit value is a coefficient of a polynomial over a small finite field, and the checksum is the remainder after dividing by a fixed generator polynomial. In code it’s ten lines:
const GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
function polymod(values) {
let chk = 1;
for (const v of values) {
const top = chk >>> 25; // the 5 bits about to fall off
chk = (((chk & 0x1ffffff) << 5) ^ v) >>> 0; // shift in the next value
for (let i = 0; i < 5; i++) if ((top >>> i) & 1) chk = (chk ^ GEN[i]) >>> 0;
}
return chk;
}
// polymod([3,3,3,0,16,18,12, 1,27,9,3,17, …, 25,5,6,9,0,17]) === 0x2bc830a3 The five GEN numbers encode the generator polynomial. They come straight from BIP-173 and are unchanged in Pearl.
Why bech32m and not bech32?
Bech32 (BIP-173) ended its checksum by XORing with 1. That turned out to have a flaw: if an address ended in p, you could insert or delete q characters just before it without breaking the checksum. Bech32m (BIP-350) fixes it by using the constant 0x2bc830a3 instead. Everything else is identical.
Bitcoin kept bech32 for its version-0 addresses (bc1q…) and uses bech32m for version 1 and up. Pearl has no version-0 addresses, so it accepts bech32m only.
What else does bech32m allow?
- All capitals.
PRL1PMFR3P9J00…HF5SSE9XFQ3is valid; QR codes encode capitals more compactly. Mixed case is invalid. - Up to 90 characters. The generic format allows long strings, but every Pearl address is exactly 63 (mainnet) or 64 (test networks) characters, because every Pearl program is 32 bytes.
- No error correction in practice. The code could, in theory, locate a small error. Wallets deliberately don’t “fix” addresses: a correction could produce a different valid address. They reject and ask you to copy it again.