Merkle Tree是区块链中一个基本组成部分。虽然理论上也可以没有Merkle Tree来构建区域链, 但是需要直接创建包含每笔交易的巨型数据块头,从长远考虑,这样做会带来巨大的扩展性挑战,除了超级计算机,最后所有其它人都无法使用区块链。有了Merkle Tree,区块链才可以在所有PC和笔记本,智能手机甚至物联网设备上运行。本文主要针对区块链中数据完整性和交易真实性验证方式进行简要介绍和记录,以帮助自己和区块链入门者快速了解区块链中这些核心概念和原理。
什么是Merkle Tree
Merkle Tree也称为Hash Tree,由Ralph Merkle于1979年提出并命名,是基于Hash的数据结构,它是一种树结构,每个叶节点是数据块的Hash,每个非叶节点是其子节点的Hash。Merkle Tree可以高效和安全地实现较大的数据内容验证,它是Hash List和Hash Chain的泛化。Merkle Tree通常实现为二叉树,如下图所示。但Merkle Tree也可以实现节点包含多个子节点的。
Merkle Tree有哪些用途
Mekle Tree被用于分布式系统中,可用于验证计算机之间存储,处理和传输的任何类型数据,确保在P2P网络中收到的数据块没有被破坏或者篡改,甚至有没有发送假数据块。Merkle tree已经被用于以下场景:
为什么不直接将数据块拼成一个大块在计算Hash
Hash Function:实现数据完整性验证最简单的方法是对整个数据进行Hash,通常对于提供下载的文件,发布者会同时分发一个checksum值,用户下载完成后对文件用同样的Hash算法计算checksum,如果用户计处的值与发布者值不同,表明文件已经损坏或者被篡改过,Hash算法保证了数据中任意bit被修改都会造成整个Hash值的改变。这里不会详细讲解Hash算法,常用的Hash算法有md5,sha1,sha2,通常使用sha2进行Hash运算,如果权验证数据是否损坏,可以使用安全性较低的校验和算法(CRC)。
Hash List:p2p被设计成一个分布式网络传输系统,在使用p2p网络传输文件时,文件被分割成大量小数据块,客户端会同时从其它p2p客户机下载数据块,由于网络中不稳定性和不可信的存在,需要对每个数据块进行完整性验证,当其中某块数据损坏时,只重传某块数据而不用重新下载整个文件。为了完成数据块的验证,在文件下载前先获取所有数据块的Hash列表,再将所有Hash列表进行Hash得到一个根Hash,将客户端计算的根Hash与可信根Hash比较来验证Hash列表的完整性。
Merkle Tree:Hash List可认为是一种树高为2的N叉Merkle Tree,实现原理与Hash List类似,将数据分割成小的Block,并计算数据块的Hash,将相邻两个Hash合并后再计算出父Hash,Hash(Hash(DataBlock1) | Hash(DataBlock2)),再将新的相邻的两个父Hash值进行Hash,生成更上层的Hash,最后会汇聚到树的根节点,称为Merkle Root。但是Merkle Tree最重要的好处是可以单独取出Hash树的一个分支对数据进行验证,而不用计算整个Merkle Tree。
Second Preimage攻击
Merkle Tree的根并不表示树的深度,这将导致second-preimage攻击,攻击者可以创建出一个具有相同Merkle Root的的新Merkle Tree分支。一个简单的解决方法在Certificate Transparency中定义:
计算叶节点Hash时,在数据前加0x00,在计算内部节点Hash时,在数据前加0x01,限制Hash树的大小是一些正式安全验证的先决条件。一些实现在Hash前使加树深前缀来限制树的深度,在获取Hash链时,每一步都要减少前缀并且到达叶节点时仍为正才被认为有效。
Merkle Proof
Merkle Proof包括一个数据块,Merkle Root,以及从数据块到根路径上的所有Hash组成的"分支"。阅读证明者可以验证给定数据块的Hash(至少对于当前分支)以及到Root路径上的所有节点的Hash的一致性,并最终验证给定的数据块是否真正在树中的节点上。
Bitcoin中的Merkle证明
Merkle proofs最早由Satoshi Nakamoto在2009年提出并应用在Bitcoin中,Bitcoin区块链使用Merkle proofs将交易存储在每个块中。
Picture From: https://blog.ethereum.org/wp-content/uploads/2015/11/mining.jpg
这样做的好处正如中本聪描述的"simplified payment verification":“light client(轻客户端)”只下载链中区块头的80byte数据块,而不是下载所有交易和所有区块,每个块仅包含以下五个元素:
如果"轻客户端"想要确认交易的状态,只需简单发起一个Merkle proof证明这个特定交易在Merkle tree上,并且树的Merkle root在主链的区块头中。
虽然这样做可以让区块链持续很久,但是Bitcoin "轻客户端"也有其局限性。其中一个限制是,虽然可以证明包含的交易状态,但是无法证明当前的状态(例如:持有的数字资产,名称注册,金融合约的状态等)。用户当前有多少Bitcoin?Bitcoin轻客户端使用一个协议,该协议涉及查询多个节点,并确信其中至少有一个节点会返回通知,包含你的地址支出的任何特定交易,虽然可以满足Bitcoin应用,但对于更加复杂的应用还远远不够。一笔交易影响的确切性取决于前几笔交易,而前一笔交易又依赖更前面的交易,最终不得不验证整条链上的每一笔交易。为了解决这个问题,Ethereum使用更先进的Merkle tree概念。
Ethereum中的Merkle证明
Ethereum区块头不是包含一棵Merkle tree,而是包含三颗树代表三种不同类型:交易、收据和状态。
Picture From: https://blog.ethereum.org/wp-content/uploads/2015/11/ethblockchain_full.png
这允许Ethereum使用更加先进的轻客户端协议让轻客户端很容易地实现以下不同查询类型的验证:
其中,第一条由事务树处理;第三条和第四条由状态树处理,第二条由收据树处理。前四条计算相对简单;服务器只需简单找到对象,获取Merkle分支(从对象到Merkle root的Hash列表)并返回给轻客户端。第五条也由状态树处理,但计算方式更加复杂。我们需要构造一个所谓的Merkle状态转换证明(Merkle state transition proof)。从本质上讲,状态转换证明中声明"如果你在具有根S的状态树上运行交易T,结果将是具有根S‘,日志L和输出为O的状态"(由于每一笔交易都是一个函数调用,所以“输出”只作为Ethereum中的一个概念存在,理论上它并不是必须的)。
为了计算Proof,服务器在本地创建一个假的区块,将状态设置为S,并在应用时充当轻客户端。也就是说,如果应用交易的过程要求客户端确定帐户余额,则轻客户端会发起一个余额查询请求。如果轻客户端需要检查特定合约中存储的特定条目,也同样发起查询请求。服务端正确地"responds"自己所有查询,但跟踪它发回的所有数据。然后,服务端将所有这些作为证明的请求的合并数据发送给客户端。最后客户端执行完全相同的过程,但使用服务端提供的证明做为数据库,如果客户端计算的结果与服务端声明的结果一致,则客户端接受此证明。
Particia Trees
最简单的Merkle tree是一棵二叉树。然而,Ethereum中使用的树更复杂- 称为"Merkle Patricia tree",可以查询Ethereum官方相关文档。
对于事务树,使用二叉Merkle树来是非常好的数据结构,因为树被一旦被创建一次会永久存在,所以编辑树所花费的时间已经不重要了。
然而,对于状态树,情况会更复杂。Ethereum中状态基本上是由键值对组成,其中键是地址,值是帐户声明,对于每个帐户列出每个帐户余额,随机数据,代码和存储(存储本身也是一棵树)。
同时,与事务历史不同,状态需要频繁被更新。账户余额和随机数也经常变化,新帐户频繁创建,存储的key也经常会增删改操作。因此,在做增删改操作后,我们期望一种快速计算新kerkle树根的数据结构,而不是重新计算整棵树。这种结构也要有两个理想的次要属性:
简单来说,Patricia tree可能是我们实现所有这些属性最接近的树。它的工作原理最简单的解释是value存储在key中,key被编码到搜索树的路径中。每个节点有16个子节点,因此路径由十六进制编码决定:例如,key为dog的十六进制编码是6 4 6 15 6 7,所以从根开始,下到第6个子节点,然后到第4个,再进行其余四步,直到最后叶节点。在实践中,当树比较稀疏时,可以根据情况进行一些额外优化,但是要遵循这些基本原则。
总结
Merkle tree使用基于Hash的密码学来确保数据传输的完整性,已经用在许多分布式文件系统,P2P网络,证书验证,可信计算,NoSQL和区块链等方面。Merkle tree中允许取出树中一个分支来验证数据,而避免计算整棵Hash树,除了加带检索效率,也确保了使用Merkle tree的系统的可持续性。本文主要简介了Merkle Tree基本原理和以及在区域链中的使用,对于Merkle tree更深层次的理解,可以阅读一些开源代码(如Ethereum和Hyperledger等)的实现。
References
https://en.wikipedia.org/wiki/Merkle_tree https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/