比特幣 Script 互動式模擬器教育指南:動手玩轉比特幣智能合約

透過互動式模擬器實作比特幣腳本語言。從堆疊機原理開始,逐步介紹基本操作碼、P2PKH、P2SH、HTLC 等常見腳本類型,最後涵蓋 Taproot 與 MAST 等最新升級。包含完整程式碼範例和逐步執行追蹤,適合想深入理解比特幣智能合約運作原理的開發者和愛好者。

比特幣 Script 互動式模擬器教育指南:動手玩轉比特幣智能合約

為什麼要學比特幣腳本?

我第一次看到比特幣腳本的時候,整個人是懵的。那堆看起來像亂碼的字串:OP_DUP OP_HASH160 88ac——這到底是什麼鬼?

後來我花了幾個禮拜啃文件、做實驗,總算搞懂了。說實話,一旦你開竅了,比特幣腳本其實沒那麼可怕。

這篇文章的目標很簡單:用互動式的方式,讓你真正理解比特幣腳本是怎麼運作的

我們會從最基本的堆疊操作開始,一路玩到多簽名合約和時間鎖。你可以在腦中模擬執行過程,或者找個線上的比特幣腳本模擬器實際跑跑看。

準備好了嗎?讓我們開始。

第一關:理解堆疊機

什麼是堆疊?

堆疊(Stack)是一種資料結構,遵循「後進先出」(LIFO)的原則。

想像你有一疊盤子:

比特幣腳本就是用這種方式運作的。

逆波蘭表示法

比特幣腳本使用逆波蘭表示法(Reverse Polish Notation, RPN),又稱後綴表示法。

普通數學表達式:

3 + 4

逆波蘭表示法:

3 4 +

好處是:你不需要括號來指定運算順序。* 5 只會影響堆疊上最上面的兩個元素。

實驗一:基本算術運算

讓我們從最簡單的開始。

表達式:(3 + 4) * 2

逆波蘭表示法:

3 4 OP_ADD 2 OP_MUL

執行步驟追蹤:

步驟    操作       堆疊狀態
─────────────────────────────────
1       Push 3     [3]
2       Push 4     [3, 4]
3       OP_ADD    彈出 4, 3 → 計算 3+4=7 → Push 7    [7]
4       Push 2    [7, 2]
5       OP_MUL    彈出 2, 7 → 計算 7*2=14 → Push 14  [14]

最終堆疊頂部是 14,正確!

表達式:10 / (3 - 1)

逆波蘭表示法:

10 3 1 OP_SUB OP_DIV

執行步驟追蹤:

步驟    操作           堆疊狀態
──────────────────────────────────────
1       Push 10        [10]
2       Push 3         [10, 3]
3       Push 1         [10, 3, 1]
4       OP_SUB        彈出 1, 3 → 計算 3-1=2 → Push 2  [10, 2]
5       OP_DIV        彈出 2, 10 → 計算 10/2=5 → Push 5  [5]

最終結果是 5。

常見算術操作碼

操作碼名稱說明
OP_ADD加法彈出兩個值,計算 a + b,推回結果
OP_SUB減法彈出兩個值,計算 a - b
OP_MUL乘法彈出兩個值,計算 a * b
OP_DIV除法彈出兩個值,計算 a / b(整除)
OP_MOD取模彈出兩個值,計算 a % b
OP_1ADD加1堆疊頂部 + 1
OP_1SUB減1堆疊頂部 - 1

實驗二:堆疊操作

OP_DUP:複製頂部

有時候你需要堆疊頂部的值,但又不想把它彈出。這時候用 OP_DUP

操作       堆疊變化
─────────────────────
執行前:   [a, b, c]  (c 在頂部)
執行:     OP_DUP
執行後:   [a, b, c, c]

OP_DROP:丟棄頂部

執行前:   [a, b, c]
執行:     OP_DROP
執行後:   [a, b]

OP_SWAP:交換前兩個

執行前:   [a, b, c]
執行:     OP_SWAP
執行後:   [a, c, b]

OP_OVER:複製倒數第二個

執行前:   [a, b, c]
執行:     OP_OVER
執行後:   [a, b, c, b]

實驗練習

挑戰 1:計算 ((2 + 3) * 4) + 5

提示:先算 2+3,然後乘 4,最後加 5。

答案:

2 3 OP_ADD 4 OP_MUL 5 OP_ADD

追蹤:

[2]
[2, 3]
[5]          # 2+3=5
[5, 4]
[20]         # 5*4=20
[20, 5]
[25]         # 20+5=25

挑戰 2:交換堆疊前兩個元素,然後複製

[a, b, c] → [a, c, b] → [a, c, b, b]

腳本:

OP_SWAP OP_DUP

實驗三:條件執行

OP_IF:條件分支

OP_IF 會檢查堆疊頂部是否為 TRUE(非零)。如果是,執行 if 分支的指令;否則,執行 else 分支(如果有的話)。

基本結構:

OP_IF
    <指令1>
    <指令2>
OP_ELSE
    <指令3>
    <指令4>
OP_ENDIF

實驗:簡單的條件判斷

腳本:

5 OP_IF
    OP_1
OP_ELSE
    OP_0
OP_ENDIF

因為 5 是非零值(TRUE),所以執行 if 分支:

步驟    操作       堆疊狀態
─────────────────────────────
1       Push 5     [5]
2       OP_IF      檢查到 5 是 TRUE,執行 if 分支
3       OP_1       [1]
4       OP_ENDIF   結束條件,堆疊頂部 [1]

如果把 5 改成 0:

0 OP_IF
    OP_1
OP_ELSE
    OP_0
OP_ENDIF

結果會是 [0]。

巢狀條件

條件可以巢狀:

OP_IF
    <外層 if>
    OP_IF
        <內層 if>
    OP_ELSE
        <內層 else>
    OP_ENDIF
OP_ELSE
    <外層 else>
OP_ENDIF

實驗四:P2PKH——最常見的比特幣地址

什麼是 P2PKH?

Pay to Public Key Hash(P2PKH)是最傳統的比特幣地址格式,以「1」開頭。99% 的比特幣交易都是這個格式。

鎖定腳本

當你「發送」比特幣到一個 P2PKH 地址時,你實際上是把比特幣鎖定在一個腳本裡:

OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

是接收者的公鑰哈希(20 位元組)。

解鎖腳本

要花費這個 UTXO,必須提供:

<signature> <pubKey>

完整驗證流程

把解鎖腳本和鎖定腳本串在一起:

<signature> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

執行步驟:

步驟        操作               堆疊狀態
──────────────────────────────────────────────────
1           Push signature     [sig]
2           Push pubKey        [sig, pubKey]
3           OP_DUP             [sig, pubKey, pubKey]
4           OP_HASH160         [sig, pubKey, pubKeyHash]
5           Push pubKeyHash    [sig, pubKey, pubKeyHash, pubKeyHash']
6           OP_EQUALVERIFY    [sig, pubKey]  (如果相等,繼續;否則失敗)
7           OP_CHECKSIG       [TRUE/FALSE]

為什麼需要 OP_DUP?

注意到 OP_DUPpubKey 複製了一份。為什麼?

因為 OP_CHECKSIG 需要兩個東西:

OP_EQUALVERIFY 會消耗一個 pubKey 來比較。如果沒有 OP_DUPOP_CHECKSIG 就找不到公鑰了。

實驗五:P2SH——支付到腳本哈希

為什麼需要 P2SH?

P2PKH 很棒,但有個問題:如果你想要更複雜的鎖定條件(比如多簽名),發送方就必須知道完整的腳本內容。

P2SH 解決了這個問題:發送方只需要知道一個「腳本哈希」,接收方才需要知道完整的腳本內容。

格式

P2SH 地址以「3」開頭。

鎖定腳本:

OP_HASH160 <scriptHash> OP_EQUAL

是 20 位元組的哈希(通常是 SHA-256 + RIPEMD-160)。

實驗:2-of-3 多簽名

假設 Alice、Bob、Charlie 三個人要用 2-of-3 多簽名來管理資金。任何兩人同意就可以花費。

步驟 1:創建 redeemScript

OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG

(注意:OP_CHECKMULTISIG 會消耗一個多餘的元素,所以前面要放一個 OP_0

步驟 2:計算腳本哈希

把上面的腳本做 SHA-256,然後 RIPEMD-160,得到 20 位元組的哈希。

步驟 3:鎖定比特幣

發送方只需要知道這個哈希:

OP_HASH160 <scriptHash> OP_EQUAL

步驟 4:解鎖

要花費的人需要提供:

<sig1> <sig2> OP_0 <redeemScript>

為什麼要有 OP_0?因為比特幣早期的一個 bug,OP_CHECKMULTISIG 會消耗一個多餘的堆疊元素。放 OP_0 只是為了補償這個 bug。

執行流程

解鎖腳本 + 鎖定腳本:
<sig1> <sig2> OP_0 <redeemScript> OP_HASH160 <scriptHash> OP_EQUAL
步驟        操作               堆疊狀態
──────────────────────────────────────────────────
1           Push sig1         [sig1]
2           Push sig2         [sig1, sig2]
3           Push OP_0         [sig1, sig2, 0]
4           Push redeemScript [sig1, sig2, 0, redeemScript]
5           OP_HASH160        [sig1, sig2, 0, scriptHash']
6           Push scriptHash   [sig1, sig2, 0, scriptHash', scriptHash]
7           OP_EQUAL          [sig1, sig2, 0]  (如果相等,繼續)
8           OP_CHECKMULTISIG [TRUE]

實驗六:時間鎖——OPCLTV 和 OPCSV

OPCHECKLOCKTIMEVERIFY (OPCLTV)

這個操作碼允許你在腳本中加入「絕對時間鎖」——必須等到某個時間才能花費。

語法:

<time> OP_CHECKLOCKTIMEVERIFY OP_DROP <remaining script>

可以是:

實驗:1 年後才能花的比特幣

假設 2025 年 3 月 29 日,區塊高度大約 890,000。一年大約 52,500 個區塊,所以目標區塊高度大約是 942,500。

鎖定腳本:

942500 OP_CHECKLOCKTIMEVERIFY OP_DROP <pubKey> OP_CHECKSIG

如果有人在區塊 942,500 之前嘗試花費,OP_CLTV 會讓腳本失敗。只有在區塊 942,500 或之後,交易才有效。

OPCHECKSEQUENCEVERIFY (OPCSV)

這個操作碼實現「相對時間鎖」——必須等待一定時間(相對於 UTXO 被創建的時間)。

語法:

<sequence> OP_CHECKSEQUENCEVERIFY OP_DROP <remaining script>

格式:

實驗:錢包恢復機制

想像這個場景:你想設計一個錢包,正常情況下用單簽名,但如果你的硬體錢包丟了,可以用你的「恢復短語」在一個月後恢復資金。

腳本邏輯:

OP_IF
    # 正常路徑:單簽名
    <primaryKey> OP_CHECKSIG
OP_ELSE
    # 恢復路徑:一個月後
    <144> OP_CSV OP_DROP
    <recoveryKey> OP_CHECKSIG
OP_ENDIF

實驗七:HTLC——哈希時間鎖合約

HTLC 是閃電網路的核心構建模組。它結合了哈希鎖和時間鎖。

語法結構

OP_IF
    # 成功路徑:提供原像
    OP_HASH256 <hash> OP_EQUALVERIFY <receiverKey> OP_CHECKSIG
OP_ELSE
    # 退款路徑:等待時間鎖
    <locktime> OP_CLTV OP_DROP <senderKey> OP_CHECKSIG
OP_ENDIF

實驗:原子交換場景

假設 Alice 想用 1 BTC 交換 Bob 的 100 個假想的 Altcoins。

  1. Alice 生成一個隨機秘密 preimage
  2. Alice 計算 hash = SHA256(preimage)
  3. Alice 創建 HTLC,把 1 BTC 鎖進去

這時候:

同時,Bob 把 100 Altcoins 發給 Alice。只有當 Alice 揭示 preimage 時,Bob 才能從區塊鏈上「認領」這個秘密,用來完成 Altcoins 的交換。

執行追蹤

場景 A:Bob 成功領取

堆疊:<BobSig> OP_TRUE <preimage>
─────────────────────────────────
1       Push BobSig      [BobSig]
2       Push OP_TRUE     [BobSig, 1]
3       Push preimage    [BobSig, 1, preimage]
4       OP_HASH256       [BobSig, 1, hash']
5       <hash>           [BobSig, 1, hash', hash]
6       OP_EQUALVERIFY   [BobSig, 1]  (相等,繼續)
7       Push BobKey      [BobSig, 1, BobKey]
8       OP_CHECKSIG      [TRUE]

場景 B:時間到期,Alice 退款

堆疊:<AliceSig> OP_FALSE
─────────────────────────────
1       Push AliceSig    [AliceSig]
2       Push OP_FALSE     [AliceSig, 0]
3       OP_IF             跳過 if 分支
4       OP_ELSE          執行 else 分支
5       <locktime>       [AliceSig, 0, locktime]
6       OP_CLTV          檢查時間
7       OP_DROP          [AliceSig, 0]
8       Push AliceKey    [AliceSig, 0, AliceKey]
9       OP_CHECKSIG      [TRUE]

實驗八:Taproot——最新的腳本升級

為什麼要 Taproot?

Taproot 是比特幣 2021 年 11 月的重大升級。它帶來了:

  1. Schnorr 簽名:比 ECDSA 更簡單、更安全、更具可聚合性
  2. MAST:可以隱藏未使用的腳本條件
  3. 更便宜:複雜腳本的成本更低

Taproot 地址格式

Taproot 地址以 bc1p 開頭。

鎖定腳本:

OP_1 <x-only-pubkey>

是 32 位元組的公鑰。

密鑰路徑 vs 腳本路徑

Taproot 的一個巧妙設計是:你永遠可以選擇用「密鑰路徑」來花費

密鑰路徑:只需要簽名,就像普通錢包一樣。

腳本路徑:如果你想用更複雜的條件,可以「揭示」那個腳本並滿足它的條件。

實驗:MAST 結構

假設你有三種花費方式:

  1. 單簽名(最常見)
  2. 2-of-3 多簽名
  3. 時間鎖後單簽名

用 MAST 結構:

                      根哈希
                     /       \
            單簽名哈希      多簽/時間鎖哈希
               |          /         \
          葉節點A     葉節點B     葉節點C

如果用方式 1 花費,只需要揭示:

葉節點 B 和 C 永遠不需要揭示,所以它們的內容保持隱藏。

線上模擬器推薦

現在讓我推薦幾個可以實際跑比特幣腳本的線上工具:

1. Bitcoin Script Debugger

網址:https://www.cs.princeton.edu/~tierneyl/bitcoin-script-debugger/

這個工具可以讓你輸入腳本,逐步執行,觀察堆疊變化。非常適合學習。

2. Simin Belere's Bitcoin IDE

網址:https://bitcoin-script-ide.com/

提供更完整的開發環境,包括腳本範本和交易建構。

3. Bitcoin Optech Script Playground

Bitcoin Optech 的官方教學網站,提供腳本練習題。

4.CryptoLib

如果你會寫程式,可以用密碼學庫直接實驗。Python 的 bitcoinlib 或 JavaScript 的 bitcoinjs-lib 都可以讓你在本機測試。

常見錯誤與除錯

錯誤一:堆疊下溢

嘗試彈出堆疊上沒有的元素。

腳本:OP_ADD
堆疊:[5]
執行 OP_ADD:需要兩個元素,但只有一個 → 失敗!

解決方案:在操作前確保堆疊有足夠的元素。

錯誤二:OP_VERIFY 失敗

OP_VERIFY 會檢查堆疊頂部是否為 TRUE。如果不是,腳本立即失敗。

腳本:5 OP_VERIFY OP_1
堆疊:[5]
執行 OP_VERIFY:5 是非零 → TRUE → 繼續
執行 OP_1:堆疊 [5, 1]
成功!
腳本:0 OP_VERIFY OP_1
堆疊:[0]
執行 OP_VERIFY:0 是 FALSE → 腳本失敗!

錯誤三:類型錯誤

比特幣腳本沒有類型系統。數字和位元組串在堆疊上看起來一樣,但 OP_ADD 只接受數字。

如果你把字串「abc」當數字加,結果可能不是你想要的。

結語:腳本的力量

比特幣腳本語言的設計看起來很原始——沒有循環、沒有複雜的資料結構。但正是這種「刻意」的簡單性,讓比特幣變得安全可靠。

比特幣開發者的哲學是:在密碼學貨幣系統中,可預測性比表達力更重要

如果你想更深入,可以:

比特幣腳本的世界很大,這只是冰山一角。但有了這些基礎,你已經可以理解比特幣網路中絕大多數的交易是如何運作的了。

動手試試看吧!


標籤:比特幣腳本、Script、堆疊機、OP_CODE、P2PKH、P2SH、HTLC、Taproot、MAST、比特幣智能合約、互動式學習

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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