导语:比特币的世界状态是一袋未花费的硬币
与以太坊的"账户余额"模型不同,比特币没有"账户"。它的世界状态完全由 UTXO(Unspent Transaction Output,未花费交易输出) 集合构成——本质上是一张"哪些硬币还没有被花掉"的表。
每一笔交易做的事情很简单:销毁一批 UTXO,创建一批新的 UTXO。这个模型看起来笨拙,却带来了比特币天然的高并发和可并行验证。
一句话总结:比特币账本不记录"某人有多少钱",只记录"哪些硬币尚未被花掉"——转账就是销毁旧硬币、铸造新硬币的过程。
1. UTXO 模型:硬币的生命周期
1.1 什么是 UTXO
一个 UTXO 由两部分组成:
┌─────────────────────────────────────────┐
│ UTXO │
├─────────────────────────────────────────┤
│ txid(产生该输出的交易 ID) │
│ vout(输出索引,从 0 开始) │
│ satoshis(面额,1 BTC = 100,000,000 sat)│
│ locking_script(锁定脚本,谁能花) │
│ height(所在区块高度) │
│ coinbase?(是否创币输出) │
└─────────────────────────────────────────┘
UTXO 集合规模:约 1 亿 2 千万个(2024 年量级),约 8GB+ 的磁盘状态。
1.2 交易的生命周期
┌─────────────────────┐
│ UTXO 集合 │
└──────┬──────┬──────┘
│ │
输入(引用旧 UTXO) │ │
tx.vin = [{txid, vout}, │ │
...] │ │
▼ ▼
┌──────────────────┐
│ 一笔交易 │
│ sum(inputs) ≥ │
│ sum(outputs) │
└────────┬─────────┘
│
输出(创建新 UTXO)
tx.vout = [{value, lock_script}, ...]
│
▼
┌─────────────────────┐
│ UTXO 集合 │
│ ← 新输出被加入 │
│ → 被引用的旧输出被 │
│ 标记为已花费 │
└─────────────────────┘
核心不变量:
sum(tx.inputs.satoshis) ≥ sum(tx.outputs.satoshis)
差额 = 矿工费(fee),激励矿工打包交易
1.3 找零与"硬币"的原子性
UTXO 的最小单位是 satoshi,交易输出必须整体花费一个 UTXO,不能拆分使用。要支付 0.5 BTC 而持有 1 BTC 的 UTXO,就必须构造两个输出:0.5 BTC 给对方 + 0.4999 BTC 找零给自己。
# 伪代码:构造一笔 P2PKH 交易(未签名)
def build_tx(utxo, change_addr, to_addr, amount, fee):
return {
"version": 1,
"vin": [{
"txid": utxo["txid"],
"vout": utxo["vout"],
"script_sig": b"", # 签名稍后填写
"sequence": 0xFFFFFFFF,
}],
"vout": [
{"value": amount, "script_pubkey": p2pkh(to_addr)},
{"value": utxo["satoshis"] - amount - fee,
"script_pubkey": p2pkh(change_addr)},
],
}
一句话总结:UTXO 是"整枚硬币",只能整体花掉——所以钱包在不停制造"找零 UTXO",这也是链上垃圾碎片(dust)与钱包地址经常变化的来源。
2. 比特币 Script 脚本语言
2.1 栈式脚本语言
比特币用 Script 语言表达"谁能花这笔钱"。它是基于栈的非图灵完备语言(无循环、无跳转),逐条执行 opcode:
执行模型(后进先出栈):
push_1 (0x51) → 压入 1
push_2 (0x52) → 压入 2
OP_ADD (0x93) → 弹出 2、1,压入 3
OP_EQUAL (0x87) → 弹出 3、3,压入 1(真)
脚本执行成功(栈顶为真值且无非法操作)→ 交易有效。
2.2 常见锁定脚本类型
| 类型 | 锁定脚本 | 解锁方式 | 用途 |
|---|---|---|---|
| P2PKH | OP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_CHECKSIG | 签名 + 公钥 | 普通转账 |
| P2SH | OP_HASH160 <redeemScriptHash> OP_EQUAL | 签名 + 赎回脚本 | 多重签名、HTLC |
| P2WPKH | OP_0 <hash160>(SegWit) | 签名 + 公钥(见证字段) | 隔离见证转账 |
| P2WSH | OP_0 <sha256(witnessScript)> | 见证脚本 | 隔离见证多签/契约 |
| P2TR | OP_1 <taproot_key> | Schnorr 签名 / MAST 路径 | Taproot 升级 |
2.3 P2PKH 脚本执行过程
锁定脚本(ScriptPubKey):
OP_DUP OP_HASH160 <H> OP_EQUALVERIFY OP_CHECKSIG
解锁脚本(ScriptSig):
<sig> <pubkey>
合并执行:
<sig> <pubkey> OP_DUP OP_HASH160 <H> OP_EQUALVERIFY OP_CHECKSIG
─────────────────────────────────────────────────────────────
1. 压入 sig, pubkey
2. OP_DUP → 栈顶复制 pubkey
3. OP_HASH160 → pubkey 哈希化
4. 压入 H → 与脚本中声明的哈希比较
5. OP_EQUALVERIFY → 相等则弹出,否则脚本失败
6. OP_CHECKSIG → 用 pubkey 验证 sig,栈顶留下真值
2.4 P2SH 与赎回脚本
P2SH 的关键在于:锁定脚本只提交赎回脚本的哈希,赎回脚本内容在解锁时才揭示。这让多重签名与复杂契约的地址保持短小统一:
P2SH 地址 → 锁定脚本:OP_HASH160 <HASH(redeemScript)> OP_EQUAL
解锁: <sig1> <sig2> <redeemScript>
构造多重签名赎回脚本:
redeemScript = OP_2 <pubkey1> <pubkey2> <pubkey3> OP_3 OP_CHECKMULTISIG
(需要 2-of-3 签名)
一句话总结:比特币用一套极简的栈式脚本实现"谁能花、何时能花、如何证明能花",而 P2SH 把复杂规则藏进哈希,让链上地址保持简洁——这是后来一切契约型协议(HTLC、跨链原子交换)的地基。
3. 隔离见证 SegWit:修复交易延展性
3.1 问题:交易 ID 可被篡改
在 SegWit 之前,签名字段(ScriptSig)是交易 ID(txid = double-SHA256 的序列化)的一部分。由于 ECDSA 签名存在延展性(同一签名可有多种合法编码),第三方可以改写 ScriptSig 而不改变交易语义——结果 txid 变了,正在处理的交易被"变形"。
这对闪电网络(需要依靠 txid 锁定通道)是致命的。
3.2 SegWit 的解决方案(BIP-141)
SegWit 把签名等"见证数据"(witness data)从交易主体中剥离:
旧格式交易序列化:
version | vin(含 ScriptSig)| vout | locktime
SegWit 格式(witness 字段追加在末尾):
version | marker(0x00) | flag(0x01) | vin | vout | witness[] | locktime
未签名的 txid 计算只基于:version | vin(ScriptSig 置空)| vout | locktime
收益:
| 收益 | 说明 |
|---|---|
| 修复延展性 | txid 不再依赖见证数据,闪电网络/原子交换可以安全锁定 |
| 区块容量提升 | witness 数据折扣 75%(1 witness byte 只计 0.25 权重单位),区块上限 1MB→4M WU |
| 更小的交易 | 一次性大额交易的 witness 折扣明显,手续费更低 |
| 软分叉兼容 | 老节点仍可验证(视为"任何人可花"的特殊输出),新节点强制校验 witness |
3.3 地址与脚本变化
传统 P2PKH 地址: 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
P2WPKH 地址(Bech32):bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
P2WPKH 的锁定脚本极简:OP_0 <hash160(pubkey)>
签名与公钥放入 witness 字段(不参与 txid 计算)
3.4 Taproot(BIP-340/341/342):Schnorr + MAST
Taproot 是 SegWit 的继承者(2021 年激活),进一步提升了脚本的隐私与效率:
| 特性 | 含义 |
|---|---|
| Schnorr 签名 | 线性签名聚合,多签交易只需一个签名,验证更快 |
| MAST(Merklized Abstract Syntax Tree) | 大量条件分支折叠进一个根,只揭示被执行的路径 |
| P2TR 统一地址 | 单签与多签/复杂脚本的外观无法区分,隐私更强 |
| Sighash 改进 | SIGHASH_ANYPREVOUT(BIP-118)为闪电网络新通道类型铺路 |
# Taproot 地址样例(bech32m)
# bc1p...(P2TR 地址以 bc1p 开头)
# 脚本路径 vs 密钥路径:默认情况下只走密钥路径(单签),复杂分支走脚本路径
一句话总结:SegWit 是比特币一次"外科手术"——把签名的权重从交易身份中剥离,既治好了延展性顽疾,又以软分叉方式悄悄扩容;Taproot 则在此基础上引入 Schnorr 与 MAST,让多签与复杂合约在链上几乎隐形。
4. 闪电网络:UTXO 之上的支付通道
4.1 为什么要闪电网络
比特币链上吞吐约 7 TPS、10 分钟出块,不适合高频微支付。闪电网络的思路是把交易移到链下:双方锁定一笔链上资金(UTXO),之后在链下无限次更新"应付款"状态,只在最后关闭通道时上链结算。
4.2 通道生命周期
开通道(On-chain):
A 与 B 各存入 0.5 BTC → 构造 2-of-2 多重签名输出(funding tx)
→ 得到 channel_id(对应一个 UTXO,双方各持一把私钥)
链下状态更新(Off-chain):
双方维护 balance_a / balance_b
每笔更新都签一个"承诺交易"(commitment tx),花费 funding UTXO
→ 但交易不广播,只有最新的承诺交易有效
→ 旧承诺交易若被广播,对方可提交"惩罚交易"没收全部资金(违约惩罚)
关通道(On-chain):
广播最新承诺交易 → 双方余额按最终状态上链结算
4.3 HTLC:条件支付的核心
HTLC(哈希时间锁合约)让支付可以在不信任的路由上流转:
条件:哈希锁 + 时间锁
支付者在链上/链下承诺:若收款人出示哈希 h = H(x) 的原像 x,则付款;
若超时(如 24 小时)未出示,则退回。
路由支付流程(A → B → C):
A 生成随机数 x,计算 h = H(x)
A → B:承诺若 B 能在 T1 前出示 x,支付 1 BTC
B → C:承诺若 C 能在 T2 前出示 x(T2 < T1),支付 0.999 BTC
C 拿到 h 后广播 HTLC 并出示 x → B 获得原像 → B 向 A 出示 x 拿回资金
整个网络用"原像即收据"的方式逐跳结算
# HTLC 的哈希原像链(Python 概念演示)
import hashlib
def hash_preimage(x: bytes) -> bytes:
return hashlib.sha256(x).digest()
# A 生成秘密与哈希
secret = b"random-secret-xyz"
h = hash_preimage(secret)
# C 只要出示 h 的原像 secret,就能逐跳解锁资金
# 每个中间节点 B 在向 C 支付后,也拿到了 secret,进而向 A 解锁
4.4 闪电网络拓扑与多跳路由
路由表:每个节点发布 channel(pubkey, capacity, fees, cltv_delta)
支付路径:Source Routing(源路由,类似 A 知道全网拓扑后选路)
大额支付拆分:AMP(Atomic Multi-Path Payment)把一笔支付拆成多片、
每片独立路由,全部到达后收款人聚合(防止单路径失败)
节点数量:全球节点数万、通道数十万(2024 年量级)
| 指标 | 链上 BTC | 闪电网络 |
|---|---|---|
| 确认时间 | ~10 分钟 | 毫秒级 |
| 手续费 | 数美分~美元级 | 可低至零点几 sat 的按路由费 |
| 吞吐 | ~7 TPS | 理论上限百万 TPS(链下) |
| 隐私 | 链上公开 | 链下路径仅参与者可见 |
| 资金锁定 | 无 | 通道内资金被锁定(占用流动性) |
一句话总结:闪电网络用"2-of-2 多签 UTXO + HTLC + 违约惩罚"在 UTXO 上搭了第二层支付网——这是 UTXO 模型被证明可承载 Layer2 的关键案例,也是理解 Rollup 之前重要的"通道式扩容"思路。
5. UTXO vs 账户模型的工程对比
| 维度 | UTXO(比特币) | 账户(以太坊) |
|---|---|---|
| 世界状态 | 未花费输出集合 | 账户地址 → 余额/存储 |
| 转账语义 | 销毁+铸造硬币 | 余额加减 |
| 并行性 | 天然高(各 UTXO 独立) | 需 nonce 防重放,同一账户串行 |
| 状态膨胀 | 花费即"清除"旧输出 | 存储永久存在(需状态租金探索) |
| 脚本能力 | 非图灵完备 | 图灵完备(gas 约束) |
| 隐私 | 天然更高(地址无需暴露账户身份) | 账户地址即身份 |
| 二层扩展 | 闪电网络(通道) | Rollup(汇总执行) |
一句话总结:UTXO 模型的"一次性硬币"天然适合并行验证与隐私保护,但也让复杂状态程序(智能合约)难以表达——以太坊用账户模型换来了可编程性,代价是状态膨胀与串行化。
6. 总结
- UTXO 生命周期:交易销毁旧输出、创建新输出,世界状态是可验证的硬币集合
- Script 脚本:非图灵完备的栈式语言,P2PKH/P2SH/P2WPKH 表达不同的花费条件
- SegWit:见证数据离链、修复延展性、软分叉扩容;Taproot 引入 Schnorr 与 MAST
- 闪电网络:链下状态通道 + HTLC 条件支付,为高频微支付提供了第二层方案
延伸阅读:
- 区块链密码学基础 — 交易签名背后的数学
- 区块链基础原理与共识机制 — PoW 与最长链规则
- Layer2 扩容:Rollup 架构 — 从通道式扩容到 Rollup 汇总
- 跨链互操作 — 基于 HTLC 的原子交换
- 区块链-web3 专题 — 比特币之上做应用的工程实践
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。