默克尔树(又叫哈希树)是一种二叉树,由一个根节点、一组中间节点和一组叶节点组成。
最下面的叶节点包含存储数据或其哈希值,每个中间节点是它的两个孩子节点内容的哈希 值,根节点也是由它的两个子节点内容的哈希值组成。
进一步的,默克尔树可以推广到多叉树的情形。
默克尔树的特点是,底层数据的任何变动,都会传递到其父亲节点,一直到树根。
默克尔树的典型应用场景包括:
快速比较大量数据:当两个默克尔树根相同时,则意味着所代表的数据必然相同。
快速定位修改:例如上例中,如果 D1 中数据被修改,会影响到 N1,N4 和 Root。因 此,沿着 Root --> N4 --> N1,可以快速定位到发生改变的 D1;
零知识证明:例如如何证明某个数据(D0……D3)中包括给定内容 D0,很简单,构造 一个默克尔树,公布 N0,N1,N4,Root,D0 拥有者可以很容易检测 D0 存在,但不知 道其它内容。
叶子节点的数据是原始数据或者原始数据的数字摘要(hash值)。非叶子节点的数据是根据其所有子节点数据计算出的数字摘要(hash值)。
快速比较大量数据,从根节点开始比较,可快速找到变更的数据。
BitCoin的Merkle Proof
Merkle Proof最早的应用是Bitcoin,它是由中本聪在2009年描述并创建的。Bitcoin的Blockchain利用Merkle proofs来存储每个区块的交易。
而这样做的好处,也就是中本聪描述到的“简化支付验证”(Simplified Payment Verification,SPV)的概念:一个“轻客户端”(light client)可以仅下载链的区块头即每个区块中的80byte的数据块,仅包含五个元素,而不是下载每一笔交易以及每一个区块:
- 上一区块头的哈希值
- 时间戳
- 挖矿难度值
- 工作量证明随机数(nonce)
- 包含该区块交易的Merkle Tree的根哈希
如果客户端想要确认一个交易的状态,它只需简单的发起一个Merkle proof请求,这个请求显示出这个特定的交易在Merkle trees的一个之中,而且这个Merkle Tree的树根在主链的一个区块头中。
但是Bitcoin的轻客户端有它的局限。一个局限是,尽管它可以证明包含的交易,但是它不能进行涉及当前状态的证明(如数字资产的持有,名称注册,金融合约的状态等)。
Bitcoin如何查询你当前有多少币?一个比特币轻客户端,可以使用一种协议,它涉及查询多个节点,并相信其中至少会有一个节点会通知你,关于你的地址中任何特定的交易支出,而这可以让你实现更多的应用。但对于其他更为复杂的应用而言,这些远远是不够的。一笔交易影响的确切性质(precise nature),可以取决于此前的几笔交易,而这些交易本身则依赖于更为前面的交易,所以最终你可以验证整个链上的每一笔交易。为了解决这个问题,Ethereum的Merkle Tree的概念,会更进一步。
Ethereum的Merkle Proof
每个以太坊区块头不是包括一个Merkle树,而是为三种对象设计的三棵树:
- 交易Transaction
- 收据Receipts(本质上是显示每个交易影响的多块数据)
- 状态State
这使得一个非常先进的轻客户端协议成为了可能,它允许轻客户端轻松地进行并核实以下类型的查询答案: - 这笔交易被包含在特定的区块中了么?
- 告诉我这个地址在过去30天中,发出X类型事件的所有实例(例如,一个众筹合约完成了它的目标)
- 目前我的账户余额是多少?
- 这个账户是否存在?
- 假如在这个合约中运行这笔交易,它的输出会是什么?
第一种是由交易树(transaction tree)来处理的;第三和第四种则是由状态树(state tree)负责处理,第二种则由收据树(receipt tree)处理。计算前四个查询任务是相当简单的。服务器简单地找到对象,获取Merkle分支,并通过分支来回复轻客户端。