第二部分:比特币——第一个区块链
第3章 中本聪的白皮书:一个绝妙的设计
3.1 2008年的世界:一切从一场危机说起
2008年9月15日,雷曼兄弟申请破产。这家拥有158年历史、资产规模超过6000亿美元的投资银行在一夜之间轰然倒塌,引爆了自1929年大萧条以来最严重的全球金融危机。数百万人失去工作,无数家庭的储蓄化为乌有,而那些"大到不能倒"的银行,则靠着纳税人的血汗钱勉强续命。
就在这场危机肆虐的同年10月31日,一个自称"中本聪(Satoshi Nakamoto)"的匿名者,在一个密码学邮件列表上悄悄投下了一颗改变历史的种子:一篇题为《Bitcoin: A Peer-to-Peer Electronic Cash System》的九页白皮书。
没有机构背书,没有风险投资,甚至没有真名。
这篇白皮书的开场白只有一句话:
"A purely peer-to-peer version of electronic cash would allow online payments to be sent directly from one party to another without going through a financial institution."
翻译过来的意思是:一种纯粹的点对点电子现金,可以让在线支付直接在双方之间完成,无需通过任何金融机构中转。
这句话听起来平淡,但背后藏着几十年来密码学家和计算机科学家反复折戟的一个终极难题。要理解比特币为什么是"绝妙的设计",我们得先搞清楚,这个问题到底难在哪里。
3.2 "双花问题":数字货币的死穴
假设你想发明一种数字货币。最直觉的想法是:一枚数字货币就是一串数字文件,比如一个编号为#000001的比特文件,谁持有谁就"拥有"这枚币。
但立刻出现了一个致命问题:数字文件可以被无限复制。
你把一首MP3发给朋友,你的MP3还在。你把一枚"数字硬币"发给商家,你手里的数字文件还在——你可以把同一枚币发给一百个人,每人都以为自己收到了钱。这个问题被称为双花(Double Spending),是数字货币领域的原罪。
传统的解决方案是:引入一个可信的中央机构来记账。
你去咖啡店刷信用卡,银行的中央数据库记录"张三账户减少10元,咖啡店账户增加10元"。银行作为唯一的权威账本,保证了同一笔钱不会被花两次。这个模型运作得相当好——只要你信任银行。
但2008年的金融危机用铁一般的事实告诉我们:信任是脆弱的。银行可以倒闭,政府可以通货膨胀,账户可以被冻结,跨境转账可以被审查。更深层的问题是:为什么我们必须信任一个中间人?
中本聪的问题由此凝练成一句话:能不能在没有可信第三方的情况下,解决双花问题?
这个问题在计算机科学里有一个经典的等价形式——拜占庭将军问题(Byzantine Generals Problem)。
3.3 拜占庭将军问题:分布式系统的噩梦
想象拜占庭帝国的军队正在围攻一座城市。多支军队驻扎在城市四周,每支军队由一位将军指挥。他们必须商定一个统一的行动方案——要么同时进攻,要么同时撤退。如果只有部分军队进攻,就会失败。
将军们只能通过信使传递消息,不能聚在一起开会。问题在于:某些将军可能是叛徒,他们会发送相互矛盾的消息,破坏共识。而且,信使也可能被截杀或者腐化。
更正式地描述:在一个有 $n$ 个节点的分布式系统中,假设其中有 $f$ 个节点是"拜占庭错误节点"(即可以任意撒谎、发送矛盾信息),系统能否达成一致?
理论上已经证明:当 $f < n/3$ 时,系统可以达成拜占庭容错(BFT)共识。
传统BFT算法(如PBFT)确实能解决这个问题,但它们有个致命缺陷:需要预先知道所有参与者的身份列表。
这在一个向全球开放、任何人都可以匿名加入的网络里根本行不通。中本聪面对的是一个更难的版本:节点数量未知,节点可以随时加入或离开,节点可以冒充多个身份(女巫攻击,Sybil Attack),而且没有任何中央机构来验证身份。
在2008年之前,几乎所有人都认为这个问题在开放网络中无解。
3.4 中本聪的核心洞见:用算力投票
中本聪的天才之处,在于他换了一个问题视角。
传统BFT把问题建模为"节点投票"——每个节点有一票,只要超过2/3的节点诚实,系统就安全。但在开放网络中,一个人可以创建无数个账号,每个账号一票,这样就能轻松控制"多数票"。
中本聪的洞见是:别用身份投票,用算力投票。
规则很简单:你想要你的交易记录被承认,你就必须花费真实的计算资源来证明你的工作。这个机制叫做工作量证明(Proof of Work,PoW)。
在这个框架下,"谁是多数"不再取决于谁注册了多少账号,而取决于谁掌握了更多的真实计算资源。伪造算力需要购买真实的硬件、消耗真实的电力——这是现实世界的物理约束,无法像创建账号一样无成本复制。
这一洞见把"社会信任问题"转化成了"物理成本问题",是整个比特币设计最核心的哲学突破。
3.5 白皮书的架构蓝图
中本聪的九页白皮书,用极其简洁的语言构建了一个完整的系统。我们来梳理它的核心组件:
3.5.1 交易(Transactions)
白皮书第2节定义了什么是"一枚电子货币的所有权":
"We define an electronic coin as a chain of digital signatures."
一枚比特币的"所有权",就是一串数字签名的历史链条。当Alice把一枚币转给Bob时,她用自己的私钥对"上一次交易的哈希值 + Bob的公钥"进行签名,并将签名广播给全网。任何人都可以用Alice的公钥验证这个签名是否合法。
这解决了伪造问题:你必须有私钥才能签名,没有私钥就无法伪造转账。
但双花问题还没有解决——Alice可以对同一笔资金发起两次签名,分别发给Bob和Carol。谁的交易算数?
3.5.2 时间戳服务器(Timestamp Server)
解决双花,需要一个所有人都认可的"事件发生顺序"。白皮书第3节引入了时间戳服务器的概念:
将一批交易打包在一起,对这个数据块(Block)计算哈希值,然后将这个哈希值广播出去。这个哈希值就像是这批交易"存在于某一时刻"的公证戳。
更妙的是:每个新区块的哈希计算,都要包含上一个区块的哈希值。这样,所有区块就像一条链条一样串联起来,后面的每个区块都"见证"了前面所有区块的存在。任何人想篡改历史上的某个区块,就必须重新计算从那个区块到最新区块的所有哈希——这是极其巨大的计算量。
这就是"区块链"这个名字的来源。
3.5.3 工作量证明(Proof of Work)
光有区块链结构还不够。谁来决定哪个版本的链条是"正确的"?
白皮书第4节给出了答案:每个新区块的产生,必须解决一个计算难题——找到一个特殊的数字(称为Nonce),使得区块的哈希值满足特定条件(比如开头有若干个0)。这个过程需要大量的试错计算,但一旦找到答案,任何人都可以一眼验证其正确性。
于是,系统的规则变成:最长的(累积工作量最多的)链条就是正确的历史。
Alice想双花——她必须比全网所有诚实节点加起来更快地产生新区块,才能让她的"篡改版历史"超过诚实链条的长度。只要诚实节点掌握超过50%的全网算力,这在概率上几乎是不可能的。
3.5.4 激励机制(Incentive)
最后,谁来做这个耗电的计算工作?白皮书第6节设计了一个精妙的激励机制:
成功创建新区块的节点(称为"矿工")将获得一定数量的新比特币作为奖励。这是比特币发行的唯一方式,总量上限被硬编码为2100万枚,每约四年奖励减半("减半"事件)。
这个设计一石三鸟:
- 解决了货币发行问题——无需中央银行,由算法控制货币供应
- 提供了维护网络的激励——矿工自愿参与,因为有利可图
- 保证了长期安全性——当区块奖励趋近于零时,交易手续费将成为激励来源
3.6 白皮书的"优雅"在哪里?
让我们退一步,欣赏一下这个设计的整体美感。
工程设计中有一个词叫做"优雅(Elegant)",指的是用最简单的机制解决最复杂的问题。比特币白皮书的优雅体现在以下几点:
1. 用博弈论代替权威
系统没有任何管理员,没有任何"信任我们"的承诺。它的安全性建立在经济理性上:攻击网络的成本远高于收益,所以理性的参与者会选择遵守规则。中本聪把计算机科学问题转化成了一个纳什均衡。
2. 用开放代替封闭
所有规则都是公开的代码,任何人都可以审计。不存在"相信我们的服务器"这种说法——规则本身就是信任。这是加密学(Cryptography)而非法律(Law)构建的信任。
3. 用简单的局部规则涌现出复杂的全局行为
每个节点只需要遵守几条简单的规则:验证交易签名、验证区块哈希、认可最长链。就这几条规则,涌现出了一个全球范围内去中心化的货币系统。这种"涌现性"让人想到蚂蚁群体的智能——每只蚂蚁都很简单,群体却展现出惊人的复杂组织能力。
4. 把不可能问题转化为概率问题
中本聪没有"消灭"双花的可能性,而是让它变得极其昂贵且概率极低。在等待足够多的区块确认后(一般是6个),双花成功的概率可以低到可以忽略不计。这是工程思维而非数学完美主义——足够安全,就是安全。
3.7 中本聪是谁?——一个美丽的谜
白皮书发布后,中本聪参与了早期比特币网络的开发,在邮件列表和论坛上留下了大量技术讨论。2010年底,他悄然将代码维护权移交给了其他开发者,之后便几乎彻底销声匿迹。
关于中本聪的真实身份,多年来有过无数猜测:有人认为是日裔美籍密码学家Hal Finney,有人怀疑是英国数学家Nick Szabo,还有人认为是一个团队的集体假名。2016年,澳大利亚商人Craig Wright声称自己就是中本聪,但密码学社区的主流观点认为他的证明站不住脚。
这个谜至今悬而未决。
但有趣的是,这个谜本身也是比特币哲学的一部分:系统的价值不依赖于创造者的身份。中本聪持有约100万枚比特币,如果他真的消失了,这笔财富就永远沉睡在区块链里,无人能动。这反而成为了比特币去中心化的一个象征性证明——即使创始人离开,系统依然完美运行。
代码就是遗产,算法就是遗嘱。
3.8 白皮书之前:站在巨人的肩膀上
中本聪并非凭空创造了比特币,他巧妙地整合了密码学领域几十年的研究成果。了解这些前驱工作,有助于我们理解比特币各个组件的来源。
| 技术/概念 | 来源 | 在比特币中的应用 |
|---|---|---|
| 公钥密码学(RSA/ECDSA) | Diffie & Hellman, 1976 | 数字签名与所有权证明 |
| 哈希函数(SHA-256) | NSA, 2001 | 区块链接与工作量证明 |
| 工作量证明 | Adam Back的Hashcash, 1997 | 反垃圾邮件机制→挖矿机制 |
| 时间戳链 | Haber & Stornetta, 1991 | 防篡改历史记录 |
| P2P网络 | BitTorrent等, 2000s | 去中心化节点网络 |
| Merkle树 | Ralph Merkle, 1979 | 高效验证交易集合 |
中本聪在白皮书的参考文献里也引用了这些工作。比特币是一次天才的综合创新(Synthesis Innovation),而非从零开始的发明。这实际上是很多伟大技术的共同特征:互联网综合了TCP/IP、HTTP、DNS等已有协议;智能手机综合了触摸屏、处理器、移动网络等已有技术。
在正确的时机,用正确的方式把正确的积木拼在一起——这本身就是一种深刻的创造力。
3.9 比特币网络的诞生:创世区块
2009年1月3日18:15 UTC,中本聪挖出了比特币的第一个区块——创世区块(Genesis Block)。
这个区块有一个极具深意的细节:中本聪在区块的Coinbase字段里嵌入了一段文字:
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks
这是当天《泰晤士报》的头版标题,意思是"财政部长即将对银行实施第二轮救助"。
这不只是一个时间戳(证明创世区块不可能是提前挖好的),更是一个宣言:比特币的诞生,正是对这个需要反复救助银行的腐朽金融体系的回应。
九天后,2009年1月12日,中本聪向Hal Finney发送了10枚比特币——这是人类历史上第一笔比特币交易。
世界从此不同。
本章小结
中本聪的比特币白皮书,本质上是对一个古老难题——"无需信任中间人的情况下防止双花"——的一个工程学答案:用工作量证明将"社会信任问题"转化为"物理成本问题",用最长链规则构建去中心化共识,用经济激励驱动网络自我维持。它的优雅不在于任何单一的技术突破,而在于将密码学、经济学与博弈论的已有积木,以前所未有的方式组合在了一起。
第4章 区块与链:数据结构层面的解析
4.1 为什么要关心数据结构?
如果说第3章告诉了你比特币"是什么"和"为什么",那么这一章要回答的是"怎么做到的"。
作为理工科学生,你可能已经学过链表、树、哈希表等数据结构。区块链的魅力在于,它用这些耳熟能详的基础积木,构建出了一个具有密码学不可篡改性的特殊数据结构。
让我们由浅入深,把区块链的数据结构拆开来看。
4.2 哈希函数:一切的基础
在深入区块结构之前,我们必须先把哈希函数讲透,因为它是整个系统的基石。
直觉定义: 哈希函数是一个"数字指纹机"。你把任意大小的数据塞进去,它吐出一个固定长度的数字串。相同的输入永远产生相同的输出,但微小的输入变化会导致输出剧烈变化。
比特币使用的是SHA-256(Secure Hash Algorithm 256-bit),输出是一个256位(32字节)的哈希值,通常以64个十六进制字符表示。
让我们看一个具体例子:
import hashlib
def sha256(text):
return hashlib.sha256(text.encode()).hexdigest()
print(sha256("Hello, Bitcoin!"))
# 输出: 4a5b6c... (64个十六进制字符)
print(sha256("Hello, Bitcoin?")) # 只改了一个标点
# 输出: 完全不同的64个字符
一个密码学安全的哈希函数需要满足三个关键性质:
性质1:抗原像性(Preimage Resistance)
给你一个哈希值 $h$,在计算上无法找到 $x$ 使得 $H(x) = h$(除非穷举所有可能的 $x$)。
用大白话说:哈希是单向的,无法从指纹反推原始数据。
数学上,SHA-256的输出空间有 $2^{256}$ 种可能,穷举的计算量相当于宇宙中所有原子的数量的平方——物理上不可行。
性质2:抗第二原像性(Second Preimage Resistance)
给你 $x$ 和 $H(x)$,在计算上无法找到 $x' \neq x$ 使得 $H(x') = H(x)$。
用大白话说:无法伪造一份不同的数据,使它的哈希值与原数据相同。
这保证了区块链的防篡改性:你改了任何一个字节,哈希值就完全变了,一眼就能被发现。
性质3:抗碰撞性(Collision Resistance)
在计算上无法找到任意的 $x \neq x'$ 使得 $H(x) = H(x')$(即哈希碰撞)。
注意: 从鸽巢原理可知,碰撞必然存在(无限个输入映射到有限个输出),但SHA-256让找到碰撞的难度达到 $O(2^{128})$ 级别,这在实践中是安全的。
雪崩效应(Avalanche Effect)
下面这个例子展示了哈希函数的"蝴蝶效应":
s1 = "The quick brown fox jumps over the lazy dog"
s2 = "The quick brown fox jumps over the lazy doe" # 最后一个词改了
print(sha256(s1))
# a9d4... (某个哈希值)
print(sha256(s2))
# 97b2... (完全不同)
即便输入只差一个字母,输出的256位中平均约128位会翻转。这个性质叫做雪崩效应,是密码学哈希函数的核心特征,也是为什么篡改任何历史区块都会导致后续所有区块哈希值全部失效的根本原因。
4.3 区块的解剖:一个区块里面有什么?
现在我们来看一个比特币区块的实际结构。每个区块由两部分组成:区块头(Block Header)和区块体(Block Body)。
4.3.1 区块头(80字节)
区块头是整个区块的"名片",只有80字节,包含以下字段:
Block Header (80 bytes total)
┌─────────────────────────────────────────────────────────┐
│ Version (4 bytes) - 协议版本号 │
│ Previous Block Hash (32 bytes) - 上一个区块的哈希值 │
│ Merkle Root (32 bytes) - 所有交易的默克尔树根 │
│ Timestamp (4 bytes) - 区块创建时间(Unix时间)│
│ Difficulty Target (4 bytes) - 挖矿难度目标值 │
│ Nonce (4 bytes) - 工作量证明的随机数 │
└─────────────────────────────────────────────────────────┘
注意:工作量证明(挖矿)实际上就是对这80字节的区块头反复计算SHA-256,直到找到满足难度要求的Nonce为止。
区块头的哈希值就是这个区块的"ID",在整个比特币网络中唯一标识一个区块。
4.3.2 区块体:交易列表
区块体包含这个区块打包的所有交易,第一笔永远是Coinbase交易——这是给矿工的区块奖励,凭空产生新的比特币(不需要任何输入)。
用Python伪代码表示一个区块的结构:
@dataclass
class BlockHeader:
version: int # 4 bytes,当前通常为1或2
prev_block_hash: bytes # 32 bytes,上一个区块头的SHA-256哈希
merkle_root: bytes # 32 bytes,所有交易的默克尔根
timestamp: int # 4 bytes,Unix时间戳
bits: int # 4 bytes,紧凑格式的难度目标
nonce: int # 4 bytes,0到4,294,967,295
@dataclass
class Block:
header: BlockHeader
tx_count: int # 交易数量(VarInt格式)
transactions: List[Transaction] # 交易列表
我们来看一个真实区块的数据(比特币区块#0,即创世区块):
Previous Hash: 0000000000000000000000000000000000000000000000000000000000000000
Merkle Root: 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
Timestamp: 1231006505 (2009-01-03 18:15:05 UTC)
Bits: 486604799
Nonce: 2083236893
Block Hash: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
注意区块哈希值开头有大量的0——这正是工作量证明的结果,我们在第5章详细讲解。
4.4 Merkle树:高效验证交易的神器
4.4.1 问题的起源
假设一个区块里有2000笔交易,你想验证其中第1573笔交易是否真的在这个区块里。最笨的方法是把所有2000笔交易都下载下来,然后一一比对。
但如果你是一个手机用户,带宽和存储都有限,能不能有更聪明的方法?
答案是Merkle树(Merkle Tree),由密码学家Ralph Merkle于1979年提出。
4.4.2 Merkle树的构造
Merkle树是一种完全二叉树,叶子节点是每笔交易的哈希值,父节点是其两个子节点哈希值拼接后的哈希值,一直往上聚合,直到得到唯一的根节点(Merkle Root)。
以4笔交易为例:
Root
/ \
H(AB) H(CD)
/ \ / \
H(A) H(B) H(C) H(D)
| | | |
Tx_A Tx_B Tx_C Tx_D
计算过程:
def merkle_root(transactions):
# 计算所有交易的哈希值作为叶子节点
layer = [sha256(tx.serialize()) for tx in transactions]
# 如果交易数为奇数,复制最后一个哈希(比特币的做法)
if len(layer) % 2 == 1:
layer.append(layer[-1])
# 自底向上构建树
while len(layer) > 1:
next_layer = []
for i in range(0, len(layer), 2):
combined = layer[i] + layer[i+1]
next_layer.append(sha256(combined))
layer = next_layer
return layer[0] # 返回根节点哈希
4.4.3 Merkle路径:高效的成员证明
Merkle树真正的价值在于Merkle路径(Merkle Path),也叫Merkle Proof。
回到刚才的问题:你想验证 $Tx_C$ 是否在某个区块里。
完整节点(Full Node)只需要提供给你以下信息:
- $H(D)$:$Tx_C$ 的兄弟节点哈希
- $H(AB)$:上一层的兄弟节点哈希
- Merkle Root
Root ✓ (你已知)
/ \
H(AB)← H(CD)←
/ \
H(C)← H(D)←(提供给你)
|
Tx_C ✓ (你有这笔交易)
验证过程:
def verify_merkle_proof(tx, merkle_path, merkle_root):
current_hash = sha256(tx.serialize())
for sibling_hash, is_left in merkle_path:
if is_left:
# 兄弟节点在左边
current_hash = sha256(sibling_hash + current_hash)
else:
# 兄弟节点在右边
current_hash = sha256(current_hash + sibling_hash)
return current_hash == merkle_root
效率对比:
对于包含 $n$ 笔交易的区块:
- 笨方法:需要传输和验证 $O(n)$ 个交易
- Merkle路径:只需要 $O(\log_2 n)$ 个哈希值
比特币单个区块最多约4000笔交易(SegWit后),Merkle路径只需要约12个哈希值(32 × 12 = 384字节),就能证明任意一笔交易的存在。
这是手机端"轻节点(SPV节点)"的理论基础——你不需要存储完整的区块链(约600GB),只需要存储所有区块头(约60MB),然后通过Merkle Proof按需验证特定交易。
4.5 区块如何连接成链?
现在我们理解了单个区块的结构,来看区块是如何形成"链"的。
关键字段是区块头中的 Previous Block Hash:每个区块头都包含上一个区块头的SHA-256哈希值。
最新区块
Block 0 (Genesis) Block 1 Block N
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Version │ │ Version │ │ Version │
│ Prev Hash: 0..│←──│ Prev Hash: H0 │←──│ Prev Hash: H(N-1)│
│ Merkle Root │ │ Merkle Root │ │ Merkle Root │
│ Timestamp │ │ Timestamp │ │ Timestamp │
│ Bits/Nonce │ │ Bits/Nonce │ │ Bits/Nonce │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ H0 │ H1 │ HN
└──────────────────→│ →│
现在,想象一个攻击者想修改Block 1中的某笔交易:
- 修改Block 1的交易数据
- Block 1的Merkle Root随之改变
- Block 1的区块头哈希 $H1$ 完全改变
- Block 2的
Previous Block Hash现在与实际的 $H1$ 不匹配——Block 2失效 - 攻击者必须重新计算Block 2的工作量证明
- Block 2重新计算后哈希改变——Block 3失效
- 以此类推,攻击者必须重新挖掘从Block 2到最新区块的所有区块
而与此同时,全网诚实的矿工还在继续往前挖新块。攻击者需要比全网所有诚实矿工加起来算得还要快,才能让他的篡改版本追上并超过诚实链的长度。
这就是区块链不可篡改性的数学保证:篡改历史的成本,随着时间推移呈指数级增长。
4.6 创世区块:链条的起点
创世区块(Block #0)是整个链条的根,它没有"上一个区块",所以其 Previous Block Hash 字段被设置为全零。
GENESIS_PREV_HASH = b'\x00' * 32 # 32字节全零
这是整个比特币系统中唯一一个"硬编码"在每个比特币客户端代码里的区块。任何声称自己是"比特币"的节点,必须认可这个创世区块,这是整个系统一致性的最终锚点。
4.7 UTXO模型:比特币如何记账?
到目前为止,我们讨论了区块和链的结构,但还没有解释比特币如何记录"谁有多少钱"。
你可能第一直觉想到的是"账户余额"模型:每个地址有一个余额,转账就是"从A账户减少,在B账户增加"。以太坊确实用这种模型,但比特币不是。
比特币使用的是**UTXO(Unspent Transaction Output,未花费交易输出)**模型。
4.7.1 什么是UTXO?
UTXO的核心思想类似于现金:
- 你有三张100元的现金 = 你有3个UTXO,每个面值100元
- 你支付250元 = 你花掉这3个UTXO,找零50元给自己(新的UTXO),另外250元给对方(新的UTXO)
- 每个UTXO只能被花一次,花掉就销毁,生成新的UTXO
用数据结构表示:
@dataclass
class UTXO:
tx_id: bytes # 32 bytes,产生这个UTXO的交易ID
output_index: int # 这个UTXO是该交易第几个输出(从0开始)
amount: int # 面值,单位是聪(Satoshi,1 BTC = 10^8 Satoshi)
script: bytes # 锁定脚本(定义谁可以花费这个UTXO)
@dataclass
class Transaction:
inputs: List[TxInput] # 引用并花掉哪些UTXO
outputs: List[TxOutput] # 创建哪些新的UTXO
locktime: int # 最早可以被打包的时间/块高度
4.7.2 一笔完整的交易
假设Alice有2个UTXO:
- UTXO_1:0.5 BTC
- UTXO_2:0.8 BTC
Alice要给Bob转0.7 BTC(手续费设为0.01 BTC):
输入(Inputs): 输出(Outputs):
┌─────────────────────┐ ┌─────────────────────┐
│ Input 1: │ │ Output 1: │
│ 引用 UTXO_1 │ ──→ │ 0.7 BTC (给Bob) │
│ (0.5 BTC) │ └─────────────────────┘
├─────────────────────┤ ┌─────────────────────┐
│ Input 2: │ │ Output 2: │
│ 引用 UTXO_2 │ ──→ │ 0.59 BTC (找零给Alice)│
│ (0.8 BTC) │ └─────────────────────┘
└─────────────────────┘
总输入: 1.3 BTC 总输出: 1.29 BTC
差额: 0.01 BTC (矿工手续费)
注意: UTXO_1和UTXO_2被完全消耗(从UTXO集合中删除),两个新的Output进入UTXO池。矿工手续费不是显式写出来的,而是输入总额与输出总额的差值——矿工自动获得这个差额。
4.7.3 UTXO模型的优势
相比账户余额模型,UTXO有几个重要优势:
并行验证: 不同UTXO之间相互独立,可以并行验证多笔交易的合法性,而不用担心状态竞争。
隐私性: 每次转账都可以使用新的接收地址,不像账户模型那样地址复用明显。
全节点轻量化: 节点只需要维护当前的"UTXO集"(约5GB),而不是整个交易历史(约600GB),就能验证新交易是否合法。
4.8 脚本系统:可编程的锁与钥匙
比特币UTXO的"锁定脚本(Locking Script)"和"解锁脚本(Unlocking Script)"构成了一个简单的、基于栈的脚本语言,称为Script。
最常见的类型是P2PKH(Pay-to-Public-Key-Hash),也就是"向公钥哈希付款":
锁定脚本(锁):
OP_DUP OP_HASH160 <Bob的公钥哈希> OP_EQUALVERIFY OP_CHECKSIG
解锁脚本(钥匙,Bob提供):
<Bob的签名> <Bob的公钥>
验证时,将两者拼接,在栈上执行:
1. 把签名压栈
2. 把公钥压栈
3. OP_DUP: 复制栈顶(公钥)
4. OP_HASH160: 计算栈顶(公钥)的哈希
5. <Bob的公钥哈希>: 把预期的公钥哈希压栈
6. OP_EQUALVERIFY: 比较两个哈希,不等则失败
7. OP_CHECKSIG: 用公钥验证签名,成功则验证通过
这个小小的脚本系统,实现了"只有知道私钥的人才能花费这笔钱"的逻辑。它也支持更复杂的条件,比如多重签名(MultiSig):需要3个人中至少2个签名才能花费。
4.9 区块链数据结构的全景图
让我们把所有组件拼起来,看一下完整的数据结构层次:
比特币区块链
│
├── Block #700000
│ ├── Block Header (80 bytes)
│ │ ├── version: 0x20000000
│ │ ├── prev_hash: 000...abc (上一区块的哈希)
│ │ ├── merkle_root: def...123 (所有交易的Merkle根)
│ │ ├── timestamp: 1634567890
│ │ ├── bits: 0x170d21b9
│ │ └── nonce: 2843245792
│ │
│ └── Block Body
│ ├── Tx[0]: Coinbase交易 (矿工奖励,凭空产生6.25 BTC)
│ ├── Tx[1]: Alice → Bob: 0.5 BTC
│ ├── Tx[2]: Carol → Dave: 1.2 BTC
│ └── ... (共约2000笔交易)
│
├── Block #699999
│ └── ...
│
└── Block #0 (Genesis)
└── ...
每一层都有哈希函数保护:
- 交易ID = SHA256(SHA256(交易数据))
- Merkle树各层 = SHA256(SHA256(左子 || 右子))
- 区块ID = SHA256(SHA256(区块头))
- 链条连接 = 区块头包含上一区块ID
注意: 比特币使用双重SHA-256(SHA256d),即对数据计算两次SHA-256,这是为了防范特定类型的长度扩展攻击。
4.10 轻节点 vs 全节点:数据结构决定信任模型
理解了区块链数据结构,我们就能理解为什么比特币可以支持不同"重量级"的参与者:
全节点(Full Node):
- 存储完整区块链(~600GB)
- 自行验证每一笔交易和每一个区块
- 不需要信任任何人——数学就是权威
轻节点(SPV Node,Simplified Payment Verification):
- 只存储区块头(~60MB,约800万×80字节)
- 通过Merkle Proof向全节点请求特定交易的证明
- 需要信任全节点不会欺骗它(但攻击代价高)
- 适合手机等资源受限设备
矿工节点:
- 通常也是全节点
- 额外维护"内存池(Mempool)"——等待被打包的交易
- 负责将交易打包成区块、计算工作量证明
本章小结
区块链的数据结构核心是三层叠加的哈希保护:SHA-256哈希链接保证了历史不可篡改,Merkle树使得交易的高效验证成为可能,UTXO模型则提供了并行性好、隐私性强的记账范式。理解这些数据结构,就理解了区块链安全性的数学基础——它的不可篡改性不是承诺,而是经过计算复杂度理论保证的数学结论。
第5章 工作量证明(PoW)与挖矿
5.1 动机:共识需要成本
我们在第3章说过,中本聪把"谁说了算"的问题转化成了"谁算得多"的问题。这一章,我们来把这个机制彻底搞透。
先问一个问题:为什么共识需要成本?
考虑一个反例:如果不需要任何代价,任何人都可以随意提出一个"历史版本"并要求网络接受它。女巫攻击变得轻而易举——创建10000个账号,每个账号都广播一个不同版本的历史,网络就陷入混乱。
成本的引入创造了皮肤在游戏中(Skin in the Game):你想要话语权,你就必须花费真实的资源。理性的行为者不会花费大量资源去做注定失败的攻击,因为那样只会亏损。
工作量证明正是这种成本的实例化。
5.2 哈希谜题:挖矿的本质
5.2.1 谜题的定义
挖矿(Mining),本质上就是解一道哈希谜题(Hash Puzzle)。
这道谜题可以用一句话描述:
找一个整数 $\text{Nonce}$,使得 $\text{SHA256}(\text{SHA256}(\text{区块头})) \leq \text{Target}$
其中 Target(目标值)是一个非常小的256位整数,比如:
Target = 0x00000000000000000002a37b0000000000000000000000000000000000000000
即要求计算出的哈希值,开头有若干个0。
用Python模拟这个过程:
import hashlib
import struct
import time
def mine_block(block_header_without_nonce: bytes, target: int) -> tuple[int, bytes]:
"""
暴力搜索满足条件的Nonce
返回 (nonce, block_hash)
"""
nonce = 0
while nonce < 2**32: # Nonce是32位整数
# 将Nonce(小端序)拼接到区块头
candidate = block_header_without_nonce + struct.pack('<I', nonce)
# 计算双重SHA-256
h1 = hashlib.sha256(candidate).digest()
h2 = hashlib.sha256(h1).digest()
# 将哈希值解释为大整数(大端序)
hash_int = int.from_bytes(h2, 'big')
if hash_int <= target:
return nonce, h2 # 找到了!
nonce += 1
return -1, b'' # Nonce空间耗尽(实际中会更换时间戳或Coinbase内容)
你可以亲手运行一个简化版本来感受一下:
def simple_pow_demo(prefix: str, difficulty: int):
"""
找一个Nonce使得 SHA256(prefix + str(nonce)) 的十六进制表示以 difficulty 个 '0' 开头
"""
target_prefix = '0' * difficulty
nonce = 0
start_time = time.time()
while True:
data = f"{prefix}{nonce}"
h = hashlib.sha256(data.encode()).hexdigest()
if h.startswith(target_prefix):
elapsed = time.time() - start_time
print(f"Nonce: {nonce}")
print(f"Hash: {h}")
print(f"尝试了 {nonce+1} 次,耗时 {elapsed:.3f} 秒")
return nonce
nonce += 1
# 难度=4时,要求哈希开头4个零,平均需要 16^4 = 65536 次尝试
simple_pow_demo("Hello, Bitcoin!", difficulty=4)
# 输出类似:
# Nonce: 48734
# Hash: 0000a9bc34f...
# 尝试了 48735 次,耗时 0.087 秒
当难度提高到6(需要6个零),平均需要约1600万次尝试。当难度提高到19(比特币目前的实际难度大约相当于哈希值开头有约76位的零),计算量令人咋舌。
5.2.2 谜题的不对称性
工作量证明有一个关键的不对称性:
- 解题(挖矿):需要海量计算,完全靠运气(暴力搜索)
- 验证(核查):只需一次哈希运算,任何普通电脑都能秒级完成
def verify_pow(block_header: bytes, target: int) -> bool:
h1 = hashlib.sha256(block_header).digest()
h2 = hashlib.sha256(h1).digest()
return int.from_bytes(h2, 'big') <= target
# 这个验证函数只需要 ~1微秒
这种不对称性是整个系统设计的精华:生产共识很昂贵,但验证共识几乎免费。这使得所有人都可以参与验证,但生产新区块需要真实投入。
5.3 难度调整:让出块时间保持稳定
5.3.1 为什么需要难度调整?
比特币的目标是每10分钟产生一个新区块。但参与挖矿的矿工数量(即全网算力)会随着比特币价格波动而剧烈变化。如果难度固定不变:
- 更多矿工加入 → 全网算力提升 → 平均10秒就出一个块
- 大量矿工退出 → 全网算力下降 → 平均1小时才出一个块
这会导致整个系统节奏混乱,交易确认时间不可预测。
比特币的解决方案:每隔2016个区块(约2周),自动调整挖矿难度。
5.3.2 难度调整算法
TARGET_BLOCK_TIME = 600 # 目标出块时间:600秒(10分钟)
ADJUSTMENT_INTERVAL = 2016 # 每2016个块调整一次
def adjust_difficulty(old_target: int, actual_time_seconds: int) -> int:
"""
actual_time_seconds: 最近2016个区块实际花费的时间
"""
expected_time = TARGET_BLOCK_TIME * ADJUSTMENT_INTERVAL # 2016 * 600 = 1,209,600秒
# 新目标 = 旧目标 × (实际时间 / 预期时间)
new_target = old_target * actual_time_seconds // expected_time
# 防止调整幅度过大(最多调整4倍)
max_target = old_target * 4
min_target = old_target // 4
new_target = max(min_target, min(max_target, new_target))
return new_target
直觉理解:
$\text{新目标} = \text{旧目标} \times \frac{\text{实际出块时间}}{\text{预期出块时间(2周)}}$
- 如果实际用了1周(矿工变多,算力提升)→ 新目标 = 旧目标 × (1/2) → 目标值变小,难度增大
- 如果实际用了4周(矿工减少,算力下降)→ 新目标 = 旧目标 × 2 → 目标值变大,难度减小
注意:目标值变小,意味着满足条件的哈希更稀少,挖出一个块需要更多尝试——即难度增大。这有点反直觉,需要特别注意。
比特币协议中用"难度(Difficulty)"来表示当前的挖矿难度:
$\text{Difficulty} = \frac{\text{最大目标值(创世区块)}}{\text{当前目标值}}$
2024年,比特币的难度约为83.5 T(83.5 × $10^{12}$),意味着现在挖一个区块的难度是创世区块时的 $8.35 \times 10^{13}$ 倍。
5.4 挖矿硬件的演化史
挖矿历史是一部精彩的技术演化史,也是一个关于"专用化必然战胜通用化"的故事。
第一代:CPU挖矿(2009–2010)
比特币诞生初期,中本聪和早期极客用普通电脑的CPU挖矿。当时比特币几乎毫无价值,挖矿纯属爱好。一台普通CPU的哈希率约为 5 MH/s(每秒500万次哈希)。
第二代:GPU挖矿(2010–2013)
有人发现,图形处理器(GPU)特别擅长大量并行的简单数学运算——这恰好是SHA-256所需要的。GPU挖矿效率比CPU高出50-100倍,哈希率达到 500 MH/s 级别。
第三代:FPGA挖矿(2011–2013)
现场可编程门阵列(FPGA)是一种可以重新编程的硬件芯片,可以比GPU更高效地执行特定算法,同时功耗更低。但FPGA的编程门槛高,很快被更专业的方案取代。
第四代:ASIC挖矿(2013至今)
专用集成电路(ASIC)是为了单一目的——计算SHA-256哈希——而设计制造的芯片。它在这件事上的效率无与伦比,相比GPU高出1000倍以上。
目前顶级的比特币矿机(如比特大陆蚂蚁矿机S21 Hydro)的哈希率约为 335 TH/s(每秒33.5万亿次哈希),能效约为 16 J/TH(每太哈希消耗16焦耳)。
挖矿效率演化(对数刻度):
CPU (2009): 5 MH/s ─────────────────────────────────────
GPU (2010): 500 MH/s ──────────────────────────────────────────────
FPGA (2011): 5 GH/s ───────────────────────────────────────────────────
ASIC (2013): 100 GH/s ──────────────────────────────────────────────────────────
ASIC (2024): 335 TH/s ─────────── (此线已超出本图范围许多个数量级)
这个军备竞赛导致了一个重要的现实:单个人用普通电脑挖矿在经济上完全不可行,导致了矿池的出现。
5.5 矿池:化整为零
5.5.1 矿工面对的随机性问题
即使你有一台顶级矿机(335 TH/s),相比全网算力(约600 EH/s = 6×$10^{20}$ H/s),你的占比大约是:
$\frac{335 \times 10^{12}}{6 \times 10^{20}} \approx 5.6 \times 10^{-7} \approx 0.000056%$
全网平均10分钟出一个块,你找到这个块的概率是 $5.6 \times 10^{-7}$。
期望等待时间: $\frac{10 \text{ 分钟}}{5.6 \times 10^{-7}} \approx 32\text{ 年}$
也就是说,你可能挖32年才挖到一个块(当然期间难度会调整)。这种巨大的随机性使得单矿工的收入极不稳定,大多数时候颗粒无收。
5.5.2 矿池的工作原理
矿池(Mining Pool)让大量矿工合并算力,共同竞争挖块,然后按照每个矿工的贡献比例分配奖励。
矿池如何衡量贡献?引入**份额(Share)**的概念:
矿池设定一个"更容易"的目标(比如只需要哈希开头20个0)
矿工每找到一个满足"份额目标"的哈希,就提交一个份额,证明他在工作
偶尔,这个哈希恰好也满足实际比特币的难度目标——块找到了!
# 矿池服务器给矿工分配任务
def assign_work(miner_id):
return {
'block_template': current_block_template,
'share_target': pool_target, # 容易目标:用于证明工作量
'block_target': bitcoin_target, # 困难目标:真正的比特币难度
}
# 矿工提交份额
def submit_share(miner_id, nonce, block_hash):
if block_hash <= pool_target:
shares[miner_id] += 1
if block_hash <= bitcoin_target:
# 找到了真正的区块!广播到比特币网络
broadcast_block(nonce)
distribute_reward()
奖励分配(PPLNS方案):
$\text{矿工A的奖励} = \text{区块奖励} \times \frac{\text{矿工A的份额数}}{\text{矿池总份额数}}$
这使得每个矿工的收入变得平滑可预期,即使你只有全网0.0001%的算力,你也能定期收到对应比例的奖励。
目前最大的比特币矿池是Foundry USA和Antpool,各占约20-25%的全网算力。
5.6 挖矿经济学:理性计算
一个精明的矿工需要计算挖矿是否有利可图:
收益: $\text{每日收益} = \frac{\text{我的算力}}{\text{全网算力}} \times \text{每日产出的区块数} \times \text{(区块奖励 + 手续费)}$
成本: $\text{每日成本} = \text{矿机功耗(kW)} \times 24 \times \text{电价($/kWh)} + \text{折旧成本}$
盈亏平衡电价:
$\text{盈亏平衡电价} = \frac{\text{每日收益}}{\text{功耗(kW)} \times 24}$
2024年比特币减半后,区块奖励降至3.125 BTC,以约70,000美元/BTC的价格计算:
- 每日全网出块:$24 \times 6 = 144$ 个
- 每个区块价值:约21.9万美元(包括手续费)
- 全网每日产出:约3153万美元
使用蚂蚁矿机S21(200 TH/s,3500W):
- 占全网算力份额:约 $3.3 \times 10^{-7}$
- 每日期望收益:约10美元
- 每日电费(按$0.04/kWh):$3.5 \times 24 \times 0.04 = 3.36$ 美元
- 净利润:约6.64美元/天
这意味着电费超过约$0.07/kWh,挖矿就开始亏损。这也是为什么大型矿场往往建在电力便宜的地区(如水电丰富的中国西南、冰岛、德克萨斯)。
5.7 51%攻击:PoW安全的边界
5.7.1 攻击原理
如果某个实体控制了超过全网50%的算力,理论上可以发动51%攻击:
- 攻击者向商家发送一笔交易(比如购买一辆特斯拉),等待确认
- 同时,攻击者悄悄在"私有分叉"上挖矿,在这个版本的历史里没有这笔交易
- 当私有链超过公开链的长度时,攻击者公开它
- 网络遵循"最长链"规则,切换到攻击者的版本
- 原先的那笔支付"消失"了,攻击者实现了双花
公开链(诚实矿工):
Genesis → B1 → B2 → B3 → B4 → B5 (长度5)
攻击者私有链:
Genesis → B1 → B2 → B3' → B4' → B5' → B6' (长度6,超过公开链)
攻击者公开私有链后,网络切换到攻击者版本
B3'中没有"Alice→商家"那笔交易 = 双花成功
5.7.2 攻击的成本
发动51%攻击需要购置或租用超过全网50%的算力。
以当前比特币网络为例:
- 全网算力:~600 EH/s
- 需要的算力:>300 EH/s
- 顶级矿机(S21,200 TH/s)数量:>150万台
- 硬件成本:>150亿美元(仅硬件)
- 还需要同等规模的电力基础设施
重要的是: 发动51%攻击会:
- 损害比特币声誉,导致价格暴跌
- 让攻击者自己持有的比特币大幅贬值
- 让那价值150亿美元的矿机变成废铁
这是一种自我伤害的攻击。有这150亿美元,攻击者不如老实挖矿——收益更高、风险更低。PoW的安全性本质上是一种经济纳什均衡。
5.7.3 较小的区块链更脆弱
对于市值较小的区块链(如比特币现金、以太坊经典),51%攻击的成本大幅下降,历史上已有多次成功的攻击案例。这也是为什么人们说"比特币最安全"——它是算力最多的PoW链。
5.8 环境争议:挖矿的电力消耗
比特币挖矿的电力消耗是一个真实存在且值得严肃讨论的问题。
事实数据(2024年):
- 比特币全网年耗电量:约120-150 TWh(太瓦时)
- 对比:全球数据中心年耗电约200-250 TWh
- 对比:全球黄金开采年耗电约100-130 TWh
- 对比:传统金融体系(银行业、ATM等)年耗电约700 TWh
支持者的观点:
- 比特币挖矿是"价格驱动的"——可以快速响应电力供需,成为电网的"负荷平衡器"
- 大量矿场使用否则会被浪费的可再生能源(水电的丰水期弃电、风电的消纳困难时段)
- 一些矿场使用油气开采中被白白燃烧掉的伴生气(Flaring Gas)
批评者的观点:
- 绝对数量仍然巨大,无论与什么对比
- 中国内蒙、新疆挖矿曾大量依赖煤电
- 同等安全性可以用其他共识机制(如PoS)以更少能耗实现
这是一个价值判断而非纯粹技术问题:你认为比特币提供的去中心化金融服务值多少电力,取决于你对这项服务价值的判断。作为工程师,我们应该基于事实做判断,而不是盲目站队。
5.9 减半事件:比特币的通缩机制
中本聪将比特币的发行量硬编码为一个精确的通缩曲线:
- 每210,000个区块(约4年),区块奖励减半
- 初始奖励:50 BTC
- 2012年第一次减半:25 BTC
- 2016年第二次减半:12.5 BTC
- 2020年第三次减半:6.25 BTC
- 2024年第四次减半:3.125 BTC
- 总供应量上限:$\sum_{i=0}^{\infty} 210000 \times 50 \times (1/2)^i = 210000 \times 50 \times 2 = 21,000,000$ BTC
def total_bitcoin_ever():
reward = 50 # 初始区块奖励
total = 0
halving = 0
while reward > 1e-8: # 精度到1聪
total += 210000 * reward
reward /= 2
halving += 1
return total
print(f"比特币总量:{total_bitcoin_ever():.0f} BTC")
# 输出: 比特币总量: 20999999 BTC (约2100万)
最后一个比特币预计在2140年前后被挖出。届时,矿工的收入将完全来自交易手续费。这是一个大胆的长期赌注:100年后,比特币网络的手续费收入是否足以激励矿工维护安全?没有人知道答案,但这是比特币最有趣的长期经济问题之一。
5.10 PoW的哲学意义:物理世界的锚
最后,让我们从技术层面抬起头,思考PoW更深层的含义。
区块链的安全性最终锚定在物理世界中:
- 挖矿需要芯片 → 芯片需要矿物和工厂
- 挖矿需要电力 → 电力需要发电设施
- 这些都是真实的、有限的、不可复制的物理资源
这是比特币与之前所有数字货币尝试的根本区别。数字信息可以无限复制,但物理能量不能。PoW是把"数字承诺"和"物理现实"锚接在一起的桥梁。
Nick Szabo把这个思想称为"不可伪造的代价(Unforgeable Costliness)",并认为它是任何可靠货币的基础属性——就像黄金的价值来自于开采它的真实成本一样。
本章小结
工作量证明是一个精妙的经济机制:它把"谁来维护账本"的社会信任问题,转化为"谁投入了最多真实计算资源"的物理成本问题,并通过难度自动调整保持系统节奏稳定。PoW的安全性不依赖法律或权威,而依赖于经济理性:攻击网络的成本远超收益,理性参与者的最优策略就是诚实挖矿。这种"用物理成本锚定数字信任"的思路,是中本聪最深刻的贡献之一。
第6章 比特币网络:节点、钱包与交易
6.1 从数据结构到运行系统
前两章我们分析了区块链的数据结构(第4章)和共识机制(第5章),但这些都还停留在"静态"层面。比特币真正运行起来时,是一个由成千上万个节点组成的动态分布式系统。
这一章,我们从网络层出发,完整地追踪一笔比特币交易从发起到最终确认的全过程,同时理解钱包、密钥、地址这些概念的底层原理。
6.2 P2P网络:没有中心的网络
6.2.1 比特币网络的拓扑
比特币网络是一个纯P2P(点对点)网络,没有服务器、没有主节点、没有任何中心化的协调机构。
网络中的每个节点都与其他数个节点直接相连(默认维护8个出站连接),构成一个非结构化的随机图(Random Graph)。
节点A ─── 节点B
│ ╲ │ ╲
│ 节点E─┤ 节点F
│ ╱ │ ╱
节点C ─── 节点D
这种拓扑的优点是极其健壮:不存在单点故障,没有任何单一节点的宕机会影响整个网络。历史上,多个国家曾试图"关掉"比特币网络,但做不到,因为根本找不到"开关"。
6.2.2 节点发现:如何加入网络
一个新节点第一次启动时,如何找到其他节点?
- DNS种子(DNS Seeds): 比特币客户端硬编码了几个DNS域名(如
seed.bitcoin.sipa.be),这些域名会返回一批已知的比特币节点IP地址 - 地址广播: 连接后,节点会收到其他节点分享的地址列表(
addr消息),并将自己的地址广播出去 - 地址缓存: 节点会在本地保存一个已知节点的地址列表,下次启动时优先使用这些地址
# 简化的节点发现过程
class BitcoinNode:
def start(self):
# 1. 尝试连接种子节点
seed_nodes = resolve_dns_seeds([
"seed.bitcoin.sipa.be",
"dnsseed.bluematt.me",
"seed.bitcoinstats.com",
])
# 2. 连接到8个节点
for peer_addr in seed_nodes[:8]:
self.connect(peer_addr)
# 3. 从已连接节点获取更多地址
for peer in self.peers:
peer.send(GetAddrMessage())
6.2.3 消息传播:Gossip协议
比特币网络使用Gossip协议传播消息(交易、区块等):
- 节点收到一条新消息(如新交易)
- 首先验证其合法性
- 如果合法,将其广播给所有直连的节点
- 每个节点重复此过程
- 消息像病毒一样在整个网络中传播
在正常情况下,一个新交易在几秒内就能传播到全球99%以上的节点,一个新区块(约1MB)在30-60秒内传播到全网。
比特币协议有防止重复传播的机制:节点会记录哪些消息已经见过(通过哈希),对于重复消息直接丢弃。
6.3 密钥与地址:从私钥到比特币地址
这是许多人觉得迷惑的地方,让我们把整个推导过程从头讲清楚。
6.3.1 非对称密码学回顾
比特币使用椭圆曲线数字签名算法(ECDSA),基于一条特定的椭圆曲线:secp256k1。
椭圆曲线上的运算有一个关键的单向性:
$\text{公钥} = \text{私钥} \times G$
其中 $G$ 是曲线上的一个固定基点,× 是椭圆曲线上的点乘运算(不是普通乘法)。
- 已知私钥(一个256位大整数),计算公钥:容易(有限次数的点加运算)
- 已知公钥,反推私钥:极其困难(椭圆曲线离散对数问题,无已知多项式时间算法)
私钥的安全性来自于数字空间的巨大:私钥是一个 $[1, n-1]$ 范围内的随机整数,其中 $n \approx 1.158 \times 10^{77}$。即使每秒尝试 $10^{18}$ 个私钥,穷举整个空间需要比宇宙年龄还要长的时间。
6.3.2 从私钥到地址的完整推导
让我们一步步追踪:
第1步:生成私钥
import os
import hashlib
# 生成256位的随机数作为私钥
private_key_bytes = os.urandom(32)
private_key_int = int.from_bytes(private_key_bytes, 'big')
# 确保在合法范围内
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
assert 1 <= private_key_int < n
print(f"私钥(hex): {private_key_bytes.hex()}")
# 例: 1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd
第2步:计算公钥(椭圆曲线点乘)
# 使用 ecdsa 库(或 coincurve)
from ecdsa import SigningKey, SECP256k1
sk = SigningKey.from_string(private_key_bytes, curve=SECP256k1)
vk = sk.get_verifying_key()
# 非压缩公钥:04 + x坐标(32字节) + y坐标(32字节) = 65字节
public_key_uncompressed = b'\x04' + vk.to_string()
# 压缩公钥:02/03 + x坐标 = 33字节
# 02: y为偶数;03: y为奇数
x = vk.to_string()[:32]
y = vk.to_string()[32:]
prefix = b'\x02' if int.from_bytes(y, 'big') % 2 == 0 else b'\x03'
public_key_compressed = prefix + x
第3步:哈希公钥
import hashlib
def hash160(data: bytes) -> bytes:
"""RIPEMD-160(SHA-256(data))"""
sha256_hash = hashlib.sha256(data).digest()
ripemd160 = hashlib.new('ripemd160')
ripemd160.update(sha256_hash)
return ripemd160.digest()
# 公钥哈希(20字节)
pubkey_hash = hash160(public_key_compressed)
第4步:生成比特币地址(Base58Check编码)
import base58 # 可用 pip install base58 安装
# 版本字节(主网为0x00)
version = b'\x00'
payload = version + pubkey_hash
# 计算校验码(SHA256d的前4字节)
checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
# Base58Check编码
address_bytes = payload + checksum
bitcoin_address = base58.b58encode(address_bytes).decode()
print(f"比特币地址: {bitcoin_address}")
# 例: 1A1zP1eP5QGefi2DMPTfTL5SLmv7Divf (这是中本聪的创世地址)
整个推导链:
$\underbrace{\text{私钥}}{\text{256位随机数}} \xrightarrow{\text{×G(椭圆曲线点乘)}} \underbrace{\text{公钥}}{\text{33字节压缩格式}} \xrightarrow{\text{HASH160}} \underbrace{\text{公钥哈希}}{\text{20字节}} \xrightarrow{\text{Base58Check}} \underbrace{\text{地址}}{\text{~34字符}}$
为什么要哈希公钥,而不是直接用公钥作为地址?
- 抗量子计算: 即使未来量子计算机能破解ECDSA,攻击者也只能从公钥恢复私钥。但如果你从未花费过某个地址的比特币,公钥从未公开(网络上只有公钥的哈希),量子计算机无从下手
- 缩短长度: 20字节比33字节短,地址更简洁
- 额外安全层: 双重哈希使得即使椭圆曲线被破解,攻击者还需额外破解HASH160
6.4 钱包:密钥的管理者
6.4.1 钱包不存储比特币
一个重要的认知纠正:比特币钱包不存储比特币。
比特币"存在"于区块链的UTXO集合中。钱包存储的是私钥——证明你对这些UTXO有支配权的秘密数字。钱包更像是一把钥匙串,而不是一个钱袋。
6.4.2 早期钱包:随机密钥集合
最早期的比特币钱包(如Bitcoin Core 0.1)为每个地址生成一个独立的随机私钥,所有私钥存储在wallet.dat文件中。
问题:每次使用新地址都需要备份钱包文件,一旦文件丢失,密钥永久消失。历史上有很多人因为硬盘损坏而永久失去比特币——据估计,约有300-400万枚比特币因此"沉睡"于丢失的密钥中。
6.4.3 HD钱包:一粒种子,万棵密钥树
**分层确定性钱包(Hierarchical Deterministic Wallet,HD Wallet)**由BIP32(2012年)提出,用一个随机种子通过确定性算法派生出无数个密钥,彻底改变了密钥管理。
种子(Seed): 128-256位随机数
│
▼(HMAC-SHA512)
主私钥(Master Private Key)+ 链码(Chain Code)
│
├── m/0'/0 (第一个账户的第一个接收地址)
├── m/0'/1 (第一个账户的第二个接收地址)
├── m/0'/2 (...)
├── m/1'/0 (第二个账户的第一个接收地址)
└── ...(可无限派生)
路径符号(如m/44'/0'/0'/0/0)遵循BIP44标准:
m:主节点44':BIP44用途('表示硬化派生,更安全)0':比特币主网0':第0个账户0:外部链(接收地址)0:第0个地址
最关键的特性: 整棵密钥树可以由一个**助记词(Mnemonic)**恢复。
6.4.4 助记词:人类可读的种子
BIP39定义了助记词标准:从一个包含2048个英文单词的词典中,选取12或24个单词,这些单词就是种子的人类可读表示。
# 助记词(12个单词,128位熵)示例:
mnemonic = "abandon abandon abandon abandon abandon abandon " \
"abandon abandon abandon abandon abandon about"
# 这12个单词通过PBKDF2派生出64字节的种子
import hashlib
seed = hashlib.pbkdf2_hmac(
'sha512',
mnemonic.encode(),
b'mnemonic' + passphrase.encode(),
2048
)
助记词的安全性: 12个单词的助记词有 $2048^{12} = 2^{132}$ 种可能,穷举不可行。
备份比特币的正确方式,就是把助记词(12或24个单词)写在纸上,存放在安全的物理位置。这张纸就是你所有比特币的唯一凭证。
6.5 一笔交易的完整生命周期
现在我们把所有知识整合起来,追踪一笔交易从发起到最终确认的每一步。
场景: Alice要向Bob转0.3 BTC,她的钱包里有一个0.5 BTC的UTXO。
第1步:构造交易
Alice的钱包软件构造一个交易对象:
transaction = Transaction(
version=2,
inputs=[
TxInput(
prev_tx_id="a1b2c3...", # Alice那个0.5 BTC的UTXO所在交易的ID
prev_output_index=0, # 那个输出的索引
sequence=0xFFFFFFFF,
# 解锁脚本稍后填入
)
],
outputs=[
TxOutput(
amount=30000000, # 0.3 BTC = 30,000,000 聪
script=p2pkh_script(bob_address) # 锁定给Bob
),
TxOutput(
amount=19900000, # 0.199 BTC 找零给自己
script=p2pkh_script(alice_change_address) # 找零地址(通常是新地址)
)
# 差额 = 0.5 - 0.3 - 0.199 = 0.001 BTC = 100,000 聪(手续费)
],
locktime=0
)
注意:这里故意留出0.001 BTC不输出,这就是矿工手续费。
第2步:签名交易
Alice的钱包用她的私钥对交易进行ECDSA签名:
# 1. 序列化交易(去掉解锁脚本,添加SIGHASH类型)
tx_hash_for_signing = hash256(transaction.serialize_for_signing(SIGHASH_ALL))
# 2. 用Alice的私钥签名
signature = ecdsa_sign(alice_private_key, tx_hash_for_signing)
# 3. 将签名和公钥填入解锁脚本
transaction.inputs[0].unlock_script = (
len(signature).to_bytes(1, 'big') + signature + # 签名
len(alice_public_key).to_bytes(1, 'big') + alice_public_key # 公钥
)
关键: 签名覆盖了整个交易的内容(输入、输出、金额)。任何对交易的修改都会使签名失效。
第3步:广播交易
Alice的钱包将签好名的交易序列化为二进制格式,通过P2P网络广播:
# 序列化的交易(十六进制表示)
raw_tx = transaction.serialize().hex()
# 例: 0100000001a1b2c3...(几百字节)
# 广播给连接的节点
for peer in wallet.peers:
peer.send(TxMessage(raw_tx))
第4步:全网节点验证并传播
每个收到交易的节点执行以下验证:
def validate_transaction(tx: Transaction, utxo_set: UTXOSet) -> bool:
# 1. 基本格式检查
assert len(tx.inputs) > 0
assert len(tx.outputs) > 0
total_input = 0
for inp in tx.inputs:
# 2. 引用的UTXO必须存在(未被花费)
utxo = utxo_set.get(inp.prev_tx_id, inp.prev_output_index)
assert utxo is not None, "UTXO不存在或已被花费"
# 3. 解锁脚本必须有效(验证签名)
assert execute_script(inp.unlock_script, utxo.lock_script), "签名无效"
total_input += utxo.amount
total_output = sum(out.amount for out in tx.outputs)
# 4. 输入总额 >= 输出总额(差值为手续费,必须非负)
assert total_input >= total_output, "输出超过输入"
# 5. 输出金额必须正数
assert all(out.amount > 0 for out in tx.outputs)
return True
验证通过后,节点将交易加入自己的内存池(Mempool),并继续广播给邻居节点。
第5步:进入内存池(Mempool)
Mempool是每个节点维护的"待确认交易池",存放已经验证但尚未被打包进区块的交易。
矿工从Mempool中选择交易打包,通常按照手续费率(sat/vByte,聪每虚拟字节)排序,优先打包手续费率最高的交易:
def select_transactions_for_block(mempool: List[Transaction]) -> List[Transaction]:
# 按手续费率排序(从高到低)
sorted_txs = sorted(mempool,
key=lambda tx: tx.fee / tx.virtual_size(),
reverse=True)
selected = []
total_weight = 0
MAX_BLOCK_WEIGHT = 4_000_000 # 4MW,SegWit后的区块大小限制
for tx in sorted_txs:
if total_weight + tx.weight() <= MAX_BLOCK_WEIGHT:
selected.append(tx)
total_weight += tx.weight()
else:
break
return selected
实际影响: 当网络拥堵(Mempool满)时,手续费低的交易会在Mempool里等待很长时间,甚至最终被丢弃。2017年比特币牛市期间,转账手续费一度高达50-100美元,造成了严重的用户体验问题。
第6步:被打包进区块
矿工将Alice的交易打包进一个新区块,找到满足难度要求的Nonce后,广播这个新区块。
区块被全网接受后,Alice的交易获得了第1次确认。
第7步:等待足够的确认数
一次确认意味着交易被打包进了区块,但双花攻击的风险并未完全消除——攻击者可能在努力构建一条不包含这笔交易的更长链。
每增加一个区块,双花攻击就需要额外追上一个区块的难度,其成功概率急剧下降。
假设攻击者掌握全网 $q$ 比例的算力($q < 0.5$),诚实矿工掌握 $p = 1-q$ 比例,等待 $z$ 个确认后双花成功的概率大约是:
$P(\text{双花成功} | z \text{ 个确认}) \approx \left(\frac{q}{p}\right)^z$
对于 $q = 0.3$(攻击者掌握30%算力):
| 确认数 $z$ | 双花成功概率 |
|---|---|
| 1 | ~17.7% |
| 3 | ~0.6% |
| 6 | ~0.02% |
| 10 | <0.001% |
这就是为什么小额交易(如咖啡)等1-3个确认就够了,大额交易(如买房)通常等6个确认(约1小时)。
对于大额交易: 交易所、大型商家一般等待6个确认,即约60分钟。
6.6 手续费市场:拍卖区块空间
随着比特币用户增多,区块空间成为稀缺资源,交易手续费变成了一个价格发现机制。
6.6.1 replace-by-fee(RBF)
如果你发出了一笔手续费过低的交易,它可能长时间得不到确认。RBF(BIP125)允许用户发送一笔"替换"原交易的新交易,花费相同的UTXO,但支付更高的手续费。
矿工会用新交易替换内存池中的旧交易。
# RBF标记:sequence字段设为非最大值
rbf_input = TxInput(
prev_tx_id=...,
prev_output_index=...,
sequence=0xFFFFFFFD, # RBF标记(< 0xFFFFFFFE)
)
6.6.2 CPFP(Child-pays-for-parent,子为父偿)
另一种加速确认的方法:如果你的交易A(手续费低)还没被确认,你可以创建一笔新交易B,花费交易A的某个输出,并在B中支付高额手续费。
矿工打包B时必须同时打包A(因为B依赖于A),所以他们会把A和B作为一个"包"来考虑手续费率:
$\text{组合手续费率} = \frac{A\text{的手续费} + B\text{的手续费}}{A\text{的大小} + B\text{的大小}}$
如果组合手续费率足够高,矿工会主动打包A+B组合。
6.7 SegWit:解决交易可塑性,提升容量
6.7.1 交易可塑性问题
在2015年之前,比特币存在一个称为**交易可塑性(Transaction Malleability)**的漏洞:
交易ID = SHA256d(交易序列化数据)
问题在于,签名中包含一些可以被第三方修改(但不影响有效性)的字段,这导致交易内容和签名都没变,但交易ID可以被修改。
这个漏洞让比特币的闪电网络等二层方案难以实现,也被黑客利用(著名的Mt.Gox事件部分原因在此)。
6.7.2 SegWit的解决方案
2017年,比特币激活了**隔离见证(Segregated Witness,SegWit)**升级(BIP141)。
核心思路:把签名数据("见证数据")从交易体中分离出去,放在一个单独的"见证区"。
交易ID只计算不包含签名的部分,签名无法再影响交易ID——交易可塑性问题被彻底解决。
旧格式(Legacy):
[版本] [输入(含签名)] [输出] [锁定时间]
SegWit格式:
[版本] [标记: 0x00] [标志: 0x01] [输入(不含签名)] [输出] [见证数据(签名)] [锁定时间]
额外好处: SegWit引入了"虚拟字节(vByte)"的概念,见证数据的权重只有普通数据的1/4。这等效地将区块容量从约1MB提升到约4MB(以虚拟字节计),但实际存储大小仍受原有限制约束。
SegWit地址通常以3(P2SH-SegWit)或bc1(原生SegWit,Bech32格式)开头。
6.8 闪电网络:比特币的Layer 2
比特币每秒约处理7笔交易(TPS),相比Visa的数千TPS,差距悬殊。同时,每笔交易都需要等待1小时才能安全确认,不适合小额高频支付。
**闪电网络(Lightning Network)**是一个建立在比特币之上的Layer 2方案,允许用户在不上链的情况下进行几乎即时、超低费用的交易。
6.8.1 支付通道的原理
Alice和Bob建立一个"支付通道":
- 双方共同在链上发起一笔开通道交易,锁定一定量的比特币(比如各0.1 BTC)到一个2-of-2多签地址
- 每次Alice向Bob支付,双方更新一个**"承诺交易"(Commitment Transaction),记录最新的余额分配,但这笔交易不上链**
- 双方可以随时将最新的承诺交易广播到链上关闭通道,结算最终余额
开通道: 链上交易,0.2 BTC锁定到 2-of-2 多签
通道内交易(不上链,即时完成):
承诺交易1: Alice 0.1 BTC, Bob 0.1 BTC
承诺交易2: Alice 0.09 BTC, Bob 0.11 BTC (Alice付给Bob 0.01 BTC)
承诺交易3: Alice 0.07 BTC, Bob 0.13 BTC (Alice又付给Bob 0.02 BTC)
关闭通道: 广播最新承诺交易,链上结算 Alice 0.07, Bob 0.13
通道内的每笔交易都是即时的(毫秒级确认),手续费低到几乎为零。
6.8.2 路由:无需直接通道
通过"路由",Alice和Carol之间无需直接建立通道——只要存在一条通过其他节点的路径(如Alice→Bob→Carol),就可以完成支付。
使用哈希时间锁合约(HTLC)保证路由的原子性:要么全路径都支付成功,要么全部回滚,中间节点无法卷款跑路。
目前(2024年),闪电网络已有约1.5万个节点、7万条通道,容量约5000 BTC,日交易量仍在增长中。
6.9 比特币脚本的进阶:多签与时间锁
除了基本的P2PKH,比特币的Script语言还支持更复杂的花费条件:
6.9.1 多重签名(Multi-Signature)
典型的2-of-3多签:需要3个密钥中的任意2个签名才能花费。常用于:
- 交易所的冷钱包(需要多人同时授权)
- 家庭联名账户
- 商业合作伙伴的共同资金
锁定脚本 (P2MS):
OP_2 <公钥A> <公钥B> <公钥C> OP_3 OP_CHECKMULTISIG
解锁脚本(需要A和C的签名):
OP_0 <签名A> <签名C>
6.9.2 时间锁(Timelock)
绝对时间锁(CHECKLOCKTIMEVERIFY,CLTV): 交易只能在某个时间/区块高度之后被花费
# 2025年1月1日之前无法花费
<block_height_or_timestamp> OP_CHECKLOCKTIMEVERIFY OP_DROP
OP_DUP OP_HASH160 <公钥哈希> OP_EQUALVERIFY OP_CHECKSIG
相对时间锁(CHECKSEQUENCEVERIFY,CSV): 交易只能在被花费的UTXO被确认后至少N个区块才能花费。闪电网络的HTLC依赖这个机制。
6.10 比特币网络的现状与规模
作为参考,截至2024年的比特币网络关键数据:
| 指标 | 数值 |
|---|---|
| 全节点数量 | ~18,000(可公开访问的) |
| 区块链大小 | ~600 GB |
| UTXO集合大小 | ~5 GB |
| 日均交易量 | ~50万笔 |
| 平均手续费 | 1-20 sat/vByte(约$0.5-$5) |
| 全网算力 | ~600 EH/s |
| 矿工数量 | 数百万(个人+矿场) |
| 闪电网络节点 | ~15,000 |
| 比特币总市值 | ~1.3万亿美元 |
这个数字的背后,是一个全年无休、没有管理员、没有停机时间的全球货币网络。自2009年1月3日启动以来,比特币主网从未停止运行,没有经历过任何计划内的维护窗口。这本身就是一项工程奇迹。
本章小结
比特币网络是由密钥学、P2P网络、激励机制三者精密配合的有机系统:私钥通过椭圆曲线密码学保证所有权,P2P Gossip协议保证信息的快速无中心传播,UTXO模型和脚本系统提供了灵活且可验证的价值转移机制。追踪一笔交易从发起到最终确认的全过程,你会发现这套系统如何把"我们不需要信任任何人"这个哲学承诺,转化成了一个每天处理数十万笔真实交易的工程现实。
第二部分完