普通的指针p指向一个结构体,p中保存的就是该结构体的内存位置。
哈希指针H()指向一个结构体,H()中不仅保存了内存位置,还保存了对应数据的哈希值,以便验证数据内容是否遭受篡改。
该特性使得区块链易检出所在链是否遭受了任何的篡改。
区块链中的第一个区块被称为genesis block(创世纪块),后续链接的每个块都会记录前面一整个区块的哈希值H(x),直至most recent block(最新区块),再对其计算一次哈希值并使用哈希指针指向它。这样便使得倘若整个区块链中存在任何被篡改的痕迹,被篡改的区块的哈希值便会发生变化,这种变化像多米诺骨牌一样一层一层向后传递,直至最后的哈希指针中的哈希值。因此只需要判断最后的哈希值是否发送了变化便可以确定整个区块链是否被篡改过。当然这种检测只能判断出是否被篡改,如果想要判断出何处被篡改,则需要遍历整个区块链分别计算哈希值进行比对。
除了链表的形式,区块链中常用的数据结构还有二叉树,称为Merkle Tree。最下层的叶子结点存放着Transaction(交易),倒数第二层存放着交易叶子结点的哈希值,两个交易的哈希值合并后再取一个哈希值,存放于更上层的父结点中,层层递进,直至根节点,再对根结点取一个根哈希值root hash。这样的Merkle Tree的形式也具有tamper-evident的特性。
区块链中的结点分为全结点和轻结点,全结点存放着两部分内容:block header和block body,其中header中保存着哈希值,而body中则保存着交易列表。轻结点只保存header部分,即哈希值。
假设用户A持有一个比特币钱包,这是一个轻结点,只保存了根哈希值,现在A与用户B之间发生了一笔交易x,那么A要如何知道这笔交易x是否真正地被包括在了这棵Merkle Tree中呢?
首先A从这个轻结点向一个全结点发起请求,请求获取到关于这笔交易x的Merkle proof,全结点收到请求后将图中红色的哈希值发送给该轻结点,此时该轻结点便可以在本地结合交易x的信息和红色的哈希值逐层计算出绿色的哈希值,最终计算出一个根哈希值,将该根哈希值与自己本身存储的根哈希值进行对比,若一致便可以确认该交易x存在于该Merkle Tree中。
由于采取了二叉树的数据结构,这种验证的时间复杂度仅为O(log n)。
那么又该如何证明一个交易不存在于一棵Merkle Tree呢?遍历一整棵Merkle Tree当然是可行的,但是时间复杂度就达到了O(n),有没有什么方法做一些优化呢?
如果先对全部交易的哈希值做一个排序,得到一棵Sorted Merkle Tree,事情就简单了许多。假设目标交易x的哈希值为H(x),根据各个交易的哈希值的大小,可以得到两个交易y和z,使得H(y)