Bech32 / Bech32m 解码与校验
把 bc1q… 或 bc1p… 地址拆成可读部分、witness 版本与 witness 程序,查看构成它的 5 位字,确认匹配的是哪个校验和常量 —— 字符串非法时给出一句人话说明原因。
请在上方输入内容后点击「转换」。不知道填什么?点「填入示例」即可载入占位符里的演示数据。
本页做什么
bech32 地址就是一串带 BCH 校验和的 base32 字符串,解码后只得到两个事实:witness 版本与
witness 程序。这两个事实就是地址的全部 —— scriptPubKey 字面上就是
OP_n 加上那个程序:
把任意 bc1…、tb1…、v0 或 v1 地址粘进来即可。本页会展示解码的每一个阶段,告诉你
匹配的是哪个校验和常量(因此适用于 BIP173 还是 BIP350),遇到非法输入则原样打印解码器给出的原因,而不是
自己猜。
1. bech32 字符串的构成
| 部分 | 规则 | 在 bc1qy86hkm0telzmvuvzm76279jp7z5p3yqjnaerk6 中 |
|---|---|---|
| 可读部分(hrp) | 1–83 个 US-ASCII 字符,取值 33–126 | bc(主网)/ tb(测试网) |
| 分隔符 | 恒为字符 1;若 hrp 里本来就有 1,则以最后一个为界 | 下标 2,一个字符 |
| 数据部分 | 至少 6 个字符,取自 32 字符集;最后 6 个是校验和 | qy86hkm0telzmvuvzm76279jp7z5p3yqjnaerk6(39 个字符) |
| 校验和 | 数据部分最后 6 个字符 —— 30 位,不承载任何载荷信息 | naerk6 |
| 总长度 | 最多 90 个字符 | 42 个字符 |
分隔符的作用是让 hrp 不必转义就能写出,而它选数字 1 而不是某个符号,是因为符号在复制粘贴时
很容易出问题。用 1 是安全的,因为它被刻意排除在数据字符集之外,hrp 到哪里结束永远没有歧义。
2. 5 位字符集,以及为什么是 5 位
数据部分是以这个字母表、这个顺序构成的 base32 字符串:
| 取值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| +0 | q | p | z | r | y | 9 | x | 8 |
| +8 | g | f | 2 | t | v | d | w | 0 |
| +16 | s | 3 | j | n | 5 | 4 | k | h |
| +24 | c | e | 6 | m | u | a | 7 | l |
恰好 32 个字符,所以每个字符恰好 5 位 —— 「5 位」这个数字就是这么来的。注意少了哪些字符:
1(它是分隔符)、b、i、o(视觉歧义),这也意味着不会
有两个形近字形同时参与编码。该字母表还经过挑选,使「看起来相似却相差不止 1 个比特」的字符对尽可能少,
因为校验和的优化目标正是检测少量的比特错误。
代价是长度 —— base32 比 base256 大约多出 15% 的字符;收益是没有大小写可弄错、可以毫无歧义地念出来, 并且能使用二维码的字母数字模式(比字节模式紧凑 45%)。
3. 8 位到 5 位的转换与补零规则
witness 程序是字节串,数据部分是 5 位值的串。转换按最高位在前进行:取出程序的比特,每 5 位切一组, 最后一组不足时用 0 补齐。
| 程序长度 | 比特 | 5 位字 | 补零比特 |
|---|---|---|---|
| 2 字节(最小) | 16 | 4(20 位) | 4 |
| 20 字节(P2WPKH) | 160 | 32(160 位) | 0 |
| 32 字节(P2WSH / P2TR) | 256 | 52(260 位) | 4 |
| 40 字节(最大) | 320 | 64(320 位) | 0 |
因为补零最多只有 4 位,多一个或少一个 base32 字符都会明显不对。所以这条规则很严格,也是解码器必须 强制执行的规则:
忽略这条规则的解码器,会让同一个程序对应多个不同的字符串,这对地址完整性不利,而且会让补零比特成为
错字藏身之处。BIP173 与 BIP350 都为这一点准备了测试向量:
bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du 属于「补零超过 4 位」,
tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv 属于「非零补零」。
本页解码器用 Invalid padding: invalid padding 拒绝了这两个 —— 具体检查是:最后一个完整字节
之后剩余的比特数必须小于 5,且移位后的累加器必须为零。
4. BCH 校验和
校验和不是哈希。它是定义在 GF(32) 上的 BCH 码 —— 一种线性纠错码,其代数结构能对「哪些错误一定被 抓到」给出保证:
GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
def polymod(values):
chk = 1
for v in values:
b = chk >> 25
chk = (chk & 0x1ffffff) << 5 ^ v
for i in range(5):
chk ^= GEN[i] if ((b >> i) & 1) else 0
return chk
# hrp 以 [每个字符的高 3 位] + [0] + [每个字符的低 5 位] 的形式参与
验证: polymod(hrp_expand(hrp) + data) == 1 # bech32
验证: polymod(hrp_expand(hrp) + data) == 0x2bc830a3 # bech32m
| 属性 | 取值 |
|---|---|
| 有限域 | GF(32) —— 那个 5 位字母表 |
| 生成常量 | 0x3b6a57b2、0x26508e6d、0x1ea119fa、0x3d4233dd、0x2a1462b3 |
| 校验符号 | 6 个字符 = 30 位 |
| bech32 常量 | 1 |
| bech32m 常量 | 0x2bc830a3 = 734539939 |
| 保证检测 | 任何影响至多 4 个字符的错误 |
| 漏检更严重错误的机会 | 低于十亿分之一(渐近值 1 / 230 = 每十亿次 0.931 次) |
hrp 也参与校验和,而且按「高位在前」折叠进去:[ord(c) >> 5 for c in hrp] + [0] + [ord(c) & 31
for c in hrp]。这个顺序意味着:只在 hrp 字符低 5 位内部翻比特的错误(比如把 b 变成
c)依然落在该码设计的保护范围内。这也正是为什么 M1VUXWEZ 无效 —— 它的校验和
是按大写 hrp 算的;而 BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4(校验和按小写取值算)
是有效的。
BIP173 还测量了错误字符超过 4 个时的表现。下表单位是「每十亿次中的失败次数」,而随机的 30 位校验和 基线是每十亿次 0.931:
| 窗口长度 | 说明 | 错 5 个字符 | 错 6 个字符 | 错 7 个及以上 |
|---|---|---|---|---|
| 8 | 能检测 6 个错误的最长窗口 | 0 | 1.127 | 0.909 – 0.931 |
| 19 | 6 个错误的最差情况窗口 | 0.093 | 0.972 | 0.931 |
| 39 | P2WPKH 地址的长度 | 0.756 | 0.935 | 0.931 |
| 59 | P2WSH 地址的长度 | 0.805 | 0.933 | 0.931 |
| 89 | 能检测 4 个错误的最长窗口 | 0.867 | 0.933 | 0.931 |
BIP350 对新常量重做了这套分析,并给出了专门针对 bc1 地址形态的数字。对于用 bech32m 解码器
校验的 bech32m 字符串:24.34% 的单错误模式被确定检出,75.66% 以概率 1 − 2−30 检出;对落在
28 字符窗口内的至多两个错误是 16.85% / 83.15%;对任意位置的至多两个错误是 15.72% / 84.23%,另有
0.039% 落在 2−25。同样这些字符串若交给 bech32 解码器,表现明显更差 —— 这正是「干净
切割」的意义。
5. bech32 与 bech32m —— 逼出 BIP350 的插入弱点
这是本页最关键的部分,因为它解释了为什么同一种地址会存在两套几乎相同的编码。
2020 年人们发现 bech32 的校验和有个洞。引用 BIP350 的原话:
「Bech32 有一个始料未及的弱点:当最后一个字符是 'p' 时,在它前面紧邻处插入或删除任意数量的 'q' 字符 都不会使校验和失效。」插入与删除并不在 BCH 码被优化覆盖的错误类型里,而字符串又不携带长度信息,于是被改 动过的字符串可以仍然是合法字符串 —— 只是载荷不同了。
我们用本页自己的解码器复现了它。取一个合法、末字符为 p 的 bech32 v0 地址,在那个末尾
p 之前紧邻插入 q:
| 字符串 | polymod 结果 | 校验和 |
|---|---|---|
bc1qk4sgyl3plhx0pmh6w80r0m2m5pmknw75uteaqp | 1 | 合法 bech32 |
bc1qk4sgyl3plhx0pmh6w80r0m2m5pmknw75uteaqqp(插入一个 q) | 1 | 仍然通过校验和 |
bc1qk4sgyl3plhx0pmh6w80r0m2m5pmknw75uteaqqqqp(插入三个) | 1 | 仍然通过校验和 |
bc1qk4sgyl3plhx0pmh6w80r0m2m5pmknw75uteap(删掉原有的那个 q) | 1 | 仍然通过校验和 |
BIP350 的修法刻意做到最小:其他一切不变,只把异或进校验和的那个常量从 1 改成
0x2bc830a3。对 bech32m 字符串做同样的插入,校验和就不再能通过(polymod 返回
920205498 而不是 734539939),于是这次改动是被校验和自己抓到的,而不是靠运气。
| bech32 | bech32m | |
|---|---|---|
| 校验和常量 | 1 | 0x2bc830a3 |
| 规范文件 | BIP173 | BIP350(修订 BIP173) |
| 用于 | witness v0:P2WPKH、P2WSH | witness v1–v16:P2TR 及未来类型 |
末尾 p 前插入 q | 校验和检测不到 | 校验和能检测到 |
| 冲突规则 | 没有任何字符串能同时合法:等长的合法 bech32 与 bech32m 字符串至少相差 3 个字符 | |
| 为什么不给 v0 同时允许两种? | BIP350:那会令错误检测能力退化到相当于只有 29 位校验和 | |
还有两点补全整个图景。第一,已有的 v0 地址从未处于危险之中:BIP350 指出该弱点「由于限制在两种特定长度,
不影响 witness v0 的 BIP173 地址现有用法」。插入字符会改变字数,于是程序要么长度变了、要么破坏了补零规则
—— 在我们上面的复现里,被改动的字符串其实是被本页的 Invalid padding 检查拒绝的;而 v1+ 的插入
则由 bech32m 常量拦下。第二,BIP173 自己在 2024 年补充了一条披露:该方案「对少于 5 个连续字符的插入与删除
并不总是稳健」,这也是 BIP173 现在只适用于 v0 输出的原因。
6. 本页执行的合法性规则
| 规则 | 要求 | 出处 |
|---|---|---|
| 长度 | 整体最多 90 个字符 | BIP173 |
| 大小写 | 全小写或全大写;混用即非法 | BIP173 |
| hrp | 非空;比特币地址为 bc(主网)或 tb(测试网) | BIP173 |
| 分隔符 | 必须存在 1,以最后一个为界 | BIP173 |
| 数据部分 | 至少 6 个字符,且全部在字符集内 | BIP173 |
| 校验和 | v0 用 1,v1+ 用 0x2bc830a3 | BIP173 / BIP350 |
| witness 版本 | 0 – 16(含) | BIP173 |
| witness 程序 | 2 – 40 字节 | BIP141 / BIP173 |
| v0 程序长度 | 必须恰好 20 字节(P2WPKH)或 32 字节(P2WSH) | BIP141 |
| 补零 | 剩余比特不超过 4 位且全为零 | BIP173 |
由此地址长度被严格约束:14 到 74 个字符,且长度对 8 取余不可能是 0、3、5;v0 地址永远是 42 或 62 个 字符。
witness 版本必须落在 0–16 之间,因为 OP_n 只对 n ≤ 16 存在;版本字为 17–31 的字符串
不是合法的 segwit 地址,哪怕它的校验和、补零与程序长度全部通过检查。BIP350 就列出了
这样一个非法向量:全大写字符串
BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R(版本 17)通过了其余
所有规则。本解码器会以
Witness version 17 is invalid: only versions 0-16 are defined 拒绝它 —— 你可以把它粘到上面的
输入框里,亲眼看到这条拒绝结果,再与版本字为 1 的 bc1p… 对照。
7. 实例演算:示例 P2WPKH 地址
| 步骤 | 值 |
|---|---|
| 地址 | bc1qy86hkm0telzmvuvzm76279jp7z5p3yqjnaerk6(42 个字符) |
| hrp | bc |
| 分隔符下标 | 2 |
| 数据部分 | qy86hkm0telzmvuvzm76279jp7z5p3yqjnaerk6(39 个字符) |
| 第一个数据值 | q = 0 → witness 版本 0 |
| 程序字(去掉校验和) | y86hkm0telzmvuvzm76279jp7z5p3yqj(32 个字) |
| 这些字的 5 位取值 | 4,7,26,23,22,27,15,11,25,31,2,27,12,28,12,2,27,30,26,10,30,5,18,1,30,2,20,1,17,4,0,18 |
| 校验和字符 | naerk6 |
| 匹配的校验和常量 | 1 → bech32,BIP173 |
| witness 程序 | 21f57b6debcfc5b67182dfb4af1641f0a8189012(20 字节) |
| scriptPubKey | 001421f57b6debcfc5b67182dfb4af1641f0a8189012 |
| 可读脚本 | OP_0 <20 字节推送> —— 最经典的 P2WPKH 输出 |
| 同一程序的测试网形式 | tb1qy86hkm0telzmvuvzm76279jp7z5p3yqjemzsdf —— 20 字节完全相同,hrp 不同,因此校验和也不同 |
注意这个 witness 程序就等于同一把密钥在 hash160 → 地址 里打印的那个 hash160; 而测试网地址携带同样的程序、却是另外 6 个校验和字符:hrp 参与校验和计算,所以「主网/测试网搞混」这种情况 不可能蒙混过关。
8. 实例演算:示例 P2TR 地址
| 步骤 | 值 |
|---|---|
| 地址 | bc1pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63(62 个字符) |
| hrp / 分隔符下标 | bc / 2 |
| 数据部分 | pmsrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3sh54u63(59 个字符) |
| 第一个数据值 | p = 1 → witness 版本 1 |
| 程序字 | msrhxnvmj8tjxxu9wj69ntvwvhvr28qufyc26tshz3rjj777tx3s(52 个字) |
| 校验和字符 | h54u63 |
| 匹配的校验和常量 | 0x2bc830a3(734539939)→ bech32m,BIP350 |
| witness 程序 | dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3(32 字节) |
| scriptPubKey | 5120dc07734d9b91d7231b8574b459ad8e65d8351c1c4930ad2e171447297bde59a3 |
| 可读脚本 | OP_1 <32 字节推送> —— 注意 OP_1 的操作码是 0x51,不是 0x01 |
| 程序从哪来 | 它是调整后输出公钥的 x 坐标:内部公钥 ff812e26…ee80、TapTweak bae91685…fa4d、输出公钥 dc07734d…59a3 |
最后一行就是两个示例地址在一句话里的区别:P2WPKH 的程序是公钥的哈希,而 P2TR 的程序本身 就是一把公钥(调整后的那把)。其余一切 —— 编码、分隔符、5 位字、6 字符校验和 —— 完全相同;不同的 只有版本字与校验和常量。
9. 本页展示的失败路径
下面的非法字符串在每一次运行中都会被现场测试,和你粘贴的内容一起:
| 测试 | 结果 |
|---|---|
| 把末字符换成字符集里的下一个字符 | Checksum verification failed (the address contains a typo or is truncated) |
| 把地址的一部分改成大写 | Mixed case is not allowed in bech32 strings |
把 v0 程序用 bech32m 常量重新编码(BIP350 向量 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh) | Witness v0 must use bech32, not bech32m |
把 v1 程序用 bech32 常量重新编码(BIP350 向量 bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd) | Witness v1+ must use bech32m, not bech32 |
| 完全没有分隔符 | No separator "1" found |
数据部分出现字符集之外的字符,例如 b | Character "b" is not in the bech32 charset |
| 超过 90 个字符 | Length 93 exceeds the BIP173 limit of 90 characters |
| v0 程序长 25 字节 | Witness v0 program must be 20 bytes (P2WPKH) or 32 bytes (P2WSH) |
还有一条会让人意外的边界说明:BIP350 自己的测试向量里包含一些合法的 bech32m 字符串(例如
A1LQFN3A),它们根本不是 segwit 地址(数据部分只有校验和)。segwit 地址解码器会在其上再套一层
地址规则,因此会拒绝它们 —— 这是正确的,会给出理由,也不会崩。
10. 安全须知与常见错误
- 绝不要让软件「修好」一个地址。 BIP173 说得很明确:实现不应纠错,最多只能提示错误可能 位于何处,因为被纠正出来的地址未必是原意,币就发去了别处。只检测,不纠正。
- 小写才是传输格式。 编码器必须输出小写;大写用于展示与二维码(字母数字模式);一个 字符串绝不能大小写混用。
bc1q…与bc1p…只差一个字母和一个常量。 正是校验和常量 让它们不可能互相误读。- 合法地址不一定花得掉。 一个 witness v2–v16 地址,或者一个你不知道脚本的 v0 程序, 都能完美解码,却可能没有任何现有软件能花掉它。往那里打币就是把币烧掉。
上游:私钥 → P2WPKH 地址 与 私钥 → P2TR 地址 负责构造这些字符串;hash160 → 地址 产出 v0 所需的 20 字节程序。下游另一种地址 编码见 Base58Check 编码 / 解码 —— 版本字节、WIF 与校验和失败都在那里。
11. 速查表
| 项目 | 值 |
|---|---|
| 字符集 | qpzry9x8gf2tvdw0s3jn54khce6mua7l(32 个字符,每个 5 位) |
| 分隔符 | 1,取最后一次出现 |
| 校验和长度 | 6 个字符(30 位) |
| bech32 常量 / 规范 | 1 / BIP173 —— witness v0 |
| bech32m 常量 / 规范 | 0x2bc830a3 / BIP350 —— witness v1+ |
| 总长度上限 | 90 个字符 |
| v0 地址长度 | 42 个字符(P2WPKH)或 62 个(P2WSH) |
| 程序长度 | 2 – 40 字节;v0 必须恰好 20 或 32 |
| 保证的错误检测 | 任何影响至多 4 个字符的错误 |
| 已知弱点 | 仅 bech32:末尾 p 之前的 q 插入 / 删除 |