精通比特幣(第六章)【交易】

風靈使發表於2018-10-09

6.1 簡介

比特幣交易是比特幣系統中最重要的部分。根據比特幣系統的設計原理,系統中任何其他的部分都是為了確保比特幣交易可以被生成、能在比特幣網路中得以傳播和通過驗證,並最終新增入全球比特幣交易總賬簿(比特幣區塊鏈)。比特幣交易的本質是資料結構,這些資料結構中含有比特幣交易參與者價值轉移的相關資訊。比特幣區塊鏈是一本全球複式記賬總賬簿,每個比特幣交易都是在比特幣區塊鏈上的一個公開記錄。

在這一章,我們將會剖析比特幣交易的多種形式、所包含的資訊、如何被建立、如何被驗證以及如何成為所有比特幣交易永久記錄的一部分。當我們在本章中使用術語“錢包”時,我們指的是構建交易的軟體,而不僅僅是金鑰的資料庫。

6.2交易細節

在[第二章比特幣概述]中,我們使用區塊瀏覽器檢視了Alice曾經在Bob的咖啡店(Alice與Bob’s Cafe的交易)支付咖啡的交易。

區塊瀏覽器應用程式顯示從Alice的“地址”到Bob的“地址”的交易。 這是一個非常簡化的交易中包含的內容。 實際上,正如我們將在本章中看到的,所顯示的大部分資訊都是由區塊瀏覽器構建的,實際上並不在交易中。

圖6-1Alice與Bob的咖啡交易

圖1. Alice與Bob’s Cafe的交易

6.2.1交易 - 幕後細節

在幕後,實際的交易看起來與典型的區塊瀏覽器提供的交易非常不同。 事實上,我們在各種比特幣應用程式使用者介面中看到的大多數高階結構實際上並不存在於比特幣系統中。

我們可以使用Bitcoin Core的命令列介面(getrawtransactiondecodeawtransaction)來檢索Alice的“原始”交易,對其進行解碼,並檢視它包含的內容。 結果如下:
Alice的交易被解碼後是這個樣子:

{
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid":"7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
      "vout": 0,
      "scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
      "sequence": 4294967295
    }
 ],
  "vout": [
    {
      "value": 0.01500000,
      "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
    },
    {
      "value": 0.08450000,
      "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",
    }
  ]
}

您可能會注意到這筆交易似乎少了些什麼東西,比如:Alice的地址在哪裡?Bob的地址在哪裡? Alice傳送的“0.1”個幣的輸入在哪裡? 在比特幣裡,沒有具體的貨幣,沒有傳送者,沒有接收者,沒有餘額,沒有帳戶,沒有地址。為了使用者的便利,以及使事情更容易理解,所有這些都構建在更高層次上。

你可能還會注意到很多奇怪和難以辨認的欄位以及十六進位制字串。 不必擔心,本章將詳細介紹這裡所示的各個欄位。

6.3交易的輸入輸出

比特幣交易中的基礎構建單元是交易輸出。 交易輸出是比特幣不可分割的基本組合,記錄在區塊上,並被整個網路識別為有效。 比特幣完整節點跟蹤所有可找到的和可使用的輸出,稱為 “未花費的交易輸出”(unspent transaction outputs),即UTXO。 所有UTXO的集合被稱為UTXO集,目前有數百萬個UTXO。 當新的UTXO被建立,UTXO集就會變大,當UTXO被消耗時,UTXO集會隨著縮小。每一個交易都代表UTXO集的變化(狀態轉換)。

當我們說使用者的錢包已經“收到”比特幣時,我們的意思是,錢包已經檢測到了可用的UTXO。通過錢包所控制的金鑰,我們可以把這些UTXO花出去。 因此,使用者的比特幣“餘額”是指使用者錢包中可用的UTXO總和,而他們可能分散在數百個交易和區塊中。 “一個使用者的比特幣餘額”,這個概念是比特幣錢包應用建立的派生之物。比特幣錢包通過掃描區塊鏈並聚集所有屬於該使用者的UTXO來計算該使用者的餘額 。大多數錢包維護一個資料庫或使用資料庫服務來儲存所有UTXO的快速參考集,這些UTXO由使用者所有的金鑰來控制花費行為。

一個UTXO可以是1“聰”(satoshi)的任意倍數(整數倍)。就像美元可以被分割成表示兩位小數的“分”一樣,比特幣可以被分割成八位小數的“聰”。儘管UTXO可以是任意值,但一旦被創造出來,即不可分割。這是UTXO值得被強調的一個重要特性:UTXO是面值為“聰”的離散(不連續)且不可分割的價值單元,一個UTXO只能在一次交易中作為一個整體被消耗。

如果一個 UTXO比一筆交易所需量大,它仍會被當作一個整體而消耗掉,但同時會在交易中生成零頭。例如,你有 一個價值20比特幣的 UTXO並且想支付1比特幣,那麼你的交易必須消耗掉整個20比特幣的UTXO,併產生兩個輸出:一個支付了1比特幣給接收人,另一個支付了19比特幣的找零到你的錢包。這樣的話,由於UTXO(或交易輸出)的不可分割特性,大部分比特幣交易都會產生找零。

想象一下,一位顧客要買1.5元的飲料。她掏出錢包並試圖從所有硬幣和鈔票中找出一種組合來湊齊她要支付的1.5 元。如果可能的話,她會選剛剛好的零錢(比如一張1元紙幣和5個一毛硬幣)或者是小面額的組合(比如3個五毛硬幣)。如果都不行的話,她會用一張大面額的鈔票,比如5元紙幣。如果她把5元給了商店老闆,她會得到3.5元的找零,並把找零放回她的錢包以供未來的交易使用。

類似的,一筆比特幣交易可以是任意金額,但必須從使用者可用的UTXO中建立出來。使用者不能再把UTXO進一步細分,就像不能把一元紙幣撕開而繼續當貨幣使用一樣。使用者的錢包應用通常會從使用者可用的UTXO中選取多個來拼湊出一個大於或等於一筆交易所需的比特幣量。

就像現實生活中一樣,比特幣應用可以使用一些策略來滿足付款需求:組合若干小額UTXO,並算出準確的找零;或者使用一個比交易額大的UTXO然後進行找零。所有這些複雜的、由可花費UTXO組成的集合,都是由使用者的錢包自動完成, 並不為使用者所見。只有當你以程式設計方式用UTXO來構建原始交易時,這些才與你有關。

一筆交易會消耗先前的已被記錄(存在)的UTXO,並建立新的UTXO以備未來的交易消耗。通過這種方式,一定數量的比特幣價值在不同所有者之間轉移,並在交易鏈中消耗和建立UTXO。一筆比特幣交易通過使用所有者的簽名來解鎖UTXO,並通過使用新的所有者的比特幣地址來鎖定並建立UTXO

從交易的輸出與輸入鏈角度來看,有一個例外,即存在一種被稱為“幣基交易”(Coinbase Transaction)的特殊交易,它是每個區塊中的第一筆交易,這種交易存在的原因是作為對挖礦的獎勵,創造出全新的可花費比特幣用來支付給“贏家”礦工。這也就是為什麼比特幣可以在挖礦過程中被創造出來,我們將在“挖礦”這一章進行詳述。

**小貼士:**輸入和輸出,哪一個是先產生的呢?先有雞還是先有蛋呢?嚴格來講,先產生輸出,因為可以創造新比特幣的 “幣基交易”沒有輸入,但它可以無中生有地產生輸出。

6.3.1 交易輸出

每一筆比特幣交易都會創造輸出,並被比特幣賬簿記錄下來。除特例之外(見“資料輸出操作符”(OP_RETURN)),幾乎所有的輸出都能創造一定數量的可用於支付的比特幣,也就是UTXO。這些UTXO被整個網路識別,所有者可在未來的交易中使用它們。

UTXO在UTXO集(UTXOset)中被每一個全節點比特幣客戶端追蹤。 新的交易從UTXO集中消耗(花費)一個或多個輸出。

交易輸出包含兩部分:

  • 一定量的比特幣,面值為“聰”(satoshis) ,是最小的比特幣單位;

  • 確定花費輸出所需條件的加密難題(cryptographic puzzle)

這個加密難題也被稱為鎖定指令碼(locking script), 見證指令碼(witness script), 或指令碼公鑰 (scriptPubKey)。

有關交易指令碼語言會在後面121頁的“交易指令碼和指令碼語言”一節中詳細討論。

現在,我們來看看 Alice 的交易(之前的章節“交易 - 幕後”所示),看看我們是否可以找到並識別輸出。 在 JSON 編碼中,輸出位於名為 vout 的陣列(列表)中:

"vout": [
  {
    "value": 0.01500000,
    "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY
OP_CHECKSIG"
  },
  {
    "value": 0.08450000,
    "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",
  }
]

如您所見,交易包含兩個輸出。 每個輸出都由一個值和一個加密難題來定義。 在 Bitcoin Core 顯示的編碼中,該值顯示以 bitcoin 為單位,但在交易本身中,它被記錄為以 satoshis 為單位的整數。 每個輸出的第二部分是設定支出條件的加密難題。 Bitcoin Core 將其顯示為 scriptPubKey,並向我們展示了一個可讀的指令碼表示。

稍後將在指令碼構造(Lock + Unlock)中討論鎖定和解鎖UTXO的主題。 在 ScriptPubKey 中用於編輯指令碼的指令碼語言在章節Transaction Scripts(交易指令碼)和Script Language(指令碼語言)中討論。 但在我們深入研究這些話題之前,我們需要了解交易輸入和輸出的整體結構。

6.3.1.1交易序列化 - 輸出

當交易通過網路傳輸或在應用程式之間交換時,它們被序列化。 序列化是將內部的資料結構表示轉換為可以一次傳送一個位元組的格式(也稱為位元組流)的過程。 序列化最常用於編碼通過網路傳輸或用於檔案中儲存的資料結構。 交易輸出的序列化格式如下表所示

表6-1交易輸出序列化格式

大多數比特幣函式庫和架構不會在內部將交易儲存為位元組流,因為每次需要訪問單個欄位時,都需要複雜的解析。為了方便和可讀性,比特幣函式庫將交易內部儲存在資料結構(通常是物件導向的結構)中。

從交易的位元組流表示轉換為函式庫的內部資料結構表示的過程稱為反序列化或交易解析。轉換回位元組流以通過網路傳輸、雜湊化(hashing)或儲存在磁碟上的過程稱為序列化。大多數比特幣函式庫具有用於交易序列化和反序列化的內建函式。

看看是否可以從序列化的十六進位制形式手動解碼 Alice 的交易中,找到我們以前看到的一些元素。包含兩個輸出的部分在下面中已加粗顯示:

img

這裡有一些提示:

  • 加粗顯示的部分有兩個輸出,每個都如本節之前所述進行了序列化。
  • 0.015比特幣的價值是1,500,000 satoshis。 這是十六進位制的16 e3 60。
  • 在序列化交易中,值16 e3 60以小端(最低有效位元組優先)位元組順序進行編碼,所以它看起來像60 e3 16。
  • scriptPubKey的長度為25個位元組,以十六進位制顯示為19個位元組。

6.3.2交易輸入

交易輸入將UTXO(通過引用)標記為將被消費,並通過解鎖指令碼提供所有權證明。

要構建一個交易,一個錢包從它控制的UTXO中選擇足夠的價值來執行被請求的付款。 有時一個UTXO就足夠,其他時候不止一個。 對於將用於進行此付款的每個UTXO,錢包將建立一個指向UTXO的輸入,並使用解鎖指令碼解鎖它。

讓我們更詳細地看一下輸入的元件。輸入的第一部分是一個指向UTXO的指標,通過指向UTXO被記錄在區塊鏈中所在的交易的雜湊值和序列號來實現。 第二部分是解鎖指令碼,錢包構建它用以滿足設定在UTXO中的支出條件。 大多數情況下,解鎖指令碼是一個證明比特幣所有權的數字簽名和公鑰,但是並不是所有的解鎖指令碼都包含簽名。 第三部分是序列號,稍後再討論。

考慮我們在之前交易幕後章節提到的例子。交易輸入是一個名為 vin 的陣列(列表):

"vin": [
  {
    "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
    "vout": 0,
    "scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
    "sequence": 4294967295
  }
]

如您所見,列表中只有一個輸入(因為一個UTXO包含足夠的值來完成此付款)。 輸入包含四個元素:

  • 一個交易ID,引用包含正在使用的UTXO的交易
  • 一個輸出索引(vout),用於標識來自該交易的哪個UTXO被引用(第一個為零)
  • 一個 scriptSig(解鎖指令碼),滿足放置在UTXO上的條件,解鎖它用於支出
  • 一個序列號(稍後討論)

在 Alice 的交易中,輸入指向的交易ID是:

7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18

輸出索引是0(即由該交易建立的第一個UTXO)。解鎖指令碼由Alice的錢包構建,首先檢索引用的UTXO,檢查其鎖定指令碼,然後使用它來構建所需的解鎖指令碼以滿足此要求。

僅僅看這個輸入,你可能已經注意到,除了對包含它引用的交易之外,我們無從瞭解這個UTXO的任何內容。我們不知道它的價值(多少satoshi金額),我們不知道設定支出條件的鎖定指令碼。要找到這些資訊,我們必須通過檢索整個交易來檢索被引用的UTXO。請注意,由於輸入的值未明確說明,因此我們還必須使用被引用的UTXO來計算在此交易中支付的費用(參見後面交易費用章節)。

不僅僅是Alice的錢包需要檢索輸入中引用的UTXO。一旦將該交易廣播到網路,每個驗證節點也將需要檢索交易輸入中引用的UTXO,以驗證該交易。

因為缺乏語境,交易本身似乎不完整。他們在輸入中引用UTXO,但是沒有檢索到UTXO,我們無法知道輸入的值或其鎖定條件。當編寫比特幣軟體時,無論何時解碼交易以驗證它或計算費用或檢查解鎖指令碼,您的程式碼首先必須從塊鏈中檢索引用的UTXO,以構建隱含但不存在於輸入的UTXO引用中的語境。例如,要計算支付總額的交易費,您必須知道輸入和輸出值的總和。但是,如果不檢索輸入中引用的UTXO,則不知道它們的值。因此,在單個交易中計算交易費用的簡單操作,實際上涉及多個交易的多個步驟和資料。

我們可以使用與比特幣核心相同的命令序列,就像我們在檢索Alice的交易(getrawtransactiondecodeawtransaction)時一樣。因此,我們可以得到在前面的輸入中引用的UTXO,並檢視:

輸入中引用的來自Alice 以前的交易中的UTXO:

"vout": [
   {
     "value": 0.10000000,
     "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
   }
 ]

我們看到這個UTXO的值為0.1BTC,並且它有一個包含“OP_DUP OP_HASH160 …”的鎖定指令碼(scriptPubKey)。

**小貼士:**為了充分了解Alice的交易,我們必須檢索引用以前的交易作為輸入。 檢索以前的交易和未花費的交易輸出的函式是非常普遍的,並且存在於幾乎每個比特幣函式庫和API中。

6.3.2.1交易序列化–交易輸入

當交易被序列化以在網路上傳輸時,它們的輸入被編碼成位元組流,如下表所示

表6-2交易輸入序列化

與輸出一樣,我們來看看我們是否可以從序列化格式的 Alice 的交易中找到輸入。 首先,將輸入解碼:

"vin":
[
  {
    "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
    "vout": 0,
    "scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
    "sequence": 4294967295
  }
]

現在,我們來看看我們是否可以識別下面這些以十六進位制表示法表示的欄位:

img

提示:

  • 交易ID以反轉位元組順序序列化,因此以(十六進位制)18開頭,以79結尾
  • 輸出索引為4位元組組的“0”,容易識別
  • scriptSig的長度為139個位元組,或十六進位制為8b
  • 序列號設定為FFFFFFFF,也容易識別

6.3.3 交易費

大多數交易包含交易費(礦工費),這是為了確保網路安全而給比特幣礦工的一種補償。費用本身也作為一個安全機制,使經濟上不利於攻擊者通過交易來淹沒網路。對於挖礦、費用和礦工得到的獎勵,在挖礦一章中將有更詳細的討論。

這一節解釋交易費是如何被包含在一個典型的交易中的。大多數錢包自動計算並計入交易費。但是, 如果你以程式設計方式構造交易,或者使用命令列介面,你必須手動計算並計入這些費用。

交易費作為礦工打包(挖礦)一筆交易到下一個區塊中的一種激勵;同時作為一種抑制因素,通過對每一筆交易收取小額費用來防止對系統的濫用。成功挖到某區塊的礦工將得到該區內包含的礦工費, 並將該區塊新增至區塊鏈中。

交易費是基於交易的千位元組規模來計算的,而不是比特幣交易的價值。總的來說,交易費是根據比特幣網路中的市場力量確定的。礦工會依據許多不同的標準對交易進行優先順序排序,包括費用,他們甚至可能在某些特定情況下免費處理交易。但大多數情況下,交易費影響處理優先順序,這意味著有足夠費用的交易會更可能被打包進下一個挖出的區塊中;反之交易費不足或者沒有交易費的交易可能會被推遲,基於盡力而為的原則在幾個區塊之後被處理,甚至可能根本不被處理。交易費不是強制的,而且沒有交易費的交易最終也可能會被處理,但是,交易費將提高處理優先順序。

隨著時間的推移,交易費的計算方式以及在交易處理優先順序上的影響已經產生了變化。起初,交易費是固定的,是網路中的一個固定常數**。**漸漸地,隨著網路容量和交易量的不斷變化,並可能受到來自市場力量的影響,收費結構開始放鬆。自從至少2016年初以來,比特幣網路容量的限制已經造成交易之間的競爭,從而導致更高的費用,免費交易徹底成為過去式。零費用或非常低費用的交易鮮少被處理,有時甚至不會在網路上傳播。

在 Bitcoin Core 中,費用傳遞政策由minrelaytxfee選項設定。 目前預設的minrelaytxfee是每千位元組0.00001比特幣或者millibitcoin的1%。 因此,預設情況下,費用低於0.0001比特幣的交易是免費的,但只有在記憶體池有空間時才會被轉發; 否則,會被丟棄。 比特幣節點可以通過調整minrelaytxfee的值來覆蓋預設的費用傳策略。

任何建立交易的比特幣服務,包括錢包,交易所,零售應用等,都必須實現動態收費。動態費用可以通過第三方費用估算服務或內建的費用估算演算法來實現。如果您不確定,那就從第三方服務開始,如果您希望去除第三方依賴,您應當有設計和部署自己演算法的經驗。

費用估算演算法根據網路能力和“競爭”交易提供的費用計算適當的費用。這些演算法的範圍從十分簡單的(最後一個塊中的平均值或中位數)到非常複雜的(統計分析)均有覆蓋。他們估計必要的費用(以位元組為單位),這將使得交易具有很高的可能性被選擇並打包進一定數量的塊內。大多數服務為使用者提供高、中、低優先費用的選擇。高優先順序意味著使用者支付更高的交易費,但交易可能就會被打包進下一個塊中。中低優先順序意味著使用者支付較低的交易費,但交易可能需要更長時間才能確認。

許多錢包應用程式使用第三方服務進行費用計算。一個流行的服務是http://bitcoinfees.21.co,它提供了一個API和一個視覺化圖表,以satoshi / byte為單位顯示了不同優先順序的費用。

**小貼士:**靜態費用在比特幣網路上不再可行。 設定靜態費用的錢包將導致使用者體驗不佳,因為交易往往會被“卡住”,並不被確認。 不瞭解比特幣交易和費用的使用者因交易被“卡住” 而感到沮喪,因為他們認為自己已經失去了資金。

下面費用估算服務bitcoinfees.21.co中的圖表顯示了10個satoshi / byte增量的費用的實時估計,以及每個範圍的費用交易的預期確認時間(分鐘和塊數)。 對於每個收費範圍(例如,61-70 satoshi /位元組),兩個水平欄顯示過去24小時(102,975)中未確認交易的數量(1405)和交易總數,費用在該範圍內。 根據圖表,此時推薦的高優先費用為80 satoshi / 位元組,這可能導致交易在下一個塊(零塊延遲)中開採。 據合理判斷,一筆常規交易的大小約為226位元組,因此單筆交易建議費用為18,080 satoshis(0.00018080 BTC)。

費用估算資料可以通過簡單的HTTP REST API(https://bitcoinfees.21.co/api/v1/fees/recommended)來檢索。 例如,在命令列中使用curl命令:

運用費用估算API

$ curl https://bitcoinfees.21.co/api/v1/fees/recommended

{"fastestFee":80,"halfHourFee":80,"hourFee":60}

API通過費用估算以 satoshi per byte 的方式返回一個 JSON 物件,從而實現”最快確認“ (fastestFee),以及在三個塊(halfHourFee)和六個塊(hourFee)內確認。

圖6-2bitcoinfees.21.co提供的費用估算服務

6.3.4 把交易費加到交易中

交易的資料結構沒有交易費的欄位。相替代地,交易費是指輸入和輸出之間的差值。從所有輸入中扣掉所有輸出之後的多餘的量會被礦工作為礦工費收集走:

交易費即輸入總和減輸出總和的餘量:交易費 = 求和(所有輸入) - 求和(所有輸出)

正確理解交易比較困難,但又尤為重要。因為如果你要構建你自己的交易,你必須確保你沒有因疏忽在交易中新增一筆大量交易費而大大減少了輸入的可花費額。這意味著你必須計算所有的輸入,如有必要則加上找零, 不然的話,結果就是你給了礦工一筆相當可觀的勞務費!

舉例來說,如果你消耗了一個20比特幣的UTXO來完成1比特幣的付款,你必須包含一筆19比特幣的找零回到你的錢包。否則,那剩下的19比特幣會被當作交易費,並將由挖出你交易的礦工收走。儘管你會得到高優先順序的處理,並且讓一個礦工喜出望外,但這很可能不是你想要的。

**警告:**如果你忘記了在手動構造的交易中增加找零的輸出,系統會把找零當作交易費來處理。“不用找了!”也許不是你的真實意願。

讓我們重溫一下Alice在咖啡店的交易來看看在實際中它如何運作。Alice想花0.015比特幣購買咖啡。為了確保這筆交易能被立即處理,Alice想新增一筆交易費,比如說0.001。這意味著總花費會變成0.016。因此她的錢包需要湊齊 0.016或更多的UTXO。如果需要,還要加上找零。我們假設他的錢包有一個0.2比特幣的UTXO可用。他的錢包就會消耗 掉這個UTXO,創造一個新的0.015的輸出給Bob的咖啡店,另一個0.184比特幣的輸出作為找零回到Alice的錢包, 並留下未分配的0.001礦工費內含在交易中。

現在讓我們看看另一種情況。Eugenia,我們在菲律賓的兒童募捐專案主管,完成了一次為孩子購買教材的籌款活動。她從世界範圍內接收到了好幾千份小額捐款,總額是50比特幣。所以她的錢包塞滿了非常小的UTXO。現在她想用比特幣從本地的一家出版商購買幾百本教材。

現在Eugenia的錢包應用想要構造一個單筆大額付款交易,它必須從可用的、由很多小數額構成的大的UTXO集合中尋求錢幣來源。這意味著交易的結果是從上百個小額UTXO中作為輸入,但只有一個輸出用來付給出版商。輸入數量這麼巨大的交易會比一千位元組要大,也許總尺寸會達到兩至三千位元組。結果是它將需要比中等規模交易要高得多的交易費。

Eugenia的錢包應用會通過測量交易的大小,乘以每千位元組需要的費用來計算適當的交易費**。**很多錢包會支付較大的交易費,確保交易得到及時處理。更高交易費不是因為Eugenia付的錢很多,而是因為她的交易很複雜並且尺寸更大——交易費是與參加交易的比特幣值無關的。

6.4比特幣交易指令碼和指令碼語言

比特幣交易指令碼語言,稱為指令碼,是一種類似Forth的逆波蘭表示式的基於堆疊的執行語言。 如果聽起來不知所云,是你可能還沒有學習20世紀60年代的程式語言,但是沒關係,我們將在本章中解釋這一切。 放置在UTXO上的鎖定指令碼和解鎖指令碼都以此指令碼語言編寫。 當一筆比特幣交易被驗證時,每一個輸入值中的解鎖指令碼與其對應的鎖定指令碼同時 (互不干擾地)執行,以確定這筆交易是否滿足支付條件。

指令碼是一種非常簡單的語言,被設計為在執行範圍上有限制,可在一些硬體上執行,可能與嵌入式裝置一樣簡單。 它僅需要做最少的處理,許多現代程式語言可以做的花哨的事情它都不能做。 但用於驗證可程式設計貨幣,這是一個經深思熟慮的安全特性。

如今,大多數經比特幣網路處理的交易是以“Alice付給Bob”的形式存在,並基於一種稱為“P2PKH”(Pay-toPublic-Key-Hash)指令碼。但是,比特幣交易不侷限於“支付給Bob的比特幣地址”的指令碼。事實上,鎖定指令碼可以被編寫成表達各種複雜的情況。為了理解這些更為複雜的指令碼,我們必須首先了解交易指令碼和指令碼語言的基礎知識。

在本節中,我們將會展示比特幣交易指令碼語言的各個元件;同時,我們也會演示如何使用它去表達簡單的使用條件以及如何通過解鎖指令碼去滿足這些花費條件。

**小貼士:比特幣交易驗證並不基於靜態模式,**而是通過指令碼語言的執行來實現的。這種語言允許表達幾乎無限的各種條件。這也是比特幣作為一種“可程式設計的貨幣”所擁有的力量。

6.4.1 圖靈非完備性

比特幣指令碼語言包含許多操作碼,但都故意限定為一種重要的模式——除了有條件的流控制以外,沒有迴圈或複雜流控制能力。這樣就保證了指令碼語言的圖靈非完備性,這意味著指令碼有限的複雜性和可預見的執行次數。指令碼並不是一種通用語言,這些限制確保該語言不被用於創造無限迴圈或其它型別的邏輯炸彈,這樣的炸彈可以植入在一筆交易中,引起針對比特幣網路的“拒絕服務”攻擊。記住,每一筆交易都會被網路中的全節點驗證,受限制的語言能防止交易驗證機制被作為一個漏洞而加以利用。

6.4.2 去中心化驗證

比特幣交易指令碼語言是沒有中心化主體的,沒有任何中心主體能凌駕於指令碼之上,也沒有中心主體會在指令碼被執行後對其進行儲存。所以執行指令碼所需資訊都已包含在指令碼中。可以預見的是,一個指令碼能在任何系統上以相同的方式執行。如果您的系統驗證了一個指令碼,可以確信的是每一個比特幣網路中的其他系統也將驗證這個指令碼,這意味著一個有效的交易對每個人而言都是有效的,而且每一個人都知道這一點。這種結果可預見性是比特幣系統的一項至關重要的良性特徵。

6.4.3 指令碼構建(鎖定與解鎖)

比特幣的交易驗證引擎依賴於兩類指令碼來驗證比特幣交易:鎖定指令碼和解鎖指令碼。

鎖定指令碼是一個放置在輸出上面的花費條件:它指定了今後花費這筆輸出必須要滿足的條件。 由於鎖定指令碼往往含有一個公鑰或比特幣地址(公鑰雜湊值),在歷史上它曾被稱為指令碼公鑰(scriptPubKey)。由於認識到這種指令碼技術存在著更為廣泛的可能性,在本書中,我們將它稱為“鎖定指令碼”(locking script)。在大多數比特幣應用程式中,我們所稱的“鎖定指令碼”將以scriptPubKey的形式出現在原始碼中。您還將看到被稱為見證指令碼(witness script)的鎖定指令碼(參見[隔離見證]章節),或者更一般地說,它是一個加密難題(cryptographic puzzle)。 這些術語在不同的抽象層次上都意味著同樣的東西。

解鎖指令碼是一個“解決”或滿足被鎖定指令碼在一個輸出上設定的花費條件的指令碼,它將允許輸出被消費。解鎖指令碼是每一筆比特幣交易輸入的一部分,而且往往含有一個由使用者的比特幣錢包(通過使用者的私鑰)生成的數字簽名。由於解鎖指令碼常常包含一個數字簽名,因此它曾被稱作ScriptSig。在大多數比特幣應用的原始碼中,ScriptSig便是我們所說的解鎖指令碼。你也會看到解鎖指令碼被稱作“見證”(witness 參見[隔離見證]章節)。在本書中,我們將它稱為“解鎖指令碼”,用以承認鎖定指令碼的需求有更廣的範圍。但並非所有解鎖指令碼都一定會包含簽名。

每一個比特幣驗證節點會通過同時執行鎖定和解鎖指令碼來驗證一筆交易。每個輸入都包含一個解鎖指令碼,並引用了之前存在的UTXO。 驗證軟體將複製解鎖指令碼,檢索輸入所引用的UTXO,並從該UTXO複製鎖定指令碼。 然後依次執行解鎖和鎖定指令碼。 如果解鎖指令碼滿足鎖定指令碼條件,則輸入有效(請參閱單獨執行解鎖和鎖定指令碼部分)。 所有輸入都是獨立驗證的,作為交易總體驗證的一部分。

請注意,UTXO被永久地記錄在區塊鏈中,因此是不變的,並且不受在新交易中引用失敗的嘗試的影響。 只有正確滿足輸出條件的有效交易才能將輸出視為“開銷來源”,繼而該輸出將被從未花費的交易輸出集(UTXO set)中刪除。

下圖是最常見型別的比特幣交易(P2PKH:對公鑰雜湊的付款)的解鎖和鎖定指令碼的示例,顯示了在指令碼驗證之前從解鎖和鎖定指令碼的並置產生的組合指令碼:

圖6-3結合scriptSig和scriptPubKey來評估交易指令碼

6.4.3.1指令碼執行堆疊

比特幣的指令碼語言被稱為基於堆疊的語言,因為它使用一種被稱為堆疊的資料結構。堆疊是一個非常簡單的資料結構,可以被視為一疊卡片。棧允許兩個操作:push和pop(推送和彈出)。 Push(推送)在堆疊頂部新增一個專案。 Pop(彈出)從堆疊中刪除最頂端的項。棧上的操作只能作用於棧最頂端專案。堆疊資料結構也被稱為“後進先出”( Last-In-First-Out)或 “LIFO” 佇列。

指令碼語言通過從左到右處理每個專案來執行指令碼。數字(資料常量)被推到堆疊上。操作碼(Operators)從堆疊中推送或彈出一個或多個引數,對其進行操作,並可能將結果推送到堆疊上。例如,操作碼 OP_ADD 將從堆疊中彈出兩個專案,新增它們,並將結果的總和推送到堆疊上。

條件操作碼(Conditional operators)對一個條件進行評估,產生一個 TRUE 或 FALSE 的布林結果(boolean result)。例如, OP_EQUAL 從堆疊中彈出兩個專案,如果它們相等,則推送為 TRUE(由數字1表示),否則推送為 FALSE(由數字0表示)。比特幣交易指令碼通常包含條件操作碼,以便它們可以產生用來表示有效交易的 TRUE 結果。

6.4.3.2一個簡單的指令碼

現在讓我們將學到的關於指令碼和堆疊的知識應用到一些簡單的例子中。

如圖6-4,在比特幣的指令碼驗證中,執行簡單的數學運算時,指令碼“ 2 3 OP_ADD 5 OP_EQUAL ”演示了算術加法操作碼 OP_ADD ,該操作碼將兩個數字相加,然後把結果推送到堆疊, 後面的條件操作符 OP_EQUAL 是驗算之前的兩數之和是否等於 5 。為了簡化起見,字首OP_在一步步的演示過程中將被省略。有關可用指令碼操作碼和函式的更多詳細資訊,請參見[交易指令碼]。

圖6-4比特幣的指令碼驗證中,執行簡單的數學運算

儘管絕大多數解鎖指令碼都指向一個公鑰雜湊值(本質上就是比特幣地址),因此如果想要使用資金則需驗證所有權,但指令碼本身並不需要如此複雜。任何解鎖和鎖定指令碼的組合如果結果為真(TRUE),則為有效。前面被我們用於說明指令碼語言的簡單算術操作碼同樣也是一個有效的鎖定指令碼,該指令碼能用於鎖定交易輸出。

使用部分算術操作碼指令碼作為鎖定指令碼的示例:

3 OP_ADD 5 OP_EQUAL

該指令碼能被以如下解鎖指令碼為輸入的一筆交易所滿足:

2

驗證軟體將鎖定和解鎖指令碼組合起來,結果指令碼是:

2 3 OP_ADD 5 OP_EQUAL

正如在上圖中所看到的,當指令碼被執行時,結果是OP_TRUE,交易有效。不僅該筆交易的輸出鎖定指令碼有效,同時UTXO也能被任何知曉這個運算技巧(知道是數字2)的人所使用。

**小貼士:**如果堆疊頂部的結果顯示為TRUE(標記為{{0x01}}),即為任何非零值,或指令碼執行後堆疊為空情形,則交易有效。如果堆疊頂部的結果顯示為FALSE(0位元組空值,標記為{{}})或指令碼執行被操作碼明確禁止,如OP_VERIFY、 OP_RETURN,或有條件終止如OP_ENDIF,則交易無效。詳見[tx_script_ops]相關內容。

以下是一個稍微複雜一點的指令碼,它用於計算 2+7-3+1 。注意,當指令碼在同一行包含多個操作碼時,堆疊允許一個操作碼的結果由於下一個操作碼執行。

2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL

請試著用紙筆自行演算指令碼,當指令碼執行完畢時,你會在堆疊得到正確的結果。

6.4.3.3 解鎖和鎖定指令碼的單獨執行

在最初版本的比特幣客戶端中,解鎖和鎖定指令碼是以連鎖的形式存在,並被依次執行的。出於安全因素考慮,在2010 年比特幣開發者們修改了這個特性——因為存在“允許異常解鎖指令碼推送資料入棧並且汙染鎖定指令碼”的漏洞。而在當前的方案中,這兩個指令碼是隨著堆疊的傳遞被分別執行的。下面將會詳細介紹。

首先,使用堆疊執行引擎執行解鎖指令碼。如果解鎖指令碼在執行過程中未報錯(例如:沒有“懸掛”操作碼),則複製主堆疊(而不是備用堆疊),並執行鎖定指令碼。如果從解鎖指令碼中複製而來的堆疊資料執行鎖定指令碼的結果為“TRUE",那麼解鎖指令碼就成功地滿足了鎖定指令碼所設定的條件,因此,該輸入是一個能使用該UTXO的有效授權。如果在合併指令碼後的結果不是”TRUE“以外的任何結果,輸入都是無效的,因為它不能滿足UTXO中所設定的使用該筆資金的條件。

6.4.4 P2PKH(Pay-to-Public-Key-Hash)

比特幣網路處理的大多數交易花費的都是由“付款至公鑰雜湊”(或P2PKH)指令碼鎖定的輸出,這些輸出都含有一個鎖定指令碼,將輸入鎖定為一個公鑰雜湊值,即我們常說的比特幣地址。由P2PKH指令碼鎖定的輸出可以通過提供一個公鑰和由相應私鑰建立的數字簽名來解鎖(使用)。參見數字簽名ECDSA相關內容。

例如,我們可以再次回顧一下Alice向Bob咖啡館支付的案例。Alice下達了向Bob咖啡館的比特幣地址支付0.015比特幣的支付指令,該筆交易的輸出內容為以下形式的鎖定指令碼:

OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

指令碼中的 Cafe Public Key Hash 即為咖啡館的比特幣地址,但該地址不是基於Base58Check編碼。事實上,大多數比特幣地址的公鑰雜湊值都顯示為十六進位制碼,而不是大家所熟知的以1開頭的基於Bsase58Check編碼的比特幣地址。

上述鎖定指令碼相應的解鎖指令碼是:

<Cafe Signature> <Cafe Public Key>

將兩個指令碼結合起來可以形成如下組合驗證指令碼:

<Cafe Signature> <Cafe Public Key> OP_DUP OP_HASH160
<Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

只有當解鎖指令碼與鎖定指令碼的設定條件相匹配時,執行組合驗證指令碼時才會顯示結果為真(TRUE)。換句話說,只有當解鎖指令碼得到了咖啡館的有效簽名,交易執行結果才會被通過(結果為真),該有效簽名是從與公鑰雜湊相匹配的咖啡館的私鑰中所獲取的。
圖6-5和圖6-6(分兩部分)顯示了組合指令碼一步步檢驗交易有效性的過程。

圖6-5評估P2PKH交易的指令碼(第1部分,共2部分)

圖6-6評估P2PKH交易的指令碼(第2部分,共2部分)

6.5數字簽名(ECDSA)

到目前為止,我們還沒有深入瞭解“數字簽名”的細節。在本節中,我們將研究數字簽名的工作原理,以及如何在不揭示私鑰的情況下提供私鑰的所有權證明。

比特幣中使用的數字簽名演算法是橢圓曲線數字簽名演算法(Elliptic Curve Digital Signature Algorithm)或ECDSA。 ECDSA是用於基於橢圓曲線私鑰/公鑰對的數字簽名的演算法,如橢圓曲線章節[elliptic_curve]所述。 ECDSA用於指令碼函式OP_CHECKSIG,OP_CHECKSIGVERIFY,OP_CHECKMULTISIG和OP_CHECKMULTISIGVERIFY。每當你鎖定指令碼中看到這些時,解鎖指令碼都必須包含一個ECDSA簽名。

數字簽名在比特幣中有三種用途(請參閱下面的側欄)。第一,簽名證明私鑰的所有者,即資金所有者,已經授權支出這些資金。第二,授權證明是不可否認的(不可否認性)。第三,簽名證明交易(或交易的具體部分)在簽字之後沒有也不能被任何人修改。

請注意,每個交易輸入都是獨立簽名的。這一點至關重要,因為不管是簽名還是輸入都不必由同一“所有者”實施。事實上,一個名為 “CoinJoin” 的特定交易方案(多重簽名方案?)就使用這個特性來建立多方交易來保護隱私。

**注意:**每個交易輸入和它可能包含的任何簽名完全獨立於任何其他輸入或簽名。 多方可以協作構建交易,並各自僅籤一個輸入。

維基百科對 “數字簽名 ”的定義:

數字簽名是用於證明數字訊息或文件的真實性的數學方案。 有效的數字簽名給了一個容易接受的理由去相信:1)該訊息是由已知的傳送者(身份認證性)建立的; 2)傳送方不能否認已傳送訊息(不可否認性;3)訊息在傳輸中未被更改(完整性)。

來源: https://en.wikipedia.org/wiki/Digital_signature*

6.5.1數字簽名如何工作

數字簽名是一種由兩部分組成的數學方案:第一部分是使用私鑰(簽名金鑰)從訊息(交易)建立簽名的演算法; 第二部分是允許任何人驗證簽名的演算法,給定訊息和公鑰。

6.5.1.1建立數字簽名

在比特幣的ECDSA演算法的實現中,被簽名的“訊息”是交易,或更確切地說是交易中特定資料子集的雜湊值(參見簽名雜湊型別(SIGHASH))。簽名金鑰是使用者的私鑰,結果是簽名:

*\(\(Sig = F{sig}(F{hash}(m), dA)\)\)*

這裡的:

  • dA 是簽名私鑰
  • m 是交易(或其部分)
  • Fhash 是雜湊函式
  • Fsig 是簽名演算法
  • Sig 是結果簽名

ECDSA數學運算的更多細節可以在ECDSA Math章節中找到。

函式Fsig 產生由兩個值組成的簽名Sig,通常稱為RS

Sig = (R, S)

現在已經計算了兩個值R和S,它們就序列化為位元組流,使用一種稱為“分辨編碼規則”(Distinguished Encoding Rules)或 DER的國際標準編碼方案。

6.5.1.2簽名序列化(DER)

我們再來看看Alice建立的交易。 在交易輸入中有一個解鎖指令碼,其中包含Alice的錢包中的以下DER編碼簽名:

3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301

該簽名是Alice的錢包生成的R和S值的序列化位元組流,證明她擁有授權花費該輸出的私鑰。 序列化格式包含以下9個元素:

  • 0x30表示DER序列的開始
  • 0x45 - 序列的長度(69位元組)
  • 0x02 - 一個整數值
  • 0x21 - 整數的長度(33位元組)
  • R-00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb
  • 0x02 - 接下來是一個整數
  • 0x20 - 整數的長度(32位元組)
  • S-4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813
  • 字尾(0x01)指示使用的雜湊的型別(SIGHASH_ALL)

看看您是否可以使用此列表解碼 Alice 的序列化(DER編碼)簽名。 重要的數字是R和S; 資料的其餘部分是DER編碼方案的一部分。

6.5.2驗證簽名

要驗證簽名,必須有簽名(RS)、序列化交易和公鑰(對應於用於建立簽名的私鑰)。本質上,簽名的驗證意味著“只有生成此公鑰的私鑰的所有者,才能在此交易上產生此簽名。”

簽名驗證演算法採用訊息(交易或其部分的雜湊值)、簽名者的公鑰和簽名(R和S值),如果簽名對該訊息和公鑰有效,則返回 TRUE 值。

6.5.3簽名雜湊型別(SIGHASH)

數字簽名被應用於訊息,在比特幣中,就是交易本身。簽名意味著簽字人對特定交易資料的承諾( commitment)。在最簡單的形式中,簽名適用於整個交易,從而承諾(commit)所有輸入,輸出和其他交易欄位。但是,在一個交易中一個簽名可以只承諾(commit)一個資料子集,這對於我們將在本節中看到的許多場景是有用的。

比特幣簽名具有指示交易資料的哪一部分包含在使用 SIGHASH 標誌的私鑰簽名的雜湊中的方式。 SIGHASH 標誌是附加到簽名的單個位元組。每個簽名都有一個SIGHASH標誌,該標誌在不同輸入之間也可以不同。具有三個簽名輸入的交易可以具有不同SIGHASH標誌的三個簽名,每個簽名簽署(承諾)交易的不同部分。

記住,每個輸入可能在其解鎖指令碼中包含一個簽名。因此,包含多個輸入的交易可以擁有具有不同SIGHASH標誌的簽名,這些標誌在每個輸入中承諾交易的不同部分。還要注意,比特幣交易可能包含來自不同“所有者”的輸入,他們在部分構建(和無效)的交易中可能僅簽署一個輸入,繼而與他人協作收集所有必要的簽名後再使交易生效。許多SIGHSASH標誌型別,只有在你考慮到由許多參與者在比特幣網路之外共同協作去更新僅部分簽署了的交易,才具有意義。

有三個SIGHASH標誌:ALL,NONE和SINGLE,如下表所示。

表6-3 SIGHASH型別和意義

另外還有一個修飾符標誌SIGHASH_ANYONECANPAY,它可以與前面的每個標誌組合。 當設定ANYONECANPAY時,只有一個輸入被簽名,其餘的(及其序列號)開啟以進行修改。 ANYONECANPAY的值為0x80,並通過按位OR運算,得到如下所示的組合標誌:

表6-4 帶修飾符的SIGHASH型別及其含義

SIGHASH標誌在簽名和驗證期間應用的方式是建立交易的副本和刪節其中的某些欄位(設定長度為零並清空),繼而生成的交易被序列化,SIGHASH標誌被新增到序列化交易的結尾,並將結果雜湊化 ,得到的雜湊值本身即是被簽名的“訊息”。 基於SIGHASH標誌的使用,交易的不同部分被刪節。 所得到的雜湊值取決於交易中資料的不同子集。 在雜湊化前,SIGHASH作為最後一步被包含在內,簽名也會對SIGHASH型別進行簽署,因此不能更改(例如,被礦工)。

**小貼士:**所有SIGHASH型別對應交易nLocktime欄位(請參閱[transaction_locktime_nlocktime]部分)。 此外,SIGHASH型別本身在簽名之前附加到交易,因此一旦簽名就不能修改它。

在Alice的交易(參見序列化簽名(DER)的列表)的例子中,我們看到DER編碼簽名的最後一部分是01,這是SIGHASH_ALL標誌。這會鎖定交易資料,因此Alice的簽名承諾的是所有的輸入和輸出狀態。 這是最常見的簽名形式。

我們來看看其他一些SIGHASH型別,以及如何在實踐中使用它們:

ALL | ANYONECANPAY

這種構造可以用來做“眾籌”交易,試圖籌集資金的人可以用單筆輸出來構建一個交易,單筆輸出將“目標”金額付給眾籌發起人。這樣的交易顯然是無效的,因為它沒有輸入。但是現在其他人可以通過新增自己的輸入作為捐贈來修改它們,他們用ALL | ANYONECANPAY簽署自己的輸入,除非收集到足夠的輸入以達到輸出的價值,交易無效,每次捐贈是一項“抵押”,直到募集整個目標金額才能由募款人收取。

NONE

該結構可用於建立特定數量的“不記名支票”或“空白支票”。它對輸入進行承諾,但允許輸出鎖定指令碼被更改。任何人都可以將自己的比特幣地址寫入輸出鎖定指令碼並兌換交易。然而,輸出值本身被簽名鎖定。

NONE | ANYONECANPAY

這種構造可以用來建造一個“吸塵器”。在他們的錢包中擁有微小UTXO的使用者無法花費這些費用,因為手續費用超過了這些微小UTXO的價值。藉助這種型別的簽名,微小UTXO可以為任何人捐贈,以便隨時隨地收集和消費。

有一些修改或擴充套件SIGHASH系統的建議。作為Elements專案的一部分,一個這樣的提案是Blockstream的Glenn Willen提出的Bitmask Sighash模式。這旨在為SIGHASH型別建立一個靈活的替代品,允許“任意的,輸入和輸出的礦工可改寫位掩碼”來表示“更復雜的合同預付款方案,例如已分配的資產交換中有變更的已簽名的報價”。

註釋: 您不會在使用者的錢包應用程式中看到SIGHASH標誌作為一個功能呈現。 少數例外,錢包會構建P2PKH指令碼,並使用SIGHASH_ALL標誌進行簽名。 要使用不同的SIGHASH標誌,您必須編寫軟體來構造和簽署交易。 更重要的是,SIGHASH標誌可以被專用的比特幣應用程式使用,從而實現新穎的用途。

6.5.4 ECDSA數學

如前所述,簽名由產生由兩個值RS組成的簽名的數學函式Fsig 建立。在本節中,我們將檢視函式*Fs*ig 的更多細節。

簽名演算法首先生成一個 ephemeral(臨時)私公鑰對。 在涉及簽名私鑰和交易雜湊的變換之後,該臨時金鑰對用於計算RS值。

臨時金鑰對基於隨機數k,用作臨時私鑰。 從k,我們生成相應的臨時公鑰P(以P = k * G計算,與派生比特幣公鑰相同);參見[pubkey]部分)。數字簽名的R值則是臨時公鑰Px座標。

從那裡,演算法計算簽名的S值,使得:

img

其中:

  • k是臨時私鑰
  • R是臨時公鑰的x座標
  • dA是簽名私鑰
  • m是交易資料
  • p是橢圓曲線的主要順序

驗證是簽名生成函式的倒數,使用R,S值和公鑰來計算一個值P,該值是橢圓曲線上的一個點(簽名建立中使用的臨時公鑰):

img

其中:

  • RS是簽名值
  • Qa是Alice的公鑰
  • m是簽署的交易資料
  • G是橢圓曲線發生器點

如果計算點Px座標等於R,則驗證者可以得出結論,簽名是有效的。

請注意,在驗證簽名時,私鑰既不知道也不顯示。

**小貼士:**ECDSA的數學很複雜,難以理解。 網上有一些很棒的指南可能有幫助。 搜尋“ECDSA解釋”或嘗試這個:http://bit.ly/2r0HhGB。

6.5.5隨機性在簽名中的重要性

如我們在ECDSA Math中所看到的,簽名生成演算法使用隨機金鑰k作為臨時私有-公鑰對的基礎。 k 的值不重要,只要它是隨機的。如果使用相同的值 k 在不同的訊息(交易)上產生兩個簽名,那麼簽名私鑰可以由任何人計算。在簽名演算法中重用相同的 k 值會導致私鑰的暴露!

警告 如果在兩個不同的交易中,在簽名演算法中使用相同的值 k,則私鑰可以被計算並暴露給世界!

這不僅僅是一個理論上的可能性。我們已經看到這個問題導致私人金鑰在比特幣中的幾種不同實現的交易簽名演算法中的暴露。人們由於無意中重複使用 k 值而將資金竊取。重用 k 值的最常見原因是未正確初始化的隨機數生成器。

為了避免這個漏洞,業界最佳實踐不是用熵播種的隨機數生成器生成 k 值,而是使用交易資料本身播種的確定性隨機程式。這確保每個交易產生不同的 k 值。在網際網路工程任務組(Internet Engineering Task Force)釋出的RFC 6979中定義了 k 值的確定性初始化的行業標準演算法。

如果您正在實現一種用於在比特幣中籤署交易的演算法,則必須使用RFC 6979或類似的確定性隨機演算法來確保為每個交易生成不同的 k 值。

6.6比特幣地址,餘額和其他摘要

在本章開始,我們發現交易的 “幕後”看起來與它在錢包、區塊鏈瀏覽器和其它面向使用者的應用程式中呈現的非常不同。 來自前幾章的許多簡單而熟悉的概念,如比特幣地址和餘額,似乎在交易結構中不存在。 我們看到交易本身並不包含比特幣地址,而是通過鎖定和解鎖比特幣離散值的指令碼進行操作。 這個系統中的任何地方都不存在餘額,而每個錢包應用程式都明明白白地顯示了使用者錢包的餘額。

現在我們已經探討了一個比特幣交易中實際包含的內容,我們可以檢查更高層次的抽象概念是如何從交易的看似原始的組成部分中派生出來的。

我們再來看看Alice的交易是如何在一個受歡迎的區塊瀏覽器(前面章節Alice與Bob’s Cafe的交易)中呈現的:

圖6-7Alice與Bob's Cafe的交易

在交易的左側,區塊瀏覽器將Alice的比特幣地址顯示為“傳送者”。其實這個資訊本身並不在交易中。當區塊連結瀏覽器檢索到交易時,它還檢索在輸入中引用的先前交易,並從該舊交易中提取第一個輸出。在該輸出內是一個鎖定指令碼,將UTXO鎖定到Alice的公鑰雜湊(P2PKH指令碼)。塊鏈瀏覽器提取公鑰雜湊,並使用Base58Check編碼對其進行編碼,以生成和顯示錶示該公鑰的比特幣地址。

同樣,在右側,區塊瀏覽器顯示了兩個輸出;第一個到Bob的比特幣地址,第二個到Alice的比特幣地址(作為找零)。再次,為了建立這些比特幣地址,區塊鏈瀏覽器從每個輸出中提取鎖定指令碼,將其識別為P2PKH指令碼,並從內部提取公鑰雜湊。最後,塊鏈瀏覽器重新編碼了使用Base58Check的公鑰雜湊生成和顯示比特幣地址。

如果您要點選Bob的比特幣地址,則塊連結瀏覽器將顯示Bob的比特幣地址的餘額:

圖6-8Bob的比特幣地址的餘額

區塊鏈瀏覽器顯示了Bob的比特幣地址的餘額。但是比特幣系統中卻沒有“餘額”的概念。這麼說吧,這裡顯示的餘額其實是由區塊鏈遊覽器按如下方式構建出來的:

為了構建“總接收”數量,區塊鏈瀏覽器首先解碼比特幣地址的Base58Check編碼,以檢索編碼在地址中的Bob的公鑰的160位雜湊值。然後,區塊鏈瀏覽器搜尋交易資料庫,使用包含Bob公鑰雜湊的P2PKH鎖定指令碼尋找輸出。通過總結所有輸出的值,瀏覽器可以產生接收的總值。

完成構建當前餘額(顯示為“最終餘額”)需要更多的工作。區塊連結瀏覽器將當前未被使用的輸入儲存為一個分離的資料庫——UTXO集。為了維護這個資料庫,區塊鏈瀏覽器必須監視比特幣網路,新增新建立的UTXO,並在已被使用的UTXO出現在未經確認的交易中時,實時地刪除它們。這是一個複雜的過程,不但要實時地跟蹤交易在網路上的傳播,同時還要保持與比特幣網路的共識,確保在正確的鏈上。有時區塊鏈瀏覽器未能保持同步,導致其對UTXO集的跟蹤掃描不完整或不正確。

通過計算UTXO集,區塊鏈瀏覽器總結了引用Bob的公鑰雜湊的所有未使用輸出的值,併產生向使用者顯示的“最終餘額”數目。

為了生成這張圖片,得到這兩個“餘額”,區塊鏈瀏覽器必須索引並搜尋數十、數百甚至數十萬的交易。

總之,通過錢包應用程式、區塊鏈瀏覽器和其他比特幣使用者介面呈現給使用者的資訊通常源於更高層次的,通過搜尋許多不同的交易,檢查其內容以及操縱其中包含的資料而的抽象而構成。為了呈現出比特幣交易類似於銀行支票從傳送人到接收人的這種簡單檢視,這些應用程式必須抽象許多底層細節。他們主要關注常見的交易型別:每個輸入上具有SIGHASH_ALL簽名的P2PKH。因此,雖然比特幣應用程式以易於閱讀的方式呈現所有了80%以上的交易,但有時候會被偏離了常規的交易 難住。包含更復雜的鎖定指令碼,或不同SIGHASH標誌,或多個輸入和輸出的交易顯示了這些抽象的簡單性和弱點。

每天都有數百個不包含P2PKH輸出的交易在塊上被確認。 blockchain瀏覽器經常向他們發出紅色警告資訊,表示無法解碼地址。以下連結包含未完全解碼的最新的“奇怪交易”:https://blockchain.info/strange-transactions。

正如我們將在下一章中看到的,這些並不一定是奇怪的交易。它們是包含比常見的P2PKH更復雜的鎖定指令碼的交易。我們將學習如何解碼和了解更復雜的指令碼及其支援的應用程式。

相關文章