🏠 总目录📚 本教程 12 · Merkle 树
📑 本页目录(点开跳转)

12 · Merkle 树

22 分钟 | ⭐ 一个哈希值,承诺一整个数据集


🎯 一句话

用一个 32 字节的根哈希,锁定任意大的数据集; 而证明"某条数据在里面"只需要 log n 个哈希。 区块链的轻节点、证书透明性、Git、IPFS ——全都靠它。

根哈希存进区块头H(AB)H(CD)H(A)H(B)H(C)H(D)交易A交易B交易C交易D验证「交易C 在里面」只需要橙色路径上的log₂n 个哈希不用下载全部交易—— 这就是轻节点能存在的原因
要证明「交易 C 确实在这个区块里」,只需要橙色路径上的 log₂n 个哈希,不用下载全部交易。⭐ 这就是手机上的轻钱包能存在的原因。

🌳 一、结构

结构就是一棵二叉哈希树:叶子 H₁=H(D₁)H₄=H(D₄) 是每条实际数据 D₁…D₄ 的哈希,相邻两个拼起来再哈希(H₁₂ = H(H₁‖H₂)H₃₄ = H(H₃‖H₄)),一层层合并到唯一的 Root = H(H₁₂ ‖ H₃₄) —— 只需要公开这一个值

两个关键性质

性质 说明
承诺 根哈希一旦公布,整个数据集就被锁死了 —— 改任何一个字节,根就变 ⭐
高效证明 证明 Dᵢ 在树里,只需要 log₂n 个哈希,不需要整个数据集 ⭐

🔍 二、Merkle 证明怎么工作

要证明 D₃ 在树里

   给验证者:D₃ + 【兄弟路径】= [H₄, H₁₂]     ← 只要 2 个哈希

   验证者自己算:
   ① H₃'  = H(D₃)
   ② H₃₄' = H(H₃' ‖ H₄)
   ③ Root' = H(H₁₂ ‖ H₃₄')
   ④ 检查 Root' == 已知的 Root ✅
   数据量        证明大小
   1,000 条   →  10 个哈希 = 320 字节
   100 万条   →  20 个哈希 = 640 字节
   10 亿条    →  30 个哈希 = 960 字节  ⭐ 仍然不到 1KB

🔑 这就是它的价值验证成本从 O(n) 降到 O(log n)。 一个手机可以在不下载整条区块链的情况下,验证"我这笔交易确实在第 80 万个区块里"。

import hashlib

def h(b): return hashlib.sha256(b).digest()

def verify(leaf, proof, root, index):
    """proof: [(兄弟哈希, 是否在左边), ...] 自底向上"""
    cur = h(leaf)
    for sibling, sibling_is_left in proof:
        cur = h(sibling + cur) if sibling_is_left else h(cur + sibling)
    return cur == root                       # ⭐ 就这么简单

⚠️ 三、三个真实的坑

坑 ① 第二原像攻击:叶子和内部节点必须区分

   💥 如果叶子和内部节点用【同样的方式】哈希:

   攻击者可以把一个【内部节点的两个子哈希拼起来】当作一条"数据"
   → 它算出来的哈希和那个内部节点一样
   → 伪造出一条不存在的"数据"及其合法证明 💀

   ✅ 解法:加【域分隔】
      叶子:   H(0x00 ‖ data)
      内部:   H(0x01 ‖ left ‖ right)   ⭐

💡 证书透明性(RFC 6962)标准里明确规定了这个前缀 —— 就是为了堵这个洞。

坑 ② 叶子数不是 2 的幂时怎么补

   ❌ 危险做法:复制最后一个叶子来凑满

   💥 后果:[A, B, C] 和 [A, B, C, C] 会产生【相同的根】
      → 比特币早期版本就有这个缺陷(CVE-2012-2459)⭐
      → 可以构造出两个不同的区块却有相同的 Merkle 根

   ✅ 解法:把叶子数编进根里,或用不平衡树(RFC 6962 的做法)

坑 ③ 证明"不存在"要另想办法

   Merkle 树天然能证明"X 在里面"(包含证明)
   ❌ 但证明"X 【不】在里面"需要额外结构

   ✅ 解法:
   ├─ 【排序 Merkle 树】:证明相邻的两个元素 A < X < B 都在树里
   │   → 说明 X 不在(因为如果在,它必须排在 A 和 B 之间)⭐
   └─ 稀疏 Merkle 树:为每个可能的 key 都留位置,证明那个位置是空的

🌍 四、它在哪些地方支撑着现代系统

系统 怎么用的
比特币 / 以太坊 区块头存 Merkle 根,轻节点(SPV)靠证明验证交易 ⭐
证书透明性 CT 所有签发的证书进入公开的 Merkle 日志,CA 无法偷偷发证
Git 每个 commit 是一棵 Merkle 树(tree/blob 对象),commit hash 锁定整个快照
IPFS / BitTorrent 内容寻址 + 分块校验
ZFS / Btrfs 文件系统的数据完整性校验
Certificate Revocation 证明某证书未被吊销

💡 证书透明性值得单独说

   问题:一个被攻破的 CA 可以给 google.com 偷偷签发证书
        → 中间人攻击,而 Google 自己都不知道

   CT 的解法:
   ① 所有证书必须提交到【公开的 Merkle 日志】
   ② 日志给出签名的"包含承诺"(SCT),浏览器会检查
   ③ 任何人可以监控日志 → Google 能发现"有人给我签了证书" ⭐

   ⭐ Merkle 树在这里提供两件事:
      · 包含证明(这张证书确实进了日志)
      · 【一致性证明】:证明新日志是旧日志的【追加】,
        没有偷偷删改历史 ⭐⭐

🔑 "一致性证明"是 Merkle 树的另一个杀手级用法: 用 O(log n) 个哈希,证明版本 n 的树是版本 m 的树的纯追加扩展。 这让"仅追加日志"变成可验证的。


🔗 和站内其他章的关系

相关的地方 和这一章的关系
第 9 章 抗碰撞 Merkle 树的安全性完全建立在它上面
第 9 章 承诺方案 根哈希就是对整个数据集的承诺
第 20 章 区块链 SPV 轻节点
第 18 章 PKI 证书透明性
推荐算法的向量检索 有意思的对照:都是"用结构把 O(n) 降下来"

✅ 检查点

  1. Merkle 树的两个关键性质是什么?
  2. 证明一条数据在 10 亿条里,需要多大的证明?
  3. 为什么叶子和内部节点必须用不同的方式哈希?不这么做会怎样?
  4. 叶子数不是 2 的幂时,复制最后一个叶子有什么问题?
  5. 怎么证明某条数据"不在"树里?
  6. 证书透明性解决了什么问题?Merkle 树在其中提供哪两件事?
  7. 什么是一致性证明?它让什么变得可验证?
👀 答案
  1. 承诺:根哈希一旦公布,整个数据集被锁死,改任何一个字节根就变 ②高效证明:证明某条数据在里面只需 log₂n 个哈希
  2. 30 个哈希 ≈ 960 字节,不到 1KB。
  3. 否则攻击者可以把一个内部节点的两个子哈希拼起来当作一条"数据",它的哈希和那个内部节点相同,从而伪造出一条不存在的数据及其合法证明。解法:域分隔——叶子 H(0x00‖data),内部 H(0x01‖left‖right)。
  4. [A,B,C] 和 [A,B,C,C] 会产生相同的根,可以构造两个不同的区块却有相同 Merkle 根。比特币早期的 CVE-2012-2459 就是这个。解法:把叶子数编进根,或用不平衡树。
  5. 排序 Merkle 树:证明相邻的 A < X < B 都在树里,说明 X 不在(若在则必须排在两者之间)。或用稀疏 Merkle 树证明对应位置是空的。
  6. 解决被攻破的 CA 可以偷偷给 google.com 签发证书的问题。Merkle 树提供:①包含证明(这张证书确实进了公开日志)②一致性证明(新日志是旧日志的纯追加,没有偷偷删改历史)。
  7. 用 O(log n) 个哈希证明版本 n 的树是版本 m 的树的纯追加扩展。它让"仅追加日志"变成可验证的

🛑 可以停在这里

走神救援

Merkle 树:用一个 32 字节的根哈希锁定任意大的数据集,叶子是数据哈希、父节点是子节点拼接再哈希。两性质:⭐承诺(改任何一字节根就变)+ ⭐证明只需 log₂n 个哈希10 亿条数据的证明也不到 1KB)→ 验证成本从 O(n) 降到 O(log n),手机不下载整条链就能验证交易。验证方法:拿兄弟路径自底向上重算到根,比对。三个真实的坑:①⭐叶子和内部节点必须域分隔(叶子 H(0x00‖data)、内部 H(0x01‖l‖r))——否则攻击者把内部节点的两个子哈希拼起来当"数据"就能伪造不存在的数据及其合法证明(RFC 6962 明确规定了前缀)②叶子数不是2的幂时不能复制最后一个叶子——[A,B,C] 和 [A,B,C,C] 根相同,💥比特币早期 CVE-2012-2459 ③证明"不存在"要另想办法:⭐排序 Merkle 树(证明相邻的 A<X<B 都在树里 ⟹ X 不在)或稀疏 Merkle 树。应用:比特币 SPV 轻节点、证书透明性 CT、Git(每个 commit 就是一棵 Merkle 树)、IPFS、ZFS。⭐CT 解决"被攻破的 CA 偷偷给 google.com 签证书":所有证书进公开 Merkle 日志,Merkle 树提供包含证明 + ⭐⭐一致性证明(用 O(log n) 个哈希证明新日志是旧日志的纯追加,没删改历史)——这让"仅追加日志"变成可验证的

下一节 👉 13-数论工具箱.md

打卡记录保存在你的浏览器里,首页能看到总进度