§ 04

§ 04 · Keys & addresses

Bech32m

The checksum that catches your typos

Bech32m 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.

Bech32m explorer

Every part of a prl1 address, and the checksum that catches typos

Valid Pearl mainnet address

Taproot (P2TR) · the checksum matches

Prefix (HRP)
prl mainnet
Witness version
p = 1 Taproot (P2TR)
Checksum
e9xfq3 matches (bech32m)
Program
da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21
Locking script
51 20 da4710964f7852695de2da025290e24af6d8c281de5a0b902b7135fd9fd74d21
See the 5-bit → 8-bit regrouping

The 52 program letters carry 260 bits. Read them as bytes (8 bits each) and you get 32 bytes, with 4 zero bits of padding left over.

11011010010001110001000010010110010011110111100001010010011010010101110111100010110110100000001001010010100100001110001001001010111101101101100011000010100000011101111001011010000010111001000000101011011100010011010111111101100111111101011101001101001000010000

1101101001000111000100001001011001001111011110000101001001101001010111011110001011011010000000100101001010010000111000100100101011110110110110001100001010000001110111100101101000001011100100000010101101110001001101011111110110011111110101110100110100100001

Show every step
  1. Check the letter case

    All lower case, as expected.

  2. Split at the last '1'

    Everything before the last '1' is the human-readable part; the 59 characters after it are the data part.

  3. Read the prefix

    "prl" means Pearl mainnet.

  4. Turn each character into a 5-bit number

    Each character is its position in the alphabet qpzry9x8gf2tvdw0s3jn54khce6mua7l (q=0 … l=31). The first is the witness version, the last 6 are the checksum.

  5. Verify the checksum

    Running the BCH polymod over the expanded prefix and all data gives the bech32m constant 0x2bc830a3, so the checksum is valid.

  6. Read the witness version

    "p" = 1: a Taproot (P2TR) address.

  7. Regroup 5-bit numbers into bytes

    52 characters × 5 bits = 260 bits; the last 4 padding bits are zero, leaving 256 bits = 32 bytes: the tweaked public key Q.

  8. Build the locking script

    OP_1 OP_PUSHBYTES_32 <program>: the scriptPubKey a transaction output uses to pay this address.

Runs in your browser · nothing is sent src/lib/bech32m.ts · BIP-350 + Pearl’s rules

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
  1. Write the program as bits

    da 47 10 96… becomes 11011010 01000111 00010000 10010110 …, 256 bits in all.
  2. 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.
  3. Put the version in front

    Witness version 1 goes first: 1, 27, 9, 3, 17, … (53 values).
  4. 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 give 3 3 3 0 16 18 12.
  5. Run the polymod

    Feed the expanded prefix, the 53 values and six zeros through the BCH polymod (below). Result: 0x199b14b2.
  6. XOR with the bech32m constant

    0x199b14b2 XOR 0x2bc830a3 = 0x32532411.
  7. Split into six checksum values

    0x32532411 as six 5-bit numbers: 25, 5, 6, 9, 0, 17.
  8. Write everything as characters

    prefix prl, separator 1, version p, program mfr3p9j00pfxjh0zmgp99y8zftmd3s5pmedqhyptwy6lm87hf5ss, checksum e9xfq3.

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:

JavaScript
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…HF5SSE9XFQ3 is 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.

The codex

Suggested

Up and down arrows move through the results, Enter opens one, Escape closes. Anywhere on the site, slash or Command K opens this search, and the square brackets [ and ] go to the previous and next page.