在计算机领域,Merkle树大多用来进行完整性验证处理。在处理完整性验证的应用场景中,特别是在分布式环境下进行这样的验证时,Merkle树会大大减小数据的传输量以及计算的复杂度。html
Merkle哈希树是一类基于哈希值的二叉树或多叉树,其叶子节点上的值一般为数据块的哈希值,而非叶子节点上的值是将该节点的全部子节点的组合结果的哈希值。node
以下图所示为一个Merkle哈希树,节点A的值必须经过节点C、D上的值计算而获得。叶子节点C、D分别存储数据块001和002的哈希值,而非叶子节点A存储的是其子节点C、D的组合的哈希值,这类非叶子节点的哈希值被称做路径哈希值,而叶子节点的哈希值是实际数据的哈希值。算法
当数据从A端传到B端时,为了检验数据的完整性,只须要验证A、B端上所构造的Merkle树的根节点是否一致便可。若一致,表示数据在传输过程当中没有发生改变。若不一致,说明数据在传输过程当中被修改。并且经过Merkle树很容易定位找到被篡改的节点。定位的时间复杂度为O(log(n))。安全
比特币的轻量级节点所采用的SPV验证就是利用Merkle树这一优势。网络
区块链中的Merkle树是二叉树,用于存储交易信息。每一个交易两两配对,构成Merkle树的叶子节点,进而生成整个Merkle树。Merkle树使得用户能够经过从区块头获得的Merkle树根和别的用户所提供的中间哈希值列表去验证某个交易是否包含在区块中。提供中间哈希值的用户并不须要是可信的,由于伪造区块头的代价很高,而中间哈希值若是伪造的话会致使验证失败。分布式
一般,加密的hash方法像SHA-2和MD5用来作Hash。但若是仅仅防止数据不是蓄意的损坏或篡改,能够改用一些安全性低但效率高的校验和算法,如CRC。学习
Second Preimage Attack: Merkle tree的树根并不表示树的深度,这可能会致使second-preimage attack,即攻击者建立一个具备相同Merkle树根的虚假文档。一个简单的解决方法在Certificate Transparency中定义:当计算叶节点的hash时,在hash数据前加0x00。当计算内部节点是,在前面加0x01。另一些实现限制hash tree的根,经过在hash值前面加深度前缀。所以,前缀每一步会减小,只有当到达叶子时前缀依然为正,提取的hash链才被定义为有效。区块链
Merkle tree操做:加密
1.建立Merckle Treespa
加入最底层有9个数据块。
step1:(红色线)对数据块作hash运算,Node0i = hash(Data0i), i=1,2,…,9
step2: (橙色线)相邻两个hash块串联,而后作hash运算,Node1((i+1)/2) = hash(Node0i+Node0(i+1)), i=1,3,5,7;对于i=9, Node1((i+1)/2) = hash(Node0i)
step3: (黄色线)重复step2
step4:(绿色线)重复step2
step5:(蓝色线)重复step2,生成Merkle Tree Root
易得,建立Merkle Tree是O(n)复杂度(这里指O(n)次hash运算),n是数据块的大小。获得Merkle Tree的树高是log(n)+1。
2.检索数据块
为了更好理解,咱们假设有A和B两台机器,A须要与B相同目录下有8个文件,文件分别是f1 f2 f3 ....f8。这个时候咱们就能够经过Merkle Tree来进行快速比较。假设咱们在文件建立的时候每一个机器都构建了一个Merkle Tree。具体以下图:
从上图可得知,叶子节点node7的value = hash(f1),是f1文件的HASH;而其父亲节点node3的value = hash(v7, v8),也就是其子节点node7 node8的值得HASH。就是这样表示一个层级运算关系。root节点的value实际上是全部叶子节点的value的惟一特征。
假如A上的文件5与B上的不同。咱们怎么经过两个机器的merkle treee信息找到不相同的文件? 这个比较检索过程以下:
Step1. 首先比较v0是否相同,若是不一样,检索其孩子node1和node2.
Step2. v1 相同,v2不一样。检索node2的孩子node5 node6;
Step3. v5不一样,v6相同,检索比较node5的孩子node 11 和node 12
Step4. v11不一样,v12相同。node 11为叶子节点,获取其目录信息。
Step5. 检索比较完毕。
以上过程的理论复杂度是Log(N)。
3. 更新,插入和删除
虽然网上有不少关于Merkle Tree的资料,但大部分没有涉及Merkle Tree的更新、插入和删除操做,讨论Merkle Tree的检索和遍历的比较多。显然,一种树结构的操做确定不只包括查找,也包括更新、插入和删除的啊。后来查到风之舞555的总结的文章,少有感悟,下面引用风之舞555对该部分讲述:
对于Merkle Tree数据块的更新操做实际上是很简单的,更新完数据块,而后接着更新其到树根路径上的Hash值就能够了,这样不会改变Merkle Tree的结构。可是,插入和删除操做确定会改变Merkle Tree的结构,以下图,一种插入操做是这样的:
插入数据块0后(考虑数据块的位置),Merkle Tree的结构是这样的:
而有的同窗在考虑一种插入的算法,知足下面条件:
而后上面的插入结果就会变成这样:
因此,Merkle Tree的插入和删除操做实际上是一个工程上的问题,不一样问题会有不一样的插入方法。若是要确保树是平衡的或者是树高是log(n)的,能够用任何的标准的平衡二叉树的模式,如AVL树,红黑树,伸展树,2-3树等。这些平衡二叉树的更新模式能够在O(lgn)时间内完成插入操做,而且能保证树高是O(lgn)的。那么很容易能够看出更新全部的Merkle Hash能够在O((lgn)2)时间内完成(对于每一个节点如要更新从它到树根O(lgn)个节点,而为了知足树高的要求须要更新O(lgn)个节点)。若是仔细分析的话,更新全部的hash实际上能够在O(lgn)时间内完成,由于要改变的全部节点都是相关联的,即他们要不是都在从某个叶节点到树根的一条路径上,或者这种状况相近。
实际上Merkle Tree的结构(是否平衡,树高限制多少)在大多数应用中并不重要,并且保持数据块的顺序也在大多数应用中也不须要。所以,能够根据具体应用的状况,设计本身的插入和删除操做。一个通用的Merkle Tree插入删除操做是没有意义的。
拓展知识:
Hash List 与 Merkle tree 有什么异同?
娓娓道来~~~~~~~
网络传输数据的时候,A收到B的传过来的文件,须要确认收到的文件有没有损坏。如何解决?
:有一种方法是B在传文件以前先把文件的hash结果给A,A收到文件再计算一次哈希而后和收到的哈希比较就知道文件有无损坏。
可是当文件很大的时候,每每须要把文件拆分不少的数据块各自传输,这个时候就须要知道每一个数据块的哈希值。怎么办呢?
:这种状况,能够在下载数据以前先下载一份哈希列表(hash list),这个列表每一项对应一个数据块的哈希值。对这个hash list拼接后能够计算一个根hash。实际应用中,咱们只要确保从一个可信的渠道获取正确的根hash,就能够确保下载正确的文件。
可是基于hash list的方案这样一个问题: 数据块不少的时候,每每遍历全部数据块的Hash List代价比较大。
有没有一种方法能够经过部分Hash就能校验整个文件的完整性呢?
:答案是确定的!Merkle Tree 就能作到!
Merkle Tree和Hash List的主要区别是,能够直接下载并当即验证Merkle Tree的一个分支。由于能够将文件切分红小的数据块,这样若是有一块数据损坏,仅仅从新下载这个数据块就好了。若是文件很是大,那么Merkle tree和Hash list都很大,可是Merkle tree能够一次下载一个分支,而后当即验证这个分支,若是分支验证经过,就能够下载数据了。而Hash list只有下载整个hash list才能验证。
【时间仓促,若有错误,欢迎指正! || 欢迎留下您的评语! 你们一块儿探讨、学习区块链!】
【转载请注明出处!http://www.cnblogs.com/X-knight/】
REFERENCE
1.Merkle Tree 学习 http://www.cnblogs.com/fengzhiwu/p/5524324.html
2. Merkle Tree 增删数据http://crypto.stackexchange.com/questions/22669/merkle-hash-tree-updates
3.Merkle Tree、Hash List http://www.javashuo.com/article/p-tjfzdjhk-bs.html