MASTERING-BITCOIN ยท Interactive Practice | Unit 4 ยท Video 5
| Concept | Rule / Value |
|---|---|
| Mainnet HRP | bc (testnet = tb) |
| Separator character | 1 (never appears in data section) |
| Bech32 checksum constant | 1 (XOR at end) โ used for witness v0 |
| Bech32m checksum constant | 0x2bc830a3 โ used for witness v1+ |
| Witness v0 address starts with | bc1q โ uses bech32 |
| Witness v1 (taproot) starts with | bc1p โ uses bech32m |
| Opcode rule | OP_0 = 0x00, but OP_1 = 0x51 (jump!) |
| Detection guarantee | Up to 5 inserted/deleted chars caught with prob > 1 โ 10โปโน |
A Bech32m address has four distinct sections: human-readable prefix (HRP), separator, witness version, witness program, and checksum. Pick an address type below and see how each piece maps to its role.
โ interactive visualization โ coming to this page format soon
Notice: The witness version character (q vs p) tells your wallet which checksum algorithm to run. The HRP and separator are identical across all three types โ only the program length and the version char differ.
When a Bech32m address compiles to a Bitcoin locking script, the witness version becomes an opcode byte. There is a famous discontinuity: OP_0 = 0x00, but OP_1 = 0x51 (not 0x01). Slide below to see how the version, address character, and opcode byte line up.
โ interactive visualization โ coming to this page format soon
Reflect: A wallet that naively writes the witness version as a raw byte (so v1 becomes 0x01) will produce invalid scripts for every address except v0. The 0x00 โ 0x51 jump is one of the most common sources of implementation bugs.
Bech32 had a flaw: the final XOR constant 1 allowed certain insertions of q before a trailing p to preserve the checksum. Bech32m changes exactly one constant to 0x2bc830a3, restoring the full BCH detection guarantee. Try the mutation below.
โ interactive visualization โ coming to this page format soon
Notice: Only the final XOR constant changed. The alphabet, generator polynomial, HRP handling, and everything else stayed the same. This is why a single address scheme can support both checksum flavors โ the witness version character tells the wallet which constant to XOR against.
Question 1
Look at the address bc1q9d3xa5ggxfn5eypfhq7lyk54uetztnw5q47m2p.
Which checksum algorithm does a compliant wallet use to verify it?
โ
Correct! The witness version character (q = v0) selects bech32; p = v1+ would select bech32m.
โ Not quite. The checksum flavor is chosen by the witness version character, not by the HRP or by trial-and-error.
Solution:
The witness version character is the single character immediately after the 1 separator:
bc โ HRP1 โ separatorq โ witness version 0 โ use bech32 checksumVersion 0 addresses (P2WPKH and P2WSH, both starting bc1q) use the original bech32 checksum. Version 1+ addresses (starting bc1p, bc1z, ...) use bech32m. A wallet must NOT try both โ doing so would undo the whole point of having distinct checksums.
Question 2
A wallet developer writes code that compiles a Bech32m address to a Bitcoin locking script. They encode the witness version by writing it directly as a single byte: version 1 becomes byte 0x01, version 2 becomes 0x02, and so on.
What will go wrong?
โ
Correct! The 0x00 โ 0x51 jump catches many implementations off guard.
โ Not quite. Recall the opcode table: OP_0 = 0x00, but OP_1..OP_16 = 0x51..0x60.
Solution:
There is a discontinuity in the opcode byte values:
| Version | Opcode | Byte |
|---|---|---|
| 0 | OP_0 |
0x00 |
| 1 | OP_1 |
0x51 |
| 2 | OP_2 |
0x52 |
| ... | ... | ... |
| 16 | OP_16 |
0x60 |
Version 0 happens to match its byte value (0x00), so naive code "works" for v0. But versions 1 through 16 jump to the range 0x51..0x60. Writing version 1 as the byte 0x01 produces an invalid script โ 0x01 is a data-push opcode, not OP_1.
This is exactly why BIP 350 recommends treating the witness version as opaque and using the official test vectors.
Question 3
True or False: Because BCH codes can mathematically correct small errors, a well-designed Bitcoin wallet should automatically fix single-character typos in a pasted Bech32m address before sending funds.
โ Correct! Detection, not correction. Any auto-fix risks silently redirecting funds.
โ Not quite. Consider what happens if the 'corrected' address happens to be valid but owned by someone else.
Solution: False.
BCH codes can technically correct small errors, but in the Bitcoin context auto-correction is dangerous:
BIP 350 is explicit: the checksum is used strictly to detect errors and reject bad input. The user is responsible for fixing typos themselves. The detection guarantee (prob > 1 โ 10โปโน for up to 5 inserted/deleted characters) is what makes this safe: if there's a typo, the wallet will almost certainly catch and reject it.
Question 4
Bech32m was introduced specifically because Bech32 had a bug where inserting certain characters could preserve checksum validity. What was the fix?
โ Correct! One constant changed. That's the whole difference between bech32 and bech32m.
โ Not quite. The fix was remarkably minimal โ only one numeric constant was modified.
Solution:
Bech32m changes exactly one thing: the final XOR constant applied to the polynomial residue.
10x2bc830a3Everything else โ the character alphabet, the generator polynomial, the HRP handling, the separator โ is unchanged. This tiny swap restores the full BCH detection guarantee: any mutation of up to 5 inserted or deleted characters is caught with probability greater than 1 โ 10โปโน.
The bug in Bech32 was specifically that with constant 1, inserting q characters just before a trailing p could preserve checksum validity. With 0x2bc830a3, that same insertion is always detected.
Solved: 0 / 4