比特幣腳本語言深度教學:P2TR、P2WSH 與進階腳本

深入探討比特幣腳本語言的進階主題,涵蓋 Pay-to-Taproot(P2TR)、Pay-to-Witness-Script-Hash(P2WSH)的運作原理與實際應用,以及現代比特幣腳本的最新發展。

比特幣腳本語言深度教學:P2TR、P2WSH 與進階腳本

本文深入探討比特幣腳本語言的進階主題,涵蓋 Pay-to-Taproot(P2TR)、Pay-to-Witness-Script-Hash(P2WSH)的運作原理與實際應用,以及現代比特幣腳本的最新發展。比特幣腳本語言(Bitcoin Script)是一種基於堆疊的圖靈非完整程式語言,其設計哲學是「簡單優先,安全至上」。雖然不如以太坊的 Solidity 那般圖靈完整,但這種設計選擇使比特幣能夠保持極高的安全性,並經受住超過十年的時間考驗。

比特幣腳本基礎架構

比特幣腳本語言(Bitcoin Script)是一種基於堆疊的圖靈非完整程式語言,其設計哲學是「簡單優先,安全至上」。雖然不如以太坊的 Solidity 那般圖靈完整,但這種設計選擇使比特幣能夠保持極高的安全性,並經受住超過十年的時間考驗。

比特幣腳本語言的設計遵循以下核心原則:

  1. 簡單性:操作碼數量有限,語法簡單,減少攻擊面
  2. 確定性:腳本執行結果在所有節點上必須一致
  3. 非圖靈完整性:故意排除迴圈結構,防止無限執行攻擊
  4. 最小權限:腳本只處理驗證,不執行複雜計算

執行模型

比特幣腳本採用後進先出(LIFO)的堆疊執行模型。所有運算都在一個堆疊上進行,腳本從左到右依次執行,每個操作碼(OP_CODE)或數據元素都會被推入或彈出堆疊。

比特幣腳本在每一筆交易中扮演核心角色。當 Alice 想要轉帳比特幣給 Bob 時,整個流程涉及三個關鍵階段:

比特幣交易腳本執行流程:

階段 1:交易創建
┌─────────────────────────────────────────────┐
│ Alice 創建交易                              │
│   - 指定轉帳金額                            │
│   - 指定 Bob 的比特幣地址                   │
│   - 设置找零地址(Alice 自己的地址)         │
└─────────────────────────────────────────────┘
                      ↓
階段 2:腳本構建
┌─────────────────────────────────────────────┐
│ 輸出腳本(鎖定腳本)- 定義花費條件          │
│   P2PKH: OP_DUP OP_HASH160 <Bob的公鑰Hash> │
│          OP_EQUALVERIFY OP_CHECKSIG         │
│                                             │
│ 輸入腳本(解鎖腳本)- 提供解鎖證明          │
│   <Alice的簽名> <Alice的公鑰>              │
└─────────────────────────────────────────────┘
                      ↓
階段 3:腳本執行(每個比特幣節點都會執行)
┌─────────────────────────────────────────────┐
│ 步驟 1:組裝完整腳本                        │
│   [Alice的簽名] [Alice的公鑰]               │
│   OP_DUP OP_HASH160 <Bob的公鑰Hash>         │
│   OP_EQUALVERIFY OP_CHECKSIG                │
│                                             │
│ 步驟 2:堆疊執行過程                       │
│   堆疊:[sig, pubkey]                      │
│   → OP_DUP → [sig, pubkey, pubkey]         │
│   → OP_HASH160 → [sig, pubkeyHash]          │
│   → 推送 <Bob的公鑰Hash>                    │
│   → [sig, pubkeyHash, expectedHash]         │
│   → OP_EQUALVERIFY → [sig] 或 失敗          │
│   → OP_CHECKSIG → [1] 或 [0]                │
│                                             │
│ 步驟 4:驗證結果                            │
│   若堆疊頂部為 1(true):交易有效          │
│   若堆疊頂部為 0(false)或腳本失敗:交易無效│
└─────────────────────────────────────────────┘

比特幣腳本的密碼學基礎

比特幣腳本的安全性建立在現代密碼學之上,以下是其核心密碼學原理:

橢圓曲線數位簽名演算法(ECDSA)

比特幣使用 secp256k1 橢圓曲線,其方程式為:

y² = x³ + 7 (在有限域 Fp 上)

其中:

簽名生成過程:

ECDSA 簽名生成演算法:

輸入:
- 私鑰 d(256位隨機數)
- 消息 m(即交易的哈希)
- 隨機數 k(臨時私鑰)

輸出:
- 簽名 (r, s)

步驟:
1. 計算 e = Hash(m)
2. 選擇隨機數 k ∈ [1, n-1]
3. 計算點 (x, y) = k × G
4. 計算 r = x mod n
5. 如果 r = 0,返回步驟 2
6. 計算 s = k^(-1) × (e + r × d) mod n
7. 如果 s = 0,返回步驟 2
8. 返回簽名 (r, s)

簽名驗證過程:

ECDSA 簽名驗證演算法:

輸入:
- 公鑰 Q = d × G
- 消息 m 的哈希 e
- 簽名 (r, s)

輸出:
- 簽名是否有效

步驟:
1. 驗證 r, s ∈ [1, n-1]
2. 計算 e = Hash(m)
3. 計算 s^(-1) mod n
4. 計算 u1 = e × s^(-1) mod n
5. 計算 u2 = r × s^(-1) mod n
6. 計算點 P = u1 × G + u2 × Q
7. 如果 P = ∞,返回 false
8. 計算 v = P.x mod n
9. 如果 v = r,返回 true
10. 否則返回 false

RIPEMD-160 哈希函數

比特幣地址使用 RIPEMD-160 產生公鑰哈希:

RIPEMD-160 輸出:
- 輸出長度:160 位元(20 bytes)
- 輸入:任意長度的訊息
- 安全性:比 SHA-1 更強

比特幣地址生成過程:
1. 生成隨機私鑰 d
2. 計算公鑰 Q = d × G
3. 計算 SHA256(Q) → 256位元
4. 計算 RIPEMD160(SHA256(Q)) → 160位元(20 bytes)
5. 添加版本字節(0x00 for P2PKH)
6. 計算 SHA256(SHA256(version + hash)) → 取前4字節
7. 附在校驗和
8. Base58Check 編碼
# P2PKH 的完整執行流程
# 鎖定腳本(輸出):OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
# 解鎖腳本(輸入):<signature> <publicKey>

# 執行步驟:
# 1. 將 signature 推入堆疊
# 2. 將 publicKey 推入堆疊
# 3. OP_DUP:複製堆疊頂部(publicKey)
# 4. OP_HASH160:對 publicKey 進行 SHA-256 + RIPEMD-160
# 5. 推入 pubKeyHash
# 6. OP_EQUALVERIFY:比較兩個哈希值
# 7. OP_CHECKSIG:驗證簽名有效性

操作碼分類詳解

比特幣腳本包含約 100 個操作碼,可分為以下類別:

常數操作碼直接將數值推入堆疊:

OP_0           # 推送空字節串
OP_1 - OP_16   # 推送數字 1-16
OP_PUSHDATA1   # 後跟 1 位元組長度
OP_PUSHDATA2   # 後跟 2 位元組長度
OP_PUSHDATA4   # 後跟 4 位元組長度

堆疊操作碼管理堆疊元素:

OP_DUP         # 複製頂部元素
OP_DROP        # 移除頂部元素
OP_SWAP        # 交換前兩個元素
OP_ROT         # 旋轉前三個元素
OP_TUCK        # 複製並插入第二位
OP_2DUP        # 複製前兩個元素
OP_2DROP       # 移除前兩個元素

密碼學操作碼執行雜湊和簽名驗證:

OP_SHA256      # SHA-256 單次雜湊
OP_HASH160     # SHA-256 + RIPEMD-160
OP_HASH256     # SHA-256 兩次雜湊
OP_CHECKSIG    # 驗證單一簽名
OP_CHECKMULTISIG  # 驗證多簽名

OP_CODE 詳細規格表

以下為比特幣腳本中所有主要操作碼的詳細規格表:

常數與數據推送操作碼

操作碼十進制說明堆疊變化範例
OP0 / OPFALSE0推送空字節串[] → [0x]OP_0
OP1 / OP1-OP_1681-96推送數字 1-16[] → [n]OP_3
OP_PUSHDATA176後跟 1 位元組長度[] → [data]OP_PUSHDATA1 10 <10 bytes>
OP_PUSHDATA277後跟 2 位元組長度[] → [data]OP_PUSHDATA2 0100 <256 bytes>
OP_PUSHDATA478後跟 4 位元組長度[] → [data]OP_PUSHDATA4 <4 bytes len>
OP_IFDUP115如果非零則複製[x] → [x, x] 或 [x]條件複製
OP_DEPTH116推入堆疊深度[] → [n][1,2,3] OP_DEPTH → 3
OP_DROP117移除頂部[x] → []棧頂彈出
OP_DUP118複製頂部[x] → [x, x]棧頂複製
OP_NIP119移除第二項[x, y] → [y]棧中彈出
OP_OVER120複製第二項[x, y] → [x, y, x]棧中複製
OP_PICK121複製第 n 項[xn, ..., x1, n] → [..., x_n]選擇深處元素
OP_ROLL122移動第 n 項至頂部[xn, ..., x1, n] → [..., x1, xn]旋轉至頂部
OP_ROT123旋轉前三項[x, y, z] → [y, z, x]三重旋轉
OP_SWAP124交換前兩項[x, y] → [y, x]兩兩交換
OP_TUCK125複製並插入第二位[x, y] → [y, x, y]插入備份
OP_2DROP109移除前兩項[x, y] → []雙重彈出
OP_2DUP110複製前兩項[x, y] → [x, y, x, y]雙重複製
OP_3DUP111複製前三項[x, y, z] → [x, y, z, x, y, z]三重複製
OP_2OVER112複製第二和第三項[x, y, z] → [x, y, z, x, y]跨層複製
OP_2ROT113旋轉第五和第六項[a, b, x, y, z] → [x, y, z, a, b]雙重旋轉
OP_2SWAP114交換前兩對[a, b, c, d] → [c, d, a, b]雙重交換

位元操作操作碼

操作碼十進制說明堆疊變化備註
OP_INVERT131位元反轉[a] → [~a]已停用
OP_AND132位元 AND[a, b] → [a&b]已停用
OP_OR133位元 OR[a, b] → [a|b]已停用
OP_XOR134位元 XOR[a, b] → [a^b]已停用
OP_EQUAL135相等比較[a, b] → [0/1]返回 1 或 0
OP_EQUALVERIFY136相等並驗證[a, b] → []不相等則失敗
OP_RESERVED1137保留-交易無效
OP_RESERVED2138保留-交易無效

運算操作碼

操作碼十進制說明堆疊變化備註
OP_1ADD139加 1[n] → [n+1]整數運算
OP_1SUB140減 1[n] → [n-1]整數運算
OP_2MUL141乘 2[n] → [n*2]已停用
OP_2DIV142除 2[n] → [n/2]已停法
OP_NEGATE143取負[n] → [-n]符號反轉
OP_ABS144取絕對值[n] → [|n|]負轉正
OP_NOT145邏輯 NOT[n] → [n==0]零轉 1,非零轉 0
OP_0NOTEQUAL146不等於零[n] → [n!=0]零轉 0
OP_ADD147加法[a, b] → [a+b]加法
OP_SUB148減法[a, b] → [a-b]減法
OP_MUL149乘法[a, b] → [a*b]已停用
OP_DIV150除法[a, b] → [a/b]已停用
OP_MOD151取模[a, b] → [a%b]已停用
OP_LSHIFT152左移[a, b] → [a<<b]已停用
OP_RSHIFT153右移[a, b] → [a>>b]已停用
OP_BOOLAND154邏輯 AND[a, b] → [a&&b]兩者非零
OP_BOOLOR155邏輯 OR[a, b] → [a||b]任一非零
OP_NUMEQUAL156數字相等[a, b] → [a==b]精確比較
OP_NUMEQUALVERIFY157數字相等驗證[a, b] → []失敗則終止
OP_NUMNOTEQUAL158數字不等[a, b] → [a!=b]不相等
OP_LESSTHAN159小於[a, b] → [a<b]比較
OP_GREATERTHAN160大於[a, b] → [a>b]比較
OP_LESSTHANOREQUAL161小於等於[a, b] → [a<=b]比較
OP_GREATERTHANOREQUAL162大於等於[a, b] → [a>=b]比較
OP_MIN163最小值[a, b] → [min(a,b)]兩者取小
OP_MAX164最大值[a, b] → [max(a,b)]兩者取大
OP_WITHIN165範圍內[x, min, max] → [min<=x<=max]範圍判斷

密碼學操作碼

操作碼十進制說明堆疊變化備註
OP_RIPEMD160166RIPEMD-160 哈希[data] → [hash]160-bit 輸出
OP_SHA1167SHA-1 哈希[data] → [hash]160-bit 輸出(已不推薦)
OP_SHA256168SHA-256 哈希[data] → [hash]256-bit 輸出
OP_HASH160169SHA256+RIPEMD160[data] → [hash]比特幣地址使用
OP_HASH256170雙重 SHA-256[data] → [hash]區塊/交易哈希
OP_CODESEPARATOR171簽名覆蓋點標記BIP 變更
OP_CHECKSIG172驗證簽名[sig, pubkey] → [0/1]ECDSA 驗證
OP_CHECKSIGVERIFY173驗證並檢查[sig, pubkey] → []失敗則終止
OP_CHECKMULTISIG174驗證多簽名[sig..., pubkey..., n] → [0/1]需要 dummy
OP_CHECKMULTISIGVERIFY175多簽並驗證[sig..., pubkey..., n] → []失敗則終止

時間鎖操作碼

操作碼十進制說明堆疊變化啟用條件
OP_NOP1176無操作[]保留未來使用
OP_CHECKLOCKTIMEVERIFY177絕對時間鎖[n] → [n] 或失敗BIP 65(BIP 113)
OP_CHECKSEQUENCEVERIFY178相對時間鎖[n] → [n] 或失敗BIP 112(BIP 68)
OP_NOP4179無操作[]保留
OP_NOP5180無操作[]保留
OP_NOP6181無操作[]保留
OP_NOP7182無操作[]保留
OP_NOP8183無操作[]保留
OP_NOP9184無操作[]保留
OP_NOP10185無操作[]保留

Taproot(Tapscript)新增操作碼

操作碼十進制說明堆疊變化啟用條件
OP_CHECKSIGADD186驗證並累加[sig, pubkey, n] → [n+1]Taproot(BIP 342)

流量控制操作碼

操作碼十進制說明堆疊變化備註
OP_NOP97無操作[]歷史保留
OP_IF99條件分支[bool] → []執行 if 分支
OP_NOTIF100條件分支[bool] → []執行 else 分支
OP_ELSE103否則分支[]與 OP_IF 配對
OP_ENDIF104結束分支[]結束 if 塊
OP_VERIFY105驗證並終止[bool] → [] 或失敗常見使用
OP_RETURN106終止並失敗[]數據承載

備用操作碼

操作碼十進制說明安全性
OP_INVALIDOPCODE任何未定義無效操作碼交易無效

OP_CODE 實際應用案例

多籤腳本

# 2-of-3 多籤腳本
# 格式:<m> <pubkey1> <pubkey2> <pubkey3> <n> OP_CHECKMULTISIG

OP_2                              # 推送 2(需要 2 個簽名)
<02a3...>                         # 公鑰 1(33 bytes)
<02b4...>                         # 公鑰 2(33 bytes)
<03c5...>                         # 公鑰 3(33 bytes)
OP_3                              # 推送 3(總共 3 個公鑰)
OP_CHECKMULTISIG                  # 驗證多籤

# 注意:OP_CHECKMULTISIG 需要一個額外的 dummy 元素
# 完整解鎖腳本:<dummy> <sig1> <sig2>

時間鎖腳本

# 相對時間鎖(CSV)腳本
# 只有在輸入創建後經過指定區塊數才能花費

<relative_timeout>                # 相對區塊數(如 144 = 1 天)
OP_CHECKSEQUENCEVERIFY            # 驗證相對時間
OP_DROP                           # 彈出值
<pubkey>                          # 公鑰
OP_CHECKSIG                       # 驗證簽名

哈希時間鎖合約(HTLC)

# 經典 HTLC 腳本

OP_HASH160                        # 計算哈希
<hash(R)>                         # 預設哈希值
OP_EQUALVERIFY                    # 驗證原像
<receiver_pubkey>                 # 收款方公鑰
OP_CHECKSIG                       # 驗證簽名

OP_IF
    # 收款方路徑(揭示原像)
    "01"
OP_ELSE
    # 退款路徑
    <locktime>                    # 絕對時間鎖
    OP_CHECKLOCKTIMEVERIFY
    OP_DROP
    <refund_pubkey>               # 退款公鑰
    OP_CHECKSIG
OP_ENDIF

P2TR(Pay-to-Taproot)深度解析

P2TR 是比特幣 2021 年 Taproot 升級引入的最新地址類型,結合了 Schnorr 簽名與 MAST(Merkelized Abstract Syntax Tree)技術。

P2TR 地址結構

OP_1 <32 bytes public key or Merkle root>

地址以 bc1p 開頭,長度為 62 個字元。

P2TR 的兩種花費路徑

P2TR 提供兩種截然不同的花費方式:

1. 金鑰路徑(Key Path)

使用 Schnorr 簽名直接花費,類似於傳統的單一簽名交易:

 Witness: <signature>
 Script: OP_1 <public key>

這是 P2TR 最常見的使用方式,隱私性極高——無論是單簽名還是多簽名交易,在區塊鏈上看起來都完全相同。

Schnorr 簽名的核心優勢在於密鑰聚合。假設有 n 個私鑰 k1, k2, ..., kn,對應的公鑰為 Ki = ki G。聚合公鑰定義為 K = K1 + K2 + ... + Kn。簽名時,每個參與者生成隨機 nonce ri,計算 R = r1 G + r2 G + ... + rn G,然後計算 e = Hash(R || m) 和 si = ri + e * ki。最終聚合簽名為 (R, s1 + s2 + ... + sn)。

# 使用 musig2 實現閾值簽名的概念驗證
import hashlib

def aggregate_pubkeys(pubkeys):
    """聚合多個公鑰"""
    # 實際需要 secp256k1 庫進行點加法
    # 這裡只是概念展示
    return sum(pubkeys) % (2**256)

def create_p2tr_address(aggregated_pubkey):
    """從聚合公鑰生成 P2TR 地址"""
    # 計算 tweak = Hash(internal_pubkey)
    tweak = hashlib.sha256(aggregated_pubkey).digest()

    # 最終公鑰 = internal_pubkey + G * tweak
    # tweaked_pubkey = internal_pubkey + G * tweak

    # bech32m 編碼
    # address = bech32m_encode("bc1p", tweaked_pubkey)
    return f"bc1p{'0' * 58}"  # 示例格式

2. 腳本路徑(Script Path)

透過滿足預定義的腳本條件來花費資金:

 Witness: <script> <script args> <signature>
 Script: OP_1 <Merkle root>
          OP_CHECKSIG

MAST 結構

MAST 是 P2TR 的核心技術,允許將多個腳本條件 MERKLE 樹狀結構組織:

                    Merkle Root
                   /
              [Hash C]
             /        \
        [Hash A]    [Hash B]
           |           |
      Script A     Script B

優點:

P2TR 實例:閾值簽名

假設一個 2-of-3 的閾值簽名設置:

# 概念驗證(示意)
combined_key = pubkey1 + pubkey2  # Schnorr 密鑰聚合
address = P2TR(combined_key)

所有 2-of-3、3-of-3、或其他任意閾值配置,在鏈上看起來都與單簽名無異。

P2WSH(Pay-to-Witness-Script-Hash)深度解析

P2WSH 是隔離見證升級引入的腳本哈希類型,支援更複雜的腳本條件。

地址格式

bc1q <32 bytes script hash>

腳本結構

鎖定腳本:

OP_0 <32-byte SHA256 hash of witness script>

見證數據(解鎖時提供):

<signature1> <signature2> ... <witness script>

執行流程

  1. 節點計算見證腳本的 SHA256 哈希
  2. 與鎖定腳本中的哈希比對
  3. 執行見證腳本中的條件邏輯
# P2WSH 地址生成的完整實作
import hashlib
import bech32

def create_p2wsh_script(witness_script):
    """生成 P2WSH 鎖定腳本"""
    script_hash = hashlib.sha256(witness_script).digest()
    # OP_0 + 32 字節哈希
    return bytes([0x00, 0x20]) + script_hash

def create_p2wsh_address(witness_script, version=0):
    """生成 P2WSH 地址"""
    script_hash = hashlib.sha256(witness_script).digest()
    # bech32 編碼
    return bech32.encode('bc', version, script_hash)

# 範例:2-of-3 多簽名腳本
witness_script = bytes.fromhex(
    '02' +  # OP_2
    '21' + '02' * 33 +  # 第一個公鑰
    '21' + '03' * 33 +  # 第二個公鑰
    '21' + '04' * 33 +  # 第三個公鍵
    '03' +  # OP_3
    'ae'   # OP_CHECKMULTISIG
)

address = create_p2wsh_address(witness_script)
print(f"P2WSH Address: {address}")

P2WSH vs P2SH

特性P2WSHP2SH
哈希算法SHA256RIPEMD160(SHA256)
哈希長度32 bytes20 bytes
腳本大小限制1,000,000 weight units10,000 bytes
見證數據折扣有(75%)
隱私性較高較低

進階腳本模式

1. OP_CHECKTEMPLATEVERIFY(CTV)

BIP 119 提出的 OP_CHECKTEMPLATEVERIFY(CTV)允許交易指定嚴格的輸出模板。

應用場景:

腳本範例:

<hash of template> OP_CHECKTEMPLATEVERIFY OP_DROP <receiver pubkey> OP_CHECKSIG

CTV 技術細節

CTV 的核心機制:

1. 輸出模板哈希
   輸出模板包含:
   - 所有輸出的地址和金額
   - 輸出數量
   - 鎖定時間

2. 哈希驗證
   OP_CHECKTEMPLATEVERIFY 執行:
   - 計算當前交易的輸出哈希
   - 與腳本中的哈希比對
   - 不匹配則失敗

3. 優勢
   - 預定義支出條件
   - 資金只能轉移到預定義地址
   - 無法被劫持

CTV 實作範例

# CTV 實現概念
class CTVTransaction:
    def __init__(self):
        self.inputs = []
        self.outputs = []
        self.locktime = 0

    def create_template(self):
        """
        創建輸出模板
        """
        template = {
            'outputs': [
                {'address': 'bc1q...', 'amount': 0.1 * 100_000_000},
                {'address': 'bc1q...', 'amount': 0.01 * 100_000_000}
            ],
            'output_count': len(self.outputs),
            'locktime': self.locktime
        }
        return template

    def compute_template_hash(self, template):
        """
        計算模板的 SHA256 哈希
        """
        import hashlib
        # 序列化模板
        data = str(template).encode()
        return hashlib.sha256(data).digest()

    def build_ctv_script(self, template_hash):
        """
        構建包含 CTV 的腳本
        """
        # 腳本結構:
        # OP_CHECKTEMPLATEVERIFY <template_hash> OP_DROP <receiver_pk> OP_CHECKSIG
        script = bytes([0xb1])  # OP_CHECKTEMPLATEVERIFY
        script += bytes([0x20])  # 推送 32 bytes
        script += template_hash   # 模板哈希
        script += bytes([0x75])  # OP_DROP
        return script

# CTV 應用場景:慈善基金
class CharityVault:
    """
    使用 CTV 創建慈善捐款Vault
    """
    def __init__(self, charity_address, periodic_amount):
        self.charity_address = charity_address
        self.periodic_amount = periodic_amount

    def create_template(self, beneficiary_address):
        """
        創建每月自動轉帳模板
        """
        # 每月轉帳給受益人
        template = {
            'outputs': [
                {
                    'address': beneficiary_address,
                    'amount': self.periodic_amount
                }
            ],
            'locktime': 0  # 可選:添加時間鎖
        }
        return template

CTV 與其他比特幣升級的比較

CTV vs 其他 Covenant 提案:

特性                CTV        Drivechain    AIP-10
────────────────────────────────────────────────────
類型              確定性      礦工投票      遞迴
激活方式          軟分叉      軟分叉        軟分叉
複雜度            低         中           高
安全性            高         中           高
應用場景          多樣        側鏈         進階合約

CTV 優勢:
- 簡單明確
- 無需信任礦工
- 資金控制權在用戶

CTV 限制:
- 非遞迴(無法創建自引用合約)
- 需要軟分叉激活

2. OPCSFS + OPCHECKSIGADD

Tapscript 引入的新操作碼,支援更靈活的簽名驗證:

# 概念:檢查多個簽名是否有效
for sig in signatures:
    if verify(sig, pubkey):
        count += 1
if count >= threshold:
    return True

3. 閃電網路腳本(HTLC)

HTLC(Hash Time Locked Contract)是閃電網路的核心構建模組:

# 收款方需要揭示原像來獲得資金
# 或在超時後,退回給付款方

witness: <signature> <preimage>
script: OP_HASH160 <hash(preimage)> OP_EQUALVERIFY <receiver pubkey> OP_CHECKSIG
         OP_IF
           <timeout> OP_CHECKSEQUENCEVERIFY OP_DROP
           <refund pubkey> OP_CHECKSIG
         OP_ENDIF

完整的 HTLC 腳本實作

# 完整的 HTLC 腳本
# 收款方需要揭示原像來獲得資金
# 或在超時後,退回給付款方

htlc_script = bytes.fromhex(
    'a9' +                              # OP_HASH160
    '14' + 'a' * 40 +                  # 20 字節的哈希
    '88' +                              # OP_EQUALVERIFY
    '21' + '02' * 33 +                 # 接收者公鑰
    'ac' +                              # OP_CHECKSIG
    '63' +                              # OP_IF
    '04' + 'b2' + '03000000' +        # OP_CHECKSEQUENCEVERIFY + 相對時間
    '75' +                              # OP_DROP
    '21' + '03' * 33 +                 # 退款公鑰
    'ac' +                              # OP_CHECKSIG
    '68'                                # OP_ENDIF
)

HTLC 的運作流程:

  1. 付款方創建包含哈希鎖的輸出
  2. 收款方必須提供與哈希匹配的原始數據(原像)才能獲得資金
  3. 如果收款方在超時前未能提供原像,付款方可以通過退款路徑取回資金
  4. 這個機制使得跨鏈原子交換成為可能

4. 秘密洩露腳本(Vault)

Vault 是一種進階的比特幣腳本模式,可以實現延時提款和安全保護:

# Vault 腳本的基本結構
# 兩層延時:熱錢包延時 + 冷錢包冷卻期

vault_script = """
OP_IF
    # 快速路徑:延時後可提款到指定地址
    OP_CHECKSEQUENCEVERIFY OP_DROP
    <hot_wallet_pubkey> OP_CHECKSIG
OP_ELSE
    # 延時路徑:冷錢包可以取消交易
    OP_CHECKSEQUENCEVERIFY OP_DROP
    OP_DUP OP_HASH160 <cold_wallet_hash> OP_EQUALVERIFY OP_CHECKSIG
OP_ENDIF
"""

Vault 實現的詳細流程

Vault 腳本的三層安全結構:

Layer 1: 即時訪問(熱錢包)
- 延時: 0 區塊
- 場景: 小額日常支付
- 風險: 較高

Layer 2: 延時提款(熱錢包延時)
- 延時: 24-48 小時
- 場景: 大額提款
- 監控: 可在延時期間取消

Layer 3: 冷存儲恢復
- 延時: 數天至數週
- 場景: 完全冷存儲
- 最高安全: 需要物理訪問

 Vault 交易的的生命週期:
1. 攻擊者嘗試盜用資金
2. 觸發 Layer 2 延時
3. Vault 運營者收到警報
4. 在延時結束前廣播取消交易
5. 資金安全轉移到冷錢包

5. PSBT(Partially Signed Bitcoin Transactions)

PSBT 是比特幣交易的標準化表示格式,用於多方協作簽名:

# PSBT 結構解析
class PSBT:
    """
    PSBT 格式:
    - 全域元數據
    - 未簽名交易
    - 输入數據(公鑰、簽名、本等)
   贖回腳 - 輸出數據(見證腳本、地址等)
    """

    def __init__(self, unsigned_tx):
        self.tx = unsigned_tx  # 未簽名交易
        self.inputs = []        # 每個輸入的簽名數據
        self.outputs = []       # 每個輸出的腳本數據
        self.globals = {}       # 全域擴展數據

    def add_input(self, prev_txid, vout, utxo):
        """添加輸入及其對應的 UTXO"""
        self.inputs.append({
            'previous_txid': prev_txid,
            'vout': vout,
            'utxo': utxo,  # 完整的上一筆交易輸出
            'sighash_type': 'ALL'
        })

    def add_output(self, address, amount):
        """添加輸出"""
        self.outputs.append({
            'address': address,
            'amount': amount
        })

    def sign(self, private_key):
        """對 PSBT 進行簽名"""
        # 為每個輸入創建簽名
        for i, inp in enumerate(self.inputs):
            sig = create_sighash(inp['utxo'], self.tx, i)
            self.inputs[i]['signature'] = sig

    def finalize(self):
        """完成 PSBT 並提取最終交易"""
        # 組合所有簽名和見證數據
        final_tx = self.tx.copy()
        for i, inp in enumerate(self.inputs):
            final_tx.add_witness(i, inp['witness'])
        return final_tx

# PSBT 流程示例:2-of-3 多簽
def create_2_of_3_psbt():
    """
    創建 2-of-3 多簽 PSBT 的完整流程
    """
    # 1. 定義參與者
    participants = [
        {'pubkey': '02' * 33, 'private_key': '...' },
        {'pubkey': '03' * 33, 'private_key': '...' },
        {'pubkey': '04' * 33, 'private_key': '...' }
    ]

    # 2. 創建未簽名交易
    unsigned_tx = {
        'version': 2,
        'inputs': [
            {'txid': 'a' * 64, 'vout': 0, 'sequence': 0xffffffff}
        ],
        'outputs': [
            {'address': 'bc1q...', 'amount': 0.5 * 100_000_000},
            {'address': 'bc1q...', 'amount': 0.499 * 100_000_000}  # 找零
        ],
        'locktime': 0
    }

    # 3. 創建 PSBT
    psbt = PSBT(unsigned_tx)

    # 4. 添加簽名數據(由各方分別完成)
    for participant in participants[:2]:  # 只需要 2 方簽名
        psbt.sign(participant['private_key'])

    # 5. 完成 PSBT
    final_tx = psbt.finalize()

    return final_tx

PSBT 協調流程詳解

PSBT 多方簽名的標準流程:

階段 1: 交易創建
- 創建者構建未簽名交易
- 添加所有輸入輸出
- 生成 PSBT 並分享給參與者

階段 2: 輸入處理
- 每個參與者驗證交易細節
- 添加自己部分的簽名數據
- 將部分簽名的 PSBT 傳遞給下一方

階段 3: 簽名聚合
- 收集足夠數量的簽名
- 檢查簽名有效性
- 完成 PSBT

階段 4: 廣播
- 提取最終交易
- 廣播到網路

腳本安全性考量

1. 時間鎖的正確使用

# 絕對時間鎖(CLTV)
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG

# 相對時間鎖(CSV)
<sequence> OP_CHECKSEQUENCEVERIFY OP_DROP <pubkey> OP_CHECKSIG

注意: CLTV 使用絕對區塊高度或 Unix 時間戳;CSV 使用相對區塊數。

2. 腳本大小與費用

不同腳本類型的費用效率:

腳本類型典型大小費用效率
P2PKH~180 bytes基準
P2SH~180 bytes基準
P2WPKH~68 bytes~2.5x
P2WSH~42 bytes~4x
P2TR~58 bytes~3x

3. 常見錯誤與防護

缺乏時間鎖

# 錯誤:無時間保護的多簽
OP_2 <pubA> <pubB> <pubC> OP_3 OP_CHECKMULTISIG

# 正確:添加時間鎖
OP_IF
  <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
  <recovery pubkey> OP_CHECKSIG
OP_ELSE
  <delay> OP_CHECKSEQUENCEVERIFY OP_DROP
  OP_2 <pubA> <pubB> <pubC> OP_3 OP_CHECKMULTISIG
OP_ENDIF

錯誤的閾值計算

比特幣的 OP_CHECKMULTISIG 有一個已知特性:需要額外的一個 dummy 元素:

# 錯誤
OP_2 <pubA> <pubB> OP_2 OP_CHECKMULTISIG

# 正確
OP_2 <pubA> <pubB> OP_2 OP_CHECKMULTISIG
# 注意:OP_CHECKMULTISIG 會消耗一個額外的堆疊元素

腳本調試技巧

使用 Bitcoin Core RPC

# 解碼腳本
bitcoin-cli decodescript "5121..."

# 驗證腳本
bitcoin-cli testmempoolaccept '[{"txid": "...", "witness": ["..."]}]'

# 分析腳本花費
bitcoin-cli gettxoutproof '["txid"]'

Miniscript 簡介

Miniscript 是一種比特幣腳本的高級表示法,讓開發者以更易讀的方式編寫腳本:

# Miniscript 表示
and_v(v:pkh(A),or_v(hash256(B),older(100)))

# 編譯後的腳本
OP_DUP OP_HASH160 <A> OP_EQUALVERIFY OP_CHECKSIG OP_IF
  OP_HASH160 <B> OP_EQUAL
OP_ELSE
  100 OP_CHECKSEQUENCEVERIFY OP_DROP
OP_ENDIF

腳本類型選擇指南

需求場景推薦類型理由
簡單支付P2WPKH費用低、隱私好
單簽名大額P2TR最佳隱私與費用
多籤需求P2WSH靈活性高
與舊系統相容P2SH向後相容
閃電網路P2TR支援 Schnorr 聚合
複雜條件P2WSH + MAST隱藏未用條件
延時提款P2TR + CTV增強安全性
原子交換P2WSH + HTLC跨鏈互換

進階主題:腳本樹與自定義條件

構建複雜的腳本樹

P2TR 允許構建複雜的腳本樹,實現多層次的支出條件:

                        Taproot Root
                       /            \
              Key Path            Script Path
              (threshold)         /           \
                               Condition A   Condition B
                                             /           \
                                      Condition B1  Condition B2
# 構建 Taproot 腳本樹
def build_taproot_tree(conditions):
    """
    構建 MAST 腳本樹

    conditions: 腳本條件列表
    返回: (root, script_paths)
    """
    if len(conditions) == 1:
        return (hash(conditions[0]), [conditions[0]])

    # 遞迴構建 Merkle 樹
    mid = len(conditions) // 2
    left = build_taproot_tree(conditions[:mid])
    right = build_taproot_tree(conditions[mid:])

    # 合併為 Merkle Root
    root = hash(left + right)

    return (root, left[1] + right[1])

# 範例條件
conditions = [
    "and_v(v:pkh(A),older(100))",      # 100 區塊延時後可提款
    "and_v(v:pkh(B),thresh_2(pk(C),pk(D),pk(E)))",  # 3-of-2 多籤
    "pk(F)",                            # 備用私鑰
]

root, paths = build_taproot_tree(conditions)

腳本分析工具

# 使用 Python 分析比特幣腳本
class ScriptAnalyzer:
    def __init__(self, script_hex):
        self.script = bytes.fromhex(script_hex)

    def get_opcodes(self):
        """提取所有操作碼"""
        opcodes = []
        i = 0
        while i < len(self.script):
            opcode = self.script[i]
            if opcode <= 75:  # 數據推送
                length = opcode
                data = self.script[i+1:i+1+length]
                opcodes.append(('PUSH', data.hex()))
                i += 1 + length
            else:
                opcodes.append(('OP', opcode))
                i += 1
        return opcodes

    def estimate_size(self):
        """估算腳本大小"""
        return len(self.script)

    def find_p2sh_hash(self):
        """檢測是否為 P2SH"""
        if len(self.script) == 23:
            if self.script[0] == 0xa9 and self.script[1] == 0x14:
                return self.script[2:22].hex()
        return None

    def find_p2wsh_hash(self):
        """檢測是否為 P2WSH"""
        if len(self.script) == 34:
            if self.script[0] == 0x00 and self.script[1] == 0x20:
                return self.script[2:34].hex()
        return None

比特幣腳本未來發展路線圖

即將到來的比特幣腳本升級

比特幣腳本發展時間線:

2021年(已完成)
├── Taproot 升級
│   ├── Schnorr 簽名(BIP-340)
│   ├── MAST(BIP-341)
│   └── Tapscript(BIP-342)
└── 隱私和效率顯著提升

2024-2025(進行中)
├── 閃電網路整合
│   ├── PTLC(Point Time Locked Contract)
│   ├── Taproot Channels
│   └── 0-conf 通道
└── 錢包支援擴展

2025-2027(提案中)
├── CTV(OP_CHECKTEMPLATEVERIFY)
│   ├── BIP-119
│   ├── 慈善Vault
│   └── 儲蓄帳戶
├── Drivechain
│   ├── BIP-300(Merge Mining)
│   └── BIP-301(Drivechain)
└── OP_CAT(簡化智能合約)

主要比特幣改進提案進度

BIP 提案狀態(截至2025年初):

已激活:
✓ BIP-340: Schnorr 簽名
✓ BIP-341: Taproot
✓ BIP-342: Tapscript
✓ BIP-143: 交易目睹化

審核中:
○ BIP-119: OP_CHECKTEMPLATEVERIFY
  狀態: 最終版本審核
  預計: 2025-2026 激活

○ BIP-300: Drivechain Merge Mining
  狀態: 社區討論
  挑戰: 礦工激勵

研究階段:
○ OP_CAT: 簡化 Tapscript
  狀態: 學術研究
  優點: 實現圖靈完整子集

○ OP_VAULT: 延時取款
  狀態: 概念驗證
  應用: 安全儲蓄

CTV 與 Drivechain 詳細比較

比特幣 Layer 2 解決方案比較:

特性              CTV           Drivechain     OP_VAULT
─────────────────────────────────────────────────────────
激活狀態          審核中         討論中          研究中
資金控制          用戶           礦工           用戶
回滾風險          無            有             無
實現複雜度        低            中            中
主要應用          Vault         側鏈           儲蓄

各方案的優劣勢:

1. CTV(OP_CHECKTEMPLATEVERIFY)
   優點:
   - 簡單可靠
   - 資金始終在用戶控制
   - 適用於 Vault、合約
   缺點:
   - 非遞迴
   - 軟分叉需要時間

2. Drivechain(BIP-300/301)
   優點:
   - 支持側鏈
   - 創新空間大
   缺點:
   - 需要礦工誠實假設
   - 爭議較大

3. OP_VAULT
   優點:
   - 專為儲蓄設計
   - 延時取款
   缺點:
   - 仍處於研究階段

BitVM 驗證遊戲完整指南

BitVM(Bitcoin Virtual Machine)是近期比特幣社群討論最熱烈的創新提案之一,它試圖在比特幣上實現圖靈完整的智能合約執行環境,而無需對比特幣共識層進行任何修改。BitVM 的核心思想是利用比特幣腳本構建一個可以驗證任意計算結果的系統,這被稱為「驗證遊戲」(Verification Game)。

BitVM 的基本原理

BitVM 的設計基於密碼學證明系統,特別是 zk-SNARKs 和交互式證明協議的概念。在 BitVM 中,計算被分為兩個角色:Prover(證明者)和 Verifier(驗證者)。

Prover 聲稱執行了某個計算並得到了特定的結果。Verifier 不需要自己重新執行整個計算,而是可以通過挑戰-回應的方式驗證 Prover 的聲稱是否正確。

這個過程類似於法庭上的辯論雙方:一方提出主張(Prover),另一方可以提出質疑(Verifier)。如果 Prover 能夠成功回應所有質疑,則其主張被視為有效。

BitVM 的運作機制

BitVM 的完整運作涉及以下幾個階段:

第一階段:承諾

Prover 對計算的輸入和輸出進行密碼學承諾。這些承諾被髮送到比特幣網路作為 Taproot 腳本的一部分。Prover 需要將整個計算表示為一個巨大的布爾電路,並為電路的每個閘門生成承諾。

具體來說,Prover 首先將要執行的程序編譯成布爾電路。這個電路可以實現任意複雜度的計算。然後,Prover 對電路的每個輸入值和每個閘門的輸出值生成 Pedersen commitments。這些承諾被放入 Taproot 腳本的腳本路徑中。

第二階段:挑戰-回應

Verifier 可以對 Prover 提出挑戰。在每個挑戰輪次中,Verifier 要求 Prover 揭示電路中特定閘門的值。如果 Prover 的回應與之前的承諾不一致,或者無法提供有效的證據,則 Prover 被認定為說謊,其承諾的輸出被視為無效。

挑戰過程的關鍵在於二分搜尋(Breadth-First Search)。如果電路有數百萬個閘門,Verifier 不需要逐一檢查。通過精心設計的挑戰協議,Verifier 可以在 log(n) 輪挑戰後定位到任何錯誤的閘門。

第三階段:獎勵與懲罰

如果 Prover 能夠成功通過所有挑戰,則其計算結果被接受,資金解鎖。如果 Prover 被發現欺騙,則其質押的比特幣被罰沒,轉給Verifier。

這種經濟機制確保了 Prover 有強烈的動機誠實行事,因為欺騙的成本遠高於誠實執行的成本。

BitVM 的實際應用場景

BitVM 為比特幣開啟了許多之前不可能實現的應用場景:

去中心化預測市場:在 BitVM 上可以構建完全去中心化的預測市場,用戶可以對任何事件的結果進行押注,而無需信任任何中心化機構。

去中心化身份驗證:BitVM 可以用於驗證用戶的身份資訊,而不需將敏感數據上傳到任何中心化服務器。

跨鏈橋接:BitVM 可以實現更加安全的比特幣跨鏈橋接,通過多方驗證確保跨鏈交易的安全性。

ZK-Rollup:BitVM 可以作為比特幣上的 ZK-Rollup 解決方案,將大量交易聚合為單一的鏈上驗證,大幅提升比特幣的吞吐量。

BitVM 的限制與挑戰

儘管 BitVM 概念上令人興奮,但它面臨著一些實際限制:

效率問題:BitVM 的驗證過程需要大量的鏈上互動,會產生昂貴的交易費用。完整驗證一個複雜計算可能需要數百甚至數千個比特幣交易。

延遲問題:挑戰-回應過程需要多輪互動,至少需要數十分鐘到數小時才能完成驗證。

複雜度問題:目前 BitVM 還沒有成熟的開發工具和標準庫,開發複雜的應用非常困難。

資金效率:參與 BitVM 需要質押大量比特幣作為擔保,這限制了普通用戶的參與。

BitVM 與其他方案的比較

BitVM 與其他比特幣智能合約方案比較
═══════════════════════════════════════════════════════════════════════════════

特性                    BitVM           閃電網路         傳統腳本
───────────────────────────────────────────────────────────────────────────
圖靈完整性             是              否               否
開發難度               高              中               低
執行效率               低              高               高
資金效率               低              高               高
隱私性                 中              高               低
───────────────────────────────────────────────────────────────────────────

OP_CAT 提案深度解析

OPCAT(也稱為 OPCAT 或 Catalyst)是比特幣腳本語言的一個提案,旨在恢復一個被禁用的操作碼並擴展比特約能力。OP幣的智能合_CAT 最初存在於比特幣的早期版本中,後來被中本聰禁用,原因是當時認為它沒有實際用途且可能帶來安全風險。

OP_CAT 的技術規格

OP_CAT 是「Concatenation」(串聯)的縮寫,它的功能非常簡單:將堆疊上的兩個元素連接成一個元素。

OP_CAT 運作方式:
堆疊輸入:[a, b]  其中 a 和 b 是字節串
堆疊輸出:[a || b]  連接後的新字節串

這個看起來簡單的操作碼可以實現非常強大的功能,特別是在與其他腳本特性結合時。

OP_CAT 的應用場景

樹狀合約結構:通過 OP_CAT,可以動態構建梅克尔樹(Merkle Tree),這對於複雜的腳本條件非常有用。例如,可以用於實現更靈活的 MAST 結構。

動態腳本生成:OP_CAT 允許在運行時動態組合腳本片段,這使得更複雜的邏輯成為可能。

密碼學原語實現:許多高級密碼學協議需要字節串的連接操作,OP_CAT 可以使這些協議在比特幣腳本中實現。

電路模擬:OPCAT 是實現通用計算的關鍵構建模組。結合其他腳本操作,可以用 OPCAT 構建圖靈完整的計算環境,這是 BitVM 等提案的基礎。

OP_CAT 的安全性考量

OP_CAT 本身不會引入新的安全漏洞,但需要小心設計使用它的腳本:

疊大小堆限制:使用 OP_CAT 時需要注意比特幣腳本的堆疊元素大小限制。單個堆疊元素不能超過 520 字節,這可能限制某些應用。

資源消耗:過度使用 OP_CAT 可能導致腳本執行消耗過多資源,這可能被惡意利用。

OP_CAT 與其他提案的互補性

OP_CAT 可以與多個其他比特幣升級提案配合使用:

與 Taproot 配合:OP_CAT 可以增強 Taproot 的腳本路徑能力,實現更靈活的支出條件。

與 CTV 配合:OP_CAT 可以幫助實現更複雜的輸出模板邏輯。

與 OPVAULT 配合:OPCAT 可以增加 Vault 腳本設計的靈活性。

OP_CAT 的最新進展

截至2025年底,OP_CAT 仍在討論和開發階段。社區對此提案存在不同意見:

支持者認為:OP_CAT 是實現更強大比特幣智能合約的關鍵;可以開啟許多創新的應用場景;與比特幣的保守設計哲學相容。

反對者認為:可能增加腳本的複雜性和錯誤風險;某些應用可以用現有功能實現;需要軟分叉激活,存在共識風險。

OP_CAT 提案狀態(2026年初)
═══════════════════════════════════════════════════════════════════════════════

狀態              描述
───────────────────────────────────────────────────────────────────────────
概念驗證           多個團隊已實現概念驗證原型
學術審查           密碼學社區正在進行安全性分析
社區討論           BIP 草案正在準備中
激活時間           尚未確定,可能在 2026-2027 年
───────────────────────────────────────────────────────────────────────────

比特幣腳本練習題

練習 1:P2PKH 腳本建構

題目:建構一個 P2PKH 鎖定腳本,假設公鑰哈希為 89abcdefabbaabbaabbaabbaabbaabbaabbaabba

解答

P2PKH 鎖定腳本格式:
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

完整腳本:
OP_DUP
OP_HASH160
14 89abcdefabbaabbaabbaabbaabbaabbaabbaabba
OP_EQUALVERIFY
OP_CHECKSIG

十六進制表示:
76 a9 14 89abcdefabbaabbaabbaabbaabbaabbaabbaabba 88 ac

練習 2:2-of-3 多籤腳本驗證

題目:驗證以下解鎖腳本是否對 2-of-3 多籤腳本有效:

解答

步驟 1:分析鎖定腳本
- OP_2:需要 2 個簽名
- <pub1>, <pub2>, <pub3>:三個公鑰
- OP_3:總共 3 個公鑰
- OP_CHECKMULTISIG:驗證多籤

步驟 2:分析解鎖腳本
- OP_0:必需的 dummy 元素(比特幣bug遺留)
- <sig1>, <sig2>:兩個簽名

步驟 3:驗證邏輯
- 2 ≤ 3:有效
- 簽名數量(2) ≥ 閾值(2):有效
- 結論:解鎖腳本有效

練習 3:HTLC 腳本建構

題目:建構一個 HTLC 腳本,要求:

解答

HTLC 腳本結構:
OP_HASH160 <hash(R)> OP_EQUALVERIFY
OP_IF
    <receiver_pubkey> OP_CHECKSIG
OP_ELSE
    <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
    <refund_pubkey> OP_CHECKSIG
OP_ENDIF

完整腳本(假設哈希為 abc123...):
a9 14 abc123def456789012345678901234567890abc 88
63                              # OP_IF
21 02xxxxxxxxxxxxxxxxxxxxxxxxx # 收款方公鑰
ac                              # OP_CHECKSIG
67                              # OP_ELSE
04 00000000 175                # 144 區塊(24小時)
b1                              # OP_CHECKLOCKTIMEVERIFY
75                              # OP_DROP
21 03yyyyyyyyyyyyyyyyyyyyyyyyy # 退款公鑰
ac                              # OP_CHECKSIG
68                              # OP_ENDIF

練習 4:時間鎖腳本計算

題目:計算 30 天相對時間鎖需要多少個區塊,並建構 CSV 腳本。

解答

計算:
- 平均區塊時間:10 分鐘
- 30 天 = 30 × 24 × 60 = 43,200 分鐘
- 區塊數 = 43,200 / 10 = 4,320 區塊

十六進制:0x10e0(小端序)

CSV 腳本:
<relative_blocks> OP_CHECKSEQUENCEVERIFY OP_DROP <pubkey> OP_CHECKSIG

示例:
00 00 10 e0          # 4,320 區塊
b2                  # OP_CHECKSEQUENCEVERIFY
75                  # OP_DROP
21 02...             # 公鑰
ac                  # OP_CHECKSIG

練習 5:腳本安全性分析

題目:分析以下腳本的安全漏洞:

OP_IF
    <pubkeyA> OP_CHECKSIG
OP_ELSE
    <pubkeyB> OP_CHECKSIG
OP_ENDIF

解答

安全漏洞分析:

1. 缺乏時間保護
   - 任何人都可以選擇任意分支
   - 沒有時間鎖或條件限制

2. 攻擊向量
   - 攻擊者可以嘗試兩個分支
   - 只要有一個簽名有效即可盜走資金

3. 改進建議
   添加時間鎖:
   OP_IF
       <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
       <pubkeyA> OP_CHECKSIG
   OP_ELSE
       <pubkeyB> OP_CHECKSIG
   OP_ENDIF

   或添加多籤:
   OP_IF
       OP_2 <pubkeyA> <pubkeyB> OP_2 OP_CHECKMULTISIG
   OP_ELSE
       <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP
       <pubkeyC> OP_CHECKSIG
   OP_ENDIF

練習 6:Miniscript 編譯

題目:將以下 Miniscript 編譯為比特幣腳本:

or_i(and_v(v:pkh(A),older(144)),pk(B))

解答

Miniscript 語法解析:
- or_i(A,B):邏輯 OR,優先執行 B
- and_v(A,B):邏輯 AND,先執行 A
- v:pkh(A):驗證公鑰哈希
- older(144):144 區塊時間鎖
- pk(B):簡單公鑰檢查

編譯步驟:
1. 內層:v:pkh(A) = OP_DUP OP_HASH160 <A> OP_EQUALVERIFY
2. older(144) = 144 OP_CHECKSEQUENCEVERIFY
3. and_v 合併:OP_DUP OP_HASH160 <A> OP_EQUALVERIFY 144 OP_CHECKSEQUENCEVERIFY
4. 外層 pk(B) = <B> OP_CHECKSIG
5. or_i 包裝需要 OP_IF...OP_ELSE...OP_ENDIF

完整腳本:
┌─────────────────────────────────────────┐
│ 完整腳本:                              │
│ OP_IF                                   │
│     OP_DUP OP_HASH160 <A> OP_EQUALVERIFY│
│     144 OP_CHECKSEQUENCEVERIFY          │
│     <B> OP_CHECKSIG                    │
│ OP_ELSE                                 │
│     <B> OP_CHECKSIG                    │
│ OP_ENDIF                                │
└─────────────────────────────────────────┘

OP_CODE 進階應用

OP_CHECKSIGADD 在閾值簽名中的應用

Tapscript 引入了 OP_CHECKSIGADD,專為閾值簽名場景設計:

# OP_CHECKSIGADD 運作原理
# 堆疊:[signature, pubkey, counter] → [counter + 1 if sig valid]

# 示例:2-of-3 閾值簽名驗證
script = """
# 初始化計數器
OP_0

# 第一個公鑰路徑
<sig1> <pubkey1> OP_CHECKSIGADD
<sig2> <pubkey2> OP_CHECKSIGADD
<sig3> <pubkey3> OP_CHECKSIGADD

# 比較閾值
OP_2 OP_NUMEQUALVERIFY
"""

# 執行流程:
# 初始:[] → [0]
# 步驟1:[sig1, pubkey1, 0] → [0+1] = [1]
# 步驟2:[sig2, pubkey2, 1] → [1+1] = [2]
# 步驟3:[sig3, pubkey3, 2] → [2+1] = [3](如果sig3無效)
# 最後:[2] → 與 2 比較 → 成功

條件分支的進階技巧

# 三重條件分支(三選一)
OP_IF
    # 條件 A
    <pubkeyA> OP_CHECKSIG
OP_ELSE
    OP_IF
        # 條件 B
        <pubkeyB> OP_CHECKSIG
    OP_ELSE
        # 條件 C(預設)
        <pubkeyC> OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

# 使用 OP_SIZE 進行金額條件判斷
# 注意:這需要腳本語言支援
<signature> <pubkey>
OP_CHECKSIG
OP_IF
    # 檢查金額(需要 OP_CAT 等操作碼)
    OP_SIZE
    OP_20 OP_LESSTHANOREQUAL
OP_ENDIF

腳本大小優化技巧

腳本大小優化策略:
═══════════════════════════════════════════════════════════════════════════════

1. 公鑰壓縮
   ┌────────────────────────────────────────┐
   │ 未壓縮公鑰:65 bytes (0x04 + x + y)   │
   │ 壓縮公鑰:33 bytes (0x02/03 + x)     │
   │ 節省:32 bytes                        │
   └────────────────────────────────────────┘

2. 使用 OP_CHECKMULTISIG 替代串聯
   ┌────────────────────────────────────────┐
   │ 錯誤:sig1 + sig2 + sig3              │
   │ 正確:OP_3 + sig1 + sig2 + sig3      │
   │ 節省:~60 bytes                       │
   └────────────────────────────────────────┘

3. MAST 結構優化
   ┌────────────────────────────────────────┐
   │ 不使用 MAST:揭露所有 N 個腳本         │
   │ 使用 MAST:只揭露 1 個腳本 + log₂(N) │
   │ 節省:N - log₂(N) 個哈希              │
   └────────────────────────────────────────┘

4. 腳本驗證器優化
   ┌────────────────────────────────────────┐
   │ 早期退出:失敗時立即終止               │
   │ 使用 OP_VERIFY:節省一個分支           │
   │ 使用 OP_EQUALVERIFY:合併比較和驗證   │
   └────────────────────────────────────────┘

常見腳本錯誤與調試

錯誤 1:OP_CHECKMULTISIG 的 Dummy 問題

# 錯誤示例
wrong_script = "OP_2 <pub1> <pub2> <pub3> OP_3 OP_CHECKMULTISIG"
# 解鎖:<sig1> <sig2>
# 結果:失敗(需要額外 dummy)

# 正確示例
correct_script = "OP_2 <pub1> <pub2> <pub3> OP_3 OP_CHECKMULTISIG"
# 解鎖:<dummy> <sig1> <sig2>
# 或
# 解鎖:OP_0 <sig1> <sig2>

錯誤 2:時間鎖類型混淆

# 錯誤:混合 CLTV 和 CSV
wrong_script = "<timestamp> OP_CHECKLOCKTIMEVERIFY <pubkey> OP_CHECKSIG"
# 問題:CLTV 需要絕對時間,無法與相對時間比較

# 正確:使用 CSV(相對時間)
correct_script = "<blocks> OP_CHECKSEQUENCEVERIFY OP_DROP <pubkey> OP_CHECKSIG"

# 正確:使用 CLTV(絕對時間)
correct_script = "<timestamp> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG"

錯誤 3:堆疊操作順序錯誤

# 錯誤:堆疊順序不正確
# 腳本:OP_SWAP <pubkey> OP_CHECKSIG
# 解鎖:<sig>
# 執行:
#   堆疊:[sig]
#   執行後:[sig, pubkey]  ← 錯誤!pubkey 在上面
#   OP_CHECKSIG 需要 [sig, pubkey] 順序

# 正確:直接推送公鑰
# 腳本:<pubkey> OP_CHECKSIG
# 解鎖:<sig>
# 執行:
#   堆疊:[sig, pubkey]  ← 正確
#   OP_CHECKSIG 成功

腳本調試工具

# 使用 Python 實現簡單的腳本調試器
class ScriptDebugger:
    def __init__(self):
        self.stack = []
        self.ops_executed = []

    def execute(self, script, witness):
        """執行腳本並調試"""
        self.stack = list(reversed(witness))  # 解鎖數據入棧
        self.ops_executed = []

        for op in self.parse_script(script):
            self.ops_executed.append(op)
            try:
                self.execute_op(op)
            except Exception as e:
                print(f"Error at {op}: {e}")
                print(f"Stack: {self.stack}")
                return False

        return len(self.stack) > 0 and bool(self.stack[-1])

    def execute_op(self, op):
        """執行單個操作碼"""
        if op == 'OP_DUP':
            self.stack.append(self.stack[-1])
        elif op == 'OP_DROP':
            self.stack.pop()
        elif op == 'OP_SWAP':
            self.stack[-1], self.stack[-2] = self.stack[-2], self.stack[-1]
        elif op == 'OP_HASH160':
            top = self.stack.pop()
            self.stack.append(hash160(top))
        elif op == 'OP_EQUALVERIFY':
            a = self.stack.pop()
            b = self.stack.pop()
            if a != b:
                raise Exception("OP_EQUALVERIFY failed")
        # ... 其他操作碼

# 使用示例
debugger = ScriptDebugger()
script = "OP_DUP OP_HASH160 <pubkeyhash> OP_EQUALVERIFY OP_CHECKSIG"
witness = ["<signature>", "<pubkey>"]
result = debugger.execute(script, witness)
print(f"Script execution: {'SUCCESS' if result else 'FAILED'}")

腳本類型選擇決策樹

腳本類型選擇流程:
═══════════════════════════════════════════════════════════════════════════════

                    ┌─────────────────┐
                    │ 需要什麼功能?  │
                    └────────┬────────┘
                             │
          ┌──────────────────┼──────────────────┐
          │                  │                  │
          ▼                  ▼                  ▼
   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐
   │ 簡單支付    │   │ 多方簽名    │   │ 時間條件   │
   └──────┬──────┘   └──────┬──────┘   └──────┬──────┘
          │                  │                  │
          ▼                  ▼                  ▼
   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐
   │ 單簽名?    │   │ 閾值簽名?  │   │ 相對時間?  │
   └──────┬──────┘   └──────┬──────┘   └──────┬──────┘
     │         │       │         │       │         │
     ▼         ▼       ▼         ▼       ▼         ▼
  是┌┐否  是┌┐否   是┌┐否   是┌┐否   是┌┐否
 ┌──┴─┐ ┌──┴─┐ ┌──┴─┐ ┌──┴─┐ ┌──┴─┐ ┌──┴─┐
 │    │ │    │ │    │ │    │ │    │ │    │
 ▼    ▼ ▼    ▼ ▼    ▼ ▼    ▼ ▼    ▼ ▼    ▼
 P2TR P2WPKH P2WSH P2SH P2TR+CSV P2SH+CSV
          │     │
          │     └──────────────────┐
          ▼                         ▼
   ┌─────────────┐           ┌─────────────┐
   │ 需要隱私?  │           │ 與舊錢包互通?│
   └──────┬──────┘           └──────┬──────┘
          │                         │
          ▼                         ▼
    是┌┐否                是┌┐否
    │ └─┐                │ └──┐
    ▼   ▼                ▼    ▼
 P2TR  P2WPKH        P2SH-P2WPKH  直接P2WSH

腳本安全性最佳實踐清單

比特幣腳本安全性檢查清單:
═══════════════════════════════════════════════════════════════════════════════

□ 基礎檢查
  □ 總是包含時間鎖
  □ 使用足夠的簽名閾值
  □ 避免單點故障

□ 隱私檢查
  □ 避免在腳本中暴露身份
  □ 使用 MAST 隱藏未使用條件
  □ 考慮使用 Taproot

□ 費用檢查
  □ 優化腳本大小
  □ 考慮閃電網路 Layer 2
  □ 選擇適當的地址類型

□ 相容性檢查
  □ 測試不同錢包
  □ 預留升級空間
  □ 避免依賴即將停用的功能

□ 測試檢查
  □ 在測試網充分測試
  □ 驗證時間鎖行為
  □ 測試簽名失敗情況

總結

比特幣腳本語言雖然簡單,但足以構建複雜的金融合約。從 P2PK 到 P2TR 的演進展示了比特幣在隱私、效率與靈活性上的持續改進。理解這些腳本類型的運作原理,是成為比特幣進階用戶與開發者的必備知識。

關鍵要點:

  1. P2TR 結合 Schnorr 簽名與 MAST,提供最佳隱私和費用效率
  2. P2WSH 支援更複雜的腳本條件,適合多簽名場景
  3. 時間鎖 是保護資金安全的重要機制,切勿忽視
  4. Miniscript 簡化了比特幣腳本的開發難度
  5. BitVM 為比特幣帶來圖靈完整的智能合約可能性
  6. OP_CAT 是實現更高級腳本功能的關鍵操作碼

未來隨著 BIP 119(CTV)、BIP 300+(Drivechain)、OP_CAT 以及 BitVM 等提案的實現,比特幣腳本將支援更多創新應用場景。這些升級將使比特幣能夠在保持安全性的同時,實現更豐富的金融應用。

本文包含

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!