Bitcoin Script 實戰教學(一):基礎概念與堆疊操作
這是「Bitcoin Script 實戰教學」系列的第一篇。本系列將從零開始,深入探索比特幣的腳本語言,理解交易如何被驗證和執行。
系列文章導航:
- 第一篇:基礎概念與堆疊操作(本篇)
- 第二篇:標準腳本類型詳解
- 第三篇:SegWit 與 Taproot 腳本
- 第四篇:多重簽名與門檻簽名
- 第五篇:時間鎖機制
- 第六篇:進階應用與智能合約
一、什麼是 Bitcoin Script?
1.1 從一個常見的誤解說起
許多人認為比特幣只能進行簡單的「A 發送給 B」這樣的轉帳操作。事實上,比特幣從一開始就內建了一套完整的程式語言——Bitcoin Script。這套語言讓比特幣不僅僅是數位現金,而是一個可程式化的貨幣系統。
中本聰在設計比特幣時,刻意選擇了一種受限但強大的腳本語言。這個選擇反映了一個核心設計哲學:安全性優先於功能性。比特幣網路處理的是真實的金錢,任何程式錯誤都可能導致資金永久損失,因此語言的可預測性和安全性至關重要。
1.2 Script 的本質:花費條件的定義
要理解 Bitcoin Script,首先需要理解比特幣的 UTXO(未花費交易輸出)模型。每一筆比特幣實際上都被「鎖」在一個 UTXO 中,而 Script 就是定義「如何解鎖」這筆錢的規則。
想像一個保險箱:
- 鎖定腳本(ScriptPubKey):定義打開保險箱需要什麼條件,例如「需要持有特定私鑰的人簽名」
- 解鎖腳本(ScriptSig):提供滿足條件的證據,例如「這是我的數位簽名」
當有人想花費一筆比特幣時,網路中的每個節點都會執行這段腳本,驗證解鎖條件是否被滿足。如果腳本執行成功(返回 TRUE),交易就是有效的;否則,交易會被拒絕。
1.3 為什麼 Script 故意設計成「不完整」?
Bitcoin Script 是非圖靈完備的,這意味著它沒有迴圈指令,無法執行無限次的運算。這是刻意的設計,原因有三:
第一,防止拒絕服務攻擊。 如果腳本可以無限迴圈,攻擊者可以創建一個永遠不會結束的腳本,讓驗證節點陷入死迴圈。在一個去中心化網路中,每個節點都必須驗證每筆交易,這種攻擊會癱瘓整個網路。
第二,確保可預測性。 沒有迴圈意味著腳本的執行時間可以被準確估計。節點可以在執行前就知道這個腳本大約需要多少運算資源,從而進行合理的資源分配。
第三,簡化安全審計。 一個非圖靈完備的語言更容易被完整分析。開發者可以更有信心地確認腳本會按預期執行,不會有隱藏的邏輯錯誤。
這種設計哲學與以太坊形成鮮明對比。以太坊選擇了圖靈完備的智能合約語言,獲得了更大的靈活性,但也因此面臨更多的安全挑戰(如著名的 The DAO 攻擊)。比特幣則選擇了保守路線,用功能的限制換取更高的安全性。
1.4 Script 能做什麼?
雖然 Script 有諸多限制,但它的能力遠超大多數人的想像:
基本支付:最常見的用途,要求接收者用對應的私鑰簽名才能花費。
多重簽名:要求多個人中的 M 個人同時簽名才能花費,例如「3 個董事中的 2 個必須同意」。這對公司資金管理、託管服務等場景非常有用。
時間鎖定:規定資金必須在特定時間後才能被花費。可以用於遺產規劃(「我死後 1 年,我的孩子可以取得這筆錢」)或定期解鎖的投資。
哈希鎖定:要求提供某個哈希值的原像才能花費。這是閃電網路和跨鏈原子交換的基礎。
條件支付:根據不同條件執行不同的花費邏輯,例如「Alice 可以立即花費,或者 Bob 在一週後可以花費」。
這些基本元件可以組合成更複雜的合約,實現去中心化託管、支付通道、甚至簡單的預測市場。
二、堆疊機模型
2.1 什麼是堆疊?
Bitcoin Script 使用一種稱為「堆疊機」的執行模型。如果你沒有接觸過這個概念,可以把堆疊想像成一疊盤子:
- 放盤子(PUSH):你只能把新盤子放在最上面
- 取盤子(POP):你只能從最上面拿走盤子
- 後進先出(LIFO):最後放上去的盤子會最先被拿走
這種結構看似簡單,卻足以實現所有 Script 需要的運算。事實上,許多早期的計算機和計算器都使用堆疊式架構,因為它實現簡單且不容易出錯。
2.2 Script 的執行過程
當比特幣節點驗證一筆交易時,它會按照以下步驟執行腳本:
- 初始化空堆疊:開始時,堆疊是空的
- 從左到右讀取腳本:依序處理每個元素
- 遇到數據就推入堆疊:數字、公鑰、簽名等都會被放到堆疊頂端
- 遇到操作碼就執行操作:操作碼會從堆疊取出數據、進行運算、再把結果放回堆疊
- 檢查最終結果:執行完畢後,如果堆疊頂端是非零值(TRUE),腳本就成功了
讓我們用一個簡單的數學運算來說明這個過程:
1
2
3
腳本:2 3 OP_ADD 5 OP_EQUAL
這段腳本的意思是:「2 加 3 等於 5 嗎?」
執行過程:
步驟 1:讀取到數字 2,推入堆疊
1
堆疊:[2]
步驟 2:讀取到數字 3,推入堆疊
1
堆疊:[2, 3] ← 3 在最上面
步驟 3:讀取到 OP_ADD,這個操作碼會:
- 從堆疊彈出兩個數字(3 和 2)
- 計算它們的和(5)
- 把結果推回堆疊
1
堆疊:[5]
步驟 4:讀取到數字 5,推入堆疊
1
堆疊:[5, 5]
步驟 5:讀取到 OP_EQUAL,這個操作碼會:
- 彈出兩個值
- 比較它們是否相等
- 如果相等推入 TRUE,否則推入 FALSE
1
堆疊:[TRUE]
結果:堆疊頂端是 TRUE,腳本執行成功!
2.3 主堆疊與輔助堆疊
Bitcoin Script 實際上有兩個堆疊:
主堆疊(Main Stack):所有主要運算都在這裡進行。上面的例子就是在主堆疊上執行的。
輔助堆疊(Alt Stack):一個臨時存儲區,用於暫存數據。當腳本邏輯比較複雜,需要暫時「保存」某個值稍後再用時,可以把它移到輔助堆疊。
兩個操作碼用於在堆疊間移動數據:
OP_TOALTSTACK:把主堆疊頂端的值移到輔助堆疊OP_FROMALTSTACK:把輔助堆疊頂端的值移回主堆疊
這個機制讓複雜的腳本邏輯成為可能,同時保持了語言的簡潔性。
三、操作碼詳解
操作碼(Opcode)是 Script 的指令集。每個操作碼都是一個單字節的數值,對應一個特定的操作。Bitcoin Script 定義了大約 100 個操作碼,但其中一部分已被禁用(出於安全考慮)。
3.1 常量與數據推送
最基本的操作是把數據推入堆疊。Script 提供了多種方式:
小數字(0-16):有專門的操作碼 OP_0 到 OP_16,只佔用 1 個字節。這是最節省空間的方式。
1
2
3
OP_0 (0x00):推入空字節數組,在布林運算中視為 FALSE
OP_1 (0x51):推入數字 1,也稱為 OP_TRUE
OP_2 (0x52) 到 OP_16 (0x60):推入對應的數字
任意數據推送:對於更長的數據(如公鑰、簽名),使用數據推送指令:
1
2
3
4
5
6
0x01 - 0x4b:直接指定後面跟隨的字節數
例如 0x14 表示「後面 20 個字節是數據」
OP_PUSHDATA1 (0x4c):後面 1 個字節指定數據長度(最多 255 字節)
OP_PUSHDATA2 (0x4d):後面 2 個字節指定數據長度(最多 65535 字節)
OP_PUSHDATA4 (0x4e):後面 4 個字節指定數據長度(理論上可以很大)
一個有趣的細節是,比特幣要求使用最短的可能編碼。如果你用 OP_PUSHDATA2 來推送一個只有 10 字節的數據,交易會被視為非標準的。這個規則防止了交易膨脹和某些類型的攻擊。
3.2 堆疊操作
這類操作碼用於操縱堆疊本身,不涉及數學運算:
OP_DUP(複製):複製堆疊頂端的元素。這是最常用的操作之一,因為很多時候我們需要同時保留原值並對其進行運算。
1
2
3
執行前:[A, B, C]
OP_DUP
執行後:[A, B, C, C]
OP_DROP(丟棄):移除堆疊頂端的元素。當某個值已經用完,不再需要時使用。
1
2
3
執行前:[A, B, C]
OP_DROP
執行後:[A, B]
OP_SWAP(交換):交換堆疊頂端的兩個元素。
1
2
3
執行前:[A, B, C]
OP_SWAP
執行後:[A, C, B]
OP_ROT(旋轉):將第三個元素移到頂端。
1
2
3
執行前:[A, B, C]
OP_ROT
執行後:[B, C, A]
這些基本操作組合起來,可以實現對堆疊的任意重排。雖然看起來原始,但對於腳本的表達能力已經足夠。
3.3 算術運算
Script 支援基本的整數運算,但有一些重要限制:
支援的操作:
OP_ADD:加法OP_SUB:減法OP_1ADD:加 1(比1 OP_ADD更節省空間)OP_1SUB:減 1OP_NEGATE:取負數OP_ABS:取絕對值OP_MIN:取兩數中較小的OP_MAX:取兩數中較大的
被禁用的操作:
OP_MUL:乘法OP_DIV:除法OP_MOD:取模- 以及一些位運算
這些操作在 2010 年被禁用,原因是它們的實現存在漏洞,可能被利用來進行攻擊。雖然修復這些漏洞在技術上是可能的,但比特幣社群選擇了保守的做法——直接禁用它們。畢竟,絕大多數腳本不需要乘除法。
數值範圍:Script 中的數字是有符號的,使用變長編碼。實際上,大多數運算被限制在 4 字節(約 ±21 億)範圍內。這足以處理比特幣的金額(最大約 21 億聰)。
3.4 密碼學操作
這是 Bitcoin Script 最核心的部分,實現了交易安全性的基礎:
哈希函數:
1
2
3
4
5
OP_RIPEMD160:計算 RIPEMD-160 哈希(160 位元輸出)
OP_SHA1:計算 SHA-1 哈希(已知不安全,但仍保留)
OP_SHA256:計算 SHA-256 哈希
OP_HASH160:先 SHA-256 再 RIPEMD-160(用於生成地址)
OP_HASH256:兩次 SHA-256(用於交易 ID 等)
OP_HASH160 特別重要,因為它是比特幣地址生成的核心。公鑰經過 OP_HASH160 處理後,再加上版本前綴和校驗和,就成為我們熟悉的比特幣地址。
簽名驗證:
1
2
3
4
OP_CHECKSIG:驗證單個簽名
OP_CHECKSIGVERIFY:驗證簽名,失敗則中止(相當於 OP_CHECKSIG OP_VERIFY)
OP_CHECKMULTISIG:驗證多重簽名
OP_CHECKMULTISIGVERIFY:驗證多重簽名,失敗則中止
OP_CHECKSIG 是整個 Script 系統的核心。它從堆疊彈出一個簽名和一個公鑰,然後驗證這個簽名是否是用對應的私鑰對當前交易簽署的。這個操作確保了只有私鑰的持有者才能花費對應的比特幣。
3.5 流程控制
雖然 Script 沒有迴圈,但它有條件分支:
1
2
3
4
OP_IF:如果堆疊頂端非零,執行後面的程式碼
OP_NOTIF:如果堆疊頂端為零,執行後面的程式碼
OP_ELSE:否則執行這部分
OP_ENDIF:結束條件區塊
這讓我們可以創建「根據情況選擇不同花費路徑」的腳本。例如:
1
2
3
4
5
OP_IF
<Alice 的公鑰> OP_CHECKSIG
OP_ELSE
<Bob 的公鑰> OP_CHECKSIG
OP_ENDIF
這個腳本的意思是:如果提供 TRUE,就驗證 Alice 的簽名;如果提供 FALSE,就驗證 Bob 的簽名。花費者可以選擇用哪條路徑來解鎖資金。
OP_VERIFY 是另一個重要的流程控制操作碼。它檢查堆疊頂端是否為 TRUE:如果是,就移除它並繼續;如果不是,整個腳本立即失敗。這讓我們可以在腳本中設置多個「檢查點」。
四、腳本的組合與執行
4.1 交易中的兩部分腳本
一筆比特幣交易涉及兩部分腳本:
鎖定腳本(ScriptPubKey / 輸出腳本):
- 存在於交易的輸出中
- 定義「解鎖條件」
- 創建 UTXO 時就已確定
- 通常由接收者的地址決定
解鎖腳本(ScriptSig / 輸入腳本):
- 存在於交易的輸入中
- 提供「解鎖證明」
- 花費時才創建
- 通常包含簽名和公鑰
當有人想花費一個 UTXO 時,他需要創建一筆新交易,其中輸入部分包含解鎖腳本。網路節點會把解鎖腳本和鎖定腳本組合起來執行,驗證花費是否合法。
4.2 驗證過程的演化
在比特幣早期,驗證過程是直接把兩個腳本串接起來執行:
1
組合腳本 = ScriptSig + ScriptPubKey
但這種方式存在安全隱患。攻擊者可以構造特殊的 ScriptSig,在執行完後留下一個 TRUE 在堆疊上,無論 ScriptPubKey 是什麼都能通過驗證。
因此,現在的驗證過程改為分開執行:
- 先執行 ScriptSig,記錄堆疊狀態
- 用這個堆疊狀態作為初始狀態,執行 ScriptPubKey
- 如果最終堆疊頂端是 TRUE 且沒有其他問題,驗證成功
這種方式確保了 ScriptPubKey 的邏輯不會被 ScriptSig 繞過。
4.3 實例:P2PKH 的完整驗證
讓我們走過一個最常見的腳本類型 —— P2PKH(Pay to Public Key Hash)的完整驗證過程。
假設 Alice 要花費一筆之前收到的比特幣:
鎖定腳本(之前的輸出中):
1
OP_DUP OP_HASH160 <Alice的公鑰哈希> OP_EQUALVERIFY OP_CHECKSIG
解鎖腳本(Alice 創建的):
1
<Alice的簽名> <Alice的公鑰>
驗證過程:
階段一:執行解鎖腳本
步驟 1:推入 Alice 的簽名
1
堆疊:[sig]
步驟 2:推入 Alice 的公鑰
1
堆疊:[sig, pubkey]
階段二:執行鎖定腳本
步驟 3:OP_DUP - 複製公鑰
1
堆疊:[sig, pubkey, pubkey]
步驟 4:OP_HASH160 - 對頂端公鑰計算哈希
1
堆疊:[sig, pubkey, pubkey_hash]
步驟 5:推入預期的公鑰哈希
1
堆疊:[sig, pubkey, pubkey_hash, expected_hash]
步驟 6:OP_EQUALVERIFY - 比較兩個哈希
- 如果相等:移除這兩個值,繼續執行
- 如果不等:立即失敗
1
堆疊:[sig, pubkey]
步驟 7:OP_CHECKSIG - 驗證簽名
- 用 pubkey 驗證 sig 是否是對當前交易的有效簽名
- 如果有效,推入 TRUE
- 如果無效,推入 FALSE
1
堆疊:[TRUE]
最終結果:堆疊頂端是 TRUE,交易有效!
這個過程展示了 P2PKH 的設計邏輯:
- 先檢查提供的公鑰是否與預期的哈希匹配(確認是正確的人)
- 再檢查簽名是否有效(確認他確實授權了這筆交易)
兩層驗證缺一不可,這就是為什麼只有掌握私鑰的人才能花費對應的比特幣。
五、調試與實驗
5.1 使用 btcdeb 學習腳本
理論學習很重要,但實際操作更能加深理解。btcdeb 是一個專門用於調試 Bitcoin Script 的工具,可以讓你逐步執行腳本,觀察堆疊的變化。
安裝 btcdeb:
1
2
3
4
5
6
7
# 在 macOS 或 Linux 上
git clone https://github.com/bitcoin-core/btcdeb.git
cd btcdeb
./autogen.sh
./configure
make
sudo make install
基本使用:
1
2
# 執行一個簡單的腳本
btcdeb '[2 3 OP_ADD 5 OP_EQUAL]'
程式會進入互動模式,你可以使用以下命令:
step或s:執行下一步stack:顯示當前堆疊continue或c:執行到結束help:查看所有命令
觀察執行過程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ btcdeb '[2 3 OP_ADD 5 OP_EQUAL]'
btcdeb 0.4.22 -- type `btcdeb -h` for start up options
valid script
5 op script loaded. type `help` for usage information
script | stack
-----------------------------------------+--------
2 |
3 |
OP_ADD |
5 |
OP_EQUAL |
#0001 2
btcdeb> step
<> PUSH stack 02
script | stack
-----------------------------------------+--------
3 | 02
OP_ADD |
5 |
OP_EQUAL |
每一步都清楚顯示堆疊的變化,這對理解腳本執行流程非常有幫助。
5.2 練習題
練習 1:基本運算
預測以下腳本的執行結果:
1
4 2 OP_SUB 1 OP_ADD
提示:先計算 4-2,再加 1。
練習 2:堆疊操作
以下腳本執行後,堆疊內容是什麼?
1
1 2 3 OP_DROP OP_SWAP
提示:OP_DROP 移除頂端元素,OP_SWAP 交換頂端兩個元素。
練習 3:理解條件分支
這個腳本定義了什麼樣的花費條件?
1
2
3
4
5
OP_IF
<pubkey_A> OP_CHECKSIG
OP_ELSE
<pubkey_B> OP_CHECKSIG
OP_ENDIF
練習 4:分析多重簽名
解釋這個腳本的含義:
1
OP_2 <pubkey_1> <pubkey_2> <pubkey_3> OP_3 OP_CHECKMULTISIG
提示:這是一個 M-of-N 多重簽名腳本。
六、總結與展望
本篇重點回顧
-
Script 的本質:Bitcoin Script 是一種定義花費條件的程式語言。它故意設計成非圖靈完備,以確保安全性和可預測性。
-
堆疊機模型:Script 使用後進先出的堆疊來進行運算。數據被推入堆疊,操作碼從堆疊取出數據、處理、再把結果放回。
-
操作碼類別:包括常量推送、堆疊操作、算術運算、密碼學操作和流程控制。其中簽名驗證是核心功能。
-
腳本驗證:交易驗證涉及解鎖腳本和鎖定腳本的組合執行。兩者分開執行以防止安全漏洞。
下一篇預告
在下一篇文章中,我們將深入探討各種標準腳本類型:
- P2PK:最早的支付方式,直接使用公鑰
- P2PKH:最常見的傳統地址格式(1 開頭)
- P2SH:支援複雜腳本的革命性改進(3 開頭)
- P2WPKH 和 P2WSH:SegWit 帶來的新格式(bc1q 開頭)
我們會詳細解釋每種類型的設計動機、優缺點,以及它們在實際應用中的使用場景。
參考資源
官方文檔
- Bitcoin Wiki: Script - 最權威的 Script 參考
- Bitcoin Developer Guide - 交易相關的詳細說明
學習工具
- btcdeb - Script 調試工具
- Learn Me a Bitcoin - 優秀的視覺化教學
延伸閱讀
- Bitcoin: A Peer-to-Peer Electronic Cash System - 中本聰的原始白皮書
- Mastering Bitcoin, 2nd Edition - Andreas Antonopoulos 的經典著作
Cypherpunks Taiwan
密碼學使自由和隱私再次偉大。Cryptography makes freedom and privacy great again.