[转帖]Merkle树

核能气质少年 提交于 2019-12-03 07:00:01

Merkle树

https://www.jianshu.com/p/fc439a8fd0de

 

所谓比特币交易就是从一个比特币钱包向另一个中转账,每笔交易都有数字签名来保证安全。一个交易一旦发生那么就是对所有人都公开的,每个交易的历史可以最终追溯到相应的比特币最初被挖出来的那个点。

先简单回顾一下比特币交易过程。

其实比特币并不存在于任何地方。有人如果持有比特币,那么他们其实是拥有特定比特币的地址,但是所谓的币并不是直接就存在于这个地址中的,地址就相当于你的银行账户。

有的只是各个地址之间的转账记录,余额时增时减。所有的交易都存放在一个非常大的账本文件中,这个文件叫做“区块链”。一个比特币地址中的余额,不是直接存放在比特币地址中的,我们需要到区块链中去计算出来。

如果 Alice 给 Bob 发送一些比特币,那么这个交易就有三项信息:

§ 输入。这里面记录了最初 Alice 拥有的这些币是从哪个地址转给她的,假设她是从她的朋友 Eve 那里得到的币。

§ 数目。这个就是 Alice 到底给 Bob 转了多少个比特币。

§ 输出。Bob 的比特币地址。

发送交易需要两样东西,比特币地址和对应的私钥。比特币地址跟银行账号不一样,不需要签署一堆文件去申请,它们是随机生成的,就是一串由字母和数字组成的字符串。私钥也是类似的一个字符串,但是这个是要严格保密的。比特币地址就好像一个透明的存钱罐,每个人都可以看到里面有什么,但是只有拥有私钥的人才能打开它。

当 Alice 想要给 Bob 转币的时候,就用私钥来签署一段信息,其中包括输入、数目和输出这三项。这样,信息广播到比特币网络上,矿工就可以验证这次交易,把交易加入区块链中了。

因为比特币只是以交易记录的形式存在,所以很多时候一个地址上面其实是对应很多个交易的。可能 Jane 发送给了Alice 40个比特币、Lucy 给了40个、Eve 给了20个,这些都是不同时间的不同的交易,他们并没有被合成到 Alice 的一个钱包里形成一个有100个币的文件,而是仍然作为独立的各个交易记录存在。

当 Alice 想要给 Bob 转币的时候,她的钱包就会找到几个交易,让它们的数额加起来正好是 Alice 想要转的数目。当然,很可能Alice 想要给 Bob 转币的时候,没有办法找到几个交易加起来正好是转账数额。也许她想要转30个币,但是钱包中根本没有一个交易或是多个交易的和正好是这个数目。

同时,她没有办法把一个交易切割成小的数额。就是这样,你没有办法切割一个大的交易成为多个小数额,每次都必须花掉整个交易。但是不用担心,系统会给她把多发送出去的币作为找零还给你。

Alice 这时就可以把 Jane 给她发送过来的两个币发送给 Bob,这样 Jane 就是“输入”,Bob 就是“输出”,“数额”是30个币,是 Alice 真正想要转账的数目。这样,Alice 的钱包就会自动给她的这次交易创建两个输出:把30个币给 Bob,剩下的币放到一个新的地址中,这个是找回的零钱。

区块结构

工作量证明中出现过一个区块信息截图:

 

​如上图(区块结构图)所示:每个数据区块包含区块头和区块体。

区块头封装了当前版本号、前一区块哈希值、当前区块PoW要求的随机数(Nonce)、时间戳、以及Merkle根信息。

区块体则包括当前区块经过验证的、 区块创建过程中生成的所有交易记录。这些记录通过 Merkle树的哈希过程生成唯一的Merkle根并记入区块头。

区块哈希值实际上并不包含在区块的数据结构里,其实区块打包时只有区块头被用于计算哈希(从网络被接收时由每个节点计算出来),常说的区块哈希值实际是区块头哈希值,它可以用来唯一、明确地标识一个区块。

区块头是80字节,而平均每个交易至少是250字节,而且平均每个区块包含2000个交易。因此,包含完整交易的区块比区块头的4千倍还要大。

所以,比特币网络中,不是每个节点都有能力储存完整的区块链数据,受限于存储空间的的限制,很多节点是以SPV(Simplified Payment Verification简单支付验证)钱包接入比特币网络,通过简单支付验证可以在不必存储完整区块链下对交易进行验证,今天我们来分析区块结构Merkle树及如何进行交易验证。首先,需要了接一下广义Merkle树

 假设你已经知道了什么是哈希算法以及哈希的作用。

网络传输数据的时候,A收到B的传过来的文件,需要确认收到的文件有没有损坏。如何解决?

有一种方法是B在传文件之前先把文件的hash结果给A,A收到文件再计算一次哈希然后与收到的哈希对比就知道文件有无损坏。

但是当文件很大的时候,往往需要把文件拆分很多的数据块各自传输,这个时候就需要知道每个数据块的哈希值。怎么办呢?

这种情况,可以在下载数据之前先下载一份哈希列表(hash list),这个列表每一项对应一个数据块的哈希值。对这个hash list拼接后可以计算一个根hash。实际应用中,我们只要确保从一个可信的渠道获取正确的根hash,就可以确保下载正确的文件。

上面基于hash list的方案这样一个问题:

有些时候我们获取(遍历)所有数据块的hash list代价比较大,只能获取部分节点的哈希。

有没有一种方法可以通过部分hash就能校验整个文件的完整性呢?

答案是肯定的,Merkle树能做到。它长这样子: 

 

特点如下:

1、数据结构是一个树,可以是二叉树,也可以是多叉树(今天以二叉树来分析)

2、Merkle树叶子节点的value是数据集合的单元数据或者单元数据哈希。

3、Merkle树非叶子节点value是其所有子节点value的哈希。

很明显,这种结构跟hash list相比较,根哈希不是用所有的数据块哈希拼接起来计算的,而是通过一个层级的关系计算出来的。

在上图中,叶子节点node7的value(v7) = hash(f1),是f1文件的哈希;而其父节点node3的value = hash(v7, v8),也就是其子节点node7 node8的值的哈希。

其它应用场景

文件下载

假设我有两台机器,A和B,有一个文件从A传输到B。B首先获取可信的文件Merkle树,当文件下载完毕后,B通过自己构建的Merkle树根节点与获取的根节点比较,如果不一致,通过这种二叉树的结构可以在log(N)的复杂度快速定位到出错的数据块。

副本同步

一个集群里的所有机器,需要保持数据的同步,如果数据不一致,需要快速的定位到不一致的节点。

可以在每台机器上针对每个区间里的数据构造一棵Merkle树,这样,在两台机器间进行数据比对时,从Merkle树的根节点开始进行比对,如果根节点一样,则表示两个副本目前是一致的,不再需要任何处理;如果不一样,则遍历Merkle树,定位到不一致的节点也非常快速。

区块链中的每个区块都包含了产生于该区块的所有交易,且以Merkle树表示。

Merkle树是一种哈希二叉树,它是一种用作快速归纳和校验大规模数据完整性的数据结构。这种二叉树包含加密哈希值。术语“树”在计算机学科中常被用来描述一种具有分支的数据结构,但是树常常被倒置显示,“根”在图的上部同时“叶子”在图的下部,你会在后续章节中看到相应的例子。

在比特币网络中,Merkle树被用来归纳一个区块中的所有交易,同时生成整个交易集合的数字指纹,且提供了一种校验区块是否存在某交易的高效途径。生成一棵完整的Merkle树需要递归地对哈希节点对进行哈希,并将新生成的哈希节点插入到Merkle树中,直到只剩一个哈希节点,该节点就是Merkle树的根。在比特币的Merkle树中两次使用到了SHA256 算法,因此其加密哈希算法也被称为double-SHA256。

当N个数据元素经过加密后插入Merkle树时,至多计算2*log2(N) 次就能检查出任意某数据元素是否在该树中,这使得该数据结构非常高效。

Merkle树是自底向上构建的。在如下的例子中,我们从A、B、C、D四个构成Merkle树树叶的交易开始,如下图1:

 
图1计算默克树中的节点

所有的交易都并不存储在Merkle树中,而是将数据哈希化,然后将哈希值存储至相应的叶子节点。这些叶子节点分别是HA、HB、HC和HD:

HA = SHA256(SHA256(Transaction A))

将相邻两个叶子节点的哈希值串联在一起进行哈希,这对叶子节点随后被归纳为父节点。 例如,为了创建父节点H~AB~,子节点 A和子节点B的两个32字节的哈希值将被串联成64字节的字符串。随后将字符串进行两次哈希来产生父节点的哈希值:

HAB = SHA256(SHA256(HA + HB))

继续类似的操作直到只剩下顶部的一个节点,即Merkle根。产生的32字节哈希值存储在区块头,同时归纳了四个交易的所有数据。图1展示了如何通过成对节点的哈希值计算Merkle树的根。

因为Merkle树是二叉树,所以它需要偶数个叶子节点。如果仅有奇数个交易需要归纳,那最后的交易就会被复制一份以构成偶数个叶子节点,这种偶数个叶子节点的树也被称为平衡树。如下图2所示,C节点被复制了一份。

 
图2 复制一个数据元素可以实现偶数个数据元素

​由四个交易构造Merkle树的方法同样适用于从任意交易数量构造Merkle树。在比特币中,在单个区块中有成百上千的交易是非常普遍的,这些交易都会采用同样的方法归纳起来,产生一个仅仅32字节的数据作为Merkle根。在图3中,你会看见一个从16个交易形成的树。需要注意的是,尽管图中的根看起来比所有叶子节点都大,但实际上它们都是32字节的相同大小。无论区块中有一个交易或者有十万个交易,Merkle根总会把所有交易归纳为32字节。

 
图3 Merkle树汇总了许多数据元素

为了证明区块中存在某个特定的交易,一个节点只需要计算log~2~(N)个32字节的哈希值,形成一条从特定交易到树根的认证路径或者Merkle路径即可。随着交易数量的急剧增加,这样的计算量就显得异常重要,因为相对于交易数量的增长,以基底为2的交易数量的对数的增长会缓慢许多。这使得比特币节点能够高效地产生一条10或者12个哈希值(320~384字节)的路径,来证明一个巨量字节大小的区块里上千笔交易中某笔交易的存在。

在图4中,一个节点能够通过生成一条仅有4个32字节哈希值长度(总128字节)的Merkle路径,来证明区块中存在的一笔交易K。该路径有4个哈希值(图中蓝色)HL、HIJ、HMNOP和HABCDEFGH。由这4个哈希值产生的认证路径,再通过计算另外四对哈希值HKL、HIJKL、HIJKLMNOP和Merkle树根(虚线标注),任何节点都能证明H~K~(黑色标注)包含在Merkle根中。

 
图4用于证明包含数据元素的Merkle路径  

​Merkle树的高效随着交易规模的增加而变得异常明显。表2展示了为了证明区块中存在某交易而所需转化为Merkle路径的数据量。

 

从表中可以看出,当区块大小由16笔交易(4KB)急剧增加至65,535笔交易(16MB)时,为证明交易存在的Merkle路径长度增长极其缓慢,仅仅从128字节到512字节。有了Merkle树,一个节点能够仅下载区块头(80字节/区块),然后从一个全节点回溯一条小的Merkle路径就能认证一笔交易的存在,而不需要存储或者传输大量区块链中的大多数内容,这些内容可能有几个G的大小。这种不需要维护一条完整的区块链的节点,又被称作简单支付验证(SPV)节点,它不需要下载整个区块而通过Merkle路径去验证交易的存在。

Merkle树和简单支付验证(SPV)

Merkle树被SPV节点广泛使用。SPV节点不保存所有交易也不会下载整个区块,仅仅保存区块头。它们使用认证路径或者Merkle路径来验证交易存在于区块中,而不必下载区块中所有交易。

例如,一个SPV节点想知道它钱包中某个比特币地址即将到达的支付。该节点会在节点间的通信链接上建立起bloom过滤器,限制只接受含有目标比特币地址的交易。当节点探测到某交易符合bloom过滤器,它将以Merkleblock消息的形式发送该区块。Merkleblock消息包含区块头和一条连接目标交易与Merkle根的Merkle路径。SPV节点能够使用该路径找到与该交易相关的区块,进而验证对应区块中该交易的有无。SPV节点同时也使用区块头去关联区块和区块链中的其余区块。这两种关联、交易与区块、区块和区块链,就可以证明交易存在于区块链。简而言之,SPV节点会收到少于1KB的有关区块头和Merkle路径的数据,其数据量比一个完整的区块(目前大约有1MB)少了一千多倍。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!