ESSAY / NOTE
区块链如何运用merkle tree验证交易真实性
大部分材料都会提到区块中保存了merkle根,并且利用它作交易真实性验证。但是具体如何作这个真实性验证,没有一篇文章可以通俗的讲出来。本文假设你已经知道区块链中merkle tree的原理,现在想搞明白具体怎么来实现交易真实性验证。
上图中最下面那一层就是交易数据,每一个交易都可以计算出一个hash,从而层层向上,得到merkle root。但是由于blockchain里面都merkle运算要求叶子节点是偶数,所以,当一个区块内包含当交易数量为奇数时,把最后一个交易复制一份,凑成偶数。
最后就是把merkle root保存在区块头中,交易数据被保存在区块体中,其实中间当那些hash并没有被保存,它们只是运算过程数据。
你可能觉得有点奇怪,为什么不直接把所有的叶子节点告诉它就行了,你用所有叶子节点能算出root hash就验证通过了。但事实就是这样,因为每一个父节点hash一定是由两个子节点hash运算得到,所以,我们只需要挑选出所有参与运算的节点,就可以证明这个叶子节点存在于树中。这样可以减少hash运算的次数。而这些被挑选出来的节点,以及它们之间的层级关系,就是验证路径,即上图中merkle root那个盒子下面的所有盒子。
Merkle Tree
这个小节简述一下merkle的原理,具体详解会另外写文章,你关注我的文章即可。简单说,merkle tree就是一个hash二叉树,父节点是两个子节点的double sha256的结果,叶子节点就是交易的content的double sha256结果。
上图中最下面那一层就是交易数据,每一个交易都可以计算出一个hash,从而层层向上,得到merkle root。但是由于blockchain里面都merkle运算要求叶子节点是偶数,所以,当一个区块内包含当交易数量为奇数时,把最后一个交易复制一份,凑成偶数。
最后就是把merkle root保存在区块头中,交易数据被保存在区块体中,其实中间当那些hash并没有被保存,它们只是运算过程数据。
SPV
为什么要搞这么复杂?直接把所有交易数据保存起来了,要验证交易是否存在还不简单吗?之所以要这么干,是因为比特币发明之初,中本聪想到有一种轻钱包的设计,这就是SPV(简化支付验证,Simplified Payment Verification)。 轻钱包并不保存完整的区块链,而是只保存每一个区块的区块头。区块体保存了完整的交易信息,而交易信息需要的存储量大部分都是交易头的千倍以上。所以,如果只保存交易头,就可以极大的减少本地客户端存储的区块链信息。 但是,不能因此让区块链无法工作啊。如果这个时候轻钱包要对某一个交易进行验证,而本地又没有这个交易的信息,那怎么验证呢?这时,区块头里面的merkle root就要起作用了。验证路径
在讲述轻钱包的验证过程之前,我们需要知道如何在merkle tree里面做验证。我们已知merkle tree里面父节点和子节点的运算关系,因此,当我们要证明一个叶子节点存在于这棵树时,只需要得到从该叶子节点到根的运算过程里面需要的那些hash即可,并不需要所有叶子节点参与计算。
你可能觉得有点奇怪,为什么不直接把所有的叶子节点告诉它就行了,你用所有叶子节点能算出root hash就验证通过了。但事实就是这样,因为每一个父节点hash一定是由两个子节点hash运算得到,所以,我们只需要挑选出所有参与运算的节点,就可以证明这个叶子节点存在于树中。这样可以减少hash运算的次数。而这些被挑选出来的节点,以及它们之间的层级关系,就是验证路径,即上图中merkle root那个盒子下面的所有盒子。
如何证明交易的真实性?
比特币网络中的交易,只有已经被记录到区块链,并且已经得到6个确认的,才被认为是真实的,只有基于这些真实交易发起的新交易(输入与输出的概念),才是合法的。 我们询问一个交易是否真实,往往基于以下前提:- 我们在问一个交易是否已被记录到区块链中
- 而且这个交易所在的区块链是最长的哪一条,没有在分叉链上
- 当每个节点接收到一条交易广播时,我们要查询作为一笔新交易的输入的真实性
- 矿工对交易进行打包之前,对所有的输入进行真实性验证(在矿工接收到交易信息时就已经验证过了,打包的时候验证2000条交易信息不可能)
- 怎么从区块链里面查一个交易?
- 怎么获取merkle验证路径?
- 怎么确保网络上这个返回的验证路径不是伪造的?
从区块链查交易
区块链的数据结构是离散的,比特币里面一个区块被保存在一个文件里面,要得到一个交易的验证路径,必须得到这个交易所在的区块链。这是一个复制的查询过程,可能需要把所有的区块都遍历一遍才能找到。因此,blockchain.info这样的网站孕育而生,帮助你通过一个信息查这个信息在区块链上的所有相关记录。但是对于客户端而言,可没那么容易,它不能信任blockchain.info这个网站,只能信任自己本地存储的区块链。所以,只能用比较合理的算法,去优化交易查询。 一种设计是,把每一个区块的数据结构修改为关系型数据库,通过关系型数据库,可以用sql语句快速查询。但是,要遍历查询所有区块链,是比较浪费的。还有一种想法是,利用交易的时间戳来快速定位区块位置,在临近的几个区块中快速找到它。如何获取merkle验证路径?
实际上,merkle的验证路径生成的前提是已经存在一棵完整的merkle树。市面上有很多merkle树的实现包,有的包直接给出来getProof的方法来获取某个叶子节点的验证路径。 在客户端收到merkle block message之后,要执行下面的步骤:- 通过上述方法找到包含该交易的区块
- 检查该区块是否是整个网络中最长链条里面的
- 取出所有交易生成merkle tree,利用getProof方法得到该交易的验证路径
- 将该验证路径发送回请求源
- 同步区块链,确保是整个网络中最长的一条
- 先拿到merkle root去区块链中查找,确保该merkle root hash是在链条中
- 利用拿到的验证路径,再进行一次merkle校验,确保验证路径全部合法