MASTERING-BITCOIN ยท Interactive Practice | Unit 4 ยท Video 5

Why Bitcoin Addresses Start with bc1: The Design of Bech32m

IKey Reference

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โปโน

IIVisualization 1: Address Anatomy Explorer

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.

IIIVisualization 2: The Witness Version โ†’ Opcode Jump

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.

IVVisualization 3: The One-Constant Fix

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.

VQuiz Questions

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.

Show solution

Solution:

The witness version character is the single character immediately after the 1 separator:

  • bc โ€” HRP
  • 1 โ€” separator
  • q โ€” witness version 0 โ†’ use bech32 checksum
  • rest โ€” program + 6-char checksum

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

Show solution

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.

Show solution

Solution: False.

BCH codes can technically correct small errors, but in the Bitcoin context auto-correction is dangerous:

  • A single-character typo might accidentally map to a different, valid address belonging to another user.
  • Bitcoin transactions are irreversible โ€” funds sent to the "corrected" address cannot be recovered.

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.

Show solution

Solution:

Bech32m changes exactly one thing: the final XOR constant applied to the polynomial residue.

  • Bech32: final XOR constant = 1
  • Bech32m: final XOR constant = 0x2bc830a3

Everything 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