默克尔树
默克尔树是区块链技术的一个基本概念。它们是一种特殊的二进制树,用于编码大块的信息。默克尔树的酷之处在于,它们是一种自下而上的 "建立",并允许你验证某些值是否存在于树中,而无需在树的每个元素上循环。这可能非常有用,我们会看到。
什么是梅克尔树?
Merkle 树是一种哈希树,其中每个叶子节点都标有数据块的加密哈希值,而每个非叶子节点都标有其子节点标签的加密哈希值。大多数哈希树的实现是二进制的(每个节点有两个子节点),但它们也可以有更多的子节点。
一个典型的 Merkle 树看起来是这样的。
让我解释一下这是怎么回事。树的所有叶子节点,即没有任何进一步子节点的节点,包括你想编码的数据的哈希值。注意,你想在树上编码的值总是只是叶子节点的一部分。因为它是一棵二叉树,每个非叶子节点有两个子节点。当你从叶子节点向上移动时,父节点会有叶子节点的组合哈希值,以此类推。
当你继续这样做时,最终你会在单一的顶级节点上结束,被称为 Merkle 树根,这将会起到非常重要的作用。
简单的例子
假设我们有 4 个交易。所有这些交易都是在同一个区块中执行的,其中包括 "交易 A"、B、C 和 D。这些交易中的每一个都将得到哈希值。让我们把这些哈希值分别称为 "哈希 A"、B、C 和 D。
以下将是这些交易的 Merkle 树的结果。
使用 Merkle Root 验证有效性
当这些交易被卷进一个区块时,区块头将包含 Merkle Root,哈希 ABCD。所有矿工都有一份迄今为止所有交易的副本,因此也有所有交易的哈希值。任何矿工都可以按需重建 Merkle 树,这意味着每个矿工都可以为同一组交易独立得出相同的 Merkle 根。
这使得任何矿工都可以验证一个欺诈性交易。假设有人试图引入一个虚假交易,而不是交易 D,我们称之为交易 E。交易 E 的哈希值是 Hash E。C 和 E 的哈希值是 Hash CE,它与 Hash CD 不同。当哈希 AB 和 CE 被哈希在一起时,你得到哈希 ABCE。由于哈希 ABCE 与哈希 ABCD 不同,我们可以得出结论,交易 E 是欺诈性的。
矿工可以在自己的区块中重新计算 Merkle Root,并试图将该版本发布到区块链上,但由于其他每个矿工都有不同的 Merkle Root,欺诈性的矿工很容易被拒绝。
哈希函数
我们之前在谈论 IPFS 的时候已经介绍了哈希函数,但只是回顾一下:为了将交易 A 哈希成哈希 A,使用了一个单向的加密哈希函数。一旦散列,哈希 A 不能变成交易 A;散列是不可逆的。
每个区块链使用不同的哈希函数,但它们都有相同的共同属性。
确定性
相同的输入在传递到散列函数时总是有相同的输出。
计算效率高
计算一个输入值的哈希值是非常快的。
不能被反转的工程
给出一个哈希值,几乎不可能确定输入值,即哈希函数是单向的函数。例如:给定 y,很难找到一个 x,使 h(x)=y
抗碰撞性
两个不同的输入不会产生相同的输出。
区块链中默克尔树的好处
Merkle Trees 允许快速验证数据的完整性。
与整个交易集相比,所占用的磁盘空间非常小。出于这个原因,Merkle 根被包含在区块头中。
如果你有两组相同的事务,用 Merkle 树来验证它们是否相同,要比一次一次地单独验证事务来得快。人们可以通过了解 Merkle Root 来验证一个区块没有被修改。
区块链之外的用例
Merkle Trees 不只是用于区块链应用。一些使用 Merkle Trees 的流行应用是。
- IPFS
- Git
- 分布式数据库,如 AWS DynamoDB 和 Apache Cassandra 使用 Merkle 树来控制差异性
验证 Merkle 树中的存在
那么,我们如何实际验证一些数据是 Merkle 树的一部分?
你不希望验证器在 Merkle 树的每一个叶子节点上循环,因为它可能相当大,那么我们怎样才能以一种更有效的方式来做这件事?
假设验证器只有 Merkle Root r,也就是树的顶层父节点。作为验证者,你想向验证者证明 Merkle 树中存在某个值 K。
为了做到这一点,你可以生成一个 Merkle 证明。让我们试着用一个 Merkle 树的例子来理解什么是 Merkle 证明。
主要的想法如下:如果你能给验证者 K 的值,以及所有从树上被散列起来的相关节点,以建立 r 散列,验证者可以将计算出的根值与他们已经拥有的 r 进行比较。如果它们是相同的哈希值,这一定意味着 K 实际上存在于 Merkle 树中,因为你不可能用不同的输入数据产生相同的 Merkle 根哈希值。
在上图中,让我们想一想,必须向验证者提供哪些信息,才能向 验证者肯定地证明 K 是 Merkle 树的一部分。
- K 本身的值(所以验证者可以自行计算 H(K))。
- H(L),所以验证者可以计算 H(KL)
- H(IJ),所以验证者可以计算 H(IJKL)
- H(MNOP),所以验证者可以计算 H(IJKLMNOP)
- H(ABCDEFGH)所以验证者可以计算 H(ABCDEFGHIJKLMNOP)
同样,重要的是要记住,只有一个给定的节点组合可以产生这个唯一的根 r,因为 Merkle 树是一个抗碰撞的哈希函数,这意味着它是一个哈希函数,给定两个输入几乎不可能产生相同的输出。
对于我们给定的例子,我们只需要提供以下节点就能够证明 H[K]在我们的节点中实际存在。
在这一点上,如果 H(ABCDEFGHIJKLMNOP)的计算值与验证者先前已知的值 r 相匹配,那么 K 在 Merkle 树中的存在一定是真的,否则哈希值就不一样了。
这比在整个 Merkle 树上进行循环要有效得多,因为对于一个有 n 个元素的树,你只需要提供大约 log(n)个元素作为证明的一部分(树的每个 "层次 "都有一个)。这意味着如果你有大量的数据,Merkle 树比存储数组或映射更有效率。
当 ENS 推出他们的代币合约时,他们向超过 10 万个钱包地址空投了$ENS 代币。他们能够在天然气费用极高的时候部署他们的合约,价格比他们将钱包地址存储在数组中(即使存储几百个地址也很容易超过区块的天然气限制)要低得多 - https://etherscan.io/tx/0xdfc76788b13ab1c033c7cd55fdb7a431b2bc8abe6b19ac9f7d22f4105bb43bff