比特币 UTXO 模型与隔离见证:Script 脚本、SegWit 与闪电网络

深入比特币交易模型:UTXO 的创建与销毁生命周期、比特币 Script 脚本语言(P2PKH/P2SH/P2WPKH)、隔离见证 SegWit 的编码与优势、轻量级支付通道闪电网络的通道生命周期与 HTLC 原理。

导语:比特币的世界状态是一袋未花费的硬币

与以太坊的"账户余额"模型不同,比特币没有"账户"。它的世界状态完全由 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 常见锁定脚本类型

类型锁定脚本解锁方式用途
P2PKHOP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_CHECKSIG签名 + 公钥普通转账
P2SHOP_HASH160 <redeemScriptHash> OP_EQUAL签名 + 赎回脚本多重签名、HTLC
P2WPKHOP_0 <hash160>(SegWit)签名 + 公钥(见证字段)隔离见证转账
P2WSHOP_0 <sha256(witnessScript)>见证脚本隔离见证多签/契约
P2TROP_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. 总结

  1. UTXO 生命周期:交易销毁旧输出、创建新输出,世界状态是可验证的硬币集合
  2. Script 脚本:非图灵完备的栈式语言,P2PKH/P2SH/P2WPKH 表达不同的花费条件
  3. SegWit:见证数据离链、修复延展性、软分叉扩容;Taproot 引入 Schnorr 与 MAST
  4. 闪电网络:链下状态通道 + HTLC 条件支付,为高频微支付提供了第二层方案

延伸阅读:

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「blockchain」更多文章

  1. 链上数据索引:The Graph、Subgraph 与数据查询架构
  2. 钱包与 HD 分层确定性钱包:助记词、派生路径与签名流程
  3. 稳定币与 DEX/AMM 机制:锚定设计、流动性池与无常损失