bc1p 前缀不是随便起的
以 bc1p 开头的 Taproot 地址使用一种叫 Bech32m 的编码。它和更常见的 bc1q 地址肉眼几乎一样,区别只在结尾校验和的计算常数。为什么要为 Taproot 换一种校验和?这要从 Bech32 的一个已公开弱点说起,而它的答案直接决定了老钱包能不能给新地址转账。

旧编码的软肋
BIP173 定义的 Bech32 编码自带校验和,能检测输入错误。但研究者发现一个意外弱点:当地址最后一个字符是 p 时,在它前面任意插入或删除若干个 q 字符,校验和依然通过。v0 地址因为长度被限定在两种固定值而实际不受影响,但未来的新版本地址无法靠长度约束规避这个问题。
Bech32m 的做法
BIP350 给出的修补很克制:把校验和公式末尾异或的常数 1 换成 0x2bc830a3,其余编码规则不变,人类可读前缀仍是 bc(测试网仍是 tb)。规则由此分工:隔离见证 v0(也就是 bc1q 那两类)继续用 Bech32;v1 到 v16 用 Bech32m。Taproot 是第一个落地的 v1 输出,于是有了 bc1p 前缀。
故意破坏的向前兼容
更反直觉的决定在后面:Bech32m 明确不再保持对所有新版本地址的向前兼容——旧钱包遇到 bc1p 地址应判定无效而拒绝发送。规范解释这是刻意设计:实验发现几乎没有钱包真正支持向 v1 及以上地址发送,而且有些实现若按旧校验规则强行解析,会把资金烧进自己无法识别的输出。与其让旧软件带着错误校验去转新地址,不如让它们在转账前就报错。规范同时强烈建议实现方把 v1 及更高版本都识别为合法收款目标,这样未来软分叉引入新版本时,正确实现的钱包无需再改就能安全转账。其官方测试向量里,把 v1 地址按 Bech32 校验或反之,都被明确列为无效用例。
大小写与核对
Bech32 系列要求地址要么全小写、要么全大写,混用直接判定无效;校验与人类可读前缀的大小写状态绑定,全大写形式的地址在部分实现里被视为无效用例处理。这带来一个实操结论:地址需要整体替换,不需要也不应该逐字符比对——校验和的存在正是为了让传输错误被发现,而篡改地址几乎必然改变校验结果。
普通用户的三个动作
一是收款方:前缀取决于你的钱包类型,bc1q 与 bc1p 的到账确认逻辑相同,费用按脚本重量计。二是付款方:若钱包提示某 bc1p 地址无效,先升级钱包再核对地址,绝不要手工改字符碰运气。三是审计视角:前缀可以当输出版本的识别线索,但归属判定永远以完整脚本与自身钱包的私钥派生为准。
边界
编码只保证检测传输错误,不证明地址属于谁;地址投毒类攻击正是利用人眼对长串字符的无力,逐字符核对或使用扫码仍是基本防御。细节以 BIP350 当前文本为准。本文只做机制科普,不构成投资建议。
脚本层面为什么安全
为什么旧软件按旧校验发给新地址可能烧币?从脚本视角看,隔离见证输出带一个版本字节加程序数据,旧软件若接受校验通过的编码,可能构造出它自己永远无法满足花费条件的脚本——资金进了一个只有新版规则才认识的输出。规范的处理思路是在编码层就划清界限:v1 及以上必须用新校验和,旧实现无法静默地生成一个看似合法的地址;即使某些旧实现曾允许任意长度程序数据通过校验,新的编码要求也让它无法构造出合法的 v1 地址。对用户来说,这一层设计的意义是把不兼容暴露成一句明确的报错,而不是链上一笔不可逆的失联资金。
对新版本地址的态度
v2 及以后目前网络上没有定义,因此也不会有钱包生成这类地址;但规范要求实现把它们识别为合法收款目标,好让未来的软分叉到来时正确实现的钱包可以直接转账。普通用户平时碰不到这些地址,唯一实际接触方式是安全审计或阅读规范。这条设计哲学的启示是:向前兼容不是无条件的善意,而是经过威胁建模的取舍——当旧校验会把钱送进黑洞时,拒绝才是最安全的答案。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。