第九章 区块链
9.1 简介
区块链数据结构是一个有序的、反向链接的包含交易的区块列表。它可以被存储为平面文件(flat file),或是存储在一个简单数据库中。Bitcoin Core客户端使用Google的LevelDB数据库存储区块链元数据。区块被“向后”链接,每个区块都指向链中的前一个区块。区块链经常被视为一个垂直的栈,第一个区块作为栈底的首区块,随后每个区块都被放置在之前的区块之上。区块彼此堆叠在一起这种形象化,可以方便使用一些术语,例如:“高度”来表示区块与首区块之间的距离,以及“顶部”或“顶端”来表示最新添加的区块。
区块链中的每个区块都由一个哈希值标识,该哈希值是对区块头进行SHA256加密哈希算法生成。同时, 每一个区块都可以通过其区块头的“父区块哈希值”字段引用前一区块,叫父区块。也就是说,每个区块头都包含它的父区块哈希值。这样把每个区块链接到各自父区块的哈希值序列就创建了一条链,可以一直追溯到有史以来的第一个区块,叫创世区块。
虽然每个区块只有一个父区块,但可以暂时拥有多个子区块。每个子区块都将同一区块作为其父区块,并且在“父区块哈希值”字段中具有相同的父区块哈希值。一个区块出现多个子区块的情况被称为“区块链分叉”。区块链分叉只是暂时状态,只有当多个不同区块被不同的矿工几乎同时发现时才会发生(参见【10.10.1 区块链分叉】)。最终,只有一个子区块会成为区块链的一部分,区块链分叉的问题就被解决了。尽管一个区块可能会有不止一个子区块,但每一区块只有 一个父区块,这是因为一个区块只有一个“父区块哈希值”字段可以指向它的唯一父区块。
由于区块头里面包含“父区块哈希值”字段,所以也会影响到当前区块的哈希值。如果父区块的标识发生变化,子区块的标识也会跟着变化。当父区块有任何改动时,父区块的哈希值也发生变化。这将迫使子区块的“父区块哈希值”字段发生改变,从而又将导致子区块的哈希值发生改变。而子区块的哈希值发生改变又将迫使孙区块的“父区块哈希值”字段发生改变,又因此改变了孙区块哈希值,以此类推。这种瀑布效应确保了一旦一个区块之后有很多后代,要对其进行修改,就不得不强制重新计算该区块所有后续的区块。正是这样的重新计算需要耗费巨大的计算量(以及由此带来的能量消耗),所以一个长区块链的存在可以让区块链的深度历史无法改变,这也是比特币安全性的一个关键特征。
你可以把区块链想象成地质构造中的地质层或者是冰川岩芯样品。表层可能会随着季节而变化,甚至在沉积之前就被风吹走了。但是越往深处,地质层就变得越稳定。到了几百英尺深的地方,你看到的将是保存了数百万年但依然未受干扰的过去的快照。在区块链里,最近的几个区块可能会由于区块链分叉所引发的重新计算而被修改。最新的六个区块就像几英寸深的表土层。但是,超过这六块后,区块在区块链中的位置越深,被改变的可能性就越小。在100个区块以后,区块链已经足够稳定,这时Coinbase交易(包含新挖出的比特币的交易)可以被花费了。几千个区块(一个月)后的区块链将变成确定的历史,实际上永远不会改变。虽然协议总是允许一条链被较长的链取代,所以任何区块被逆转的可能性总是存在,但这样的事件的概率随着时间的流逝而减小,直到永不可能。
9.2 区块结构
区块是一种容器数据结构,用于聚合要包含在公共分类账簿(区块链)中的交易。它由一个包含元数据的区块头和紧跟其后的占据区块最大空间的一长串交易列表组成。区块头是80字节,而平均每个交易至少是400字节,而且平均每个区块至少包含超过1900个交易。因此,一个包含所有交易的完整区块比区块头大1000倍。表7-1描述了一个区块结构。
表7-1 区块结构
4 bytes
Block Size
The size of the block, in bytes, following this field
80 bytes
Block Header
Several fields form the block header
1–9 bytes (VarInt)
Transaction Counter
How many transactions follow
Variable
Transactions
The transactions recorded in this block
9.3 区块头
区块头由三组区块元数据组成。首先,有一个对前一个区块哈希的引用,它将此区块连接到区块链中的前一个区块。第二组元数据,即难度difficulty、时间戳timestamp和随机数nonce,与挖矿竞争相关,详见【第10章挖矿】。第三组元数据是默克尔树根,一种用来有效地汇总区块中所有交易的数据结构。表7-2描述了区块头的数据结构。
表7-2 区块头结构
4 bytes
Version
A version number to track software/protocol upgrades
32 bytes
Previous Block Hash
A reference to the hash of the previous (parent) block in the chain
32 bytes
Merkle Root
A hash of the root of the merkle tree of this block’s transactions
4 bytes
Timestamp
The approximate creation time of this block (seconds from Unix Epoch)
4 bytes
Difficulty Target
The Proof-of-Work algorithm difficulty target for this block
4 bytes
Nonce
A counter used for the Proof-of-Work algorithm
随机数、难度目标和时间戳会用于挖矿过程,更多细节将在【第10章挖矿】讨论。
9.4 区块标识符:区块头哈希值和区块高度
区块标识符最主要的是它的加密哈希值,一个通过SHA256算法对区块头进行二次哈希计算而得到的数字指纹。产生的32字节哈希值被称为区块哈希值block hash,但是更准确的名称是:区块头哈希值,因为只有区块头被用于计算。例如:000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f是比特币第一个区块的区块哈希值。区块哈希值可以唯一、明确地标识一个区块,并且任何节点通过简单地对区块头进行哈希计算都可以独立地获取该区块哈希值。
请注意,一个区块不管是在网络上传输时,还是它作为区块链的一部分被存储在某节点的永久性存储设备上,其区块哈希值实际上都不包含在区块的数据结构里。相反,区块哈希值是由从网络上收到该区块的每个节点计算出来的。区块哈希值可能会作为区块元数据的一部分被单独存储在一个数据库表中,以便于索引和更快地从磁盘检索区块。
第二种识别区块的方式是通过该区块在区块链中的位置,即区块高度block height。第一个区块,其区块高度为 0,和之前哈希值000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f所引用的区块为同一个区块。因此,区块可以通过两种方式被标识:区块哈希值或者区块高度。每一个随后被存储在第一个区块之上的区块在区块链中都比前一区块“高”出一个位置,就像箱子一样,一个接一个堆叠在其他箱子之上。2017年1月1日的区块高度大约是 446,000,说明已经有446,000个区块被堆叠在2009年1月创建的第一个区块之上。
和区块哈希值不同的是,区块高度并不是唯一的标识符。虽然一个单一的区块总是会有一个明确的、固定的区块高度, 但反过来却并不成立,一个区块高度并不总是标识一个唯一的区块。两个或两个以上的区块可能有相同的区块高度,在区块链里争夺同一位置。这种情况在【10.10.1 区块链分叉】一节中有详细讨论。区块高度也不是区块数据结构的一部分, 不被存储在区块里。当节点接收来自比特币网络的区块时,会动态地识别该区块在区块链里的位置(区块高度)。 区块高度也可作为元数据存储在一个索引数据库表中以便快速检索。
提示一个区块的区块哈希值总是能唯一地标识出一个特定区块。一个区块也总是有特定的区块高度。但是,一个特定的区块高度并不一定总是能唯一地标识一个特定区块。更确切地说,两个或者更多数量的区块也许会为了区块链中的一个位置而竞争。
9.5 创世区块
区块链里的第一个区块创建于2009年,被称为创世区块。它是区块链里面所有区块的共同祖先,这意味着你从任一区块,循链向后回溯,最终都将到达创世区块。 因为创世区块被静态编入到比特币客户端软件里,无法改变,所以每一个节点都是从最少包括这个区块的区块链开始。每一个节点都“知道”创世区块的哈希值、结构、被创建的时间和里面的一个交易。因此,每个节点都有了区块链的起点,一个安全的“根”,从中构建一个可信的区块链。
在chainparams.cpp里可以查看Bitcoin Core客户端里创世区块静态编码。
创世区块的哈希值为:
0000000000 19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
你可以在任何区块浏览网站搜索这个区块哈希值,如blockchain.info,你会发现一个描述这一区块内容的页面,该页面的链接包含了这个区块哈希值:
https://blockchain.info/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
在命令行使用Bitcoin Core客户端:
创世区块包含一个隐藏的信息。在其Coinbase交易的输入中包含这样一句话“The Times 03/Jan/2009 Chancellor on brink of second bailout forbanks.”这句话是泰晤士报当天的头版文章标题,引用这句话,是证明该区块最早的创建日期,也可视为半开玩笑地提醒人们一个独立的货币制度的重要性,告诉人们与比特币的发行,同时伴随着一场前所未有的世界性货币危机。该消息是由比特币的创立者中本聪嵌入创世区块中的。
9.6 将区块连成区块链
比特币的全节点在本地保存了区块链从创世区块起的完整副本。随着新的区块的产生,该区块链的本地副本会不断地更新用于扩展这个链条。当一个节点从网络接收传入的区块时,它会验证这些区块,然后链接到现有的区块链上。为建立这种链接,节点将检查传入的区块头并寻找该区块的“父区块哈希值”。
假设,例如一个节点在区块链的本地副本中有277,314个区块。该节点知道最后一个区块为第277,314区块,这个区块的区块头哈希值为:
00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249
然后该比特币节点从网络上接收到一个新的区块,该区块描述如下:
对于这一新的区块,节点会在“父区块哈希值”字段里找出包含它的父区块哈希值。对于本节点来说这是已知的哈希值,也就是第 277314块区块的哈希值。故这个新区块是这个链条里的最后一个区块的子区块,因此现有的区块链得以扩展。节点将新的区块添加至链条的尾端,使区块链延长到一个新的高度277315。图9-1显示了通过“父区块哈希值”字段连接三个区块的链。
图9-1 区块通过引用前面的区块头哈希连接成区块链
9.7 默克尔树
区块链中的每个区块都使用默克尔树merkle tree归纳了区块链内的所有交易。
默克尔树是一种二叉哈希树binary hash tree,它是一种有效归纳和验证数据集完整性的数据结构。默克尔树是包含密码哈希的二叉树。术语“树”在计算机学科中常被用来描述一种具有分支的数据结构,但是树常常被倒置显示,“根”在图的上部而“叶子”在图的下部,你会在后续章节中看到相应的例子。
在比特币网络中,默克尔树被用来归纳一个区块中的所有交易,生成整个交易集合的数字指纹,且提供了一种验证某交易是否存在于某个区块的高效方法。生成一棵完整的默克尔树需要递归地对每一对节点进行哈希,直到只剩一个哈希值,它就是根root,或者默克尔树根Merkle root。在比特币的默克尔树中使用的加密散列算法SHA256被应用了两次,因此也被称为双哈希double-SHA256。
当N个数据元素被哈希和归纳到默克尔树中,你至多计算2* log2(N) 次就能检查出任意某数据元素是否在该树中,这使得该数据结构非常高效。
Merkle树是自底向上构建的。在如下的例子中,我们从A、B、C、D四个构成Merkle树树叶的交易开始,如图9-2。这些交易都并不存储在默克尔树中,而是将数据哈希化,然后将哈希值存储至相应的叶子节点。这些叶子节点分别是HA、HB、HC和HD:
HA = SHA256(SHA256(Transaction A))
然后,将两个哈希值连接起来再将它们哈希在一起,这样连续的叶子节点对就被归纳到父节点中。例如,要构造父节点HAB,将子节点的两个32字节哈希值连接起来,创建1个64字节字符串。然后对该字符串进行双哈希运算,生成父节点的哈希:
HAB = SHA256(SHA256(HA + HB))
这个过程一直持续到顶部只剩一个节点,即默克尔树根节点。这个32字节的哈希值存储在区块头中,归纳了所有4个交易中的所有数据。图9-2显示如何通过节点的成对哈希计算出根。
图9-2 计算默克树中的节点
因为默克尔树是一个二叉树,所以它需要偶数个叶子节点。如果要归纳的交易数为奇数,那么就复制最后一个交易哈希,凑够偶数个叶节点,这称为平衡树 balanced tree。图9-3中交易C就被复制了。
图9-3 复制一个数据元素,凑够偶数个数据元素
同样的,从4个交易构造树的方法可以推广到构造任意大小的树。在比特币中,一个区块中有几百到一千多个交易是很常见的,这些交易的归纳方式完全相同,最后只产生1个32字节的数据作为唯一的默克尔树根。在图9-4中,将会看到一个由16个交易组成的树。注意,尽管图中根节点外观看起来比叶节点大,但它的大小完全相同,只有32个字节。无论区块中有一个交易还是十万个交易,默克尔树根总是将它们归纳为32字节。
为了证明一个区块中包含了一个特定的交易,节点只需要进行log2(N)次32字节的哈希计算,就构成了一个连接特定交易到树根的认证路径authentication path或默克尔树路径merkle path。随着交易数量的增加,这一点尤其重要,因为交易数量的以2为底的对数增长要慢得多。这使得比特币节点能够高效地生成10或12个哈希值(320–384字节)的路径,这些哈希值可以在1M字节大小的区块包括的1000多个交易中,提供某一个交易的存在证明。
图9-4 归纳许多数据元素的默克尔树
在下面的图9-5中,节点可以通过生成一个只有4个32字节哈希值(总共128字节)的默克尔树路径来证明交易K包含在该区块中。这条路经包括了4个哈希值(图9-5中的阴影背景部分):HL, HIJ, HMNOP, and HABCDEF。通过把这4个哈希值作为认证路径,任何节点都可以通过计算另外四个成对哈希值HKL、HIJKL、HIJKLMNOP和默克尔树根(在图中用虚线勾勒)来证明HK(在图的底部黑色背景)包含在默克尔树根中。
图9-5 用于证明包含数据元素的默克尔树路径
下面例9-1中的代码演示如何使用libbitcoin库的辅助函数,创建从叶节点哈希到根节点的默克尔树。
例9-1 创建默克尔树
link:code/merkle.cpp[]
例9-2显示编译和运行上面代码的结果。
例9-2 编译和运行默克尔树示例代码
随着规模的增大,默克尔树的效率变得更为明显。表9-3显示用作默克尔树路径交换,证明交易属于区块的数据量。
表9-3 默克尔树效率
16 transactions
4 kilobytes
4 hashes
128 bytes
512 transactions
128 kilobytes
9 hashes
288 bytes
2048 transactions
512 kilobytes
11 hashes
352 bytes
65,535 transactions
16 megabytes
16 hashes
512 bytes
从表中可以看到,虽然区块大小快速增加,从4 KB(包含16个交易)增加到16 MB(容纳65535个交易),但证明包含一个交易所需的默克尔树路径增长就慢得多,仅仅从128字节增加到512字节。使用默克尔树,节点可以只下载区块头(每个区块只有80字节),并且仍然能够通过从全节点检索一个很小的默克尔树路径来识别区块中包含的交易,而无需存储或传输大部分区块链(可能是几GB的大小)。不维护完整区块链的节点称为简单支付验证(SPV)节点,它们使用默克尔树路径验证交易,而无需下载完整的区块。
9.8 默克尔树和简单支付验证(SPV)
默克尔树被SPV节点广泛使用。SPV节点不保存所有交易,也没有下载完整的区块,只存有区块头。为了验证交易是否包含在区块中,而又无需下载区块中的所有交易,它们可以使用验证路径或默克尔树路径。
例如,考虑一个SPV节点,该节点感兴趣的是它的钱包中的收款地址收到的交易。SPV节点在它自己与对等节点的连接上建立一个布隆过滤器(参见[bloom_filters]),只接收与自己感兴趣地址相关的交易。当对等节点看到与布隆过滤器匹配的交易时,会使用merkleblock消息发送这个区块。merkleblock消息包含区块头以及感兴趣的交易连接到区块的默克尔树根的默克尔树路径。SPV节点可以使用这个默克尔树路径将交易连接到区块,并验证该交易是否包含在区块中。SPV节点还使用区块头将区块链接到区块链的其余部分。交易与区块、区块与区块链这两个环节结合起来,就能证明交易已经记录在区块链中。总而言之,SPV节点接收到的区块头和默克尔树路径的数据量将小于1KB,这个比完整区块(目前约为1MB)少了一千倍以上的数据量。
9.9 比特币测试区块链
你可能会惊讶地发现不止有一个比特币区块链。“主”比特币区块链,由中本聪于2009年1月3日创建,与我们在本章研究的创世区块一起被称为比特币区块链*“主网mainnet”*。还有其他用于测试的比特币区块链:testnet、segnet和regtest。我们逐个了解下。
9.9.1 比特币测试场景Testnet
Testnet是用于测试目的的测试区块链、网络和货币的名称。Testnet是一个功能齐全的实时点对点网络,同样有钱包、测试比特币(testnet coins)、挖矿以及主网所具备的其他功能。有两个实质区别:Testnet上的币没有价值,挖矿难度也足够低,任何人都可以相对容易地挖testnet币(让它们毫无价值)。
任何打算在比特币主网上生产使用的软件开发都应该首先在Testnet上用测试币进行测试。这既保护了开发人员免受错误造成的资金损失,也保护了网络免受错误造成的意外行为。
然而,让这些币一文不值,让挖矿变得容易并不简单。尽管有开发者呼吁,一些人还是使用先进的挖矿设备(GPU和ASIC)在Testnet上进行挖矿。这增加了难度,单纯使用CPU已经没办法挖矿,最终使得难以获得测试币,人们开始重视测试币,它们不再是一文不值。因此,时不时地,Testnet必须被废弃,并从一个新的创世区块重新启动,重新设置难度。
当前的Testnet被称为Testnet3,是Testnet的第三次迭代,于2011年2月重新启动,为的是重置以前的Testnet的难度。
请记住,Testnet3是一个大型区块链,在2017年初超过20GB。计算机满负荷完全同步大约需要一天的时间。数据量虽然不如主网,但也不完全是“轻量级”的。运行Testnet节点的一个好方法是将其作为专用与此的虚拟机映像(例如VirtualBox、Docker、云服务器等)。
9.9.1.1 使用Testnet
就像大多数其他客户端软件一样,Bitcoin Core完全支持在Testnet而不只是在主网上运行。Bitcoin Core的所有功能都能在Testnet上工作,包括钱包、挖Testnet币和同步一个完整的Testnet节点。
要在Testnet而不是主网上启动Bitcoin Core,可以使用Testnet开关:
$ bitcoind -testnet
在日志中,您应该看到bitcoind正在默认bitcoind目录的Testnet3子目录中构建新的区块链:
bitcoind: Using data directory /home/username/.bitcoin/testnet3
要连接到bitcoind,可以使用bitcoin-cli命令行工具,但还必须将其切换到Testnet模式:
还可以在Testnet3上运行其他全节点实现,例如btcd(用Go编写d的)和bcoin(用JavaScript编写的),使用其他编程语言和框架进行实验和学习。
在2017年初Ttestnet3支持主网的所有特性,包括隔离见证(参见【7.8 隔离见证】)。因此,Testnet3也可以用于测试隔离见证功能。
9.9.2 Segnet:隔离见证测试网
2016年,启动了一个专用测试网,主要用于帮助开发和测试隔离见证(又名segwit,参见【7.8 隔离见证】)。这个测试区块链称为segnet,可以通过运行特殊版本(分支)的Bitcoin Core与它连接。
由于隔离见证被添加到testnet3中,因此不再需要使用segnet来测试隔离见证的功能。
将来,就像segnet一样,我们可能会看到其他专门设计用于测试单个功能或主要架构更改的测试网区块链。
9.9.3 Regtest:本地区块链
Regtest代表“回退测试”,是Bitcoin Core的功能,允许创建用于测试的本地区块链。与Testnet3不同,Testnet3是一个公开和共享的测试区块链,Regtest区块链是运行在本地的封闭的测试系统。你可以使用它从头开始启Rregtest区块链,创建本地创世区块。还可以将其他节点添加到网络中,或者只使用单个节点运行它,测试Bitcoin Core软件。
要在Regtest模式下启动Bitcoin Core,使用regtest标记:
$ bitcoind -regtest
与Testnet一样,Bitcoin Core将在比特币默认目录的regtest子目录下初始化一个新的区块链:
bitcoind: Using data directory /home/username/.bitcoin/regtest
使用命令行工具时,还需要指定regtest标记。下面尝试getblockchaininfo命令来检查regtest区块链:
就像你看到的,这里还没有区块。可以挖矿一些区块(500个区块)并获得奖励:
只需几秒钟就可以挖出所有这些区块,这无疑使测试变得容易。如果查看钱包余额,会看到在前400个区块中获得了奖励(创币交易的奖励必须是区块深度达到100个区块才能使用):
9.10 使用测试区块链进行开发
比特币的各种区块链(Regtest、segnet、Testnet3、主网)为比特币开发提供了一系列测试环境。无论是针对 Bitcoin Core开发,还是另一个全节点共识客户端,都可以使用测试区块链。可以是应用程序,如钱包、交易所、电子商务网站,甚至是开发最新的智能合约和复杂脚本。
可以使用测试区块链建立开发管道。开发时,可以在Regtest上本地测试代码。一旦具备在公网上进行尝试,可切换到Testnet,将代码公开到一个具有代码和应用程序多样性的动态环境中。最后,确信代码能够按预期工作,就可以切换到主网部署到生产环境中。如果要进行更改、改进、修复错误等,可以再次启动管道,还是先在Regtest上部署每个更改,然后在Testnet上测试,最后部署到生产环境中。