密码学 29:Merkle 包含证明究竟证明什么——叶、路径、根与可信区块头
钱包经 HTTPS RPC 发了一笔测试交易,RPC 返回一条交易哈希和一串“Merkle proof”。有人由此断言“交易已经写入不能修改的历史,订单一定交付”。要使“包含”有意义,最先问的不是路径有多少个哈希,而是:根是谁给的,它被谁认可,它承诺的到底是哪一组字节?
先用四份合成订单把证明画出来
以下是教学二叉树,不是 Bitcoin 真实交易树,更不是 Ethereum 的十六进制 Patricia Trie。四个合成订单先各自编码为确定字节;叶哈希加 00 前缀,父节点加 01 前缀以区分不同位置的拼接:
1 | |
验证者先计算 h(00|order-b),依据序号 1 把兄弟放在正确一侧得到左子树,再与右子树哈希合成根;只有结果等于已经可信地取得的根,才能说 order-b 在这棵被根承诺的树里。仅知道几个兄弟摘要、却不检查顺序和叶子的规范化字节,攻击者就可能改变待验证的对象。仅有路径而没有可信根,攻击者可以先做一棵“有假订单”的树,再把它的根和路径一并交给验证者——自洽,却不指向任何共同历史。
实验里同一订单在正确根下通过,改订单字节、交换位置或改期望根都会失败;将根换成攻击者自选假树的根,攻击者的假订单也会在那棵假树下通过。这不是哈希碰撞,攻击者没有推翻哈希安全性,只是控制了验证者本应预先可信的输入。
真实链的根是哪一种根
Bitcoin 的交易包含路径、Bitcoin 区块头里的承诺与区块工作/链选择规则有特定编码和可信前提;把上面的 00/01 教学前缀抄过去不会复现 Bitcoin 共识。Ethereum 执行层区块头有 transactionsRoot,承诺的是按交易序号为键、指定编码交易为值的十六进制 trie;receipt、状态又各有自己的根。验证 transactionsRoot 下某交易编码,只回答“这段交易编码在这个区块的交易 trie 中”;它不回答收据是否成功、世界状态是否如期变更,更不回答用户取到的区块头是否在可信共同历史中。
1 | |
examples/cryptography/29_merkle_inclusion.py 的第二个实验使用冻结版本的 PyEVMBackend/PragueVM 进程内开发链,两个无价值测试账户各发一笔交易,再打包为同一个两交易块。它以成熟 HexaryTrie 组件重建交易 trie,根与该块头记录的 transaction_root 一致;对序号 1 生成和验证实际 trie 节点路径。故意改待比对交易字节或使用错误根,成员关系不再成立。另从 receipt 查看执行 status=1;这是第二项独立证据,不是证明字节自行推导出“执行成功”。
先按仓库内 examples/cryptography/README.md 完成环境准备;以下命令从仓库根目录执行。
1 | |
2026-10-06 UTC 两条命令退出码 0、2 项测试通过。区块头是从同一个本地进程读出的,因而实验能够核对它和自己构造的交易字节相一致,却没有独立验证公网权威、验证者共识、链最终性或远端 RPC 是否撒谎。若攻击者能够同时伪造被信的区块头根与证明,本次路径算法仍可能对假交易返回 true。实验的 public_chain_header_trust_established=false 必须随结论保留。Bitcoin regtest 仍未跑通,也不能把这个以太坊 MPT 实验反写为 Bitcoin SPV 验收。
包含、合法、执行、确认、交付是五个不同问题
一条 Merkle 成员路径是“字节属于此根所承诺集合”的局部证明,并不枚举出所有本该出现的交易。构造该根的规则、区块有效性、历史先后、被其他节点采纳的程度和状态转移,都在路径之外。即使在本地 receipt status=1,也不代表本地自动出块链与公网一致,更不代表买家的订单已发货。反之,签名合法也不能自动生成包含证明;签名要回答的是“私钥授权了哪些确定字节”,根证明要回答的是“这些字节归属哪棵可信树”。
此外,HTTPS RPC 只认证一次网络传输的服务端。即便 RPC 服务真实,它也可能给客户端一个不符合所需共识/最终性要求的区块头。对轻客户端而言,如何拿到并验证可信头与它支持的历史/共识规则,必须独立设计;把“TLS 是绿的”当成“链根可信”会跨越信任边界。
两道带答案的练习
画图题: 为教学树中的 order-b 写出叶、兄弟哈希和左右顺序;再假设攻击者能独自给你一条路径和根,画出他如何让 attacker-order 也“通过”,指出验证缺了哪一个可信输入。
可核对答案: 对 order-b 先算 h(00|b),左侧放 h(00|a),右子树为 h(01|h(00|c)|h(00|d)),最终与预先可信 root 对比。攻击者自造假叶、相应兄弟和自己的根,自然也能重算到那个假根;他不需要找到碰撞。需要从攻击者不能随意替换的规则/状态/可信区块头取得预期 root,并验证根属于正确对象与历史。
实验变更题: 在 29_merkle_inclusion.py 教学模型里,把 verify_binary(values[1], 1, path, root) 的最后一个参数替换为 forged_root;在真实 Ethereum trie 证明处把传给 HexaryTrie.get_from_proof 的 root 换成 32 个零字节。两者应分别如何失败?即使换回正确根,能否不查 receipt 就证明 EVM 执行成功?
可核对答案: 第一处在根比较时返回 false,正例断言失败;第二处库无法从这组节点推出伪造的零根(或结果不等于原交易),wrong_header_root_rejected 为 true。正确根下路径恢复真实交易字节,不携带 receipt status;执行结果仍须读并验证另一份状态/收据证据。
资料与导航
- Ethereum Yellow Paper、py-trie 4.0.0:交易 trie / 区块头根与实际路径算法;Bitcoin 的不同编码另按其专门规范。
- 03 哈希能证明什么 · 27 Bitcoin 交易到底签哪些字节 · 28 Ethereum 交易与 EIP-712。




