本文将会介绍比特币区块原始数据的存储格式,以及各个字段所代表的含义。
比特币程序将数据存在了4个地方。
blocks/blk*.dat
的文件中存储了实际的块数据,这些数据以网络格式存储。它们仅用于重新扫描钱包中丢失的交易,将这些交易重新组织到链的不同部分,并将数据块提供给其他正在同步数据的节点。blocks/index/*
是一个levelDB数据库,存储着目前已知块的元数据,这些元数据记录所有已知的块以及它们存储在磁盘上的位置。没有这些文件,查找一个块将是非常慢的。chainstate/*
是一个levelDB数据库,以紧凑的形式存储所有当前未花费的交易以及它们的元数据。这里的数据对于验证新传入的块和交易是必要的。在理论上,这些数据可以从块数据中重建,但是这需要很长时间。没有这些数据也可以对数据进行验证,但是需要现有块数据进行扫面,这无疑是非常慢的。blocks/rev*.dat
中包含了“撤销”数据,可以将区块视为链的“补丁”(它们消耗一些未花费的输出并生成新的输出),那么这些撤销数据将是反向补丁。如果需要回滚链,这些数据将是必须的。比特币程序从网络中接受数据后,会将数据以.dat
的形式转储到磁盘上。
一个块文件大约为128MB。每个块文件会有一个对应的撤销文件,比如文件blocks/blk1234.dat
和blocks/recv1234.dat
对应。
每个块都包含一些近期的交易记录,和对之前块的引用。同时它还包含了一个难以解决的数学难题的答案,每个块的答案唯一。如果没有正确的答案,新的块将不能提交给网络--“挖矿”的过程本质是竞争的过程,最先找到答案的人将获得挖矿的奖励。每个块中的数学问题是非常难以解决的,但一旦找到有效的解决方案,网络中的其他部分就很容易确认方案的正确性。对任何给定的块,会有多个有效的解决方案--找到一个方案即可以宣称解决了该难题。
比特币网络在设计上每小时出6个块,因此需要自动调整数学问题的难度。每隔2016个块(大概历时两周),所有的比特币客户端会将挖出的块数量和目标数量进行比较,并对目标进行修改。网络会达成共识,并自动调整生成块的难度。
每个块都包含对先前块的引用,所以现有块的集合可以形成区块链。然而,该链可能会存在分叉,比如在两个矿工同时到达同一区块的两个不同的有效解决方案时。点对点的网络会在短时间内解决这些分叉,使链中只有一个分支存活。
比特币客户端会接受“最长”的链作为有效链。链的长度是指具有最大组合难度的链,而不是具有最多块的链。
比特币数据块的结构如下:
大小(字节) | 名称 | 数据类型 | 描述 |
---|---|---|---|
4 | magic_number | uint32 | 总是0xD9B4BEF9,作为区块之间的分隔符 |
4 | block_size | uint32 | 后面数据到块结束的字节数 |
80 | block_header | char[] | block header |
varies | transaction_cnt | uint | 交易数量 |
varies | transaction | char[] | 交易详情 |
从原始数据中读取的流程大概如下
block header固定80字节大小,结构如下
大小(字节) | 名称 | 数据类型 | 描述 |
---|---|---|---|
4 | version | int32_t | 版本号 |
32 | previous_block_hash | char[32] | 前一个block的hash值 |
32 | merkle_root_hash | char[32] | 区块内所有交易的merkle hash值 |
4 | time | uint32 | unix时间戳,矿工挖矿的时间 |
4 | nBits | uint32 | 该块的标题hash必须小于的值。难度 |
4 | nonce | uint32 | 随机值,用于产生满足难度的hash值 |
hash字段使内部字节顺序存储;其他的值以小端序存储。
其中,内部字节顺序需要以字节为单位逆序读取,如下面的python代码:
def format_hash(data):
# data为读取的32字节的二进制数据
return str(hexlify(data[::-1]).decode('utf-8'))
下面是一个header例子
02000000 ........................... Block version: 2
b6ff0b1b1680a2862a30ca44d346d9e8
910d334beb48ca0c0000000000000000 ... Hash of previous block's header
9d10aa52ee949386ca9385695f04ede2
70dda20810decd12bc9b048aaab31471 ... Merkle root
24d95a54 ........................... Unix time: 1415239972
30c31b18 ........................... Target: 0x1bc330 * 256**(0x18-3)
fe9f0864 ........................... Nonce
对header进行两次hash,可以得到区块的hash值,示例代码如下:
def double_sha256(data):
return hashlib.sha256(hashlib.sha256(data).digest()).digest()
Merkle树,或者叫hash树,是每个叶子节点用数据块标记的树,并且每个非叶子节点用其子节点的值加密hash后进行标记。这种数据结构允许高效和安全地验证大型数据结构的内容。
在比特币区块中,Merkle root由交易列表生成,如下图。
Merkle树提供了一种验证区块中交易的方式。
Target值是256位无符号整数。header的SHA-256散列值必须低于或等于网络接收的块的当前目标。这个值越小,生成块的难度越高。
前面提到的数学难题,即是找到一个随机数(这个数介于0~2的256次方),使得对header的hash值满足条件。
矿工们每次取一个随机值,计算header的hash,如果低于目标,则“挖矿”成功;如果没有,则递增随机数,再次验证。能否挖到矿,除了取决矿工手里的算力,还要加上一点运气。
交易的结构如下
大小(字节) | 名称 | 数据类型 | 描述 |
---|---|---|---|
4 | version | uint32 | 交易版本号 |
varint | tx_in_count | uint | 交易输入数量 |
varies | tx_in | tx_in | 交易输入 |
varint | tx_out_count | uint | 交易输出数量 |
varies | tx_out | tx_out | 交易输出 |
4 | lock_time | uint32 | 锁定时间 |
从数据中解析流程大致如下:
一个示例交易数据如下:
01000000 ................................... Version
01 ......................................... Number of inputs
|
| 7b1eabe0209b1fe794124575ef807057
| c77ada2138ae4fa8d6c4de0398a14f3f ......... Outpoint TXID
| 00000000 ................................. Outpoint index number
|
| 49 ....................................... Bytes in sig. script: 73
| | 48 ..................................... Push 72 bytes as data
| | | 30450221008949f0cb400094ad2b5eb3
| | | 99d59d01c14d73d8fe6e96df1a7150de
| | | b388ab8935022079656090d7f6bac4c9
| | | a94e0aad311a4268e082a725f8aeae05
| | | 73fb12ff866a5f01 ..................... Secp256k1 signature
|
| ffffffff ................................. Sequence number: UINT32_MAX
01 ......................................... Number of outputs
| f0ca052a01000000 ......................... Satoshis (49.99990000 BTC)
|
| 19 ....................................... Bytes in pubkey script: 25
| | 76 ..................................... OP_DUP
| | a9 ..................................... OP_HASH160
| | 14 ..................................... Push 20 bytes as data
| | | cbc20a7664f2f69e5355aa427045bc15
| | | e7c6c772 ............................. PubKey hash
| | 88 ..................................... OP_EQUALVERIFY
| | ac ..................................... OP_CHECKSIG
00000000 ................................... locktime: 0 (a block height)
交易中使用可变长度整数来表示下一条数据中的字节数。对于不同的数值,存储的空间不一样。
对于0~252的值,只占用一个字节;对于其他小于0xffffffffffffffff的值,第一个字节将成为长度标识位。值和存储空间的关系如下表:
值 | 存储空间(字节) | 数据类型 |
---|---|---|
>=0 && <=252 | 1 | uint8_t |
>=253 && <=0xffff | 3 | 后2个字节uint16_t |
>=0x10000 && <=0xffffffff | 5 | 后4个字节uint32_t |
>=0x100000000 && <=0xffffffffffffffff | 9 | 后8个字节uint64_t |
解析的示例代码如下:
def decode_varint(data):
assert(len(data) > 0)
size = to_int(data[0])
assert(size <= 255)
if size < 253:
return size, 1
format_ = None
if size == 253:
format_ = '
elif size == 254:
format_ = '
elif size == 255:
format_ = '
else:
assert 0, 'unknown format_ for size: {size}'.format(size=size)
size = struct.calcsize(format_)
return struct.unpack(format_, data[1:size+1])[0], size + 1
每个非coinbase的交易输入都是之前某个交易的交易输出。
交易输入的结构如下
大小(字节) | 名称 | 数据类型 | 描述 |
---|---|---|---|
32 | previous_output_hash | outpoint | 前置交易hash |
4 | previous_output_index | uint32 | 前置交易index |
varint | script_bytes | uint | 解锁脚本长度 |
varies | signature_script | char[] | 解锁脚本 |
4 | sequence | uint32 | 序列号 |
交易输出的结构如下
大小(字节) | 名称 | 数据类型 | 描述 |
---|---|---|---|
8 | value | int64 | 花费的数量,单位是聪 |
1+ | pk_script_size | uint | pubkey脚本中的字节数量 |
varies | pk_script | char[] | 花费这笔输出需要满足的条件 |
解析块文件的程序可以参考下这里。