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

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

区块

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

区块头中数据有:

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

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

区块头

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

区块头结构如下:

Field Size Data
Version 4 bytes Little-endian
Previous Block Hash 32 bytes Little-endian ⟲
MerkleRoot 32 bytes Little-endian ⟲
Timestamp 4 bytes Little-endian ⟲
Bits (Difficulty Target) 4 bytes Little-endian ⟲
Nonce 4 bytes Little-endian ⟲

Merkle Root

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

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

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

为什么使用 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。 图来自https://github.com/bitcoinbook/bitcoinbook/

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

交易数量 区块大小 路径数量 路径大小
16 4 KB 4 128 bytes
512 128 KB 9 288 bytes
2048 512 KB 11 352 bytes
4096 1 MB 12 384 bytes
65,535 16 MB 16 512 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
Field Size Data
Version 4 bytes Little-endian
* Marker 1 bytes 0x00
* Flag 1 bytes 0x01
Input Counter 1–9 bytes VarInt
Inputs Variable  
Output Counter 1–9 bytes VarInt
Outputs Variable  
* Witness Variable  
Locktime 4 bytes Little-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 数据:

Inputs

交易输入结构:

Field Size Data
TXID 32 bytes Little-endian
VOUT 4 bytes Little-endian ⟲
ScriptSig Size 1–9 bytes VarInt
ScriptSig Variable  
Sequence 4 bytes Little-endian ⟲

输入JSON:

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

outputs

交易输出结构:

Field Size Data
Value 8 bytes Little-endian
ScriptPubKey Size 1–9 bytes VarInt
ScriptPubKey Variable  

输出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

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

Size Example Description
<= 0xfc 0x12 1 个字节
<= 0xffff 0xfd1234 前缀是fd,小端表示接下来的2个字节
<= 0xffffffff 0xfe12345678 前缀是fe,小端表示接下来的4个字节
<= 0xffffffffffffffff 0xff1234567890abcdef 前缀是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|

参考

  • https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch09.asciidoc
  • http://learnmeabitcoin.com/glossary/
  • http://learnmeabitcoin.com/glossary/merkle-root
  • http://learnmeabitcoin.com/glossary/varint
  • http://yogh.io/#mine:last
  • http://royalforkblog.github.io/2014/11/20/txn-demo/