跳至主要內容
高級

Transaction Malleability

了解交易可延展性問題,它如何影響比特幣應用,以及 SegWit 如何解決這個問題。

12 分鐘

Transaction Malleability(交易可延展性)是指第三方可以在不使交易無效的情況下 修改交易的 TXID。這個問題曾經困擾比特幣多年,直到 SegWit 才徹底解決。

什麼是可延展性?

在 SegWit 之前,TXID 是對整個交易(包括簽名)進行雜湊計算得出的。 由於 ECDSA 簽名有多種有效表示形式,任何人都可以修改簽名的格式而不改變其有效性。

原始交易
├── inputs
│   └── scriptSig: [signature] [pubkey]  ← 可被修改
├── outputs
└── TXID = hash(整個交易) ← 因此改變

修改後的交易
├── inputs
│   └── scriptSig: [modified_signature] [pubkey]
├── outputs(相同)
└── TXID = 不同的雜湊值

可延展性的來源

1. ECDSA 簽名的 S 值

ECDSA 簽名包含 (r, s) 兩個值。對於每個有效的 s,(n - s) 也是有效的 (其中 n 是曲線的階)。

// ECDSA 簽名的兩種形式
signature = (r, s)
signature' = (r, n - s)  // 同樣有效!

// BIP-146 要求低 S 值
if (s > order / 2) {
    s = order - s;
}

// secp256k1 曲線的階
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

2. DER 編碼變異

簽名使用 DER 編碼,而 DER 允許一些不同的表示方式:

// 標準 DER 編碼
30 44  // SEQUENCE, length
  02 20 [32 bytes r]
  02 20 [32 bytes s]

// 可能的變異
// - 前導零的添加/移除
// - 長度欄位的不同編碼
// - 負數表示的處理

3. scriptSig 的額外數據

可以在 scriptSig 中添加額外的操作碼而不影響驗證:

// 原始
scriptSig: [sig] [pubkey]

// 添加 OP_NOP 不影響結果
scriptSig: OP_NOP [sig] [pubkey]

// 或添加 OP_DROP
scriptSig: <junk_data> OP_DROP [sig] [pubkey]

實際影響

Mt. Gox 事件

2014 年,Mt. Gox 交易所聲稱由於交易可延展性攻擊損失了大量比特幣。 攻擊者修改了提款交易的 TXID,使交易所的系統認為交易失敗,從而重複發送。

閃電網路的挑戰

在 SegWit 之前,建立可靠的支付通道非常困難:

// 開通通道的理想流程
1. Alice 和 Bob 創建資金交易 (funding_tx)
2. 在廣播前,創建退款交易,引用 funding_tx 的 TXID
3. 廣播 funding_tx

// 可延展性問題
如果第三方修改了 funding_tx 的簽名:
- funding_tx 的 TXID 改變
- 退款交易變得無效(引用了錯誤的 TXID)
- Alice 和 Bob 可能永久失去資金

SegWit 的解決方案

SegWit(隔離見證)將簽名數據從 TXID 計算中移除:

// 傳統交易
TXID = hash(version + inputs + outputs + locktime)
       ↑ inputs 包含 scriptSig(簽名)

// SegWit 交易
TXID = hash(version + inputs + outputs + locktime)
       ↑ inputs 的 scriptSig 為空,簽名在 witness 中

witness 數據不參與 TXID 計算

第三方不可延展:由於簽名不在 TXID 計算範圍內,第三方無法 通過修改簽名來改變 TXID。這使得支付通道和其他依賴 TXID 的應用成為可能。

相關 BIP

  • BIP-62:處理可延展性(已撤回,被 SegWit 取代)
  • BIP-66:嚴格 DER 簽名
  • BIP-141:隔離見證(共識層)
  • BIP-143:交易簽名驗證的 v0 見證程式
  • BIP-146:低 S 值和 NULLDUMMY
  • BIP-147:NULLDUMMY 軟分叉

軟分叉修復

在 SegWit 之前,Bitcoin Core 通過軟分叉逐步解決了部分可延展性問題:

// BIP-66: 嚴格 DER 簽名(2015 年 7 月啟用)
// 強制使用標準 DER 編碼

// BIP-146: 低 S 值
// 要求 S 值小於 order/2
SCRIPT_VERIFY_LOW_S

// BIP-147: NULLDUMMY
// CHECKMULTISIG 的虛擬元素必須為空
SCRIPT_VERIFY_NULLDUMMY

剩餘的可延展性

即使在 SegWit 之後,交易創建者仍然可以修改自己的交易:

  • 自身可延展:簽名者可以重新簽名,產生不同的 WTXID
  • 腳本可延展:某些腳本條件允許不同的滿足方式

但這些不構成安全問題,因為只有交易創建者能做這些修改。

驗證規則

// Bitcoin Core 的驗證標誌
enum {
    SCRIPT_VERIFY_STRICTENC = (1U << 1),      // 嚴格編碼
    SCRIPT_VERIFY_DERSIG = (1U << 2),         // 嚴格 DER
    SCRIPT_VERIFY_LOW_S = (1U << 3),          // 低 S 值
    SCRIPT_VERIFY_NULLDUMMY = (1U << 4),      // 空虛擬元素
    SCRIPT_VERIFY_WITNESS = (1U << 11),       // 驗證見證
    SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), // 壓縮公鑰
};

// 標準交易必須通過所有檢查
unsigned int STANDARD_SCRIPT_VERIFY_FLAGS =
    SCRIPT_VERIFY_STRICTENC |
    SCRIPT_VERIFY_DERSIG |
    SCRIPT_VERIFY_LOW_S |
    SCRIPT_VERIFY_NULLDUMMY |
    SCRIPT_VERIFY_WITNESS;

歷史意義

交易可延展性問題的解決是比特幣發展史上的重要里程碑:

  • 使閃電網路成為可能
  • 提高了交易追蹤的可靠性
  • 為更複雜的智能合約鋪平道路
  • 展示了軟分叉升級的可行性
已複製連結
已複製到剪貼簿