Bitcoin Script 實戰教學(二):標準腳本類型詳解
這是「Bitcoin Script 實戰教學」系列的第二篇。本篇將詳細解析各種標準腳本類型,了解它們的結構、用途和演進歷史。
系列文章導航:
- 第一篇:基礎概念與堆疊操作
- 第二篇:標準腳本類型詳解(本篇)
- 第三篇:SegWit 與 Taproot 腳本
- 第四篇:多重簽名與門檻簽名
- 第五篇:時間鎖機制
- 第六篇:進階應用與智能合約
一、腳本類型的歷史演進
1.1 為什麼會有不同的腳本類型?
比特幣的腳本系統並非一成不變。從 2009 年誕生至今,腳本類型經歷了多次重大演進,每一次演進都是為了解決前一代的問題或增加新的功能。
理解這個演進歷程很重要,因為它解釋了為什麼比特幣有這麼多種地址格式,以及為什麼某些看似複雜的設計是必要的。
1.2 演進時間線
2009年:P2PK(Pay to Public Key)
中本聰在最初的比特幣實現中使用的就是 P2PK。這是最直接的方式——公鑰直接寫在腳本中,花費時提供簽名即可。早期的 coinbase 交易(礦工獎勵)都使用這種格式。
2010年:P2PKH(Pay to Public Key Hash)
人們很快意識到直接暴露公鑰有安全隱患,於是引入了公鑰哈希。這種方式把公鑰隱藏起來,只有在花費時才需要揭露。同時,這也催生了「地址」的概念——以 1 開頭的熟悉格式就是從這時開始的。
2012年:P2SH(Pay to Script Hash)
BIP 16 提出了一個革命性的想法:把任意複雜的腳本用一個哈希值代替。這讓多重簽名等複雜功能變得實用,因為發送者不需要知道腳本的具體內容,只需要發送到一個以 3 開頭的地址。
2017年:SegWit(P2WPKH / P2WSH)
隔離見證是比特幣歷史上最重要的升級之一。它不僅解決了交易延展性問題,還提高了區塊容量效率。bc1q 開頭的地址就是 SegWit 地址。
2021年:Taproot(P2TR)
Taproot 引入了 Schnorr 簽名和 MAST(Merkelized Alternative Script Trees),讓複雜的智能合約在正常情況下看起來和普通交易一樣。bc1p 開頭的地址代表 Taproot。
1.3 地址格式一覽
不同腳本類型對應不同的地址格式:
| 腳本類型 | 主網地址前綴 | 測試網前綴 | 引入年份 |
|---|---|---|---|
| P2PKH | 1 | m 或 n | 2010 |
| P2SH | 3 | 2 | 2012 |
| P2WPKH | bc1q(42字元) | tb1q | 2017 |
| P2WSH | bc1q(62字元) | tb1q | 2017 |
| P2TR | bc1p | tb1p | 2021 |
注意 P2WPKH 和 P2WSH 都以 bc1q 開頭,但長度不同——P2WPKH 是 42 字元,P2WSH 是 62 字元。這是因為它們使用不同長度的哈希(20 字節 vs 32 字節)。
二、P2PK:最原始的支付方式
2.1 歷史背景
P2PK(Pay to Public Key)是比特幣最原始的支付腳本類型。中本聰在設計比特幣時,最自然的想法就是:用公鑰標識接收者,用對應私鑰的簽名證明所有權。
這種方式在密碼學上是正確的,但在實際使用中很快暴露出問題。
2.2 腳本結構
P2PK 的結構非常簡單:
鎖定腳本(ScriptPubKey):
1
<公鑰> OP_CHECKSIG
解鎖腳本(ScriptSig):
1
<簽名>
執行過程也很直觀:
- 把簽名推入堆疊
- 把公鑰推入堆疊
OP_CHECKSIG驗證簽名是否有效
如果簽名確實是用這個公鑰對應的私鑰簽署的,驗證就通過。
2.3 P2PK 的問題
問題一:公鑰提前暴露
在 P2PK 中,公鑰從 UTXO 創建的那一刻起就是公開的。這帶來兩個安全隱患:
首先是量子計算威脅。雖然目前的量子計算機還無法破解橢圓曲線密碼學,但理論上,擁有足夠強大量子計算機的攻擊者可以從公鑰推導出私鑰。如果你的公鑰已經暴露,你的比特幣就處於風險中。
其次是隱私問題。公鑰是長期身份標識,如果某人知道你的公鑰,就能追蹤你的所有交易。
問題二:空間效率低
未壓縮的公鑰有 65 字節,即使是壓縮公鑰也有 33 字節。這些數據存儲在區塊鏈上,佔用寶貴的空間。相比之下,公鑰的哈希只有 20 字節。
問題三:用戶體驗差
公鑰是一長串十六進制字符,不適合作為「地址」使用。人們很難記住、比對或手動輸入公鑰。
2.4 現代使用情況
今天,P2PK 幾乎不再被使用。但你仍然可以在早期區塊中看到它,特別是創世區塊和早期的 coinbase 交易。這些古老的比特幣如果要移動,其公鑰就會暴露——這也是為什麼中本聰的早期挖礦收益(如果他還有私鑰的話)被認為是「危險資產」。
三、P2PKH:地址的誕生
3.1 從公鑰到地址
P2PKH(Pay to Public Key Hash)解決了 P2PK 的主要問題。它的核心創新是:不直接使用公鑰,而是使用公鑰的哈希值。
這個哈希值稱為「公鑰哈希」(Public Key Hash),計算方式是:
1
公鑰哈希 = RIPEMD160(SHA256(公鑰))
這就是我們常說的 HASH160 操作。結果是一個 20 字節(160 位元)的哈希值。
3.2 地址的誕生
有了公鑰哈希,我們需要一種用戶友好的方式來表示它。這就是「地址」的由來:
1
地址 = Base58Check(版本前綴 + 公鑰哈希)
Base58Check 是一種編碼方式,它:
- 使用 58 個字符(去掉了 0、O、I、l 等易混淆字符)
- 包含校驗和(防止輸入錯誤)
- 主網版本前綴是 0x00,產生以 1 開頭的地址
- 測試網版本前綴是 0x6F,產生以 m 或 n 開頭的地址
這就是為什麼傳統比特幣地址都以「1」開頭。
3.3 腳本結構詳解
鎖定腳本(ScriptPubKey):
1
OP_DUP OP_HASH160 <公鑰哈希> OP_EQUALVERIFY OP_CHECKSIG
這個腳本的設計很巧妙,讓我們逐步解析:
OP_DUP:複製堆疊頂端的值(花費者提供的公鑰)OP_HASH160:計算公鑰的哈希<公鑰哈希>:推入預期的哈希值OP_EQUALVERIFY:比較兩個哈希是否相等OP_CHECKSIG:驗證簽名
解鎖腳本(ScriptSig):
1
<簽名> <公鑰>
3.4 執行流程深入解析
假設 Alice 想要花費一筆發送給她的比特幣:
初始狀態:堆疊為空
第一步:處理解鎖腳本
推入 Alice 的簽名:
1
堆疊:[sig]
推入 Alice 的公鑰:
1
堆疊:[sig, pubkey]
第二步:執行鎖定腳本
OP_DUP - 複製公鑰:
1
堆疊:[sig, pubkey, pubkey]
OP_HASH160 - 計算頂端公鑰的哈希:
1
堆疊:[sig, pubkey, pubkey_hash]
推入預期的公鑰哈希:
1
堆疊:[sig, pubkey, pubkey_hash, expected_hash]
OP_EQUALVERIFY - 比較兩個哈希:
- 如果相等:彈出這兩個值,繼續
- 如果不等:腳本立即失敗
1
堆疊:[sig, pubkey]
OP_CHECKSIG - 驗證簽名:
1
堆疊:[TRUE] (如果簽名有效)
3.5 P2PKH 的優勢
更好的安全性
公鑰只在花費時才暴露。在花費之前,即使量子計算機出現,也無法從地址(公鑰哈希)推導出公鑰,更無法推導出私鑰。
當然,一旦花費過,公鑰就暴露了。這就是為什麼安全最佳實踐建議「不要重複使用地址」——每次收款使用新地址,可以保持未花費資金的安全性。
更短的地址
公鑰哈希只有 20 字節,比 33 字節的壓縮公鑰更短。加上 Base58Check 編碼後,地址長度約 34 個字符,相對容易處理。
錯誤檢測
Base58Check 編碼包含 4 字節的校驗和。如果用戶輸入地址時打錯了字符,校驗和幾乎肯定會對不上,錢包軟件會提醒用戶。這大大降低了因輸入錯誤而損失資金的風險。
3.6 P2PKH 的局限
儘管 P2PKH 是一大進步,但它仍有局限:
只支援單一簽名
P2PKH 只能表示「需要某個特定私鑰的簽名」。如果你想實現多重簽名(如「3 個人中的 2 個必須簽名」),就需要更複雜的腳本,而這些腳本會讓地址變得又長又複雜。
發送者的負擔
如果接收者想使用複雜的花費條件,發送者需要知道完整的腳本。這不僅麻煩,還暴露了接收者的安全策略。
這些問題催生了 P2SH 的出現。
四、P2SH:腳本哈希革命
4.1 核心創新
P2SH(Pay to Script Hash)是 2012 年通過 BIP 16 引入的。它的核心思想非常優雅:
不是把腳本放在輸出中,而是放腳本的哈希。
這意味著,無論你的花費條件多複雜,發送者只需要發送到一個短短的地址。複雜的腳本由接收者保管,只在花費時才需要揭露。
4.2 運作原理
假設 Alice 想設置一個 2-of-3 多重簽名地址。傳統上,她需要把整個多簽腳本告訴所有要給她付款的人。使用 P2SH,流程變成:
- Alice 創建她的多簽腳本(稱為「贖回腳本」,Redeem Script)
- Alice 計算這個腳本的哈希
- Alice 把這個哈希轉換成地址(以 3 開頭)
- 付款人只需發送到這個地址,不需要知道背後的腳本
當 Alice 要花費這筆錢時,她需要提供:
- 原始的贖回腳本
- 滿足贖回腳本的數據(如簽名)
網路節點會驗證:
- 提供的腳本哈希確實等於地址中的哈希
- 提供的數據確實滿足腳本的條件
4.3 腳本結構
鎖定腳本(ScriptPubKey):
1
OP_HASH160 <腳本哈希> OP_EQUAL
這個結構非常簡潔!只有 23 字節,無論背後的贖回腳本多複雜。
解鎖腳本(ScriptSig):
1
<數據...> <贖回腳本>
解鎖腳本包含兩部分:
- 滿足贖回腳本所需的數據(如簽名)
- 贖回腳本本身
4.4 驗證流程
P2SH 的驗證分兩階段:
第一階段:驗證腳本哈希
- 取出解鎖腳本的最後一個元素(贖回腳本)
- 計算它的 HASH160
- 與鎖定腳本中的哈希比較
- 如果相等,進入第二階段
第二階段:執行贖回腳本
- 把解鎖腳本中的其他數據作為輸入
- 執行贖回腳本
- 如果執行成功(堆疊頂部為 TRUE),整個驗證通過
這種兩階段設計是 P2SH 的精髓。它保證了:
- 只有知道原始腳本的人才能花費資金
- 發送者不需要了解腳本細節
4.5 實際例子:2-of-3 多簽
讓我們看一個具體的例子。假設三個人(Alice、Bob、Charlie)想創建一個需要三人中任意兩人簽名的共同賬戶。
贖回腳本:
1
OP_2 <Alice公鑰> <Bob公鑰> <Charlie公鑰> OP_3 OP_CHECKMULTISIG
計算地址:
- 對贖回腳本進行 HASH160
- 加上 P2SH 版本前綴(0x05)
- 進行 Base58Check 編碼
- 得到以「3」開頭的地址
花費時(假設 Alice 和 Bob 簽名):
1
解鎖腳本:OP_0 <Alice簽名> <Bob簽名> <贖回腳本>
注意開頭的 OP_0,這是 OP_CHECKMULTISIG 的一個歷史 bug(多消耗一個堆疊元素),必須提供一個虛擬值。
4.6 P2SH 的限制
贖回腳本大小限制
為了防止濫用,贖回腳本有 520 字節的大小限制。這限制了可以使用的腳本複雜度。例如,標準的多重簽名最多支持 15-of-15,更多的參與者就需要使用更高級的技術。
腳本必須揭露
當你花費 P2SH 輸出時,完整的贖回腳本會被記錄在區塊鏈上。這意味著:
- 你的安全策略最終會公開
- 如果贖回腳本很大,交易費用會更高
簽名放在錯誤的位置
在 P2SH 中,簽名是解鎖腳本的一部分,包含在交易的「可修改」區域。這導致了「交易延展性」問題——第三方可以在不改變交易有效性的情況下修改交易 ID。這個問題在 SegWit 中得到解決。
五、SegWit 簡介:P2WPKH 和 P2WSH
5.1 為什麼需要 SegWit?
2017 年激活的隔離見證(Segregated Witness,SegWit)是比特幣最重要的升級之一。它解決了幾個關鍵問題:
交易延展性
在 SegWit 之前,交易 ID(txid)是基於整個交易計算的,包括簽名。由於簽名有多種有效的編碼方式,第三方可以修改簽名的編碼而不影響其有效性,從而改變 txid。這對閃電網路等依賴 txid 的協議是致命的。
SegWit 把簽名(見證數據)移到交易結構之外,交易 ID 不再包含簽名,從根本上解決了延展性問題。
區塊容量
SegWit 引入了「區塊重量」的概念,見證數據的重量只有普通數據的 1/4。這有效地增加了區塊容量,讓更多交易可以被打包。
腳本版本控制
SegWit 引入了「見證版本」的概念,讓未來的升級更容易實現。Taproot 就是作為見證版本 1 實現的。
5.2 P2WPKH:原生 SegWit 單簽
P2WPKH(Pay to Witness Public Key Hash)是 P2PKH 的 SegWit 版本。
鎖定腳本(ScriptPubKey):
1
OP_0 <20字節公鑰哈希>
是的,就這麼簡單!OP_0 表示見證版本 0,後面跟著 20 字節的公鑰哈希。
解鎖腳本(ScriptSig):
1
(空)
在 SegWit 中,ScriptSig 是空的。所有的解鎖數據都放在新的「見證」(Witness)區域:
見證數據(Witness):
1
<簽名> <公鑰>
這種設計實現了真正的「隔離見證」——簽名數據被隔離到單獨的區域,不影響交易 ID 的計算。
5.3 P2WSH:原生 SegWit 腳本哈希
P2WSH(Pay to Witness Script Hash)是 P2SH 的 SegWit 版本,用於複雜腳本。
鎖定腳本(ScriptPubKey):
1
OP_0 <32字節腳本哈希>
注意哈希長度是 32 字節(使用 SHA256),比 P2SH 的 20 字節更長。這提供了更強的安全性,防止生日攻擊。
見證數據(Witness):
1
<數據...> <見證腳本>
5.4 地址格式:Bech32
SegWit 引入了新的地址格式 Bech32(BIP 173),以 bc1 開頭:
- bc1q:見證版本 0(P2WPKH 和 P2WSH)
- bc1p:見證版本 1(Taproot,使用 Bech32m)
Bech32 相比 Base58Check 有幾個優勢:
- 全部小寫,減少混淆
- 更好的錯誤檢測能力
- 更適合二維碼(字符集更緊湊)
5.5 向後兼容:P2SH 包裝的 SegWit
為了讓舊錢包也能發送到 SegWit 地址,比特幣支持把 SegWit 腳本「包裝」在 P2SH 裡:
P2SH-P2WPKH:以 3 開頭,內部是 SegWit P2SH-P2WSH:以 3 開頭,內部是 SegWit 腳本哈希
這種包裝方式讓不支援 SegWit 的錢包也能發送資金到 SegWit 地址。不過,現在大多數錢包都支援原生 SegWit 地址,這種包裝方式正在逐漸被淘汰。
六、如何選擇腳本類型?
6.1 現代最佳實踐
對於 2024 年的用戶和開發者,以下是建議的選擇順序:
首選:P2TR(Taproot)
如果你的錢包和對方的錢包都支持,優先使用 Taproot 地址(bc1p…)。它提供最好的隱私性和最低的費用。
次選:P2WPKH(原生 SegWit)
如果不支持 Taproot,使用原生 SegWit 地址(bc1q…)。它是目前最廣泛支持的高效格式。
向後兼容:P2SH-P2WPKH
如果對方錢包很舊,無法發送到 bc1 地址,使用 P2SH 包裝的 SegWit(3 開頭)。
盡量避免:P2PKH
除非有特殊原因,不建議使用舊的 P2PKH 地址(1 開頭)。它們費用更高,隱私性更差。
6.2 費用比較
不同腳本類型的交易費用差異顯著(以典型的 1 輸入 2 輸出交易為例):
| 類型 | 大約大小 | 相對費用 |
|---|---|---|
| P2PKH | 226 vbytes | 100% |
| P2SH-P2WPKH | 167 vbytes | 74% |
| P2WPKH | 141 vbytes | 62% |
| P2TR | 111 vbytes | 49% |
使用 Taproot 可以節省約一半的交易費用!
七、實作練習
練習 1:識別地址類型
看以下地址,判斷它們的類型:
1
2
3
4
1. 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
2. 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
3. bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
4. bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr
練習 2:理解腳本執行
給定以下 P2PKH 腳本:
鎖定腳本:OP_DUP OP_HASH160 <0x89abcdef...> OP_EQUALVERIFY OP_CHECKSIG
問題:
- 解鎖腳本需要包含什麼?
- 如果提供了錯誤的公鑰(哈希不匹配),腳本會在哪一步失敗?
- 如果公鑰正確但簽名無效,腳本會在哪一步失敗?
練習 3:計算地址
給定公鑰(十六進制):
1
02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737
計算它的 P2PKH 地址。(提示:需要計算 HASH160,然後進行 Base58Check 編碼)
八、總結
本篇重點
-
演進歷程:從 P2PK 到 P2PKH 到 P2SH 到 SegWit,每一步都解決了前一代的問題。
-
P2PKH 的創新:引入公鑰哈希和地址概念,提高了安全性和用戶體驗。
-
P2SH 的革命:讓複雜腳本變得實用,發送者不需要了解腳本細節。
-
SegWit 的突破:解決交易延展性,提高區塊效率,為未來升級鋪路。
-
現代選擇:優先使用 Taproot,其次是原生 SegWit,盡量避免舊格式。
下一篇預告
下一篇將深入探討 SegWit 和 Taproot 的技術細節:
- SegWit 的完整交易結構
- 見證數據的組織方式
- Schnorr 簽名的優勢
- Taproot 的 MAST 機制
- 密鑰路徑與腳本路徑
參考資源
BIP 文檔
延伸閱讀
Cypherpunks Taiwan
密碼學使自由和隱私再次偉大。Cryptography makes freedom and privacy great again.