Bitcoin 区块和交易数据结构

yingheone 2019-06-29

Bitcoin 区块和交易数据结构

区块链是一种账本数据库,分布式,数据存储的每个节点都会同步复制整个账本,信息透明难以篡改。

区块中存放着交易,区块按照高度有序链接起来的这种数据结构被称为区块链。每个区块都指向前一个区块,一个区块由 区块头 和 交易 组成。

区块

区块由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易列表组成。区块结构如图:

Bitcoin 区块和交易数据结构

区块头中数据有:

  • 区块版本
  • 前个区块哈希
  • Merkle Root 哈希
  • 时间戳
  • Bits 难度
  • Nonce “挖矿” 随机数

Coinbase 交易:每个区块中的第一条交易都是 Coinbase 交易,用于奖励 “矿工” 将交易打包到区块。奖励分为两部分,一是区块奖励,一是区块中除 Coinbase 交易外其它交易手续费总和。

<!--more-->

区块头

区块头的长度是80字节,将区块头数据进行两次 Sha256 运算将得到区块哈希

区块头结构如下:

FieldSizeData
Version4 bytesLittle-endian ⟲
Previous Block Hash32 bytesLittle-endian ⟲
MerkleRoot32 bytesLittle-endian ⟲
Timestamp4 bytesLittle-endian ⟲
Bits (Difficulty Target)4 bytesLittle-endian ⟲
Nonce4 bytesLittle-endian ⟲

Merkle Root

Merkle Tree 是一种哈希二叉树,用于归纳一个区块中的所有交易,同时生成整个交易集合的数字签名,且提供了一种校验区块是否存在某交易的高效途径。

区块链中的每个区块都包含了产生于该区块的所有交易,且以Merkle树表示。那么如何获得它呢?

  1. 从区块交易列表中获取每对Txid,并对它们进行两次 Sha256 运算,得到哈希。
  2. 如果为奇数,则复制一份凑成偶数再进行两次 Sha256 运算,得到哈希。
  3. 递归1、2,直到最终获得一个哈希,它就是Merkle Root。

Bitcoin 区块和交易数据结构

为什么使用 Merkle Root 方式呢,为什么不对区块内所以交易一次性进行哈希呢,那样不是更快么?

使用 Merkle Root 原因有二:

  1. 完整性验证,任何一个节点的哈希发生变化,都会导致最终的 Root 节点哈希发生变化。这一特性也可用于快速定位问题节点。
  2. 零知识证明,如下图,16 条交易的节点能够通过生成一条仅有 4 个 32 字节哈希值长度(总128字节)的 Merkle 路径,来证明区块中存在一笔交易 K,该路径有4个哈希值 HL、HIJ、HMNOP 和 HABCDEFGH。由这4个哈希值产生的认证路径,再通过计算另外四对哈希值 HKL、HIJKL、HIJKLMNOP 和 Merkle Root,任何节点都能证明 HK 包含在Merkle Root。

Bitcoin 区块和交易数据结构

关于 Merkle 路径,我们来看一张图:

交易数量区块大小路径数量路径大小
164 KB4128 bytes
512128 KB9288 bytes
2048512 KB11352 bytes
40961 MB12384 bytes
65,53516 MB16512 bytes

即使增长到交易数量为 262144,区块大小到 65M,路径数也才 18。所以使用 Merkle Root 非常高效,如果是所有交易进行一次哈希,虽然获得 Hash 是快些,但对于要验证问题交易或节点,将是个灾难。

Bits 和 Nonce

Bits 是挖矿目标难度 Target 的紧凑格式。

Nonce 是挖矿遍历时的随机数。

关于 “挖矿” 的具体内容,我会单独总结一篇,感兴趣可以关注后续文章。
这里有一个在线的模拟挖矿,各种数据显示都很直观,适合了解挖矿内部运作:点我前往模拟挖矿

交易

目前存在两种交易结构。比特币白皮书中定义的交易结构和新的隔离见证交易结构。

BIP 141 定义一种新的交易结构,我们称之为 “Segregated Witness (隔离见证)” 交易。旧交易结构签名数据放在交易输入里,隔离见证交易则将签名数据 “分离” 出来,放在交易的 Locktime 之前。

交易结构:

[nVersion] [n] [tx inputs] [n] [tx outputs] [nLockTime]
    |       |               |                    |
 4 bytes  varint          varint             4 bytes

隔离见证(Segregated Witness)交易结构:

[nVersion] [marker] [flag][n] [tx inputs] [n] [tx outputs] [witness][nLockTime]
    |       \    /   \  /  |               |                \     /     |
 4 bytes     0x00    0x01 varint         varint            scriptSig  4 bytes
FieldSizeData
Version4 bytesLittle-endian ⟲
* Marker1 bytes0x00
* Flag1 bytes0x01
Input Counter1–9 bytesVarInt
InputsVariable
Output Counter1–9 bytesVarInt
OutputsVariable
* WitnessVariable
Locktime4 bytesLittle-endian ⟲

* 为 Segregated Witness 交易字段。

我们解析一条 Segregated Witness (P2SH-P2WPKH)交易:

如果你对 P2SH-P2WPKH 交易感兴趣可以查看我之前写的一篇介绍 Segregated Witness 的文章。

{
  "txid": "af0c4cdd2537ae367b0e03db0cd795fa37543bd672c9234b2c307009a9a8108f",
  "hash": "ff300093626745127aad29e8628ee4606b87cb660f7ff63817dd4857cd15a175",
  "version": 2,
  "size": 248,
  "vsize": 166,
  "locktime": 0,
  "vin": [],
  "vout": []
}

HEX 数据:

Bitcoin 区块和交易数据结构

Inputs

交易输入结构:

FieldSizeData
TXID32 bytesLittle-endian ⟲
VOUT4 bytesLittle-endian ⟲
ScriptSig Size1–9 bytesVarInt
ScriptSigVariable
Sequence4 bytesLittle-endian ⟲

输入JSON:

{
  "txid": "6635d3f451478f0e1f88692d5cfb9194f133fbb314cb87fa9483f89bd296284b",
  "vout": 0,
  "scriptSig": {
    "asm": "0014db75523757a256579a197746568f331103417b85",
    "hex": "160014db75523757a256579a197746568f331103417b85"
  },
  "txinwitness": [
    "3045022100c8b2a6027f939bb964e395c94cd6f1d8ff9d1f406e41975b2ac979a6007c3ac4022019953dfe376d0152152955e02d72e7a7b9a61f8d5700642c97a2dc4ba13628b801",
    "02983f3687310bcfe2ad1ad55d011112c3f8d659950c10cab9ff43ae34d7b6280e"
  ],
  "sequence": 4294967293
}

outputs

交易输出结构:

FieldSizeData
Value8 bytesLittle-endian ⟲
ScriptPubKey Size1–9 bytesVarInt
ScriptPubKeyVariable

输出JSON:

{
  "value": 1.00000000,
  "n": 0,
  "scriptPubKey": {
    "asm": "OP_HASH160 2228cecac3c1fa3143ba2ac7d2525d8b9b05c87b OP_EQUAL",
    "hex": "a9142228cecac3c1fa3143ba2ac7d2525d8b9b05c87b87",
    "reqSigs": 1,
    "type": "scripthash",
    "addresses": [
      "2MvMqrBRct4F2zuyrpgrq2qqq61VZc1znPB"
    ]
  }
},
{
  "value": 48.99996680,
  "n": 1,
  "scriptPubKey": {
    "asm": "OP_HASH160 22e2f5339cdf0778935e8127dc51e4a0ae62d162 OP_EQUAL",
    "hex": "a91422e2f5339cdf0778935e8127dc51e4a0ae62d16287",
    "reqSigs": 1,
    "type": "scripthash",
    "addresses": [
      "2MvRgr7SeyTVutrUaJdmZ5ETVdoPp5eWoj5"
    ]
  }
}

VarInt

比特币使用可变字节表示脚本长度,当一个字节不够表示时,如下规律:

SizeExampleDescription
<= 0xfc0x121 个字节
<= 0xffff0xfd1234前缀是fd,小端表示接下来的2个字节
<= 0xffffffff0xfe12345678前缀是fe,小端表示接下来的4个字节
<= 0xffffffffffffffff0xff1234567890abcdef前缀是ff,小端表示接下来的8个字节

如:0x48,没有 fdfeff 前缀,所以我们知道它是2 bytes表示的。

VarInt: 0x48
        = 72 bytes

如:fd2606,因为是 fd 前缀,我们读取fd之后的 4 bytes,反转小端再转换为十进制表示。

VarInt: 0xfd2606
        = 0x2602    (next 2 bytes)
        = 0x0226    (Little-Endian)
        = 550 bytes

Little-Endian

小端 (Little-endian) 字节序。如:0x12345678

大端类似于十六进制字节从左到右的阅读顺序。小端最低位字节是 0x78 存储在最低的内存地址处,后面的字节依次往后存。

Big-Endian:    |12|34|56|78|
Little-Endian: |78|56|34|12|

比特币在很多地方数据都是用小端序表示。

参考

相关推荐