什么是 Merkle Proof?空投名单为什么只用一个 Root 就能验证
Merkle Tree 能把大量名单压成一个 Root,领取者用短 Proof 证明自己属于集合。本文讲清树、叶子、验证路径、重复领取与实现风险。
一场空投有十万个合格地址。最直接的做法,是把十万个地址全部写进合约,但存储和更新成本会很高。Merkle Tree 提供了另一种方式:在链下整理完整名单,计算出一个固定长度的 Merkle Root,合约只保存这个根。领取者再提交一小段 Merkle Proof,证明自己的数据确实属于那份名单。
它不是把名单“加密藏起来”,而是用哈希把大集合压成一个可验证承诺。根很小,验证所需的数据也只随树的高度增长。
Merkle Root 像整份名单的数字指纹;Proof 不是交出整份名单,而是提供从某片叶子走回根节点所需的相邻线索。
Merkle Tree 怎样长出来
先把每项数据编码并哈希成叶子节点。相邻两个叶子哈希组合后再次哈希,得到上一层节点;不断向上合并,最后只剩一个 Merkle Root。只要任意叶子或组合规则变化,沿路径向上的哈希都会变化,最终根也不同。

树的具体规则必须一致:叶子怎样编码、是否排序、成对节点怎样排序、奇数节点怎样处理、使用什么哈希函数。生成方和合约验证方只要有一处规则不同,同一份名单也无法通过验证。
Proof 为什么不需要整棵树
要证明某片叶子属于树,只需提供它从底部走向根时每一层的相邻哈希。验证者从叶子开始,与 proof 中的相邻值逐层计算,最终重建出一个根;如果与合约保存的 root 相同,说明这片叶子按既定规则属于该集合。

对于约十万项的数据,proof 通常只需要十几到二十个左右的兄弟节点,而不是十万项原始名单。具体长度取决于树的结构和实现。
空投里叶子应该包含什么
叶子不一定只是地址。它常常包含索引、地址、可领取数量、批次或其他业务参数,再按明确编码方式哈希。如果只把地址放进叶子,合约还需要用其他可信方式决定数量;如果把地址和数量一起放进去,proof 同时承诺“谁能领”和“能领多少”。
| 叶子内容 | 能证明 | 需注意 |
|---|---|---|
| address | 地址在名单中 | 领取数量需另行确定 |
| index + address + amount | 具体条目与数量 | 编码和索引必须一致 |
| address + tier | 地址所属等级 | 等级如何映射权益要明确 |
| orderId + recipient + asset | 某笔跨链或分发指令 | 还要防重复执行与跨域重放 |
Proof 有效不等于可以重复领取
Merkle Proof 只证明成员关系,不会自动记录这片叶子是否使用过。空投合约仍要维护 claimed 映射、位图或其他消费状态,并在转账前标记。否则同一份有效 proof 可能被反复提交。
同样,proof 也不会自动限制领取期限、调用者、接收方或链。相关规则必须进入叶子数据或合约逻辑,并配合权限与状态检查。
为什么 root 更新需要谨慎
若管理员可以随时替换 root,名单和数量就可能改变。项目应说明 root 是否固定、谁能更新、更新是否经过多签或时间锁、旧 proof 在更新后如何处理。用户看到“合约只存一个 root”时,还要继续检查 root 的管理权。
常见实现风险
| 风险 | 表现 | 防范思路 |
|---|---|---|
| 编码歧义 | 不同字段组合产生意外碰撞 | 使用明确类型与安全编码 |
| 排序规则不一致 | 链下生成的 proof 全部失败 | 统一库、哈希和 pair 顺序 |
| 叶子构造不安全 | 内部节点可能被误解释为叶子 | 使用成熟库的安全叶子规则 |
| 未防重复领取 | 同一 proof 多次执行 | 在外部交互前消费领取状态 |
| Root 权限过大 | 管理员可替换整份名单 | 多签、时间锁、事件与监控 |
OpenZeppelin 文档特别提醒,在默认哈希组合规则下,应避免使用“哈希前长度为 64 字节”的原始叶子,或采用不同哈希方案,以免一对内部节点的拼接被重新解释成叶子。其 JavaScript Merkle Tree 库会默认生成规避该问题的树。
verify 在做什么
OpenZeppelin MerkleProof 的 verify 接收 proof、root 与 leaf,逐层处理 proof 后比较重建结果是否等于 root。默认实现假定节点对经过排序,并适用于可交换哈希组合规则。若你的树不排序或哈希方向有意义,就需要匹配的额外逻辑,不能直接套用不兼容验证器。
普通用户怎样核查空投 Proof
- 确认领取网站、链和合约地址来自可信官方渠道。
- 阅读交易模拟:领取资产、数量和接收地址是否正确。
- 检查调用的是 claim,而不是额外授权或资产转出。
- 确认是否需要支付正常 Gas,警惕先转账“激活资格”。
- 不要把助记词或私钥交给所谓 proof 生成器;生成 proof 不需要私钥。
- 若合约可更新 root,查看管理员、时间锁与最近事件。
开发者检查清单
- 固定并文档化叶子编码、哈希函数、排序和树生成版本。
- 在叶子中包含所有需要承诺的业务参数,避免只证明地址。
- 使用成熟生成与验证库,并用固定测试向量交叉验证。
- 先标记领取状态,再进行代币转账,兼顾重入防护。
- 为 root 更新设置最小权限、事件、延迟和清晰版本。
- 测试错误 proof、重复领取、错误数量、错误接收者与旧 root。
三个误区
- “Root 能还原完整名单”:哈希承诺用于验证,不是可逆压缩文件。
- “Proof 会隐藏我的地址”:领取交易和 calldata 通常仍公开,树也不天然提供隐私。
- “验证通过就代表项目可信”:它只证明数据属于某个 root,不证明 root 的生成、公平性和管理权限可信。
Merkle Proof 把“存下整个集合”变成“保存一个根、按需验证成员”,因此特别适合空投、白名单、跨链消息和批量分发。但密码学证明只回答定义好的问题:这片叶子是否属于这棵树。领取是否只能一次、root 谁能修改、网站是否可信,仍要由合约和治理另外保证。
风险提示:空投与领取页面常被仿冒,Merkle Proof 本身不能保证项目或前端安全。交互前请核验域名、网络、合约、权限和交易内容。本文仅作技术科普,不构成任何投资建议。
本文为链上指南原创内容。技术依据参考 OpenZeppelin MerkleProof 官方文档。