比特幣有時被稱為
可程式化貨幣。由於其數位性質,它允許用戶在設定如何使用資金的條件時具有很大程度的靈活性。
討論比特幣時,我們會談及
錢包和
代幣。但是,我們也可以把錢包想象成鑰匙,把代幣想象成支票,把
區塊鏈想象成一排排鎖著的保險箱。每個保險箱都有一個細細的插槽,這樣任何人都可以存入支票或查看保險箱裡有多少價值。但是,只有鑰匙持有人才能進入內部。
當鑰匙持有人想要付款給其他人時,他們會打開他們的箱子。他們將開一個引用舊支票的新支票(舊支票隨後將被銷毀),並將其鎖在收件人可以打開的箱子中。新的收件人要重複此過程,才能花這筆錢。
在本文中,我們將詳細研究腳本,這是一種由比特幣網路上的
節點進行解釋的可程式化語言。腳本是用於治理保險箱的鎖定/解鎖機製。
採用上述類比,可以說每筆交易都有兩個部分 – 鑰匙(用於解鎖箱子)和鎖。您使用鑰匙打開包含要發送的支票的箱子,然後您再用另一個鎖新增一個新的箱子。要花費新箱子裡的錢,您需要另一個鑰匙。
非常簡單。您還可以獲得系統中鎖類型的一些變化。可能有些保險箱需要您提供
多個鑰匙,可能其他保險箱需要您證明您知道密鑰。可以設定多個條件。
我們的鑰匙稱為 scriptSig。鎖稱為 scriptPubKey。如果我們更詳細地觀察這些組件,會發現他們實際上都是由資料位元和代碼塊所組成。這些結合在一起,就建立了一個小程式。
當您進行交易時,您正在將此組合推播至網路。接收的每個節點都將檢查程式,程式將告知交易是否有效。如果無效,就會被丟棄,您將無法使用鎖定的資金。
您所持有的支票(代幣)稱為
未花費的交易輸出 (UTXO)。只要能提供這把鎖的鑰匙,任何人都可以使用這些資金。具體來說,鑰匙就是 scriptSig,鎖就是 scriptPubKey。
如果 UTXO 在您的錢包中,他們可能會有一個條件,即
只有能夠證明該公鑰所有權的人才能解鎖這些資金。要解鎖,您需要提供一個包含
數位簽章的 scriptSig,使用映射到 scriptPubKey 中指定公鑰的
私鑰。這一切很快就會變得清晰起來。
腳本即是所謂的基於堆疊的語言。這就意味著,當我們閱讀一組指令時,我們將其放置於被認為是垂直的欄中。例如,清單 A、B、C 將產生一個堆疊,A 位於底部,C 位於頂部。當指令告訴我們要做某事時,我們從堆疊的頂部開始操作一個或多個元素。
元素 A、B 和 C 被新增並從堆疊中「彈出」。
我們可以區分資料(如
簽章、
雜湊和公鑰)和指令(或
作業碼)。指令移除資料並對其進行處理。以下是一個非常簡單的腳本示例︰
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
紅色部分為資料,藍色部分為作業碼。從左至右讀,因此我們首先把字串 <xyz> 放在堆疊上。接下來是作業碼 <md5 hasher>。此作業碼在比特幣中不存在,但我們假設它移除了堆疊頂部的元素 (<xyz>) 並使用 MD5 演算法進行雜湊。然後,再將輸出新增至堆疊上。此處的輸出正好就是 d16fb36f0911f878998c136191af705e。
多麼巧合!我們要新增的下一個元素正是 <d16fb36f0911f878998c136191af705e>,因此現在我們的堆疊有兩個相同的元素。最後,<check if equal> 從頂部彈出兩個元素,檢查是否相等。如果相等,則在堆疊中新增 <1>。如果不相等,則新增 <0>。
我們已經到了指令清單的末尾。我們的腳本可能會以兩種方式失敗 – 如果剩餘元素為零,或如果當某些條件不滿足時,其中一個運算子導致其失敗。在本例中,我們沒有任何這樣的運算子,最終得到一個非零元素 (<1>),因此,我們的腳本有效。這些規則也適用於真正的比特幣交易。
該示例只是一個虛構的程式。現在,讓我們來看看真實的示例。
支付到公鑰 (P2PK) 非常簡單,涉及將資金鎖定到一個特定公鑰。如果您想要以該種方式接收資金,您需要向發送者提供您的公鑰,而不是比特幣地址。
2009 年
中本聰 和哈爾‧芬尼之間的
第一次交易便是 P2PK 交易。該種結構在比特幣早期被大量使用,但如今,已普遍替代為支付到公鑰雜湊 (P2PKH)。
P2PK 交易鎖定腳本的格式為 <public key> OP_CHECKSIG。非常簡單。您可能已經猜到,OP_CHECKSIG 會依據所提供的公鑰檢查簽章。因此,我們的 scriptSig 將是一個簡單的 <signature>。記住,scriptSig 是鎖的鑰匙。
再沒有比這更簡單的了。簽章被新增至堆疊,然後是公鑰。OP_CHECKSIG 將二者彈出,並依據公鑰驗證簽章。如果相符,則在堆疊中新增 <1>。否則,將新增 <0>。
實際上,P2PK 已不再使用,我們將在下一節中詳細說明原因。
支付到公鑰雜湊 (P2PKH) 是現在最常見的交易類型。除非您想下載過時的軟體,否則您的錢包可能會在預設情況下使用此種交易。
P2PKH 中的 scriptPubKey 如下︰
OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
在介紹 scriptSig 之前,讓我們分析一下新的作業碼將做什麼︰
OP_DUP
OP_DUP 彈出第一個元素,並將其複製。然後,將這兩個元素都新增至堆疊。通常,這樣做是為了在不影響原本的情況下對副本進行操作。
OP_HASH160
彈出第一個元素,並進行兩次雜湊。第一輪將使用 SHA-256 演算法進行雜湊。之後使用 RIPEMD-160 演算法對 SHA-256 輸出進行雜湊。然後,再將輸出新增至堆疊上。
OP_EQUALVERIFY
OP_EQUALVERIFY 組合了另外兩個運算子 – OP_EQUAL 和 OP_VERIFY。OP_EQUAL 彈出兩個元素並驗證是否相同。如果相同,則在堆疊中新增 1。如果不同,則新增 0。OP_VERIFY 彈出頂端元素並驗證是否為真(即非零)。如果不為真,則交易失敗。綜合起來,如果頂端兩個元素不相符,則 OP_EQUALVERIFY 會導致交易失敗。
這一次,scriptSig 如下所示︰
<signature> <public key>
需要提供簽章和對應的公鑰才能解鎖 P2PKH 輸出。
詳細演示如上述 GIF 所示。它與 P2PK 腳本並沒有太大區別。我們只是新增了一個額外的步驟來檢查公鑰與腳本中的雜湊是否相符。
然而,有一點需要注意。在 P2PKH 鎖定腳本中,公鑰是不可見的 – 僅可看到其雜湊。如果我們前往
區塊鏈瀏覽器並查看尚未花費的 P2PKH 輸出,我們將無法確定公鑰。只有在收款人決定轉帳資金時才會公開。
這有幾個好處。首先,公鑰雜湊比完整的公鑰更易於傳遞。正是出於這一原因,中本聰於 2009 將其推出。公鑰雜湊即為我們如今所知的比特幣地址。
第二個好處是,公鑰雜湊可以為
量子計算提供額外的安全層。因為我們要在花費資金時才知道公鑰,因此其他人就更難計算私鑰。他們必須逆轉兩輪雜湊(RIPEMD-160 和 SHA-256)才能得到。
支付到腳本雜湊 (P2SH) 是
比特幣一個非常有趣的發展。允許發送者將資金鎖定至腳本的雜湊 – 而無需知道腳本實際上做了什麼。以下列 SHA-256 雜湊為例︰
e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1
您不需要知道雜湊的輸入來鎖定資金。然而,花費者需要提供雜湊所使用的腳本,並需要滿足該腳本的條件。
上述雜湊是從以下腳本建立︰
<multiply by 2> <4> <check if equal>
如果您想花費綁定到該 scriptPubKey 的代幣,您不僅要提供這些命令。您還需要一個令完成的腳本計算值為真的 scriptSig。在此例中,即為您 <multiply by 2> 以得到 <4> 結果的元素。當然,這意味著我們的 scriptSig 只是 <2>。
在現實生活中,P2SH 輸出的 scriptPubKey 為︰
OP_HASH160 <redeemScript hash> OP_EQUAL
此處沒有新的運算子。但是,我們確實有一個新元素 <redeemScript hash>。顧名思義,這就是我們需要提供用於贖回資金的腳本雜湊(稱為 redeemScript)。scriptSig 將依據 redeemScript 的內容進行變化。但是,一般來說,您會發現它是簽章和所附加公鑰的組合,然後是(強制性的)redeemScript︰
<signature> <public key> <redeemScript>
我們的計算與目前所看到的堆疊執行略有不同。它分為兩部分。第一部分僅檢查您已提供的正確雜湊。
您將注意到,我們不會對 redeemScript 之前的元素採取任何處理。此時還不會用到他們。我們已經到了這個小程式的末尾,頂端元素是非零的。也就是說,它是有效的。
不過,我們還沒有完成。網路
節點將此結構識別為 P2SH,因此他們實際上已經讓 scriptSig 的元素在另一個堆疊中等待。在那裡,將用到簽章和公鑰。
目前為止,我們一直將 redeemScript 視為一個元素。但是現在,它將被解釋為指令,可以是任何東西。讓我們以 P2PKH 鎖定腳本為例,我們必須向其提供與 <redeemScript> 內的 <public key hash> 相符的<signature> 和 <public key>。
一旦您的 redeemScript 被擴展,您便可看到我們的情況看起來與常規 P2PKH 交易完全一樣。自此,您只需要像正常交易一樣運行就可。
我們在此演示了所謂的 P2SH(P2PKH) 腳本,但您在自然環境下不太可能找到這樣的腳本。沒有什麼可以阻止您製作一個,但卻不會為您帶來任何好處,而且最終會佔用更多的區塊空間(因此也成本更高)。
P2SH 通常用於
多重簽名或
SegWit 相容的交易。多重簽名交易的規模可以非常大,因為他們需要多個密鑰。在實現支付到腳本雜湊之前,發送者必須列出其鎖定腳本中的所有可能公鑰。
但是使用 P2SH,支付條件無論多麼複雜都無關緊要。redeemScript 的雜湊始終為固定大小。因此,成本會轉移給想要解鎖鎖定腳本的用戶。
SegWit 相容性是 P2SH 派上用場的另一個示例(我們將在下一節中詳細介紹交易結構有何不同)。SegWit 是一個導致區塊/交易格式發生變化的軟分叉。因為這是一項選擇進行的升級,所以並非所有錢包軟體都能識別此變更。
用戶端是否將 SegWit 腳本雜湊打包進 P2SH 並不重要。因為對於此種類型的所有交易,他們不需要知道解鎖的 redeemScript 將是什麼。
要理解 SegWit 中的交易格式,您只需要知道我們不再只有一個 scriptSig 和一個 scriptPubKey。現在,我們還有一個新欄位,稱作 witness。我們之前儲存於 scriptSig 的資料被移至 witness,因此,scriptSig 為空。
如果您遇到以「bc1」開頭的地址,那就是我們所說的 SegWit 原生地址(而不是僅與 SegWit 相容,後者以「3」開頭,因為該地址是 P2SH 地址)。
支付到見證公鑰雜湊 (P2WPKH)
支付到見證公鑰雜湊 (P2WPKH) 是 P2PKH 的 SegWit 版本。我們的見證如下︰
<signature> <public key>
您將會注意到,這與 P2PKH 的 scriptSig 相同。此處,scriptSig 為空。同時,scriptPubKey 類似如下︰
<OP_0> <public key hash>
看起來很奇怪,是嗎?讓我們比較簽章、公鑰及其雜湊的作業碼在何處?
此處我們並未顯示其他運算子,因為接收交易的節點依據 <public key hash> 的長度知道該如何處理。他們將計算長度,並理解必須以與傳統 P2PKH 交易相同的方式運行。
未升級的節點不知道如何以此種方式解釋交易,但這並不重要。在舊規則下,沒有見證,因此他們讀取一個空的 scriptSig 和一些資料。他們將對此進行評估並將其標記為有效 – 就他們而言,每個人都可以花費輸出。這就是為什麼 SegWit 被認為是向後相容的
軟分叉。
支付到見證腳本雜湊 (P2WSH)
支付到見證腳本雜湊 (P2WSH) 是一種新的 P2SH。如果您已進展至這一步,或許就能弄清楚它的運作方式了,但無論如何,我們都會過一遍。我們所說的見證就是通常會放入 scriptSig 的內容。例如,在打包 P2PKH 交易的 P2WSH 中,可能看起來是這樣子的:
<signature 1> <public key>
下面是我們的 scriptPubKey:
<OP_0> <script hash>
這裡採用同樣的規則。SegWit 節點會讀取腳本雜湊的長度,並確定其為 P2WSH 輸出,評估方式類似於 P2SH。與此同時,舊節點只不過將其視為任何人可以花費的輸出。
在這篇文章中,我們已了解一些有關比特幣構建區塊的資訊。我們來做個快速總結:
腳本類型 | 說明 |
---|
支付到公鑰 (P2PK) | 將資金鎖定至特定的公鑰 |
支付到公鑰雜湊 (P2PKH) | 將資金鎖定至特定的公鑰雜湊(如地址) |
支付到腳本雜湊 (P2SH) | 將資金鎖定至接收方可提供之腳本雜湊 |
支付到見證公鑰雜湊 (P2WPKH) | P2PK 的 SegWit 版本 |
支付到見證腳本雜湊 (P2WSH) | P2SH 的 SegWit 版本 |
深入研究比特幣後,您就會開始理解為何它具有如此大的潛力。交易可由多個不同的組成部分構成。透過操縱這些構建區塊,用戶可極其靈活地設定資金使用方式及時間的條件。