比特币创世区块原始数据:逐字节解码与自行验证
比特币创世区块的完整字段级解码——80 字节区块头、coinbase 交易十六进制、嵌入的留言,以及一段可以自己运行、复算出 merkle root 与区块哈希的脚本。
比特币创世区块一共 285 字节。本文把它们全部解码:每个字段是什么含义、字节序怎么读,以及如何从零复算出区块哈希——这样你信的是算术,而不是某个区块浏览器。
下文每一个数值在撰写时都做了两重验证:一是从原始字节在本地重新计算哈希,二是与公共节点 API 交叉比对。验证脚本附在文中,你可以自己重跑一遍。
完整的原始区块
全部 285 字节,十六进制:
0100000000000000000000000000000000000000000000000000000000000000
000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa
4b1e5e4a29ab5f49ffff001d1dac2b7c01010000000100000000000000000000
00000000000000000000000000000000000000000000ffffffff4d04ffff001d
0104455468652054696d65732030332f4a616e2f32303039204368616e63656c
6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f75742066
6f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe554827
1967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4
f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000换行只是为了显示,拼接起来才是完整字节流。它分三段:80 字节的区块头、1 字节的交易计数、204 字节的 coinbase 交易。
第一段:80 字节区块头
| 字段 | 原始十六进制 | 解码值 | 说明 |
|---|---|---|---|
| 版本 | 01000000 | 1 | 小端序 4 字节整数 |
| 前区块哈希 | 32 个 00 字节 | 无 | 链的根没有父区块 |
| Merkle root | 3ba3edfd...4b1e5e4a | 见下文 | 以内部字节序存储 |
| 时间戳 | 29ab5f49 | 1231006505 | 2009 年 1 月 3 日 18:15:05 UTC |
| Bits(难度目标) | ffff001d | 0x1d00ffff | 十进制 486604799,难度 1 |
| Nonce | 1dac2b7c | 2083236893 | 满足目标的那个值 |
这里有三处最容易搞错。
字节序。 区块头里的整数是小端序。时间戳字节 29ab5f49 读作 0x495fab29,即十进制 1231006505。哈希以内部字节序存储、以反转顺序显示,所以 merkle root 在原始区块里以 3ba3edfd 开头,而各处印出来都是 4a5e1e4b...。两者是同样的 32 个字节。
紧凑目标编码。 bits 字段 0x1d00ffff 不是一个直接用来比较的数字,而是一种紧凑浮点表示。首字节 0x1d 是指数,其余三字节 0x00ffff 是系数。展开后的目标值为:
00000000ffff0000000000000000000000000000000000000000000000000000任何数值上小于它的区块头哈希都算有效。这是难度 1,比特币允许的最低难度。
多出来的那两个零。 难度 1 要求哈希以 8 个十六进制零开头,而创世区块哈希开头有 10 个:
000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f大约比及格线好了 256 倍。通常的解读是:中本聪在一段时间里挖了很多候选区块,最后挑了个好看的发布出去——这与创世区块和区块 1 之间六天的间隔是吻合的。
第二段:coinbase 交易
先是 1 字节 01,表示交易数量。随后是 204 字节的交易:
01000000 版本 = 1
01 输入数量 = 1
0000...0000(32 字节) 前序 txid:空
ffffffff 前序输出索引:0xffffffff
4d scriptSig 长度 = 77 字节
04 ffff001d 推入 4 字节:bits 的值
01 04 推入 1 字节:0x04
45 <69 字节> 推入 69 字节:留言
ffffffff sequence
01 输出数量 = 1
00f2052a01000000 金额 = 5000000000 聪 = 50 BTC
43 scriptPubKey 长度 = 67 字节
41 <65 字节公钥> ac 推入公钥,然后 OP_CHECKSIG
00000000 locktime = 0空的前序 txid 加上 0xffffffff 的输出索引,正是"这是一笔 coinbase 交易"的标志——它没有资金来源,它凭空创造价值。
输出类型是 pay-to-public-key,2009 年最初的输出形式:一个未压缩的 65 字节原始公钥,后面跟 OP_CHECKSIG。区块里根本没有出现地址。人们熟悉的地址 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa 是从那个公钥推导出来的:先 SHA-256 再 RIPEMD-160,然后以版本字节 0x00 做 base58check 编码。用区块里的公钥跑一遍这个推导,会精确得到该地址。
第三段:那句留言
scriptSig 里推入的 69 字节是 ASCII:
5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72
206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f7220
62616e6b73解码后是:The Times 03/Jan/2009 Chancellor on brink of second bailout for banks(《泰晤士报》2009 年 1 月 3 日:财政大臣正考虑对银行进行第二轮救助)。
这是当天伦敦《泰晤士报》头版的标题,指的是英国财政大臣进一步救助银行的计划。报纸版面本身、其图片与报道正文的著作权属于 Times Newspapers;嵌进区块里的只是这 69 个字符的标题字符串。我们在此只作描述而不复制原件——要读报道,请前往该文章的存档页。
从技术上说,这句留言充当"最早可能日期"的证明:一个包含 1 月 3 日头条的区块,不可能创建于 1 月 3 日之前。这与 Haber 和 Stornetta 把哈希登在报纸上是同一个把戏,只是方向相反。
自己验证一遍
把下面的代码存为 verify-genesis.mjs,用 Node 运行。它会从各个组成部分重建 coinbase 交易和区块头,再做哈希。
import { createHash } from "crypto";
const dsha = (b) =>
createHash("sha256").update(createHash("sha256").update(b).digest()).digest();
const display = (b) => Buffer.from(b).reverse().toString("hex");
const message = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
const messageHex = Buffer.from(message, "ascii").toString("hex");
const scriptSig = "04ffff001d" + "0104" + "45" + messageHex;
const coinbaseHex =
"01000000" +
"01" + "0".repeat(64) + "ffffffff" +
(scriptSig.length / 2).toString(16) + scriptSig + "ffffffff" +
"01" + "00f2052a01000000" + "43" +
"41" +
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" +
"649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f" +
"ac" +
"00000000";
const txid = dsha(Buffer.from(coinbaseHex, "hex"));
console.log("txid / merkle root:", display(txid));
const le32 = (n) => Buffer.from(new Uint8Array(new Uint32Array([n]).buffer)).toString("hex");
const header =
"01000000" + "0".repeat(64) + txid.toString("hex") +
le32(1231006505) + "ffff001d" + le32(2083236893);
console.log("block hash:", display(dsha(Buffer.from(header, "hex"))));预期输出:
txid / merkle root: 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
block hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f如果你自己跑着全节点,等价的一行命令是:
bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f 2注意 merkle root 等于 coinbase 的 txid。区块里只有一笔交易,默克尔树就只有一个叶子,树根就是这笔交易的哈希——不需要配对,也不需要拼接。
那 50 BTC 为什么花不出去
输出脚本本身完全有效。持有对应私钥的人可以为它签名。但这些币确实花不出去,原因不在脚本,而在最初实现的一个特殊处理。
比特币最早的代码把创世区块当作特例处理,从未把它的 coinbase 输出加入可花费输出数据库(UTXO 集)。此后每一个实现都保留了这个行为,因为改动它就是一次破坏共识的分叉。一笔试图花费创世输出的交易会被每个节点拒绝——在它们的数据库看来,那个输出根本不存在。
这究竟是疏忽还是有意,无人知晓。实际结果是:比特币最早的 50 枚币被永久排除在供应之外。该地址此后还陆续收到一批小额致敬转账——那些是普通输出,技术上可由持有私钥者花费,但从未有一笔被动过。
两个常见误读
"创世区块 1 月 3 日出块,所以比特币那天上线。" 区块时间戳是 2009 年 1 月 3 日 18:15:05 UTC,但软件直到 1 月 8 日才发布,区块 1 直到 2009 年 1 月 9 日 02:54:25 UTC 才被挖出,中间隔了约 5.4 天。这段时间里,除中本聪外没有人在运行这个网络。
"这个哈希说明中本聪做了海量工作。" 当时难度是 1,最低值。现代 ASIC 在这个目标下找到有效 nonce 只需要不到一秒。开头那 10 个零意味着在 2009 年的硬件上多次尝试中抽到了一个好签,而不是一次英雄式的计算。
字段速查
| 属性 | 值 |
|---|---|
| 高度 | 0 |
| 区块哈希 | 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f |
| Merkle root | 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b |
| 时间戳 | 1231006505 —— 2009 年 1 月 3 日 18:15:05 UTC |
| 版本 | 1 |
| Bits | 0x1d00ffff(486604799),难度 1 |
| Nonce | 2083236893 |
| 交易数 | 1 |
| 区块大小 | 285 字节 |
| 出块奖励 | 50 BTC,不可花费 |
| 输出地址 | 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa |
| 下一区块 | 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048 |
资料来源
- 创世区块十六进制转储 —— Bitcoin Wiki
- 比特币 v0.1 源代码 —— 中本聪当年写下的硬编码区块
- 区块链结构参考 —— 区块头字段规范
- Bitcoin Core chainparams.cpp —— 当前代码中该区块的构造方式