比特幣腳本語言入門

理解 Bitcoin Script 的基本指令與運作原理。

比特幣腳本語言入門

比特幣腳本(Bitcoin Script)是比特幣交易輸出中嵌入的簡單程式語言,用於定義花費比特幣的條件。不同於以太坊的 Solidity 等圖靈完整的智慧合約語言,比特幣腳本刻意設計為圖靈不完備的語言,這是出於安全性的深思熟慮——簡單的語法意味著可預測的執行結果和更少的攻擊面。

腳本基礎

堆疊式語言

比特幣腳本是一種堆疊式語言(Stack-based language),類似於 FORTH 語言。指令從左到右執行,數據和運算子都推入堆疊,運算元從堆疊彈出,運算結果再推回堆疊。

堆疊操作示意:

初始狀態:[]
執行 <signature>:["signature"]
執行 <pubKey>:["signature", "pubKey"]
執行 OP_DUP:["signature", "pubKey", "pubKey"]
執行 OP_HASH160:["signature", "pubKey", "pubKeyHash"]
執行 <pubKeyHash>:["signature", "pubKey", "pubKeyHash", "pubKeyHash"]
執行 OP_EQUALVERIFY:["signature", "pubKey"]
執行 OP_CHECKSIG:[]
結果:TRUE(交易有效)

基本結構

比特幣腳本分為兩個部分:

典型 P2PKH 交易結構:

┌─────────────────────────────────────────────────────────────┐
│  解鎖腳本(ScriptSig)                                      │
│  <signature> <pubKey>                                       │
│                                                             │
│  鎖定腳本(ScriptPubKey)                                   │
│  OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG │
└─────────────────────────────────────────────────────────────┘

合併後完整腳本:
<signature> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

常見指令詳解

數據指令

指令說明示例
OP_PUSHBYTES_1OP_PUSHBYTES_75推入 1-75 位元組OP_PUSHBYTES_20 推入 20 位元組
OP_PUSHDATA1推入可變長度(1 字節長度前綴)
OP_PUSHDATA2推入可變長度(2 字節長度前綴)
OP_PUSHDATA4推入可變長度(4 字節長度前綴)
OP_1NEGATE推入 -1
OP_1OP_16推入數字 1-16OP_1 = 1, OP_16 = 16

堆疊操作指令

指令說明
OP_TOALTSTACK將堆疊頂部移到替代堆疊
OP_FROMALTSTACK從替代堆疊移回主堆疊
OP_DUP複製堆疊頂部
OP_DROP彈出並丟棄堆疊頂部
OP_SWAP交換堆疊頂部兩個元素
OP_ROT旋轉前三個元素
OP_OVER複製第二個元素到頂部
OP_PICK複製第 n 個元素到頂部

密碼學指令

指令說明
OP_HASH160RIPEMD160(SHA256(x))
OP_SHA256SHA256 哈希
OP_RIPEMD160RIPEMD160 哈希
OP_CHECKSIG驗證 ECDSA 簽名
OP_CHECKSIGVERIFY驗證簽名並檢查結果
OP_CHECKMULTISIG驗證多簽名
OP_CHECKMULTISIGVERIFY驗證多簽名並檢查結果

流程控制指令

指令說明
OP_IF如果條件為真,執行分支
OP_ELSE否則執行此分支
OP_ENDIF結束條件分支
OP_VERIFY如果堆疊頂部為 FALSE,終止腳本
OP_RETURN無條件終止腳本(標記為不可花費)

時間鎖指令

指令說明
OP_CHECKLOCKTIMEVERIFY(CLTV)絕對時間鎖:驗證 locktime
OP_CHECKSEQUENCEVERIFY(CSV)相對時間鎖:驗證 sequence

常見腳本類型

1. P2PK(Pay to Public Key)

最簡單的腳本類型,直接支付給公鑰。早期比特幣區塊使用此格式。

腳本格式:
<signature> <pubKey> OP_CHECKSIG

特點:
- 公鑰直接暴露在區塊鏈上
- 簽名驗證簡單
- 隱私性較差(公鑰一旦使用即暴露)

2. P2PKH(Pay to Public Key Hash)

支付給公鑰的哈希,更安全。這是比特幣早期最廣泛使用的格式。

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

特點:
- 公鑰在花費時才暴露
- 只需 20 位元組的地址
- 支援大多數錢包

3. P2SH(Pay to Script Hash)

支付給腳本哈希,支援複雜的鎖定條件。多簽名錢包常用此格式。

腳本格式(鎖定):
OP_HASH160 <scriptHash> OP_EQUAL

腳本格式(解鎖):
<signature1> <signature2> <redeemScript>

特點:
- 允許複雜的兌現條件
- 發送方只需知道腳本哈希
- 適合多簽名、HTLC 等場景

4. P2WPKH(Pay to Witness Public Key Hash)

SegWit 升級後的格式,見證數據在隔離見證區塊中。節省區塊空間。

腳本格式:
OP_0 <pubKeyHash>

特點:
- 區塊容量增加
- 交易費用降低(每位元組費用相同,但數據更少)
- 解決交易延展性問題

5. P2WSH(Pay to Witness Script Hash)

SegWit 升級後的腳本哈希格式。用於更複雜的見證腳本。

腳本格式:
OP_0 <scriptHash>

特點:
- 支援更大的腳本(最多 3.6MB)
- 更安全的多簽名實現

6. P2TR(Pay to Taproot)

Taproot 升級引入,支援更複雜的腳本同時保持隱私。

腳本格式:
OP_1 <taproot_output_key>

特點:
- 支援 Schnorr 簽名
- 可以隱藏複雜的兌現條件
- 更低的交易費用(對於複雜腳本)

腳本執行過程深度解析

腳本驗證流程

比特幣腳本執行分為兩個階段:

驗證流程:

┌─────────────────────────────────────────────────────────────┐
│  第一階段:腳本合併                                          │
│  ScriptSig + ScriptPubKey → 完整腳本                       │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  第二階段:腳本執行                                          │
│  從左到右執行每個指令                                        │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  第三階段:結果驗證                                          │
│  堆疊非空且最後元素為非零 → 交易有效                       │
└─────────────────────────────────────────────────────────────┘

完整示例:P2PKH 驗證過程

讓我們逐步追蹤 P2PKH 腳本的執行:

假設數據:
- signature: 30440220...
- pubKey: 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
- pubKeyHash: 62E907B15CBF27D5425399EBF6F0FB50EBB88F18

完整腳本:
30440220... 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
OP_DUP OP_HASH160 62E907B15CBF27D5425399EBF6F0FB50EBB88F18 OP_EQUALVERIFY OP_CHECKSIG

步驟執行:

步驟  | 指令              | 堆疊狀態
──────┼──────────────────┼────────────────────────────────
1     | (signature)      | [sig]
2     | (pubKey)         | [sig, pk]
3     | OP_DUP           | [sig, pk, pk]
4     | OP_HASH160       | [sig, pk, pk_hash160]
5     | (pubKeyHash)     | [sig, pk, pk_hash160, pkh]
6     | OP_EQUALVERIFY   | [sig, pk] (相等則繼續,否則失敗)
7     | OP_CHECKSIG      | [result] (TRUE = 交易有效)

比特幣腳本地址格式

地址前綴

不同腳本類型使用不同的地址前綴:

類型前綴(Base58Check)示例
P2PKH 主網11BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
P2SH 主網33J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
P2WPKH 主網bc1(Bech32)bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
P2WSH 主網bc1bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3

腳本限制

故意限制

比特幣腳本是圖靈不完備的,這是刻意設計:

禁止的指令:
- OP_LOOP(循環)
- OP_JUMP(無條件跳轉)
- OP_JUMPI(條件跳轉)
- 任何形式的遞歸

為什麼如此設計?
1. 可預測的執行時間(無無限迴圈)
2. 簡化的資源計算
3. 更容易的靜態分析
4. 減少攻擊面

其他限制

限制類型數值
最大腳本大小(鎖定腳本)10,000 位元組
最大腳本大小(P2WSH)3,600,000 位元組
每個指令最大運算單位500,000
最大見證數據大小10,000,000 位元組
多簽名最大金鑰數15 個

進階應用

1. 多簽名腳本(Multisig)

2-of-3 多簽名:
OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG

鎖定腳本:
OP_2 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
  02A60F608D6D3E1E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E
  03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
OP_3 OP_CHECKMULTISIG

需要任意 2 個私鑰簽名才能花費。

2. 時間鎖(Time Lock)

OP_CHECKLOCKTIMEVERIFY(CLTV)

絕對時間鎖,指定最早可花費的區塊高度或時間。

腳本示例:
< expiry_time > OP_CHECKLOCKTIMEVERIFY OP_DROP <pubKey> OP_CHECKSIG

說明:
- 只有在 locktime 到達後才能花費
- locktime 可以是區塊高度或 Unix 時間戳

OP_CHECKSEQUENCEVERIFY(CSV)

相對時間鎖,指定從該輸出被確認後經過的時間。

腳本示例:
< sequence > OP_CHECKSEQUENCEVERIFY OP_DROP <pubKey> OP_CHECKSIG

說明:
- 只能用於相對於花費輸入的時間
- 常用於閃電網路通道

3. HTLC(Hash Time Locked Contract)

用於閃電網路和原子交換,實現跨鏈交易。

HTLC 鎖定腳本:
OP_IF
    OP_HASH160 <hash(R)> OP_EQUALVERIFY <receiver_pubkey> OP_CHECKSIG
OP_ELSE
    <timeout> OP_CHECKSEQUENCEVERIFY OP_DROP <sender_pubkey> OP_CHECKSIG
OP_ENDIF

說明:
- 如果提供 R 的原像(preimage),接收者可以立即獲得資金
- 如果超時,原則者可以取回資金

4. 秘密揭露(Hash Puzzle)

簡單的密碼學謎題:
OP_HASH160 <hash> OP_EQUAL

解鎖時需要提供滿足 hash(x) = <hash> 的 x

5. 條件分支

根據不同條件有不同的兌現方式:
OP_IF
    <condition_A_pubkey> OP_CHECKSIG
OP_ELSE
    OP_IF
        <condition_B_pubkey> OP_CHECKSIG
    OP_ELSE
        <condition_C_pubkey> OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

實際交易解析

讓我們看一個真實的比特幣交易來理解腳本實際運作:

交易 ID: 0a3b6f4e2b3e5c8a9f7d6e5c8a9b7d6e5c8a9f7d6e5c8a9b7d6e5c8a9b7d6e

輸入結構:
{
    "txid": "previous_transaction_id",
    "vout": 0,
    "scriptSig": "30440220... 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
    "sequence": 0xffffffff
}

輸出結構:
{
    "value": 0.5 BTC,
    "scriptPubKey": "OP_DUP OP_HASH160 62E907B15CBF27D5425399EBF6F0FB50EBB88F18 OP_EQUALVERIFY OP_CHECKSIG"
}

說明:
- 花費 0.5 BTC
- 鎖定到 P2PKH 地址:62E907B15CBF27D5425399EBF6F0FB50EBB88F18
- 對應地址:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

實際腳本範例與驗證

使用 Bitcoin Core RPC 創建腳本

以下展示如何使用 Bitcoin Core 的 createmultisig RPC 創建多簽名腳本:

# 創建 2-of-3 多簽名地址
$ bitcoin-cli createmultisig 2 '
[
    "0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
    "02A60F608D6D3E1E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E",
    "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
]
'

# 回應:
# {
#     "address": "3Dyt1gQjf4KdFNoxDvhGM4vCrVB5KzS2W8",
#     "redeemScript": "52210279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817982102A60F608D6D3E1E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E2103FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53AE"
# }

解碼腳本範例

使用 decodescript 解析腳本結構:

$ bitcoin-cli decodescript "52210279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817982102A60F608D6D3E1E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E2103FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53AE"

# 回應:
# {
#     "asm": "OP_2 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 02A60F608D6D3E1E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E 03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF OP_3 OP_CHECKMULTISIG",
#     "type": "multisig",
#     "reqSigs": 2,
#     "addresses": [
#         "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
#         "1DDiZ4kKq7q5zEwH5Z4kKq7q5zEwH5Z4kK",
#         "1EQw5Z4kKq7q5zEwH5Z4kKq7q5zEwH5Z4kK"
#     ],
#     "p2sh": "3Dyt1gQjf4KdFNoxDvhGM4vCrVB5KzS2W8"
# }

P2PKH 腳本完整驗證流程

以下展示完整的 P2PKH 腳本建立與花費流程:

// 使用 libbitcoin 庫建構 P2PKH 交易的完整範例

#include <bitcoin/bitcoin.hpp>

int main() {
    // 1. 定義私鑰(請勿在實際環境暴露私鑰)
    bc::ec_secret secret = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
    };

    // 2. 從私鑰導出公鑰
    bc::ec_point public_key = bc::secret_to_public_key(secret);

    // 3. 公鑰 SHA256 + RIPEMD160 = 公鑰哈希
    bc::data_chunk pubkey_data(public_key.begin(), public_key.end());
    bc::data_hash sha256_hash = bc::sha256_hash(pubkey_data);
    bc::short_hash pubkey_hash = bc::ripemd160_hash(sha256_hash);

    // 4. 建立 P2PKH 鎖定腳本
    // OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
    bc::script lock_script = bc::script::to_pay_key_hash_pattern(pubkey_hash);

    // 5. 解鎖腳本(花費時提供)
    bc::script unlock_script = bc::script::to_sign(secret);

    // 6. 組合完整輸入腳本
    bc::script input_script = bc::script::to_pay_key_hash_pattern(
        unlock_script, lock_script);

    return 0;
}

原子交換(Atomic Swap)腳本範例

比特幣與萊特幣之間的原子交換使用 HTLC 腳本:

// 原子交換腳本範例(比特幣側)

// 接收者的鎖定腳本:
// 如果提供 R 的原像,接收者在 timeout 之前可以取走資金
// 如果超時,發送者可以取回資金

const char* atomic_swap_lock = R"(
    OP_IF
        OP_HASH160 <hash(R)> OP_EQUALVERIFY
        <receiver_pubkey> OP_CHECKSIG
    OP_ELSE
        <timeout> OP_CHECKSEQUENCEVERIFY OP_DROP
        <sender_pubkey> OP_CHECKSIG
    OP_ENDIF
)";

// 接收者解鎖(提供 R 的原像):
// <signature> <R> 1 <redeemScript>

// 發送者解鎖(超時後):
// <signature> 0 <redeemScript>

時間鎖定存款合約

建立一個需要等待特定區塊數後才能提取的存款合約:

// CLTV 時間鎖定腳本
// 資金鎖定至特定時間

const char* timelock_contract = R"(
    <expiry_time> OP_CHECKLOCKTIMEVERIFY OP_DROP
    OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
)";

// 其中 expiry_time 可使用區塊高度或 Unix 時間戳
// 區塊高度格式:OP_CHECKLOCKTIMEVERIFY 使用區塊高度
// Unix 時間戳格式:OP_CHECKLOCKTIMEVERIFY + OP_CLTV 可用時間戳

秘密揭露腳本

實現「只有你知道秘密才能花費」的条件:

// 秘密揭露腳本範例

// 鎖定腳本:只接受能夠揭露秘密的人
// OP_HASH160 <secret_hash> OP_EQUAL

// 步驟 1: 選擇一個秘密(256 位元組隨機數)
const char* secret = "a1b2c3d4e5f6...";  // 實際使用真正的隨機數

// 步驟 2: 計算 SHA256 哈希作為鎖定值
// SHA256(secret) = 3d4e5f6a1b2c3d4...  (作為鎖定值)

// 步驟 3: 建立鎖定腳本
// OP_HASH160 3d4e5f6a1b2c3d4... OP_EQUAL

// 解鎖時:
// <secret>  (提供秘密本身)

// 驗證過程:
// 1. 執行 OP_HASH160,計算 secret 的哈希
// 2. 與鎖定值比較
// 3. 相等則允許花費

閃電網路通道腳本

閃電網路使用 CSV 和 HTLC 實現雙向支付通道:

// 閃電網路 commitment 交易輸出腳本

// 延遲兌現(CSV):延遲後雙方都能兌現
OP_IF
    <revocation_pubkey> OP_CHECKSIG
OP_ELSE
    OP_IF
        // 接收者可以立即提取(延遲後)
        <relative_delay> OP_CHECKSEQUENCEVERIFY OP_DROP
        <receiver_pubkey> OP_CHECKSIG
    OP_ELSE
        // 發送者可以延遲後提取
        <relative_delay> OP_CHECKSEQUENCEVERIFY OP_DROP
        <sender_pubkey> OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

// HTLC 輸出腳本:
// 提供 HTLC 的三種兌現方式:
// 1. 接收者提供 R 原像(立即)
// 2. 發送者超時後撤回
// 3. 雙方簽名(協商關閉)

OP_RETURN 使用場景

OP_RETURN 用於將不可花費的數據寫入區塊鏈:

// 使用 Bitcoin Core RPC 創建 OP_RETURN 輸出

// 1. 簡單 OP_RETURN(嵌入數據)
$ bitcoin-cli createrawtransaction '[]' '{
    "data": "48656c6c6f20576f726c64"
}'

// data 是十六進制編碼:"Hello World"
// OP_RETURN <data>

// 2. 多重 OP_RETURN
$ bitcoin-cli createrawtransaction '[]' '{
    "data": "48656c6c6f20576f726c64",
    "data": "4c6f766520426974636f696e"
}'

腳本指紋識別

不同腳本類型有獨特的「指紋」模式:

# 腳本類型識別範例

def identify_script(script_hex):
    """根據腳本字節識別腳本類型"""

    # P2PKH: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG
    if script_hex[:6] == "76a914" and script_hex[-8:] == "88ac":
        return "P2PKH"

    # P2SH: OP_HASH160 <20 bytes> OP_EQUAL
    if script_hex[:4] == "a914" and script_hex[-2:] == "87":
        return "P2SH"

    # P2WPKH: OP_0 <20 bytes>
    if script_hex[:4] == "0014":
        return "P2WPKH"

    # P2WSH: OP_0 <32 bytes>
    if script_hex[:4] == "0020":
        return "P2WSH"

    # OP_RETURN: OP_RETURN <data>
    if script_hex[:2] == "6a":
        return "OP_RETURN"

    return "Unknown"

比特幣腳本與 BIP 標準

比特幣腳本的發展受到多個比特幣改進提案(BIP)的規範與推動。以下是與腳本相關的重要 BIP:

BIP-13:P2SH 地址格式

BIP-13 定義了 Pay to Script Hash(P2SH)地址格式,允許複雜的鎖定腳本:

# P2SH 地址生成示例
import hashlib
import base58

def create_p2sh_address(redeem_script):
    """從贖回腳本生成 P2SH 地址"""
    # RIPEMD160(SHA256(redeem_script))
    sha256_hash = hashlib.sha256(redeem_script).digest()
    ripemd160 = hashlib.new('ripemd160')
    ripemd160.update(sha256_hash)
    script_hash = ripemd160.digest()

    # 版本位元組:0x05(P2SH 主網)
    versioned_payload = bytes([0x05]) + script_hash

    # 校驗碼
    checksum = hashlib.sha256(hashlib.sha256(versioned_payload).digest()).digest()[:4]

    # Base58Check 編碼
    return base58.b58encode(versioned_payload + checksum)

# 示例:2-of-3 多簽名腳本的 P2SH 地址
redeem_script = bytes.fromhex('52210279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817982102A60F608D6D3E1E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E9F3D7E2103FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF53AE')
p2sh_address = create_p2sh_address(redeem_script)
print(f"P2SH 地址: {p2sh_address.decode()}")
# 輸出: 3Dyt1gQjf4KdFNoxDvhGM4vCrVB5KzS2W8

BIP-143:交易簽名哈希計算

BIP-143 標準化了 SegWit 交易的簽名哈希計算,解決了舊標準的多個問題:

# BIP-143 簽名哈希計算示例
import hashlib

def bip143_sighash(tx_in_index, tx_out_value, script_code, hash_type, tx_to_spend, tx_to_sign):
    """BIP-143 標準化的簽名哈希計算"""

    # 1. nVersion (4 bytes, little-endian)
    hash_prevouts = hashlib.sha256(hashlib.sha256(tx_to_spend['prevouts']).digest()).digest()

    # 2. nHashType (4 bytes, little-endian)
    # 3. hashSequence
    hash_sequence = hashlib.sha256(hashlib.sha256(tx_to_spend['sequences']).digest()).digest()

    # 4. outpoint (36 bytes: 32-byte txid + 4-byte vout)
    outpoint = tx_to_spend['txid'] + tx_to_spend['vout'].to_bytes(4, 'little')

    # 5. scriptCode (variable length)
    script_code_len = len(script_code).to_bytes(1, 'little')

    # 6. amount (8 bytes, little-endian)
    amount = tx_out_value.to_bytes(8, 'little')

    # 7. nSequence (4 bytes, little-endian)
    sequence = tx_to_spend['sequence'].to_bytes(4, 'little')

    # 8. hashOutputs
    hash_outputs = hashlib.sha256(hashlib.sha256(tx_to_sign['outputs']).digest()).digest()

    # 9. nLocktime (4 bytes, little-endian)
    locktime = tx_to_sign['locktime'].to_bytes(4, 'little')

    # 10. sighash type
    sighash_type = hash_type.to_bytes(4, 'little')

    # 組合所有組件
    data = (tx_to_sign['version'].to_bytes(4, 'little') +
            hash_prevouts +
            hash_sequence +
            outpoint +
            script_code_len +
            script_code +
            amount +
            sequence +
            hash_outputs +
            locktime +
            sighash_type)

    return hashlib.sha256(hashlib.sha256(data).digest()).digest()

BIP-341 與 BIP-342:Taproot 腳本

Taproot 升級(2021年11月)引入 BIP-341(Taproot)和 BIP-342(Tapscript),這是比特幣腳本語言的重大演進:

# Taproot 地址生成示例
import hashlib

def taproot_tweak_public_key(public_key, script_tree=None):
    """根據 BIP-341 生成 Taproot 內部金鑰"""

    # 如果有腳本樹,計算腳本樹的哈希
    if script_tree:
        script_tree_hash = hashlib.sha256(script_tree).digest()
    else:
        script_tree_hash = b'\x00' * 32  # 空腳本

    # 計算調整後的金鑰
    tweak = hashlib.sha256(public_key + script_tree_hash).digest()
    tweaked_key = int.from_bytes(public_key, 'big') + int.from_bytes(tweak, 'big')
    tweaked_key = tweaked_key % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F

    return tweaked_key.to_bytes(32, 'big')

# Taproot 地址(bech32m 編碼)
# 輸出格式:OP_1 <tweaked_public_key>
# 以 bc1p 開頭

BIP-47:可重用支付代碼

BIP-47 定義了可重用支付代碼,允許用戶公開單一地址進行多次付款,同時保護隱私:

# BIP-47 支付代碼處理示例
import hashlib
import hmac

def derive_payment_code_private_key(payment_code, chain_code, index):
    """從支付代碼派生出子私鑰"""

    # BIP-47 使用 ECIA(Elliptic Curve Instantiation)
    # 基於 BIP-32 HD 錢包標準

    data = payment_code + index.to_bytes(4, 'big')
    h = hmac.new(chain_code, data, hashlib.sha512).digest()

    # 返回子私鑰
    child_private_key = int.from_bytes(h[:32], 'big')
    child_chain_code = h[32:]

    return child_private_key, child_chain_code

腳本執行環境與網路協議

比特幣腳本不僅是交易驗證的核心,還與比特幣網路協議密切互動。

腳本驗證的網路傳播

當用戶廣播一筆比特幣交易時,腳本的執行涉及以下網路協議層:

比特幣腳本網路傳播流程:

┌─────────────────────────────────────────────────────────────────┐
│  第一層:交易創建                                               │
│  節點 A 創建交易並使用私鑰簽名                                  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  第二層:交易驗證(本地)                                       │
│  節點 A 執行腳本驗證:                                          │
│  - 輸入腳本 ScriptSig 解碼                                     │
│  - 輸出腳本 ScriptPubKey 解析                                  │
│  - 組合腳本並執行                                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  第三層:INV 消息傳播                                           │
│  驗證通過後,節點 A 向相鄰節點發送 INV 消息                     │
│  包含交易哈希(txid)                                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  第四層:GETDATA 請求                                           │
│  收到 INV 的節點請求完整交易數據                                │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  第五層:TX 消息傳播                                            │
│  完整交易數據傳播到網路各節點                                   │
│  每個節點獨立驗證腳本                                           │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│  第六層:記憶池儲存                                             │
│  驗證通過的交易進入記憶池,等待礦工打包                         │
└─────────────────────────────────────────────────────────────────┘

腳本大小限制與區塊空間

比特幣區塊的容量限制直接影響腳本複雜度:

腳本類型最大大小每筆輸入所需空間
P2PKH25 bytes~148 vbytes
P2SH520 bytes~148 vbytes
P2WPKH25 bytes~68 vbytes
P2WSH10,000 bytes~64 vbytes + 4 vbytes per 50 bytes
P2TR40 bytes~57.5 vbytes

腳本運算單位(SigOps)限制

比特幣對腳本中的簽名操作數量有嚴格限制:

# SigOps 限制計算示例

def calculate_sigops(script):
    """計算腳本中的簽名操作數量"""

    sigops_count = 0
    op_codes = {
        'OP_CHECKSIG': 1,
        'OP_CHECKSIGVERIFY': 1,
        'OP_CHECKMULTISIG': 1,  # 實際每多簽名中的 N 個公鑰算 N
        'OP_CHECKMULTISIGVERIFY': 1,
    }

    # 解析腳本字節
    i = 0
    while i < len(script):
        op = script[i]

        if op in [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                  0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10]:
            # OP_1 到 OP_16(多簽名中的閾值)
            if i > 0 and script[i-1] in range(0x51, 0x61):
                sigops_count += script[i-1] - 0x50

        i += 1

    return sigops_count

Miniscript:結構化比特幣腳本

Miniscript 是一種在比特幣腳本之上的抽象層,允許以更高層級的方式表達腳本邏輯:

# Miniscript 表達式示例

# 簡單的 timelock:
# and_v(v:pkh(sender), older(144))

# 多簽名:
# thresh(2,pk(key1),pk(key2),pk(key3))

# 帶時間鎖的多簽名:
# andor(thresh(2,pk(key1),pk(key2)), older(1000), pk(recovery_key))

# 比特幣核心的 miniscript 解析:
# $ bitcoin-cli decodepsbt <psbt>

Miniscript 的優勢包括:

腳本安全注意事項

常見錯誤與防範

// 1. 金額驗證缺失(導致灰塵攻擊)
// 錯誤:
// OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

// 正確:檢查輸入金額
// <amount> OP_GREATERTHANVERIFY OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

// 2. 時間鎖使用錯誤
// 錯誤:locktime 類型混淆
// CLTV 需要交易別的 locktime 設置

// 正確:
// - OP_CHECKLOCKTIMEVERIFY 使用 nLockTime
// - OP_CHECKSEQUENCEVERIFY 使用 nSequence

// 3. 多簽名密鑰數限制
// 比特幣限制:最多 15 個公鑰
// OP_2 ... OP_16 OP_CHECKMULTISIG

// 4. 腳本大小限制
// - P2PKH: 最大 25 bytes
// - P2PK: 最大 35 bytes (compressed pubkey)
// - P2SH: 最大 520 bytes (推入的 redeemScript)
// - P2WSH: 最大 3,600,000 bytes

腳本驗證最佳實踐

// Bitcoin Core 腳本驗證流程

// 1. 語法檢查
bool check_script_syntax(const CScript& script) {
    // 檢查無效 opcode
    // 檢查編碼錯誤
    // 檢查堆疊溢出風險
}

// 2. 腳本標誌驗證
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC |
                     SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_MINIMALDATA;

// 3. 執行腳本
bool result = EvalScript(stack, script, flags, checker, &serror);

// 4. 結果驗證
if (!result || stack.empty() || !CastToBool(stack.back())) {
    return false;  // 腳本驗證失敗
}

結論

比特幣腳本是一種安全、簡單的程式語言,專為比特幣交易設計。雖然功能有限,但足以實現各種支付條件:

圖靈不完備的設計確保了腳本執行是可預測的,這也是比特幣安全性的重要基礎。理解比特幣腳本是深入理解比特幣運作機制的關鍵步驟。

本文包含

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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