本篇讲解比特币中的数据结构。这里要用到的一个重要概念就是哈希指针。普通的指针存储的是某个结构体在内存中的地址,比如说,有一个结构体,p是指向这个结构体的一个指针,那么p里面存放的就是这个结构体在内存中的起始位置。哈希指针除了要保存这个地址之外,还要保存这个结构体的哈希值。一般是用H( )表示一个哈希指针,这样做的好处是,从这个哈希指针,不只可以找到这个结构体的位置,同时还能够检测出这个结构体的内容有没有被篡改,因为我们保存了它的哈希值。比特币中一个最基本的数据结构就是区块链。区块链是什么?就是一个一个区块组成的链表。区块链和普通的链表相比,有什么区别呢?一个区别就是用哈希指针代替了普通的指针。
我们可以画一个小型区块链的例子。最前面那个区块是系统中产生的第一个区块,叫做创世块,最后这个区块是最近产生的区块。每个区块都包含指向前一个区块的哈希指针,这里面都是有一个小的hash pointers,最后这个区块也有一个hash pointers保存在系统里。这种数据结构的好处是什么呢?取哈希的时候,是把整个区块的内容合在一起取哈希。比如,某个区块的哈希指针是怎么算出来的?是把整个区块的内容,包括里面这个hash pointers合在一起取哈希,算出哈希值。通过这样的数据结构可以实现tamper-evident log。
假如有一个人篡改了区块链中某个地方的某个内容,会有什么结果?这个地方内容改了之后,后面区块的哈希指针就对不上了,因为它是根据前面整个区块内容算出来的,所以后面区块的哈希指针也得改,而再下一个区块里的哈希又核对不上,所以也得改,到最后,系统中保留的哈希也得改,而这个系统的哈希值是我们保存下来的,所以这个数据结构的好处是,只要记住最后的哈希值,就可以检测出对区块链中任何部位的修改。不论是在区块链的哪个部位做了改动,最后都会导致保存的系统哈希值会发生变化。这个是区块链和普通链表的一个区别。普通链表,可以改变其中一个元素,对链表中的其他元素是没有影响的,而区块链是牵一发而动全身。改变前面的任何一个区块,都会引发多米诺骨牌效应,后面所有的区块都得跟着改,所以只要保存最后区块的哈希值就可以知道有没有改区块链中的任何一个部位。区块有这个性质使得比特币中有些节点就不一定要保存整条区块链的内容,它可以只保存最近的几千个区块,再往前的就不用存了。如果要用到以前的区块,可以向系统中的其它节点去要这个区块。有些节点可能是有恶意的,这是一个去中心化的系统,那你怎么知道别人给你的区块是不是正确的,这就用到哈希指针的这个性质。
比如,只保存了后面几个区块,前面的没有保存,要用到前面的区块了,我向别人要。有人给我这样一个区块,那么这个区块是不是正确的,我算一下他的哈希值跟我保存的哈希值对比一下就知道了。
比特币中的另外一个数据结构是merkle tree。你以前应该听说过binary tree,那merkle tree和binary tree有什么区别呢?一个区别就是用哈希指针代替了普通的指针。可以画一个merkle tree的例子。
上面就是一棵merkle tree,最下面的一层是数据块date block,上面这些内部节点都是哈希指针,比如下面某个数据块,它的哈希值存在上面对应节点。旁边数据块的哈希值也是存在上面对应的节点,这两个哈希值拼在一起,再取哈希存在上层节点,旁边的分支也是一样,最后是根结点。这样一层层推上去,根节点也可以取哈希,叫做根哈希值。
这样的数据结构有什么好处呢?只要记住这个根哈希值就能检测出对树中任何部位的修改。区块链的哈希指针组成的链表,只要记住最后这个哈希值,就能检测出这个链表中任何部位的修改。这个是类似的,效率要更高一点,它是一个二叉树的形式,只要记住最上面这个根节点的哈希值,那么整棵树都保护了,如果有某个地方进行了修改,都能检测的到。这是merkle tree的一个特点。比特币当中,各个区块之间用哈希指针连接在一起,每个区块所包含的交易组织成一个merkle tree,这种merkle tree下每个数据块实际上是一个交易transaction。
每个区块分为两部分,块头block header和块身block body。
在block header里面有根哈希值,就是这个区块所包含的所有交易组成的merkle tree的根哈希值是存在这个区块的block header,但是block header里没有交易的具体内容,只有一个根哈希值。block body里面有交易的列表。