本文还是对《区块链:原理、设计与应用》的一个基础技术的总结和摘录。

默克尔树的特点

  默克尔树是多层散列表(Hash List),目的是做多层摘要,把对多个 item 的校验,转化为对一个 item 的校验。

  默克尔树可以是二叉树,也可以是多叉树。

  叶子节点是 item 的 value 和 value 的散列值。中间每一层的值,都是它们子女散列值的的散列值-多叉树的结果就是多个散列值加法的结果的再散列,孤儿的计算结果就是在孤儿散列值上进行再散列。

默克尔树的用途

快速比较大量数据

  两组数据的排序后构建默克尔树,只要比较两个树的root 就可以确定两组数据是否一样。

快速 diff

  如果默克尔树是二叉树,则只要从 root 开始做二分的 diff,就能快速定位到不一致的叶子节点。这在 p2p 传输文件数据的场景里非常有用。实际上 rsync 的 diff 算法就是一个一层的 Hash List

零(部分知识)知识证明

  所谓的零知识证明,就是不告诉 verifier 验证一个论断真伪的全部信息。只提供部分信息,就可以让 verifier 相信某个事情是真的。

  在默克尔树中,我们可以剪去部分枝节,只向 verifier 提供某一个叶子节点通往 root 所需要的 node,然后 verifier 就能断定一个特定的叶子 node 是否在此默克尔树中。也就是说,verifier 要求公允的验算可以让它自己来,我们只提供部分信息。在我看来,这不能算是零知识证明,只能算是部分知识证明。

默克尔树在比特币中的用途

  在比特币中,每个区块里都存有所有交易的完整信息。这些完整信息又可以组成默克尔树的叶子节点,进而组成默克尔树。而每个区块中80bytes的区块头中,都含有五个数据:

  1. 上一个区块的散列值
  2. 时间戳
  3. 挖矿难度值
  4. 工作量证明随机数(nounce)
  5. 包含该区块交易的默克尔树的 root

  轻节点可以只下载这个头,就能做到 SPV(Simplified Payment Verification)。具体过程如下:

验证某个交易是否真实存在时,理论上,用户可以通过以下方式进行验证:

  1. 从网络上获取并保存最长链的所有block header至本地;
  2. 计算该交易的hash值tx_hash;
  3. 定位到包含该tx_hash所在的区块,验证block header是否包含在已知的最长链中;
  4. 从区块中获取构建merkle tree所需的hash值;
  5. 根据这些hash值计算merkle_root_hash;
  6. 若计算结果与block header中的merkle_root_hash相等,则交易真实存在。
  7. 根据该block header所处的位置,确定该交易已经得到多少个确认。

优点:极大地节省存储空间。减轻终端用户的负担。无论未来的交易量有多大,block header的大小始终不变,只有80字节。按照每小时6个的出块速度,每年产出52560个区块。当只保存block header时,每年新增的存储需求约为4兆字节,100年后累计的存储需求仅为400兆,即使用户使用的是最低端的设备,正常情况下也完全能够负载。

默克尔树在以太坊中的应用

  以太坊中实际上有三种默克尔树:

  1. transaction tree
  2. reciept tree
  3. state tree

这三种 tree 可以构建一个很强大的客户,允许轻客户端轻松地进行并核实以下类型的查询答案:

  • 这笔交易被包含在特定的区块中了么?
  • 告诉我这个地址在过去30天中,发出X类型事件的所有实例(例如,一个众筹合约完成了它的目标)
  • 目前我的账户余额是多少?
  • 这个账户是否存在?
  • 假如在这个合约中运行这笔交易,它的输出会是什么?

  第一种是由交易树(transaction tree)来处理的;第三和第四种则是由状态树(state tree)负责处理,第二种则由收据树(receipt tree)处理。计算前四个查询任务是相当简单的。服务器简单地找到对象,获取Merkle分支,并通过分支来回复轻客户端。

  第五种查询任务同样也是由状态树处理,但它的计算方式会比较复杂。这里,我们需要构建一个Merkle状态转变证明(Merkle state transition proof)。从本质上来讲,这样的证明也就是在说“如果你在根S的状态树上运行交易T,其结果状态树将是根为S’,log为L,输出为O” (“输出”作为存在于以太坊的一种概念,因为每一笔交易都是一个函数调用;它在理论上并不是必要的)。