交易

簡介

交易是比特幣系統中最重要的部分。比特幣中其他的一切都旨在確保交易可以創建,傳播,驗證並最終添加到交易(區塊鏈)的全球總賬中。交易是對比特幣系統參與者之間的價值轉移進行編碼的資料結構。每筆交易都是比特幣區塊鏈中的公開條目,即全球複式簿記分類賬。

在本章中,我們將檢查各種形式的交易,它們包含的內容,如何創建它們,如何驗證以及它們如何成為所有交易永久記錄的一部分。當我們在本章中使用術語"錢包"時,我們指的是構建交易的軟體,而不僅僅是密鑰的資料庫。

交易詳情

[ch02_bitcoin_overview] 中,我們使用區塊瀏覽器查看了Alice在Bob的咖啡店購買咖啡的交易( Alice’s transaction to Bob’s Cafe )。

區塊瀏覽器顯示一個從Alice的"地址"到Bob的"地址"的交易。這是交易中包含的內容的簡化視圖。事實上,我們將在本章中看到,大部分訊息都是由區塊瀏覽器構建的,實際上並不在交易中。

Alice Coffee Transaction
Figure 1. Alice’s transaction to Bob’s Cafe

交易背後

實際的交易看起來與典型的區塊瀏覽器提供的非常不同。實際上,我們在各種比特幣應用界面中看到的高層次結構 並不實際存在於 比特幣系統中。

我們可以使用Bitcoin Core的命令行界面( getrawtransaction 和 decoderawtransaction )來檢索Alice的"原始"交易,對其進行解碼並查看它包含的內容。結果如下所示:

Alice’s transaction decoded
{
  "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輸入在哪裡?在比特幣中,沒有硬幣,沒有發送者,沒有接收者,沒有餘額,沒有帳戶,也沒有地址。所有這些東西都是在更高層次上構建的,以使事情更易於理解。

你可能會注意到很多奇怪的,難以辨認的欄位和十六進制字串。別擔心,我們將在本章中詳細解釋每個欄位。

交易的輸出和輸入

比特幣交易的基本構建區塊是 交易的輸出 transaction output 。交易輸出是不可分割的比特幣貨幣,記錄在區塊鏈中,被整個網路識別為有效的。比特幣完整節點跟蹤所有可用和可花費的輸出,稱為 未花費的交易輸出 unspent transaction outputsUTXO 。所有UTXO的集合被稱為 UTXO set ,目前有數以百萬的UTXO。UTXO集的大小隨著新UTXO的增加而增長,並在UTXO被消耗時縮小。每個交易都表示UTXO集中的更改(狀態轉移)。

當我們說用戶的錢包"收到"比特幣時,意思是錢包檢測到一個可以使用該錢包控制的密鑰來花費的UTXO。因此,用戶的比特幣"餘額"是用戶錢包可以花費的所有UTXO的總和,可以分散在數百個交易和數百個區塊中。餘額的概念是由錢包應用創建的。錢包掃描區塊鏈並將錢包可以使用它的密鑰花費的任何UTXO彙總計算用戶的餘額。大多數錢包維護資料庫或使用資料庫服務來儲存它們可以花費的所有UTXO的快照。

一個交易輸出可以有一個任意的(整數)等於satoshis倍數的值作為。正如美元可以分為小數點後兩位數字一樣,比特幣可以被分為小數點後八位,作為satoshis。儘管輸出可以具有任意值,但一旦創建就是不可分割的。這是需要強調的輸出的一個重要特徵:輸出是 不連續的不可分割的 的價值,以整數satoshis為單位。未使用的輸出只能由交易全部花費。

如果UTXO大於交易的期望值,它仍然必須全部使用,並且必須在交易中生成零錢。換句話說,如果你有一個價值20比特幣的UTXO,並且只需要支付1比特幣,那麼你的交易必須消費整個20比特幣的UTXO,併產生兩個輸出:一個支付1比特幣給你想要的收款人,另一個支付19比特幣回到你的錢包。由於交易輸出的不可分割性,大多數比特幣交易將不得不產生零錢。

想象一下,一個購物者購買了1.50美元的飲料,並試圖從她的錢包找到硬幣和鈔票的組合,以支付1.50美元。如果可能,購物者將找到正好的零錢,例如,一美元鈔票和兩個二十五分硬幣(0.25美元),或小面值(六個二十五分硬幣)的組合;或者,直接向店主支付5美元,她會得到3.50美元的找零,放回她的錢包並且可用於未來的交易。

同樣,比特幣交易必須從用戶的UTXO創建,無論用戶有什麼樣的面額。用戶無法將UTXO削減一半,就像不能將美元分成兩半使用一樣。用戶的錢包應用通常會從用戶的可用UTXO中進行選擇,使組合的金額大於或等於期望交易金額。

與現實一樣,比特幣應用可以使用多種策略來滿足支付需求:合併幾個較小的單位,找到正好的零錢,或者使用比交易價值更大的單元並進行找零。所有這些花費UTXO的複雜操作都由用戶的錢包自動完成,對用戶不可見。只有在編寫程序構建來自UTXO的原始交易時才有意義。

交易消耗先前記錄的未使用的交易輸出,並創建可供未來交易使用的新交易輸出。這樣,大量的比特幣價值通過創建UTXO的交易鏈在所有者之間轉移。

輸出和輸入鏈的例外是稱為 幣基 coinbase 交易的特殊類型的交易,它是每個區塊中的第一個交易。這筆交易由"獲勝"的礦工設置,創建全新的比特幣並支付給該礦工作為挖礦獎勵。此特殊的coinbase交易不消費UTXO,相反,它有一種稱為"coinbase"的特殊輸入類型。這就是比特幣在挖礦過程中創造的貨幣數量,正如我們將在 [minig] 中看到的那樣。

Tip

先有的什麼?輸入還是輸出?雞還是雞蛋?嚴格地說,輸出是第一位的,因為產生新比特幣的幣基交易沒有輸入,是憑空產生的輸出。

交易輸出

每筆比特幣交易都產生輸出,這些輸出記錄在比特幣賬本上。除了一個例外(參見 [op_return] ),幾乎所有這些輸出都創造了稱為UTXO的可支付的比特幣,由整個網路認可並可供所有者在未來的交易中花費。

每個完整節點比特幣客戶端都跟蹤UTXO。新交易消耗(花費)UTXO集合的一個或多個輸出。

交易輸出由兩部分組成:

  • 一些比特幣,最小單位為 satoshis

  • 定義了花費這些輸出所需條件的加密謎題

這個謎題也被稱為 鎖定腳本 locking script見證腳本 witness script ,或者 scriptPubKey。

交易腳本和腳本語言 中詳細討論了前面提到的鎖定腳本中使用的交易腳本語言。

現在,我們來看看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顯示的編碼中,該值以比特幣為單位,但在交易本身中,它被記錄為以satoshis為單位的整數。每個輸出的第二部分是設置消費條件的加密謎題。 Bitcoin Core將其顯示為 scriptPubKey 並展示了該腳本的人類可讀的表示。

鎖定和解鎖UTXO的主題將在稍後的 創建腳本 ( 鎖定 + 解鎖 ) 中討論。在 交易腳本和腳本語言 中討論了 scriptPubKey 中使用的腳本語言。但在深入研究這些話題之前,我們需要了解交易輸入和輸出的總體結構。

交易序列化 —— 輸出

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

Table 1. Transaction output serialization
Size Field Description

8 位元組 (小端序)

數量 Amount

以聰(satoshis = 10-8 bitcoin) 為單位的比特幣價值

1——9 位元組 (VarInt)

鎖定腳本的大小 Locking-Script Size

後面的鎖定腳本的位元組數

變數

鎖定腳本 Locking-Script

定義花費該輸出的條件的腳本

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

從交易的位元組流表示轉換為庫的內部表示資料結構的過程稱為 反序列化 deserialization交易解析 transaction parsing 。轉換回位元組流以通過網路進行傳輸,進行雜湊或儲存在硬碟上的過程稱為 序列化 serialization。大多數比特幣庫具有用於交易序列化和反序列化的內置函數。

看看你是否可以從序列化的十六進制形式手動解碼Alice的交易,找到我們以前看到的一些欄位。兩個輸出部分在 Alice’s transaction, serialized and presented in hexadecimal notation 中突出顯示:

Example 1. Alice’s transaction, serialized and presented in hexadecimal notation

0100000001186f9f998a5aa6f048e51dd8419a14d8a0f1a8a2836dd73 4d2804fe65fa35779000000008b483045022100884d142d86652a3f47 ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039 ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813 01410484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade84 16ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc1 7b4a10fa336a8d752adfffffffff0260e31600000000001976a914ab6 8025513c3dbd2f7b92a94e0581f5d50f654e788acd0ef800000000000 1976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac 00000000

這裡有一些提示:

  • 突出顯示的部分有兩個輸出,每個輸出按照 Transaction output serialization 所示進行了序列化。

  • 0.015比特幣是1,500,000聰. 十六進制表示為 16 e3 60 .

  • 在序列化的交易中,16 e3 60 以小端序(低位位元組在前)編碼,所以看起來是: 60 e3 16。

  • scriptPubKey 的長度是 25 位元組, 十六進制表示為 19 。

交易輸入

交易輸入標識(通過引用)將使用哪個UTXO並通過解鎖腳本提供所有權證明。

為了建立交易,錢包從其控制的UTXO中選擇具有足夠價值的UTXO進行所請求的付款。有時候一個UTXO就足夠了,有時候需要多個UTXO。對於將用於進行此項付款的每個UTXO,錢包將創建一個指向UTXO的輸入,並使用解鎖腳本將其解鎖。

讓我們更詳細地看看輸入的組成部分。輸入的第一部分是指向UTXO的指針,引用交易的雜湊值和輸出索引,該索引標識該交易中特定的UTXO。第二部分是一個解鎖腳本,由錢包構建,為了滿足UTXO中設置的花費條件。大多數情況下,解鎖腳本是證明比特幣所有權的數位簽章和公鑰。但是,並非所有解鎖腳本都包含簽名。第三部分是序列號,稍後將進行討論。

考慮 交易背後 中的示例,交易的輸出是 vin 陣列:

The transaction inputs in Alice’s transaction
"vin": [
  {
    "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
    "vout": 0,
    "scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
    "sequence": 4294967295
  }
]

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

  • 交易ID,引用包含正在使用的UTXO的交易

  • 輸出索引( vout ),標識使用來自該交易的哪個UTXO(第一個從0開始)

  • scriptSig,滿足UTXO上的條件的腳本,用於解鎖並花費

  • 一個序列號(後面討論)

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

7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18

輸出索引 0(即由該交易創建的第一個UTXO)。解鎖腳本由Alice的錢包構建,首先檢索引用的UTXO,檢查其鎖定腳本,然後使用它構建必要的解鎖腳本以滿足它。

只看輸入內容,你可能已經注意到我們對這個UTXO一無所知,只有對包含它的交易的引用。我們不知道它的價值(satoshi的數量),也不知道設置花費條件的鎖定腳本。要找到這些訊息,我們必須通過檢索底層交易來檢索引用的UTXO。請注意,因為輸入值沒有明確說明,我們還必須使用引用的UTXO來計算將在此次交易中支付的費用(請參見 交易費用 )。

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

這些交易本身似乎不完整,因為它們缺乏上下文。他們在其輸入中引用UTXO,但不檢索該UTXO,我們不知道輸入值或鎖定條件。在編寫比特幣軟體時,只要你想要驗證交易,計算費用或檢查解鎖腳本,你的程式碼首先必須從區塊鏈中檢索引用的UTXO,以便構建輸入中引用的UTXO隱含但不包括的上下文。例如,要計算支付的費用金額,你必須知道輸入和輸出值的總和。如果不檢索輸入中引用的UTXO,則不知道它們的價值。因此,像單筆交易中計費的看似簡單的操作實際上涉及多個交易的多個步驟和數據。

我們可以使用在檢索Alice的交易時使用的相同的Bitcoin Core命令序列( getrawtransaction 和 decoderawtransaction )。得到前面輸入中引用的UTXO:

Alice’s UTXO from the previous transaction, referenced in the input
"vout": [
   {
     "value": 0.10000000,
     "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
   }
 ]

我們看到這個UTXO的值為 0.1 BTC,包含一個鎖定腳本( scriptPubKey ): "OP_DUP OP_HASH160…​".

Tip

為了完全理解Alice的交易,我們必須檢索輸入引用的交易。幾乎每個比特幣庫和API中都有一個函數,用於檢索以前的交易和未使用的交易輸出。

交易序列化 —— 輸入

當交易被序列化以便在網路上傳輸時,它們的輸入被編碼為位元組流,如 Transaction input serialization 所示。

Table 2. Transaction input serialization
Size Field Description

32 位元組

交易的雜湊值 Transaction Hash

指向包含要花費的UTXO的交易的指針

4 位元組

輸出的索引 Output Index

要花費的UTXO的索引,從0開始

1——9 位元組 (VarInt)

解鎖腳本的大小 Unlocking-Script Size

後面的解鎖腳本的位元組長度

變數

解鎖腳本 Unlocking-Script

滿足UTXO鎖定腳本條件的腳本

4 位元組

序列號 Sequence Number

用於鎖定時間(locktime)或禁用 (0xFFFFFFFF)

與輸出一樣,看看是否能夠在序列化格式中查找來自Alice的交易的輸入。首先,解碼的輸入如下:

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

現在,看看我們是否可以在 Alice’s transaction, serialized and presented in hexadecimal notation 中的序列化的十六進制編碼中識別這些欄位:

Example 2. Alice’s transaction, serialized and presented in hexadecimal notation

0100000001186f9f998a5aa6f048e51dd8419a14d8a0f1a8a2836dd73 4d2804fe65fa35779000000008b483045022100884d142d86652a3f47 ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039 ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813 01410484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade84 16ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc1 7b4a10fa336a8d752adfffffffff0260e31600000000001976a914ab6 8025513c3dbd2f7b92a94e0581f5d50f654e788acd0ef800000000000 1976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac00000 000

提示:

  • 交易ID是以反向位元組順序序列化的,因此它以(十六進制)18 開頭並以 79 結尾

  • 輸出索引是一個4位元組的零,容易識別

  • scriptSig 的長度為139個位元組,十六進制的 8b

  • 序列號設置為 FFFFFFFF,也易於識別

交易費用

大多數交易包括交易費用,以獎勵比特幣礦工,保證網路安全。費用本身也可以作為一種安全機制,因為攻擊者通過大量交易充斥網路在經濟上是不可行的。 [mining] 更詳細地討論了礦工以及礦工收取的費用和獎勵。

本節探討交易費用如何包含在典型的交易中。大多數錢包會自動計算幷包含交易費用。但是,如果你以寫程式的方式構建交易或使用命令行界面,則必須手動進行計算幷包含這些費用。

交易費用是將交易納入下一個區塊的激勵措施,也是對每次交易徵收小額費用以抵制系統濫用的防範機制。交易費由礦工收集,該礦工將開採在區塊鏈上記錄交易的區塊。

交易費用是以交易數據的大小(KB)計算的,而不是比特幣交易的價值。總體而言,交易費用是根據比特幣網路內的市場力量設定的。礦工根據許多不同的優先條件(包括費用)處理交易,也可能在某些情況下免費處理交易。交易費用會影響處理優先權,這意味著如果交易費用足夠,交易就可能包含在下一個開採區塊中,而費用不足或不收費的交易可能會延遲,在幾個區塊後以盡力而為的方式處理,或者根本不處理。交易費用不是強制性的,沒有費用的交易最終可以被處理;但是,包括交易費用鼓勵優先處理。

隨著時間的推移,交易費用的計算方式以及它們對交易優先級的影響已經發生了變化。起初,交易費用在整個網路中是固定不變的。逐漸地,收費結構放鬆,並可能受到基於網路容量和交易量的市場力量的影響。至少從2016年初開始,比特幣的容量限制已經造成了交易之間的競爭,導致了更高的費用,使免費的交易成為了歷史。免費或低費用的交易很少能被開採,有時甚至不會通過網路傳播。

在Bitcoin Core中,收費中繼策略由 minrelaytxfee 選項設置。當前的預設值是每KB數據0.00001比特幣或0.01毫比特幣。因此,預設情況下,低於0.00001比特幣的交易將被視為免費,並且只在Memory pool有空間時才會被中轉;否則,它們將被丟棄。比特幣節點可以通過調整 minrelaytxfee 的值來覆蓋預設的收費中繼策略。

任何創建交易的比特幣服務,包括錢包,交易所,零售應用等,都 必須 實施動態費用。動態費用可以通過第三方費用估算服務或內置費用估算演算法來實現。如果你不確定,請先從第三方服務開始,如果你希望移除第三方依賴關係,設計並實現自己的演算法。

費用估算演算法根據容量和"競爭"交易提供的費用計算適當的費用。這些演算法的從簡單(最後一個區塊的平均費用或中值費用)到複雜(統計分析)。他們估計必要的費用(每字節多少satoshis),使交易被選中幷包含在一定數量的區塊內的可能性很高。大多數服務為用戶提供選擇高、中、低優先級費用的選項。高優先級意味著用戶支付更高的費用,但交易很可能包含在下一個區塊中。中等和低優先級意味著用戶支付較低的交易費用,但交易可能需要更長時間才能確認。

許多錢包應用使用第三方服務計算費用。一種流行的服務是 http://bitcoinfees.21.co,它提供了一個API和一個可視圖表,顯示了不同優先級的 satoshi/位元組 費用。

Tip

比特幣網路上的固定費用已不再可行。設置固定費用的錢包將產生糟糕的用戶體驗,因為交易通常會"卡住",不被驗證。不瞭解比特幣交易和費用的用戶會因為"停滯的"交易感到沮喪,他們會認為錢已經丟失了。

Fee estimation service bitcoinfees.21.co 中的圖表以10 satoshi/位元組的增量顯示實時的費用估算值,以及每個費用範圍內的預期確認時間(以分鐘和區塊數表示)。對於每個費用範圍(例如,61-70 satoshi/位元組),兩個橫條顯示了未確認交易的數量(1405)和過去24小時內的交易總數(102,975)。根據圖表,此時建議的高優先級費用為 80 satoshi /位元組,可能使交易在下一個區塊中開採(0區塊延遲)。交易規模的中位數為226位元組,所以此交易規模的建議費用為 18,080 satoshis(0.00018080 BTC)。

費用估算數據可以通過簡單的HTTP REST API檢索, https://bitcoinfees.21.co/api/v1/fees/recommended. 例如,在命令行中使用 curl 命令:

Using the fee estimation API
$ curl https://bitcoinfees.21.co/api/v1/fees/recommended

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

API返回一個帶有當前費用估計的JSON物件,包含最快速度確認( fasterFee ),三個區塊內確認( halfHourFee )和六個區塊內確認( hourFee )的費用,單位是 satoshi/位元組。

Fee Estimation Service bitcoinfees.21.co
Figure 2. Fee estimation service bitcoinfees.21.co

將費用添加到交易

交易的資料結構沒有費用欄位。相反,費用隱含表示為輸入總和與輸出總和的差額。從所有輸入中扣除所有輸出後剩餘的金額都是礦工收取的費用:

Transaction fees are implied, as the excess of inputs minus outputs:
Fees = Sum(Inputs) – Sum(Outputs)

這是一個有點令人困惑的交易元素,也是需要理解的重要一點,因為如果你正在構建自己的交易,則必須確保你不會花費了很少的輸入卻無意中包含非常高的費用。這意味著你必須考慮所有輸入,必要時創建找零,否則最終會給礦工一個非常高的小費!

例如,如果你使用20比特幣UTXO進行1比特幣支付,則必須將19比特幣零錢輸出回你的錢包。否則,19比特幣將被算作交易費用,並將由礦工在一個區塊中進行交易。雖然你會得到優先處理並讓礦工很高興,但這可能不是你想要的。

Warning

如果你忘記在手動構建的交易中添加找零輸出,則你將支付零錢作為交易費用。 "不用找了!" 可能不是你想要的。

我們再來看看Alice購買咖啡的情況,看看它在實踐中是如何運作的。 Alice 想花0.015比特幣來買咖啡。為確保此交易得到及時處理,她希望包含交易費用,例如0.001。這意味著交易的總成本將是0.016。她的錢包因此必須提供一些UTXO,加起來0.016比特幣或更多,如有必要,可以創建找零。假設她的錢包有一個0.2比特幣的UTXO。因此,它需要消費這個UTXO,創建一個給Bob 0.015的輸出,和一個0.184比特幣的零錢輸出,返回她自己的錢包,剩下0.001比特幣未分配,作為隱含的交易費用。

現在讓我們看看不同的場景。菲律賓的兒童慈善總監Eugenia已經完成了為兒童購買教科書的籌款活動。她收到了來自世界各地的數千人的小額捐款,共計50比特幣,所以她的錢包充滿了非常多的小額未使用輸出(UTXO)。現在她想從本地出版商處購買數百本教科書,用比特幣支付。

Eugenia的錢包應用試圖構建一個較大的付款交易,因此它必須從可用的小金額UTXO集合中獲取資金。這意味著由此產生的交易將有超過一百個小型UTXO輸入,只有一個輸出支付給書籍出版商。具有許多輸入的交易將大於一千位元組,也許幾千位元組大小。因此,它需要比中等規模交易高得多的費用。

Eugenia的錢包應用程式將通過衡量交易規模並將其乘以每千位元組的費用來計算適當的費用。許多錢包會為較大的交易多付費用,以確保交易得到及時處理。較高的費用並不是因為Eugenia花費更多的錢,而是因為她的交易規模更大更復雜 - 收費與交易的比特幣價值無關。

交易腳本和腳本語言

比特幣交易腳本語言,稱為 Script ,是一種類似Forth的逆波蘭表示法的基於堆疊的執行語言。如果這聽起來像是胡言亂語,那麼你可能沒有研究過60年代的程式語言,但沒關係 - 我們將在本章中解釋它。放置在UTXO上的鎖定腳本和解鎖腳本都是用這種腳本語言編寫的。當一個交易被驗證時,每個輸入中的解鎖腳本將與相應的鎖定腳本一起執行,以查看它是否滿足花費條件。

腳本是一種非常簡單的語言,在有限的範圍內設計,可在一系列硬體上執行,可能與嵌入式設備一樣簡單。它只需要很少的處理,並且不能完成許多現代程式語言能夠做的事情。為了用於驗證可程式化的金錢,這是一個深思熟慮的安全特性。

今天,大多數通過比特幣網路處理的交易具有"支付給Bob的比特幣地址"的形式,並且基於稱為 Pay-to-Public-Key-Hash(付費到公鑰雜湊) 的腳本。但是,比特幣交易不限於"支付給Bob的比特幣地址"類型的腳本。事實上,可以編寫鎖定腳本來表達各種複雜的條件。為了理解這些更復雜的腳本,我們必須首先了解交易腳本和腳本語言的基礎知識。

在本節中,我們將演示比特幣交易腳本語言的基本組件,並說明如何使用它來表達簡單的花費條件以及解鎖腳本如何滿足這些條件。

Tip

比特幣交易驗證不是基於靜態模式的,而是通過執行腳本語言來實現的。這種語言允許表示幾乎無限的各種條件。這就是比特幣如何獲得"可程式化金錢"力量的。

圖靈不完備

比特幣交易腳本語言包含許多操作符,但是故意在一個重要方面進行了限制 - 除了條件控制外,沒有迴圈或複雜的流程控制功能。這確保語言不是 圖靈完備 Turing Complete 的,這意味著腳本具有有限的複雜性和可預測的執行時間。腳本不是通用語言。這些限制確保了該語言不能用於創建無限迴圈或其他形式的"邏輯炸彈",這種"邏輯炸彈"可能嵌入交易中,導致對比特幣網路的拒絕服務攻擊。請記住,每筆交易都由比特幣網路上的每個完整節點驗證。有限制的語言會阻止交易驗證機制被當作漏洞。

無狀態驗證

比特幣交易腳本語言是無狀態的,在執行腳本之前沒有狀態,在執行腳本之後也不保存狀態。因此,執行腳本所需的所有訊息都包含在腳本中。腳本在任何系統上都能可預測地執行。如果你的系統驗證了腳本,你可以確定比特幣網路中的其他每個系統都會驗證該腳本,這意味著有效的交易對每個人都有效,每個人都知道這一點。結果的可預測性是比特幣系統的一個重要好處。

創建腳本 ( 鎖定 + 解鎖 )

比特幣的交易驗證引擎依靠兩種類型的腳本來驗證交易:鎖定腳本和解鎖腳本。

鎖定腳本是放置在輸出上的花費條件:它指定將來要花費輸出必須滿足的條件。由於歷史原因,鎖定腳本被稱為 scriptPubKey ,因為它通常包含公鑰或比特幣地址(公鑰的雜湊)。在本書中,我們將其稱為"鎖定腳本",以表示此腳本技術更廣泛的可能性。在大多數比特幣應用中,我們所稱的鎖定腳本將作為 scriptPubKey 出現在源程式碼中。你還會看到被稱為 witness script 的鎖定腳本(參見 [segwit])或更一般地稱為 cryptographic puzzle 。這些術語在不同的抽象層次代表著相同的東西。

解鎖腳本是可以"解決"或滿足鎖定腳本放置到輸出上的條件,從而花費輸出的腳本。解鎖腳本是每個交易輸入的一部分。大多數情況下,它們包含用戶錢包利用私鑰生成的數位簽章。由於歷史原因,解鎖腳本被稱為 scriptSig ,因為它通常包含數位簽章。在大多數比特幣應用中,源程式碼將解鎖腳本稱為 scriptSig 。你還將看到稱為 witness 的解鎖腳本(參見[segwit])。在本書中,我們將其稱為"解鎖腳本"來表示更廣泛的鎖定腳本,因為並非所有解鎖腳本都必須包含簽名。

每個比特幣驗證節點通過一起執行鎖定和解鎖腳本來驗證交易。每個輸入都包含一個解鎖腳本,並引用先前存在的UTXO。驗證軟體將複製解鎖腳本,檢索輸入引用的UTXO,並從該UTXO複製鎖定腳本。然後按順序執行解鎖和鎖定腳本。如果解鎖腳本滿足鎖定腳本條件,則輸入有效(參見 單獨執行解鎖和鎖定腳本 )。所有輸入都是作為交易整體驗證的一部分獨立驗證的。

請注意,UTXO永久記錄在區塊鏈中,因此不會改變,也不會因為在新交易中花費它的失敗嘗試而受到影響。只有正確滿足輸出條件的有效交易才會導致輸出被視為"已花費"並從未使用的交易輸出集和(UTXO集)中移除。

Combining scriptSig and scriptPubKey to evaluate a transaction script 是最常見類型的比特幣交易(支付到公鑰的雜湊)的解鎖和鎖定腳本示例,顯示了在腳本驗證之前將解鎖腳本和鎖定腳本連接在一起所產生的組合腳本。

scriptSig_and_scriptPubKey
Figure 3. Combining scriptSig and scriptPubKey to evaluate a transaction script
腳本執行堆疊

比特幣的腳本語言稱為基於堆疊的語言,因為它使用稱為 堆疊 stack 的資料結構。堆疊是一個非常簡單的資料結構,可以將其視為一疊卡片。一個堆疊允許兩個操作:push和pop。Push會在堆疊頂部添加一個項目。 Pop從堆疊中刪除頂部的項目。堆疊上的操作只能作用於堆疊中最頂端的項目。堆疊資料結構也稱為後進先出或"LIFO"隊列。

腳本語言通過從左向右處理每個項目來執行腳本。"數字"(數據常量)被push進入堆疊。"操作"從堆疊中pop一個或多個參數,執行操作,並可能將結果push到堆疊。例如,OP_ADD 會從堆疊中彈出兩個項目,做加法,並將結果push到堆疊上。

條件運算符評估一個條件,產生TRUE或FALSE的布林結果。例如,OP_EQUAL pop堆疊中的兩個項目,如果它們相等,則push TRUE(TRUE由數字1表示),如果不相等,則push FALSE(由零表示)。比特幣交易腳本通常包含一個條件操作符,以便它們可以生成表示有效交易的TRUE結果。

一個簡單的腳本

現在讓我們將有關腳本和堆疊的知識應用於一些簡單的示例。

Bitcoin’s script validation doing simple math 中,腳本 2 3 OP_ADD 5 OP_EQUAL 演示了算術加法運算符 OP_ADD,將兩個數字相加並將結果放在堆疊上,後面跟著條件運算符 OP_EQUAL,它檢查結果總和是否相等到 5 。為簡潔起見,在示例中省略了 OP_ 前綴。有關可用腳本運算符和函數的更多詳細訊息,請參見 [tx_script_ops]

雖然大多數鎖定腳本都是指公鑰雜湊(本質上是比特幣地址),因此需要所有權證明來支付資金,腳本並不一定非常複雜。生成TRUE值的鎖定和解鎖腳本的任何組合都是有效的。我們用作腳本語言示例的簡單算術也是一個有效的鎖定腳本,可用於鎖定交易輸出。

使用算術示例腳本的一部分作為鎖定腳本:

3 OP_ADD 5 OP_EQUAL

可以被包含以下解鎖腳本的交易滿足:

2

驗證軟體將鎖定和解鎖腳本結合在一起:

2 3 OP_ADD 5 OP_EQUAL

正如我們在 Bitcoin’s script validation doing simple math 中的示例中看到的,執行此腳本時,結果為 OP_TRUE,交易有效。這不僅是一個有效的交易輸出鎖定腳本,而且由此產生的UTXO可以被具有任何知道數字2滿足腳本的人花費。

Tip

如果堆疊頂層結果為 TRUE( 標記為 {0x01} ),任何其他非零值,或者腳本執行後堆疊為空,則交易有效。如果堆疊頂部的值為 FALSE(一個零長度的空值,標記為{}),或者腳本被運算符顯式終止了,例如 OP_VERIFY,OP_RETURN 或一個條件終止符,如 OP_ENDIF,則交易無效。詳細訊息,請參見 [tx_script_ops]

TxScriptSimpleMathExample
Figure 4. Bitcoin’s script validation doing simple math

以下是一個稍微複雜的腳本,計算 2 + 7 - 3 + 1 。請注意,當腳本在一行中包含多個運算符時,堆疊允許一個運算符的結果由下一個運算符執行:

2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL

嘗試使用筆和紙驗證前面的腳本。當腳本執行結束時,在堆疊中應該保留值 TRUE。

單獨執行解鎖和鎖定腳本

在原始的比特幣客戶端中,解鎖和鎖定腳本按順序連接並執行。出於安全原因,2010年發生了變化,原因是存在一個漏洞,允許惡意解鎖腳本將數據推送到堆疊並破壞鎖定腳本。在當前的實現中,如下所述,腳本是在兩次執行之間傳輸堆疊的情況下單獨執行的。

首先,使用堆疊執行引擎執行解鎖腳本。如果解鎖腳本沒有錯誤地執行(例如,它沒有遺留的"懸掛(dangling)"操作符),則複製主堆疊並執行鎖定腳本。如果使用從解鎖腳本複製的堆疊數據執行鎖定腳本的結果為"TRUE",則解鎖腳本已成功解決由鎖定腳本施加的條件,證明該輸入是用於花費UTXO的有效授權。如果在執行組合腳本後仍然存在除"TRUE"之外的結果,則輸入無效,因為它未能滿足放置在UTXO上的消費條件。

支付到公鑰雜湊 Pay-to-Public-Key-Hash (P2PKH)

在比特幣網路上處理的絕大多數交易花費由支付到公鑰雜湊(P2PKH)鎖定的輸出這些輸出包含一個鎖定腳本。這些輸出包含將它們鎖定到公鑰雜湊(比特幣地址)的腳本。由P2PKH腳本鎖定的輸出可以通過出示公鑰,和由相應私鑰創建的數位簽章來解鎖(花費)( 參見 數位簽章 (ECDSA) )。

例如,讓我們再看看Alice對Bob’s Cafe的付款。Alice向咖啡廳的比特幣地址支付了0.015比特幣。該交易輸出將具有以下形式的鎖定腳本:

OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG

Cafe Public Key Hash 等同於咖啡館的比特幣地址,沒有Base58Check編碼。大多數應用程式會以十六進制編碼顯示 public key hash ,而不是以"1"開頭的大家熟悉的比特幣地址Base58Check格式。

上述鎖定腳本可以由以下形式的解鎖腳本滿足:

<Cafe Signature> <Cafe Public Key>

這兩個腳本組合在一起形成以下的驗證腳本:

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

執行時,只有在解鎖腳本與鎖定腳本設置的條件匹配時,此組合腳本才會輸出TRUE。換句話說,如果解鎖腳本具有來自咖啡館的私鑰的有效簽名,該公鑰對應於公鑰雜湊集合作為負擔,則結果為TRUE。

#P2PubKHash1#P2PubKHash2 顯示(分兩部分)了逐步執行的組合腳本,證明這是一個有效的交易。

Tx_Script_P2PubKeyHash_1
Figure 5. Evaluating a script for a P2PKH transaction (part 1 of 2)
Tx_Script_P2PubKeyHash_2
Figure 6. Evaluating a script for a P2PKH transaction (part 2 of 2)

數位簽章 (ECDSA)

到目前為止,我們還沒有深入探討"數位簽章"的細節。在本節中,我們將探討數位簽章如何工作,以及如何在不洩露私鑰的情況下提供私鑰的所有權證明。

比特幣中使用的數位簽章演算法是 Elliptic Curve Digital Signature AlgorithmECDSA 。 ECDSA是用於基於橢圓曲線私鑰/公鑰對的數位簽章的演算法,如 [elliptic_curve] 中所述。 ECDSA由腳本函數 OP_CHECKSIG,OP_CHECKSIGVERIFY,OP_CHECKMULTISIG 和 OP_CHECKMULTISIGVERIFY 使用。無論何時,你在鎖定腳本中看到這些腳本的話,解鎖腳本都必須包含ECDSA簽名。

數位簽章在比特幣中有三個用途(參見下面的邊欄)。首先,簽名證明私鑰的所有者,暗示資金的所有者,已經 授權 支出這些資金。其次,授權證明是 不可否認的 undeniable(nonrepudiation)。第三,簽名證明交易(或交易的特定部分)在簽名後沒有也不能被任何人修改。

請注意,交易的每個輸入都是獨立簽署的。這是至關重要的,因為簽名和輸入都不必屬於同一個"所有者"或被其使用。事實上,一個名為"CoinJoin"的特定交易方案利用這一事實來創建隱私的多方交易。

Note

交易的每個輸入及其可能包含的任何簽名完全獨立於任何其他輸入或簽名。多方可以協作構建交易並各自簽署一個輸入。

Wikipedia’s Definition of a "Digital Signature"

A digital signature is a mathematical scheme for demonstrating the authenticity of a digital message or documents. A valid digital signature gives a recipient reason to believe that the message was created by a known sender (authentication), that the sender cannot deny having sent the message (nonrepudiation), and that the message was not altered in transit (integrity).

數位簽章如何工作

數位簽章是由兩部分組成的數學模式 mathematical scheme。第一部分是使用私鑰(簽名密鑰)從訊息(交易)創建簽名的演算法。第二部分是,允許任何人使用訊息和公鑰驗證簽名的演算法

創建數位簽章

在比特幣的ECDSA演算法實現中,被簽名的"訊息"是交易,或者更準確地說是交易中特定數據子集的雜湊(參見 簽名雜湊的類型 (SIGHASH) )。簽名密鑰是用戶的私鑰。結果是如下簽名:

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

其中:

  • dA 是簽名私鑰

  • m 是交易(或交易的一部分)

  • Fhash 是雜湊函數

  • Fsig 是簽名演算法

  • Sig 是簽名結果

更多關於ECDSA的細節可以在 ECDSA 數學 中找到。

Fsig 方法生成簽名 Sig ,由兩部分組成: R 和 S:

Sig = (R, S)

現在已經計算了兩個值+ R 和 S +,它們使用稱為 Distinguished Encoding RulesDER 的國際標準編碼方案序列化為位元組流。

簽名的序列化 (DER)

讓我們再看一下Alice創建的交易。在交易輸入中有一個解鎖腳本,其中包含來自Alice錢包的DER編碼簽名:

3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301

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

  • 0x30 —— 標識 DER 序列的開始

  • 0x45 —— 序列長度 (69 bytes)

  • 0x02 —— 接下來是一個整數

  • 0x21 —— 整數的長度 (33 bytes)

  • R —— 00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb

  • 0x02 —— 接下來是另一個整數

  • 0x20 —— 另一個整數的長度 (32 bytes)

  • S —— 4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813

  • 一個後綴 (0x01) 標識使用的雜湊類型 (SIGHASH_ALL)

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

驗證簽名

要驗證簽名,必須拿到簽名( R 和 S ),序列化交易和公鑰(對應的用於創建簽名的私鑰)。實質上,對簽名的驗證意味著"只有生成此公鑰的私鑰的所有者才能在此交易上產生此簽名"。

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

簽名雜湊的類型 (SIGHASH)

數位簽章是應用於訊息的,對比特幣來說,訊息就是交易。簽名意味著簽名者對具體交易數據的 保證 commitment 。最簡單的形式是,簽名應用於整個交易,從而保證所有輸入,輸出和其他交易欄位。但是,簽名也可以只保證交易中的一部分數據,在許多場景下很有用,我們將在本節中看到。

比特幣的簽名可以使用 SIGHASH 指示交易數據的哪部分包含在由私鑰簽名的雜湊中。SIGHASH 標誌是附加到簽名後面的單個位元組。每個簽名都有一個 SIGHASH 標誌,並且該標誌對於不同輸入是不同的。具有三個簽名輸入的交易可以具有三個不同的帶有 SIGHASH 標誌的簽名,每個簽名簽署(保證)交易的不同部分。

請記住,每個輸入都能在其解鎖腳本中包含一個簽名。因此,包含多個輸入的交易可能具有不同的帶有 SIGHASH 標誌的簽名,這些標誌會在每個輸入中保證交易的不同部分。還要注意的是,比特幣交易可能包含來自不同"所有者"的輸入,他們可能在部分構建的(無效的)交易中僅簽署一個輸入,需要其他人合作收集所有必要的簽名才能進行有效交易。許多 SIGHASH 標誌類型只有在你認為多位參與者在比特幣網路之外協作並各自更新部分簽名的交易時才有意義。

有三種 SIGHASH 標誌: ALL, NONE, 和 SINGLE, 如 SIGHASH types and their meanings 所示。

Table 3. SIGHASH types and their meanings
SIGHASH flag Value Description

ALL

0x01

簽名應用於所有輸入和輸出。

NONE

0x02

簽名應用於所有輸入,不包括任何輸出

SINGLE

0x03

簽名應用於所有輸入,但僅應用於與簽名輸入具有相同索引編號的一個輸出

另外,還有一個修飾符標誌 SIGHASH_ANYONECANPAY,它可以與前面的每個標誌結合使用。當設置了 ANYONECANPAY 時,只有一個輸入被簽名,剩下的(及其序列號)保持開放可以修改。 ANYONECANPAY 的值為 0x80,並按位OR應用,生成組合的標誌,如 SIGHASH types with modifiers and their meanings 所示。

Table 4. SIGHASH types with modifiers and their meanings
SIGHASH flag Value Description

ALL|ANYONECANPAY

0x81

簽名應用於一個輸入和所有輸出

NONE|ANYONECANPAY

0x82

簽名應用於一個輸入,不應用於輸出

SINGLE|ANYONECANPAY

0x83

簽名應用於一個輸入和有相同索引號的輸出

在簽名和驗證過程中應用 SIGHASH 標誌的方式是創建交易的副本,將內部的某些欄位截斷(設置長度為零並清空)。將產生的交易序列化。將 SIGHASH 標誌添加到序列化交易的末尾,並對結果進行雜湊雜湊。雜湊本身就是被簽名的"訊息"。根據使用哪個 SIGHASH 標誌,交易的不同部分被截斷。結果雜湊取決於交易中數據的不同子集。在雜湊之前最後一步包含了 SIGHASH ,簽名也保證了 SIGHASH 類型,不能被(礦工)改變。

Note

所有 SIGHASH 類型都簽署了交易的 nLocktime 欄位(請參見 [transaction_locktime_nlocktime])。另外,SIGHASH 類型本身在簽名之前附加到交易中,在簽名後不能修改。

在Alice的交易示例中(請參見 簽名的序列化 (DER) 中的列表),我們看到DER編碼簽名的最後一部分是 01 ,它是 SIGHASH_ALL 標誌。這會鎖定交易數據,所以Alice的簽名會保證所有輸入和輸出的狀態。這是最常見的簽名形式。

讓我們看看其他類型的 SIGHASH 以及它們如何在實踐中使用:

ALL|ANYONECANPAY

這種結構可以用來進行"眾籌"式的交易。試圖籌集資金的人可以創建一個單一輸出的交易。單一輸出向資金籌集人支付"目標"金額。這樣的交易顯然是無效的,因為它沒有輸入。現在,其他人可以通過添加自己的輸入來進行修改這筆交易,作為捐贈。他們用 ALL|ANYONECANPAY 來簽名自己的輸入。除非收集到足夠的投入,達到輸出的價值,否則交易無效。每一筆捐款都是一種"承諾/抵押",在籌集到目標金額之前,籌款不能收回。

NONE

這種結構可用於創建特定數量的"不記名支票"或"空白支票"。它交付輸入,但允許更改輸出鎖定腳本。任何人都可以將自己的比特幣地址寫入輸出鎖定腳本並贖回資金。但是,輸出值本身被簽名鎖定。

NONE|ANYONECANPAY

這種結構可以用來建立一個"集塵器"。錢包裡有微型UTXO的用戶,如果不支付超過灰塵價值的費用,就無法消費這些東西。有了這種簽名,微型UTXO可以捐贈給任何人,聚集並在任何時候花費它們。

有一些關於修改或擴展 SIGHASH 系統的建議。其中一個是 Blockstream 的 Glenn Willen 提出的 BitTek Sighash Modes ,是 Elements 項目的一部分。它旨在創建一個靈活的 SIGHASH 類型替代方案,允許"輸入和輸出的任意的,礦工可重寫的位掩碼",可以表達"更復雜的合同預先承諾方案,例如在分佈式資產交換中籤署帶有更改的報價"。

Note

你不會在用戶的錢包應用程式中看到+ SIGHASH 標誌選項。除了少數例外,錢包構建P2PKH腳本並使用 +SIGHASH_ALL 標誌進行簽名。要使用不同的 SIGHASH 標誌,你必須編寫軟體來創建和簽署交易。更重要的是,SIGHASH 標誌可以被特殊用途的比特幣應用程式使用,實現新用途。

ECDSA 數學

如前所述,簽名是由一個數學函數 Fsig 創建的,產生由兩個值 RS 組成的簽名。在本節中,我們將更詳細地討論函數 Fsig

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

臨時密鑰對基於隨機數 k ,也就是臨時私鑰。從 k 開始,我們生成相應的臨時公鑰 P(按照P = k * G計算,與比特幣公鑰的生成方式相同;參見 [pubkey] )。數位簽章的 R 值就是臨時公鑰 P 的 x 座標。

演算法計算簽名的S值,如下:

S = k-1 (Hash(m) + dA * R) mod p

其中:

  • k 是臨時私鑰

  • R 是臨時公鑰的 x 座標

  • dA 是簽名私鑰

  • m 是交易數據

  • p 是橢圓曲線的主要階數

"驗證"是簽名生成函數的反函數,使用 RS 值和公鑰來計算一個值 P,它是橢圓曲線上的一個點(簽名創建中使用的臨時公鑰):

P = S-1 * Hash(m) * G + S-1 * R * Qa

where:

  • RS 是簽名的值

  • Qa 是Alice的公鑰

  • m 是被簽名的交易數據

  • G 是橢圓曲線的生成點

如果計算點 P 的 x 座標等於 R ,那麼驗證者可以推斷簽名是有效的。

請注意,在驗證簽名時,沒有用到私鑰,也不會被洩露。

Tip

ECDSA是一門相當複雜的數學;完整的解釋超出了本書的範圍。許多優秀的在線指南會一步一步地講解它:搜索"ECDSA解釋"或嘗試這一個:http://bit.ly/2r0HhGB[]。

隨機性在簽名中的重要性

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

Warning

如果在兩個不同交易的簽名演算法中使用相同的 k ,則可以計算私鑰並將其公開給全世界!

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

為避免此漏洞,最佳做法是不生成帶有熵的隨機數生成器的 k,而是使用通過交易數據本身作為種子的確定性隨機過程。這確保每筆交易產生不同的 kk 的確定性初始化的行業標準演算法在 Internet Engineering Task Force 發佈的 RFC 6979 中定義。

如果你正在實施一種演算法來簽署比特幣交易,你必須使用RFC 6979或類似的確定性隨機演算法來確保你為每筆交易生成不同的 k

比特幣地址,餘額和其他抽象

我們發現交易在"幕後"看起來與它們在"錢包",區塊鏈瀏覽器,和其他面向用戶的應用程式中的呈現方式非常不同。交易的結構中似乎沒有來自前幾章的許多簡單和熟悉的概念,比如比特幣地址和餘額。我們看到交易本身不包含比特幣地址,而是通過鎖定和解鎖比特幣的離散值的腳本進行操作。餘額不存在於此係統的任何位置,但每個錢包應用程式會突出顯示用戶錢包的餘額。

現在我們已經研究了實際包含在比特幣交易中的內容,我們可以研究更高層次的抽象是如何從交易的看似原始的組成部分中獲得的。

讓我們再看看Alice的交易是如何在區塊鏈瀏覽器( Alice’s transaction to Bob’s Cafe )上展示的。

Alice Coffee Transaction
Figure 7. Alice’s transaction to Bob’s Cafe

在交易左側,區塊鏈瀏覽器顯示Alice的比特幣地址為"發件人"。事實上,這些訊息並不在交易本身中。當區塊鏈瀏覽器檢索到該交易時,它還檢索到輸入中引用的前一個交易,並從這個之前的交易中提取第一個輸出。該輸出中是一個鎖定腳本,將UTXO鎖定到Alice的公鑰雜湊(一個P2PKH腳本)。區塊鏈瀏覽器提取公鑰雜湊並使用Base58Check編碼對其進行編碼,以生成並顯示代表該公鑰的比特幣地址。

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

如果你點擊了Bob的比特幣地址,區塊鏈瀏覽器會顯示 The balance of Bob’s bitcoin address 中的視圖。

The balance of Bob's bitcoin address
Figure 8. The balance of Bob’s bitcoin address

區塊鏈瀏覽器顯示Bob的比特幣地址的餘額。但比特幣系統中沒有任何地方存在"餘額"的概念。這裡顯示的值是由區塊鏈瀏覽器構建的,如下所示。

為了構建"總共收到的"金額,區塊鏈瀏覽器首先解碼比特幣地址的Base58Check編碼,以檢索編碼在地址中的Bob的公鑰的160位雜湊。然後,區塊鏈瀏覽器將搜索交易資料庫,尋找包含Bob公鑰雜湊P2PKH鎖定腳本的輸出。通過彙總所有輸出的值,區塊鏈瀏覽器可以產生收到的總價值。

構建當前餘額(顯示為"最終餘額 Final Balance")需要更多的工作。區塊鏈瀏覽器維護了目前未使用的輸出的單獨的資料庫,即UTXO集。為了維護此資料庫,區塊鏈瀏覽器必須實時監控比特幣網路,添加新創建的UTXO,並實時刪除已花費的UTXO,當它們出現在未經確認的交易中時。這是一個複雜的過程,它依賴於跟蹤交易的傳播過程,以及與比特幣網路保持一致,以確保遵循正確的鏈條。有時,區塊鏈瀏覽器不同步,並且其UTXO集的視角不完整或不正確。

從UTXO集合中,區塊鏈瀏覽器彙總所有引用Bob的公鑰雜湊值的未使用輸出的值,併產生顯示給用戶的"最終餘額"數字。

為了製作這一張帶兩個"餘額"圖片,區塊鏈瀏覽器必須對幾十,幾百甚至幾十萬的交易進行索引和搜索。

總之,錢包應用程式,區塊鏈瀏覽器和其他比特幣用戶界面呈現給用戶的訊息通常由更高級別的抽象組成,這些抽象通過搜索許多不同的交易,檢查其內容並操縱其中包含的數據而派生。為了呈現這種簡單的比特幣交易視圖,類似於從一個發件人到一個收件人的銀行支票,這些應用程式必須抽象許多底層細節。他們主要關注常見類型的交易:P2PKH 和 SIGHASH_ALL 在每個輸入上簽名。因此,雖然比特幣應用程式可以以易於閱讀的方式呈現超過80%的交易,但它們有時會被偏離規範的交易所難倒。包含更復雜的鎖定腳本,或不同的 SIGHASH 標誌,或許多輸入和輸出的交易,表明了這些抽象的簡單性和缺陷。

每天,在區塊鏈中確認數百個不包含P2PKH輸出的交易。區塊鏈瀏覽器通常會用紅色警告訊息顯示他們無法解碼地址。以下鏈接包含未完全解碼的最新的"奇怪交易":[https://www.blockchain.com/btc/strange-transactions](https://www.blockchain.com/btc/strange-transactions) 。

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