比特币的区块头里塞着一个默克尔根,用来承诺“这个块装了哪些交易”。轻钱包正是靠这条承诺,才能不下载全区块就相信自己那笔交易已经上链。BIP-53 这份提案盯上的,就是这份承诺的一个形状巧合:如果有一笔交易的序列化长度正好 64 字节,它就可以伪装成默克尔树里两个哈希拼起来的那一段。
64 字节为什么恰好敏感
先记住一个尺寸:比特币交易 ID 是双 SHA-256 的输出,长度 32 字节。两个交易 ID 拼接,正好 64 字节。假设一个块里有两笔交易,它们的默克尔父节点哈希等于对“交易 ID 零拼交易 ID 一”这 64 个字节做哈希。而一笔交易的前 4 个字节是版本号——如果攻击者造出一笔交易,它的整段序列化内容恰好就是“父节点下面那两个子哈希原样拼起来”的字节串,那么把这笔交易本身当作区块内容重新计算默克尔根,会得到同一个根。提案把这条规则的规格部分写得极简:非见证序列化部分为 64 字节的交易不再被允许。
第一个缺口:区块可延展
同一份提案分两种情形算了工作量。第一种,攻击者只要造出一笔“序列化正好是那 64 个字节”的交易,甚至不要求它是合法交易——他拿这堆字节构造出一个默克尔根相同但内容不同的块。接收方在验证到那笔交易时报错、判这个块无效;而网络里其他节点收到的正规块则完全正常。提案引用的分析把这类碰撞的成本算成大约 22 位工作量:先花大约 8 位磨出可用的币基交易,再花大约 22 位中约五分之一的量去凑第二笔。这个量级对手握全网络哈希率的攻击者来说很便宜。历史上这个缺陷在 2012 年的一次紧急修补(CVE-2012-2459,对应 0.6.2)里被堵住过,后来又在 0.13.x 的改动中被重新引入,直到 0.14 再修一次——反复的来回正是提案作者想钉死的理由。第二种情形要求攻击者造出一笔完全符合共识规则的 64 字节交易,成本被算成 224 位量级,现实中不可行,但它能造成的后果更严重:一条对某节点合法、对另一节点非法的持续分叉。
第二个缺口:轻客户端可以被“证明”假交易存在
更贴近普通用户的是针对简化验证支付那条路线的攻击。提案指出,用于给轻钱包交付默克尔分支的那种格式并不承诺这棵树有多深、你的交易挂在第几层。于是攻击者可以构造一笔真实的 64 字节合法交易塞进某个块,同时把后 32 字节造成与某笔假交易哈希碰撞,再给轻客户端交付一条合法的分支——钱包会信那笔从未发生的假交易确实入块。这一步的成本被算成 81 位工作量,另外还要 40 位来构造资金来源。提案给出的另一条修补路线是让轻客户端额外索取同块的币基交易,靠它锁定树深,代价是证明体积增大约七成——两害相权,作者认为直接封掉 64 字节更省实现复杂度。
为什么政策层已经先动过手
这里要把它和“交易不能太小”的标准政策区分清楚:那一条出自节点的标准交易判定,非见证部分低于 65 字节的交易默认不中继,细节见 ‘+L(202609103112)+‘。政策管的是中继与打包意愿,共识管的是区块合法性——政策能挡住绝大多数恶意流量,却挡不住“已经被矿工写进块”的情况。也正因为有政策层这道地板,提案在兼容性一节统计的历史数据很少:写作时链上总共只有 5 笔 64 字节交易,最后一笔出现在第 419,606 块,其清单附在提案文件里。隔离见证之后还能凑出 64 字节的,只剩单入单出、支付给 2 字节见证程序这一种用法,也就是临时锚点输出那条路线,提案专门讨论了它。
对普通用户意味着什么
先说结论的分量:这是一份状态为 Draft 的提案,没有激活,任何钱包都不该按“64 字节交易已经不存在”来做产品设计。但它对使用者有三点实际意义。第一,交易 ID 与“交易在块里”是两层不同的承诺:前者关心内容能不能被改动,‘+L(2026090100033)+’ 讲的就是这一层;后者靠默克尔分支,本提案盯的是第二层的边角。第二,收款方坚持等足够的确认数,是在同时防御这两层——零确认状态下,任何依赖单个区块的解释都可能被上面这类巧合钻空子。第三,如果你的实现要自己解析默克尔分支,不能只看“哈希对上了”就接受,必须把树的结构信息一并校验;这类实现细节正是这类提案存在的现实土壤。
一句风险提示
本文所有数字都出自提案原文与其引用材料,属于安全性分析,不代表现网存在正在发生的攻击。涉及资产安全的判断请以现网规则和你所用软件的实现为准;这里不提供任何投资、买卖或套利建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。