区块链是一种账本数据库,分布式,数据存储的每个节点都会同步复制整个账本,信息透明难以篡改。
区块中存放着交易,区块按照高度有序链接起来的这种数据结构被称为区块链。每个区块都指向前一个区块,一个区块由 区块头 和 交易 组成。
区块
区块由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易列表组成。区块结构如图:
区块头中数据有:
- 区块版本
- 前个区块哈希
- Merkle Root 哈希
- 时间戳
- Bits 难度
- Nonce “挖矿” 随机数
Coinbase 交易:每个区块中的第一条交易都是 Coinbase 交易,用于奖励 “矿工” 将交易打包到区块。奖励分为两部分,一是区块奖励,一是区块中除 Coinbase 交易外其它交易手续费总和。
<!--more-->
区块头
区块头的长度是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树表示。那么如何获得它呢?
- 从区块交易列表中获取每对Txid,并对它们进行两次
Sha256
运算,得到哈希。 - 如果为奇数,则复制一份凑成偶数再进行两次
Sha256
运算,得到哈希。 - 递归1、2,直到最终获得一个哈希,它就是Merkle Root。
为什么使用 Merkle Root 方式呢,为什么不对区块内所以交易一次性进行哈希呢,那样不是更快么?
使用 Merkle Root 原因有二:
- 完整性验证,任何一个节点的哈希发生变化,都会导致最终的 Root 节点哈希发生变化。这一特性也可用于快速定位问题节点。
- 零知识证明,如下图,16 条交易的节点能够通过生成一条仅有 4 个 32 字节哈希值长度(总128字节)的 Merkle 路径,来证明区块中存在一笔交易
K
,该路径有4个哈希值 HL、HIJ、HMNOP 和 HABCDEFGH。由这4个哈希值产生的认证路径,再通过计算另外四对哈希值 HKL、HIJKL、HIJKLMNOP 和 Merkle Root,任何节点都能证明 HK 包含在Merkle Root。
关于 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
,没有 fd
、fe
、ff
前缀,所以我们知道它是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|
比特币在很多地方数据都是用小端序表示。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。