普通指针存储的是某个结构体在内存中的地址。假如P是指向一结构体的指针,那么P里面存放的就是该结构体在内存中的起始位置。而哈希指针除了要存地址之外,还要保存该结构体的哈希值H()。好处是:从哈希值这个哈希指针,不仅可以找到该结构体的位置,同时还能够检测出该结构体的内容有没有被篡改,因为我们保存了它的哈希值。
![](https://i-blog.csdnimg.cn/blog_migrate/3899b65bee6bc0f9998f49bc9f5fa93d.png)
比特币中最基本的结构就是区块链,区块链就是一个一个区块组成的链表。
区块链和普通的链表的区别:
用哈希指针代替了普通指针 (Bblock chain is a linked list using hash pointers) | 区块链第一个区块叫作创世纪块(genesis block) , 最后一个区块是最近产生的区块(most recent block), 每一个区块都包含指向前一个区块的哈希指针。 |
tamper-evident log | 一个区块的哈希指针怎么算:是把前面整个区块的内容,包括里面的hash pointer ,合在一起取哈希值。通过这种结构,可以实现tamper-evident log。这个性质是说,不论是在哪个区块做了改动,都会导致系统中保存的哈希值的变化,也就是只要记录那一个哈希值就能检测出区块链任何位置有发生了修改。因为修改了某个区块的内容,会导致它前面的区块(相较它新产生的)区块保存的哈希值对不上了,那个哈希值就也要改,然后再往前的就也要改,就像多米诺骨牌一样。这个也就是和普通链表的区别,普通链表可以修改里面任何一个结点,对整个链表其它结点没有影响,但在区块链里修改一个区块就会影响到所有比它后产生的区块。 有了这个性质,某一个用户也就没必要保存系统中的所有区块了,可以只保存最近的一些区块,如果要用到先前产生的区块,再向别人要就可以。 比如只保存了下图中绿线右侧的区块,这些是比较新产生的区块,然后如果要用到紫色圈圈出来的区块,可以向别人要。保存的哈希指针在这里可以校验给的区块是不是正确的(取个哈希值然后和保存的哈希指针的哈希值比较一下),只要用自己保存的哈希指针往下个区块校验,然后再拿校验好的区块的哈希指针再往下校验,如此反复就可以了。
|
比特币中的另外一个结构是:Merkle tree
其中最下面一层是数据块(data blocks),上面三层内部节点都是哈希指针(hash pointers),第一层是根节点,根节点的区块也可以取个哈希,叫根哈希(root hash))
和区块链一样,在Merkle Tree中,只要记录下根哈希值,就能检测出对树中任何部位的修改,也就是用根哈希值保护了整棵树上没有篡改。相比前面的区块链这个效率更高。
在比特币系统中,Merkle Tree的每个数据块代表着一个交易,整棵树也就是在记录比特币系统中的交易,用根哈希值防止这些交易信息被篡改。
另外一个概念:binary tree。
这种结构的好处:只要记住根哈希值,就能检测出对树中任何部位的修改。
它们的区别:①用哈希指针代替了普通指针。
比特币当中各区块之间用哈希指针连接在一起,每个区块所包含的交易组织成一个merkle tree的形式,最下面一行data blocks每个区块实际上是一个交易,每个区块分为两部分,分别是块头和块身(block header ,block body)。块头里面有根哈希值,每个区块所包含的所有交易组成的merkle tree的根哈希值存在于区块的块头里面,但是,块头里没有交易的具体内容,只有一个根哈希值,块身里面是有交易的列表的。
Markle Tree的实际用途
merkle tree 的作用:①提供merkle proof
比特币中的节点分为两类 : 全节点(保存整个区块的内容,即块头块身都有,有交易的具体信息)和轻节点(例如手机上的比特币钱包)(只有块头)
(1)全结点
既有块头又有块身的区块,保存了交易的具体信息。
(2)轻结点
只保存了块头,没有保存块身。例如手机上的比特币钱包就使用的是轻结点。
这时存在一个问题:如何向一个轻节点证明某个交易是写入区块链的?
如何向轻结点证明某个交易是写入了区块链的?例如有人向自己转账比特币,在手机上的比特币钱包使用的是轻结点,轻结点没有块身也就没有存储交易列表,只有一个根哈希值,如何证明这笔交易是真实存在某个区块中的?
Merkle proof要做的就是通过区块中的块头中的Merkle Tree的根哈希值证明某个交易是存在于这个区块所对应的Merkle Tree中的,简单的说就是验证一下Merkle Tree中存在某个交易,这也叫作proof of membership或proof of inclusion。这涉及在Merkle Tree中从指定交易的数据结点到根节点的路径。
例如,在下图中,天蓝色数据结点是待证明存在的交易,这里轻结点只有一个根哈希值是不够的,要向全结点请求下图中标红的三个哈希值,然后就只需在本地为交易的数据结点向上一步步计算和拼接计算哈希值,最终和根哈希值对比,来知晓这个交易是不是真实存在这个Merkle Tree中的了。
上拍的图:
最上面一行是小型的区块链,该图展现的是一个区块的merkle tree,最下面一行是包含的交易。假设某个轻节点想知道图中黄色的交易,是否包含在了merkle tree里面。该轻节点没有包含交易列表,没有这颗merkle tree的具体内容,只有一个根哈希值。这时轻节点向一个全节点发出请求,请求证明黄色的交易被包含在这颗merkle tree里面的merkle proof。全节点收到这个请求之后,只需要将图中标为红色的这三个哈希值发给轻节点即可。有了这些哈希值之后,轻节点可以在本地计算出图中标为绿色三个哈希值。首先算出黄色交易的哈希值,即它正上方的那个绿的哈希值,然后跟旁边红色的哈希值拼接起来,可以算出上层节点绿色的哈希值。然后再拼接,再算出上层绿色哈希值,再拼接,就可以算出整棵树的根哈希值。轻节点把这个根哈希值和block header里的根哈希值比较一下,就能知道黄色的交易是否在这颗merkle tree里。
全节点在merkle proof 里提供的这几个哈希值(红色的H()),就是从黄色的交易所在的节点的位置到树根的路径上用到的这些哈希值。轻节点收到这样一个merkle proof之后,只要从下往上验证,沿途的哈希值都是正确的即可。(验证时只能验证该路径的哈希值,其他路径是验证不了的,即该图中红色的哈希值是验证不了的)
对于一个轻节点来说,验证一个merkle proof 复杂度是多少?
假设最底层有n个交易,则merkle proof 复杂程度是θ(log(n))。
merkle proof 可以证明 merkle tree 里面包含了某个交易,所以这种证明又叫proof of membership或 proof of inclusion。
思考:是否存在不安全的情况?如下图我们要验证B,但是H(1)和H(4)都是全节点提供的。全节点可否修改B,通过H(1)调整,使得修改过后的H(1)和轻节点计算出的H(2)一起取得哈希仍然为H(3)?
实际上,这种情况为人为制造哈希碰撞。而由于公开课笔记2中可知,由于哈希函数的collision resistance性质,这种情况是不会发生的。从而,保证了系统的不可篡改性。同时,这样一个Markle Proof的事件复杂度为O(log n),非常高效【证明交易存在】。如果要证明交易不存在,如果不对叶节点规定排序顺序,没有一个效率较高的方法证明不存在。
在比特币系统中,没有相应的需求,所以在比特币系统中并没有对Markle Tree进行排序。
如何证明merkle tree里面没有包含某个交易?
即proof of non-membership。可以把整棵树传给轻节点,轻节点收到后验证树的构造都是对的,每一层用到的哈希值都是正确的,说明树里只有这些叶节点,要找的交易不在里面,就证明了proof of non-membership。问题在于,它的复杂度是线性的θ(n),是比较笨的方法。
如果对叶节点的排列顺序做一些要求,比如按照交易的哈希值排序。每一个叶节点都是一次交易,对交易的内容取一次哈希,按照哈希值从小到大排列。要查的交易先算出一个哈希值,看看如果它在里面该是哪个位置。
比如说在第三个第四个之间,这时提供的proof是第三个第四个叶节点都要往上到根节点。如果其中哈希值都是正确的,最后根节点算出的哈希值也是没有被改过的,说明第三、四个节点在原来的merkle tree里面,确实是相邻的点。要找的交易如果存在的话,应该在这两个节点中间。但是它没有出现,所以就不存在。其复杂度也是log形式,代价是要排序。排好序的叫作sorted merkle tree。
比特币中没有用到这种排好序的merkle tree,因为比特币中不需要做不存在证明。
这节讲了比特币中两种最基本的结构:区块链和merkle tree,都是用哈希指针来构造的。
除了这两种之外,哈希指针还能用另一个方面。
只要一个数据结构是无环的(非循环链表),都能用哈希指针代替普通指针。有环的话存在一个问题,他们的哈希值没法计算,没法确定一个哈希值固定的区块。