科普|区块链中的Merkle Tree
Posted 博森科技小北
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了科普|区块链中的Merkle Tree相关的知识,希望对你有一定的参考价值。
什么是Merkle Tree?
- Merkle Tree也就是Merkle树,中文译名还有梅克尔树或默克尔树,因为这是一棵用哈希值搭建起来的树,树的所有节点都存储了哈希值,所以也叫哈希树,英文名为Hash Tree。默克尔树通常情况下看着是一棵倒置的树型结构,根节点在顶部,分叉向下,叶子节点在底部。
- Merkle Tree包括这三部分的节点,即根节点、中间节点、叶节点,其作用如下:
- 根节点有且只有一个,也就是Merkle根,也叫Merkle Root,这是终止节点。
- 中间节点会有子节点两两匹配,子节点哈希值合并成新的字符串,对合并结果再次进行哈希运算,得到的哈希值,就是对应的中间节点,这是过程节点。
- 叶节点就是没有子节点的节点,这是初始节点,对于一个区块而言,每一笔买卖数据,进行哈希运算后,得到的哈希值就是叶节点。
- Merkle Tree最早由Merkle Ralf在1980年提出,一开始广泛用于一些文件系统和P2P 系统中。
A
Q
Merkle Tree的应用场景
- 因为Merkle Tree是一种树,虽然大多数是二叉树,但也可以是多叉树。不过无论是几叉树,它都具有树结构的所有特点,其叶子节点的value是数据集合的单元数据或者单元数据HASH。非叶子节点的value是根据它下面所有的叶子节点值,然后按照Hash算法计算而得出的。
- (一)区块链
“大饼”区块链系统中采用的是Merkle Tree二叉树,它的作用主要是快速归纳和校验区块数据的完整性,它会将区块链中的数据分组进行哈希运算,向上不断递归运算产生新的哈希节点,最终只剩下一个Merkle root存入区块头中。
这样首先是极大地提高了区块链的运行效率和可扩展性,使得区块头只需包含根哈希值而不必封装所有底层数据,这使得哈希运算可以高效地运行在智能手机甚至物联网设备上;其次是Merkle Tree可支持“简化支付验证协议”(SPV),即在不运行完整区块链网络节点的情况下,也能够对买卖数据进行检验。所以,在区块链中使用Merkle Tree这种数据结构是非常具有意义的。
- (二)数字签名
最初Merkle Tree目的是高效的处理Lamport one-time signatures,每一个Lamport key只能被用来签名一个消息,但是与Merkle tree结合可以来签名多条Merkle。这种方法成为了一种高效的数字签名框架,即Merkle Signature Scheme。
- (三)P2P网络
在P2P网络中,Merkle Tree用来确保从其他节点接受的数据块没有损坏且没有被替换,甚至检查其他节点不会欺骗或者发布虚假的数据块。大家所熟悉的BT下载就是采用了P2P技术来让客户端之间进行数据传输,一来可以加快数据下载速度,二来可以减轻下载服务器的负担。BT即BitTorrent,是一种中心索引式的P2P文件分析通信协议。
A
Q
平台如何利用Merkle Tree自证
- 这次FTX被曝挪用储户资金导致资不抵债,只是因为大家“信任”把钱放进去了,但其实不清楚自己存放进去的钱是否被挪用了。而默克尔树的作用主要就是用很小的成本就可以验证数据完整性。
- 由于每个买卖都会影响默克尔树根节点的最终哈希值,更改区块中的任意数据都会完全改变默克尔树根节点的哈希。
- 因此,只需存储和验证默克尔树的根节点哈希,就可以验证完整的数据列表。当然,这可以通过在所有买卖上执行任意的哈希操作来实现。
- 也就是说,通过区块中每笔买卖的哈希值(TXID),我们就能获得“树叶”。用户只需要按照规则将自己账户进行一次哈希计算,找到其在这个树中的位置,和相邻节点,然后再一层层的向上计算哈希,最终计算出树根,如果和官方公布的一致那就说明是准备金无误的。
A
1.3.2 区块链中的密码学——Merkle 树
在计算机领域,Merkle树大多用来进行完整性验证处理。在处理完整性验证的应用场景中,特别是在分布式环境下进行这样的验证时,Merkle树会大大减少数据的传输量以及计算的复杂度。
Merkle哈希树是一类基于哈希值的二叉树或多叉树,其叶子节点上的值通常为数据块的哈希值,而非叶子节点上的值是将该节点的所有子节点的组合结果的哈希值。
如下图所示为一个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 Tree
加入最底层有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的结构是这样的:
而有的同学在考虑一种插入的算法,满足下面条件:
- re-hashing操作的次数控制在log(n)以内
- 数据块的校验在log(n)+1以内
- 除非原始树的n是偶数,插入数据后的树没有孤儿,并且如果有孤儿,那么孤儿是最后一个数据块
- 数据块的顺序保持一致
- 插入后的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 https://blog.csdn.net/pony_maggie/article/details/74538902
以上是关于科普|区块链中的Merkle Tree的主要内容,如果未能解决你的问题,请参考以下文章