# 第10章 挖礦
## 10.1 簡介
“挖礦”這個詞有點誤導。一般意義的挖礦類似貴金屬的提煉,于是人們將更多的注意力集中到挖礦的回報,也就是每個區塊創造的新比特幣。雖然挖礦行為會被這種獎勵所激勵,但挖礦的主要目的不是這個獎勵或者產生新幣。如果你只把挖礦看作是創比特幣的過程,那么你就會誤把這個過程中的手段(作為激勵)作為目標。挖礦是一種去中心化的交易清算機制,通過這種機制,交易得到驗證和清算。挖礦是使得比特幣與眾不同的發明,是一種去中心化的安全機制,是點對點數字現金的基礎。
挖礦*確保了比特幣系統安全*,并且在全網范圍實現了*沒有中央權威機構的共識*。挖出新幣以及交易費的獎勵是一種激勵機制,它將礦工的行為與網絡的安全性結合起來,同時實施貨幣供應。
>**提示**:挖礦的目的不是創造新的比特幣。這是激勵機制。 挖礦是一種使比特幣的*安全security去中心化decentralized*的機制。
礦工驗證新交易并將其記錄在全球分類賬簿上。每10分鐘就會有一個新的區塊被“挖掘”出來,這個新區塊里包含著從上一個區塊產生到目前這段時間內發生的交易,這樣這些交易就被添加到區塊鏈中了。我們把包含在區塊內且被添加到區塊鏈上的交易稱為“已確認confirmed”交易,交易經過“確認”之后,新的擁有者才能夠花費他在交易中收到的比特幣。
礦工們在挖礦過程中會得到兩種類型的獎勵:創建新區塊的新幣獎勵,以及區塊中所含交易的交易費。為了得到這些獎勵,礦工們爭相完成一種基于加密哈希算法的數學難題,這些難題的解決方案,稱為“工作量證明”,包括在新區塊中,作為礦工花費大量計算工作的證明。解決工作證明算法在區塊鏈上獲得獎勵的競爭以及獲勝者有權在區塊鏈上記錄交易,這二者是比特幣安全模型的基石。
這個過程被稱為挖礦,是因為獎勵機制(新比特幣產生)被設計為模擬收益遞減模式,類似于貴重金屬的挖礦過程。比特幣的貨幣是通過挖礦發行的,類似于央行通過印刷鈔票來發行貨幣。礦工通過創造一個新區塊得到的比特幣數量大約每四年(或準確說是每210,000個區塊)減少一半。開始時為2009年1月每個區塊獎勵50個比特幣,然后到2012年11月減半為每個區塊獎勵25個比特幣。之后在2016年7月 再次減半為每個新區塊獎勵12.5個比特幣。基于這個公式,比特幣挖礦獎勵以指數方式遞減,直到2140年。屆時所有的比特幣(20,999,999,980)全部發行完畢。換句話說在2140年之后,不會再有新的比特幣產生。
礦工們同時也會獲取交易費。每筆交易都可能包含一筆交易費,交易費是每筆交易記錄的輸入和輸出的差額。在挖礦過程中成功“挖出”新區塊的礦工可以得到該區塊中包含的所有交易“小費”。目前,這筆費用占礦工收入的0.5%或更少,大部分收益仍來自挖礦所得的新比特幣獎勵。然而隨著挖礦獎勵的遞減,以及每個區塊中包含的交易數量增加,交易費在礦工收益中所占的比重將會逐漸增加。在2140年之后,所有的礦工收益都將由交易費構成。
在本章中,我們先來審視比特幣的貨幣發行機制,然后再來了解挖礦的最重要的功能:支撐比特幣安全的去中心化的共識機制。
為了了解挖礦和共識,我們將跟隨Alice的交易,是如何被礦工Jing的挖礦設備接收并被添加到區塊中的。 然后,我們將繼續跟蹤區塊被挖礦,加入區塊鏈,并通過自發共識被比特幣網絡接受。
### 10.1.1 比特幣經濟學和貨幣創造
比特幣是在每一個區塊的創建過程中以固定和遞減的速度被“鑄造出來”的。大約每十分鐘產生一個新區塊,每一個新區塊都伴隨著一定數量的全新比特幣,它是從無到有的。每開采210,000個區塊,或者大約每4年,貨幣發行速率降低50%。在比特幣運行的第一個四年中,每個區塊創造出50個新比特幣。
2012年11月,比特幣的新發行速度降低到每區塊25個比特幣。2016年7月,降低到12.5比特幣/區塊。2020年的某個時候,也就是在區塊630,000,它將再次下降至6.25比特幣。新幣的發行速度會以指數級進行32次“等分”,直到第6,720,000塊(大約會在2137年挖出),達到比特幣的最小貨幣單位1聰。最終,在經過693萬個區塊之后,所有的共 2,099,999,997,690,000聰,或者說接近 2,100萬比特幣將全部發行完畢。在那之后,新的區塊不再包含比特幣獎勵,礦工的收益全部來自交易費。圖10-1展示了在發行速度不斷降低的情況下,比特幣總流通量與時間的關系。

圖10-1 基于發行率幾何下降的比特幣供應量和時間的關系
>**注意**:比特幣挖礦發行的最大數量也就成為挖礦獎勵的*上限*。 在實踐中,礦工可能故意挖那些低于全額獎勵的區塊。已經有這些區塊被挖出來了,未來就會有更多被開采,這樣導致貨幣發行總量的下降。
在例10-1的代碼中,我們計算了比特幣的總發行量
例10-1 比特幣發行總量的計算腳本
```
# Original block reward for miners was 50 BTC = 50 0000 0000 Satoshis
start_block_reward = 50 * 10**8
# 210000 is around every 4 years with a 10 minute block interval
reward_interval = 210000
def max_money():
current_reward = start_block_reward
total = 0
while current_reward > 0:
total += reward_interval * current_reward
current_reward /= 2
return total
print("Total BTC to ever be created:", max_money(), "Satoshis")
```
例10-2顯示了運行腳本的輸出結果
例10-2 運行上述腳本的輸出結果
```
$ python max_money.py
Total BTC to ever be created: 2099999997690000 Satoshis
```
總量有限并且發行量遞減創造了一個固定的貨幣供應量來抵御通貨膨脹。法幣可被央行無限制地印刷出來,而比特幣卻永遠不會因超額印發而出現通脹。
**通貨緊縮**
發行總量固定并且遞減的最重要和有爭議的后果是,貨幣往往會趨向于內在*通貨緊縮性deflationary*。通縮是一種由于貨幣的供應和需求不匹配導致的貨幣價值(和匯率)上漲的現象。與通貨膨脹相反,價格通縮意味著隨著時間的推移,貨幣擁有更多的購買力。
許多經濟學家認為,通縮經濟是一場災難,應該不惜一切代價避免。這是因為在快速通貨緊縮時期,人們預期商品價格會下跌,傾向于囤積貨幣而不是消費。這種現象在日本“失落的十年”期間出現,需求崩潰將日元推入通縮漩渦。
比特幣專家們認為通縮本身并不壞。相反,通貨緊縮與需求崩潰聯系在一起,因為這是我們必須研究的通貨緊縮的唯一例子。對于存在無限印刷可能的法定貨幣,除非需求完全崩潰,不愿意印刷貨幣,否則很難進入通縮漩渦。比特幣的通縮不是由需求崩潰造成的,而是由可預見的供應受限造成的。
當然,通貨緊縮的積極方面是,它與通貨膨脹相反。通貨膨脹導致貨幣的緩慢但不可避免的貶值,造成了一種隱藏的稅收形式,懲罰儲蓄者,解救了債務人(包括政府這個最大的債務人)。政府控制下的貨幣遭受輕易發債的道德風險,這種風險后來可能會以犧牲儲蓄者為代價,通過貶值來抹去債務。
貨幣的通縮,如果不是由經濟快速收縮所推動,那么這是一個問題,還是一個優勢,仍有待觀察,因為防止通脹和貶值遠遠大于通縮帶來的風險。
## 10.2 去中心化共識
在上一章中我們研究了區塊鏈,即所有交易的全球公共分類賬(列表),比特幣網絡中的每個人都接受它,把它作為所有權的權威記錄。
但在不考慮相信任何人的情況下,網絡中的所有人如何就誰擁有什么這一普遍的“真相”達成一致呢?所有傳統的支付系統都依賴于一個信任模型,該模型由一個中央機構提供清算服務,驗證和清算所有交易。比特幣沒有中央權威機構,但是所有的全節點都有一份完整的公共賬本副本,可以作為權威記錄來信任。區塊鏈不是由中央機構創建的,而是由網絡中的每個節點獨立組裝的。網絡中的所有節點,依靠著節點間的不安全的網絡連接所傳輸的信息,最終得出同樣的結果并共同維護了同一個公共總帳。本章探討比特幣網絡在沒有中央權威的情況下達成全球共識的過程。
中本聰的主要發明就是這種去中心化的*自發共識emergent consensus*機制。自發,是指共識沒有明確的完成點,沒有選舉和共識達成的固定時刻。換句話說,共識是數以千計的獨立節點遵守了簡單的規則通過異步交互自發形成的產物。所有的比特幣屬性,包括貨幣、交易、支付以及不依靠中心機構和信任的安全模型等都依賴于這個發明。
比特幣的去中心化共識由所有網絡節點的4種獨立過程相互作用而產生:
* 每個全節點依據一個詳盡的標準列表對每個交易進行獨立驗證
* 挖礦節點將交易記錄獨立打包進新區塊,通過工作證明算法進行證明計算
* 每個節點獨立的對新區塊進行校驗并組裝進區塊鏈
* 每個節點對區塊鏈進行獨立選擇,在工作量證明機制下選擇累計工作量最大的區塊鏈
在接下來的幾節中,我們將研究這些過程,了解它們之間如何相互作用并達成全網的自發共識,從而使任意節點組合出它自己的權威、可信、公開的全局總帳副本。
## 10.3 交易的獨立驗證
在[【第6章交易】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch06.md)中,我們知道了錢包軟件通過收集UTXO、提供正確的解鎖腳本、構造一個給新所有者的新輸出來創建交易。產生的交易隨后將被發送到比特幣網絡臨近的節點,從而使得該交易能夠在整個比特幣網絡中傳播。
然而,在交易傳遞到臨近的節點前,每一個收到交易的比特幣節點都會首先驗證該交易,這將確保只有有效的交易才會在網絡中傳播,而無效的交易將會被遇到的第一個節點廢棄掉。
每一個節點在校驗每一筆交易時,都需要對照一個長長的標準列表:
?交易的語法和數據結構必須正確。
?輸入與輸出列表都不能為空。
?交易的字節數小于 MAX\_BLOCK\_SIZE 。
?每一個輸出值,以及總量,必須在規定值的范圍內 (小于2,100萬個幣,大于dust參數設置的閾值)。
?沒有哈希等于0,N等于-1的輸入(coinbase交易不應當被傳遞)。
?nLockTime是小于或等于 INT\_MAX 的。或者nLocktime and nSequence的值滿足MedianTimePast(譯者注:MedianTime是這個塊的前面11個塊按照block time排序后的中間時間)
?交易的字節大小是大于或等于100的。
?交易中的簽名數量\(SIGOPS\)應小于簽名操作數量上限。
?解鎖腳本( scriptSig )只能夠將數字壓入棧中,并且鎖定腳本( scriptPubkey )必須要符合isStandard的格式 (該格式將會拒絕非標準交易)。
?池中或位于主分支區塊中的一個匹配交易必須是存在的。
?對于每一個輸入,引用的輸出是必須存在的,并且沒有被花費。
?對于每一個輸入,如果引用的輸出存在于池中任何別的交易中(譯者注:這筆輸入引用的輸出有人家自己的輸入,不是你),該交易將被拒絕。
?對于每一個輸入,在主分支和交易池中尋找引用的輸出交易。如果輸出交易缺少任何一個輸入,該交易將成為一個孤 立的交易。如果與其匹配的交易還沒有出現在池中,那么將被加入到孤立交易池中。
?對于每一個輸入,如果引用的輸出交易是一個coinbase輸出,該輸入必須至少獲得 COINBASE\_MATURITY\(100\)個確認。
?使用引用的輸出交易獲得輸入值,并檢查每一個輸入值和總值是否在規定值的范圍內 (小于2100萬個幣,大于0)。
?如果輸入值的總和小于輸出值的總和,交易將被中止。
?如果交易費用太低以至于無法進入一個空的區塊,交易將被拒絕。
?每一個輸入的解鎖腳本必須依據相應輸出的鎖定腳本來驗證。
?這些條件能夠在比特幣標準客戶端下的 AcceptToMemoryPool 、 CheckTransaction 和 CheckInputs 函數中獲得更詳細的闡述。 請注意,這些條件會隨著時間發生變化,為了處理新型拒絕服務攻擊,有時候也為交易類型多樣化而放寬規則。
?在收到交易后,每一個節點都會在全網廣播前對這些交易進行獨立校驗,并以接收時的相應順序,為有效的新交易建立一個驗證池(還未確認),這個池可以叫做交易池,或者memory pool或者mempool。
## 10.4 挖礦節點
在比特幣網絡中,一些節點被稱為專業節點“礦工”。第1章中,我們介紹了Jing,在中國上海的計算機工程專業學生,他 就是一位礦工。Jing通過礦機挖礦獲得比特幣,礦機是專門設計用于挖比特幣的計算機硬件系統。Jing的這臺專業挖礦設備連接著一個運行完整比特幣節點的服務器。與Jing不同,一些礦工是在沒有完整節點的條件下進行挖礦,正如我們 在“礦池”一節中所述的。與其他任一完整節點相同,Jing的節點在比特幣網絡中進行接收和傳播未確認交易記錄。然而,Jing的節點也能夠把這些交易記錄打包進入一個新區塊。
?同其他節點一樣,Jing的節點時刻監聽著傳播到比特幣網絡的新區塊。而這些新加入的區塊對挖礦節點有著特殊的意 義。礦工間的競爭以新區塊的傳播而結束,如同宣布誰是最后的贏家。對于礦工們來說,收到一個新區塊進行驗證意味著別人已經贏了,而自己則輸了這場競爭。然而,一輪競爭的結束也代表著下一輪競爭的開始。新區塊并不僅僅是象征著競賽結束的方格旗;它也是下一個區塊競賽的發令槍。
## 10.5 打包交易至區塊
驗證交易后,比特幣節點會將這些交易添加到自己的內存池中。內存池也稱作交易池,用來暫存尚未被加入到區塊的交 易記錄。與其他節點一樣,Jing的節點會收集、驗證并傳遞新的交易。而與其他節點不同的是,Jing的節點會把這些交 易整合到一個候選區塊中。
?讓我們繼續跟進,看下Alice從Bob咖啡店購買咖啡時產生的那個區塊。Alice的交易在區塊 277,316。為了演示本章中提到的概念,我們假設這個區塊是由Jing的挖礦系統挖出的,并且繼續跟進Alice的交易,因 為這個交易已經成為了新區塊的一部分。
Jing的挖礦節點維護了一個區塊鏈的本地副本。當Alice買咖啡 的時候,Jing節點的區塊鏈已經收集到了區塊277,314,并繼續監聽著網絡上的交易,在嘗試挖掘新區塊的同時,也監 聽著由其他節點發現的區塊。當Jing的節點在挖礦時,它從比特幣網絡收到了區塊277,315。這個區塊的到來標志著終 結了產出區塊277,315競賽,與此同時也是產出區塊277,316競賽的開始。
在上一個10分鐘內,當Jing的節點正在尋找區塊277,315的解的同時,它也在收集交易記錄為下一個區塊做準備。目前 它已經收到了幾百筆交易記錄,并將它們放進了內存池。直到接收并驗證區塊277,315后,Jing的節點會檢查內存池中 的全部交易,并移除已經在區塊277,315中出現過的交易記錄,確保任何留在內存池中的交易都是未確認的,等待被記 錄到新區塊中。
?Jing的節點立刻構建一個新的空區塊,做為區塊277,316的候選區塊。稱作候選區塊是因為它還沒有包含有效的工作量證明,不是一個有效的區塊,而只有在礦工成功找到一個工作量證明解之后,這個區塊才生效。
?現在,Jing的節點從內存池中整合到了全部的交易,新的候選區塊包含有418筆交易,總的礦工費為0.09094925個比特 幣。你可以通過比特幣核心客戶端命令行來查看這個區塊,如例10-3所示:
例10-3 使用命令行檢索區塊277,316
```
$ bitcoin-cli get blockhash 277316
0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
$ bitcoin-cli getblock 0000000000000001b6b9a13b095e96db41c4a928b97ef2d9\44a9b31b2cc7bdc4
```
```
{
"hash" : "0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4",
"confirmations" : 35561,
"size" : 218629,
"height" : 277316,
"version" : 2,
"merkleroot" : "c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e",
"tx" : [
"d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
"b268b45c59b39d759614757718b9918caf0ba9d97c56f3b91956ff877c503fbe",
... 417 more transactions ...//這一行顯示交易數量
],
"time" : 1388185914,
"nonce" : 924591752,
"bits" : "1903a30c",
"difficulty" : 1180923195.25802612,
"chainwork" : "000000000000000000000000000000000000000000000934695e92aaf53afa1a",
"previousblockhash" : "0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569"
}
```
### 10.5.1 創幣交易
區塊中的第一筆交易是筆特殊交易,稱為創幣交易或者coinbase交易。這個交易是由Jing的節點構造并用來獎勵礦工們所做的貢獻的。
**注意**:當塊277,316開采時,每個塊的獎勵是25比特幣。 此后,已經過了一個“減半”時期。 2016年七月份的獎勵為12.5比特幣,2020年達到210000區塊時,將再次減半。
?Jing的節點會創建“向Jing的地址支付25.09094928個比特幣”這樣一個交易,把生成交易的獎勵發送到自己的錢包。Jing挖出區塊獲得的獎勵金額是coinbase獎勵(25個全新的比特幣)和區塊中全部交易礦工費的總和。
如 例10-4所示:coinbase交易
```
$ bitcoin-cli getrawtransaction d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f 1
```
```
{
"hex" : "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f03443b0403858402062f503253482fffffffff0110c08d9500000000232102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac00000000",
"txid" : "d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"coinbase" : "03443b0403858402062f503253482f",
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 25.09094928,
"n" : 0,
"scriptPubKey" : {
"asm" : "02aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21OP_CHECKSIG",
"hex" : "2102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac",
"reqSigs" : 1,
"type" : "pubkey",
"addresses" : ["1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N"
]
}
}
]
}
```
與常規交易不同,創幣交易沒有輸入,不消耗UTXO。它只包含一個被稱作coinbase的輸入,僅僅用來創建新的比特 幣。創幣交易有一個輸出,支付到這個礦工的比特幣地址。創幣交易的輸出將這25.09094928個比特幣發送到礦工的比 特幣地址,如本例所示的1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N。
### 10.5.2 Coinbase獎勵與礦工費
為了構造創幣交易,Jing的節點需要計算礦工費的總額,將這418個已添加到區塊交易的輸入和輸出分別進行求和,然 后用輸入總額減去輸出總額得到礦工費總額,公式如下:
```
Total Fees = Sum(Inputs) - Sum(Outputs)
```
?在區塊277,316中,礦工費的總額是0.09094925個比特幣。
緊接著,Jing的節點計算出這個新區塊正確的獎勵額。獎勵額的計算是基于區塊高度的,以每個區塊50個比特幣為開始,每產生210,000個區塊減半一次。這個區塊高度是277,316,所以正確的獎勵額是25個比特幣。
?詳細的計算過程可以參看比特幣核心客戶端中的GetBlockValue函數,如例10-5所示:
?例10-5 計算區塊獎勵—函數GetBlockValue, Bitcoin Core Client, main.cpp
```
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params
&
consensusParams){
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
// Force block reward to zero when right shift is undefined.
if (halvings
>
= 64)
return 0;
CAmount nSubsidy = 50 * COIN;
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
nSubsidy
>
>
= halvings;
return nSubsidy;
}
```
?變量Subsidy表示初始獎勵額,值為 COIN 常量(100,000,000聰\)與50的乘積,也就是說初始獎勵額為50億聰。
緊接著,這個函數用當前區塊高度除以減半間隔\(SubsidyHalvingInterval 函數\)得到減半次數(變量 halvings )。每 210,000個區塊為一個減半間隔,對應本例中的區塊277316,所以減半次數為1。
變量 halvings 最大值64,如果超出這個值,代碼算得的獎勵額為0。
然后,這個函數會使用二進制右移操作將獎勵額\(變量 nSubsidy\)右移一位(等同與除以2),每一輪減半右移一次。在這個例子中,對于區塊277,316只需要將值為50億聰的獎勵額右移一次,得到25億聰,也就是25個比特幣的獎勵額。之所以采用二進制右移操作,是因為相比于整數或浮點數除法,右移操作的效率更高。
?最后,將coinbase獎勵額(變量 nSubsidy )與礦工費\(nFee\)總額求和,并返回這個值。
**注意**: 如果Jing的挖礦節點把coinbase交易寫入區塊,那么如何防止Jing獎勵自己100甚至1000比特幣? 答案是,不正確的獎勵將被其他人視為無效,浪費了Jing用于工作證明的投入。 只有這個區塊被大家認可,Jing才能得到報酬。
### 10.5.3創幣交易的結構
經過計算,Jing的節點構造了一個創幣交易,支付給自己25.09094928枚比特幣。
例10-4所示,創幣交易的結構比較特殊,與一般交易輸入需要指定一個先前的UTXO不同,它包含一個“coinbase“輸 入。在之前的章節中,我們已經給出了交易輸入的結構。現在讓我們來比較一下常規交易輸入與創幣交易輸入。表10-1給出了常規交易輸入的結構,表10-2給出的是創幣交易輸入的結構。
?表10-1 常規交易輸入結構

表10-2,coinbase交易輸入結構

在Coinbase交易中,“交易哈希”字段32個字節全部填充0,“交易輸出索引”字段全部填充0xFF\(十進制的255\),這兩個字段的值表示不引用UTXO。“解鎖腳本”由coinbase數據代替,數據可以由礦工自定義。
### 10.5.4 Coinbase數據
?創幣交易不包含“解鎖腳本“\(又稱作 scriptSig\)字段,這個字段被coinbase數據替代,長度最小2字節,最大100字節。除 了開始的幾個字節外,礦工可以任意使用coinbase的其他部分,隨意填充任何數據。
以創世塊為例,中本聰在coinbase中填入了這樣的數據“The Times 03/Jan/ 2009 Chancellor on brink of second bailout for banks“\(泰晤士報 2009年1月3日 財政大臣將再次對銀行施以援手\),表示對日期的證明,同時也表達了對銀行系統的不信任。現在,礦工使用coinbase數據實現extra nonce功能,并嵌入字符串來標識挖出它的礦池,這部分內容會在后面的小節描述。
coinbase前幾個字節也曾是可以任意填寫的,不過在后來的第34號比特幣改進提議\(BIP34\)中 規定了版本2的區塊(版本字段為2的區塊),這個區塊的高度必須跟在腳本操作“push“之后,填充在coinbase字段的起始處。
?我們以例10-4中的區塊277,316為例,coinbase就是交易輸入的“解鎖腳本“(或scriptSig)字段,這個字段的十六進制值 為03443b0403858402062f503253482f。下面讓我們來解碼這段數據。
?第一個字節是03,腳本執行引擎執行這個指令將后面3個字節壓入腳本棧,緊接著的3個字節——0x443b04, 是以小端格式\(最低有效字節在先\)編碼的區塊高度。翻轉字節序得到0x043b44,表示為十進制是277,316。
?緊接著的幾個十六進制數(03858402062)用于編碼extra nonce\(參見"10.11.1 隨機值升位方案"\),或者一個隨機值,從而求解一個適當的工作量證明。
?coinbase數據結尾部分\(2f503253482f\)是ASCII編碼字符 /P2SH/,表示挖出這個區塊的挖礦節點支持BIP0016所定義的 pay-to-script-hash\(P2SH\)改進方案。在P2SH功能引入到比特幣的時候,曾經有過一場對P2SH不同實現方式的投票, 候選者是BIP0016和BIP0017。支持BIP0016的礦工將/P2SH/放入coinbase數據中,支持BIP0017的礦工將 p2sh/CHV 放入他們的coinbase數據中。最后,BIP0016在選舉中勝出,直到現在依然有很多礦工在他們的coinbase中填 入/P2SH/以表示支持這個功能。
10-6使用了libbitcoin庫(在之前“其他替代客戶端、資料庫、工具包”中提到)從創世塊中提取coinbase數據,并顯示 出中本聰留下的信息。libbitcoin庫中自帶了一份創世塊的靜態拷貝,所以這段示例代碼可以直接取自庫中的創世塊數 據。
?例10-6 從創世區塊中提取coinbase數據?
```
code/satoshi-words.cpp\[\]
```
?例8-7中,我們使用GNU C++編譯器編譯源代碼并運行得到的可執行文件,
?例8-7 編譯并運行satoshi-words示例代碼
```
$ # Compile the code
$ g++ -o satoshi-words satoshi-words.cpp $(pkg-config --cflags --libs libbitcoin)
$ # Run the executable
$ ./satoshi-words
^D??
<
GS
>
^A^DEThe Times 03/Jan/2009 Chancellor on brink of second bailout for banks
```
## 10.6 構造區塊頭
為了構造區塊頭,挖礦節點需要填充六個字段,如表10-3中所示。
表10-3 區塊頭結構

?在區塊277,316被挖出的時候,區塊結構中用來表示版本號的字段值為2,長度為4字節,以小端格式編碼值為 0x20000000。
?接著,挖礦節點需要填充“前區塊哈希”,在本例中,這個值為Jing的節點從網絡上接收到的區塊277,315 的區塊頭哈希值,它是區塊277316候選區塊的父區塊。區塊277,315的區塊頭哈希值為:
```
0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569
```
**提示**:通過選擇候選塊頭中的先前塊哈希字段指定的特定父區塊,Jing正在通過確認挖礦能力來擴展區塊鏈。 從本質上講,這是用他的采礦權為最長難度的有效鏈進行的“投票”。
?為了向區塊頭填充merkle根字段,要將全部的交易組成一個merkle樹。創幣交易作為區塊中的首個交易,后將余下的 418筆交易添至其后,這樣區塊中的交易一共有419筆。在之前,我們已經見到過“Merkle樹”,樹中必須有偶數個葉子 節點,所以需要復制最后一個交易作為第420個葉子節點,每個葉子節點是對應交易的哈希值。這些交易的哈希值逐層地、成對地組合,直到最終組合并成一個根節點。merkle數的根節點將全部交易數據摘要為一個32字節長度的值,例10-3中 merkel根的值如下:
```
c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e
```
?挖礦節點會繼續添加一個4字節的時間戳,以Unix紀元時間編碼,即自1970年1月1日0點到當下總共流逝的秒數。本例 中的1388185914對應的時間是2013年12月27日,星期五,UTC/GMT。
接下來,Jing的節點需要填充Target字段(難度目標值),為了使得該區塊有效,這個字段定義了所需滿足的工作量證明的難度。難度在區塊中以“尾數-指數”的格式,編碼并存儲,這種格式稱作target bits(難度位)。這種編碼的首字節表示指數,后面的3字節表示尾數\(系 數\)。以區塊277316為例,難度位的值為0x1903a30c,0x19是指數的十六進制格式,后半部0x03a30c是系數。這部分的概念在后面的“難度目標與難度調整”和“難度表示”有詳細的解釋。
?最后一個字段是nonce,初始值為0。
?區塊頭完成全部的字段填充后,挖礦就可以開始進行了。挖礦的目標是找到一個使區塊頭哈希值小于難度目標的 nonce。挖礦節點通常需要嘗試數十億甚至數萬億個不同的nonce取值,直到找到一個滿足條件的nonce值。
## 10.7 構建區塊
既然Jing的節點已經構建了一個候選區塊,那么就輪到Jing的礦機對這個新區塊進行“挖掘”,求解工作量證明算法以使這個區塊有效。從本書中我們已經學習了比特幣系統中不同地方用到的哈希加密函數。比特幣挖礦過程使用的是 SHA256哈希函數。
?用最簡單的術語來說,挖礦就是重復計算區塊頭的哈希值,不斷修改該參數,直到與哈希值匹配的一個過程。哈希函數 的結果無法提前得知,也沒有能得到一個特定哈希值的模式。哈希函數的這個特性意味著:得到哈希值的唯一方法是不斷的嘗試,每次隨機修改輸入,直到出現適當的哈希值。
### 10.7.1 工作量證明算法
?哈希函數輸入一個任意長度的數據,輸出一個長度固定且絕不雷同的值,可將其視為輸入的數字指紋。對于特定輸入,哈希的結果每次都一樣,任何人都可以用相同的哈希函數,計算和驗證哈希結果。一個加密哈希函數的主要特征就是不同的 輸入幾乎不可能出現相同的數字指紋。因此,有意的選擇一個輸入去生成一個想要的哈希值值是幾乎不可能的,更別提用隨機的方式生成想要的哈希值了。
?無論輸入的大小是多少,SHA256函數的輸出的長度總是256bit。在例10-8中,我們將使用Python解釋器來計算語句 "I am Satoshi Nakamoto" 的SHA256的哈希值。
?例10-8 SHA256示例
```
$ python
Python 2.7.1
>
>
>
import hashlib
>
>
>
print hashlib.sha256("I am Satoshi Nakamoto").hexdigest()
5d7c7ba21cbbcd75d14800b100252d5b428e5b1213d27c385bc141ca6b47989e
```
在例10-8中, 5d7c7ba21cbbcd75d14800b100252d5b428e5b1213d27c385bc141ca6b47989e 是"I am Satoshi Nakamoto"的哈希值。改變原句中的任何一個字母、標點、或增加字母都會產生不同的哈希值。
如果我們改變原句,得到的應該是完全不同的哈希值。 例如,我們在句子末尾加上一個數字,運行例10-9中的Python腳本。例10-9 通過反復修改 nonce 來生成不同哈希值的腳本(SHA256)
```
code/hash\_example.py\[\]
```
執行這個腳本就能生成這些只是末尾數字不同的語句的哈希值。例10-10 中顯示了我們只是增加了這個數字,卻得到了非 常不同的哈希值。
例10-10 通過反復修改nonce 來生成不同哈希值的腳本的輸出
```
$ python hash_example.py
I am Satoshi Nakamoto0 =
>
a80a81401765c8eddee25df36728d732...
I am Satoshi Nakamoto1 =
>
f7bc9a6304a4647bb41241a677b5345f...
I am Satoshi Nakamoto2 =
>
ea758a8134b115298a1583ffb80ae629...
I am Satoshi Nakamoto3 =
>
bfa9779618ff072c903d773de30c99bd...
I am Satoshi Nakamoto4 =
>
bce8564de9a83c18c31944a66bde992f...
I am Satoshi Nakamoto5 =
>
eb362c3cf3479be0a97a20163589038e...
I am Satoshi Nakamoto6 =
>
4a2fd48e3be420d0d28e202360cfbaba...
I am Satoshi Nakamoto7 =
>
790b5a1349a5f2b909bf74d0d166b17a...
I am Satoshi Nakamoto8 =
>
702c45e5b15aa54b625d68dd947f1597...
I am Satoshi Nakamoto9 =
>
7007cf7dd40f5e933cd89fff5b791ff0...
I am Satoshi Nakamoto10 =
>
c2f38c81992f4614206a21537bd634a...
I am Satoshi Nakamoto11 =
>
7045da6ed8a914690f087690e1e8d66...
I am Satoshi Nakamoto12 =
>
60f01db30c1a0d4cbce2b4b22e88b9b...
I am Satoshi Nakamoto13 =
>
0ebc56d59a34f5082aaef3d66b37a66...
I am Satoshi Nakamoto14 =
>
27ead1ca85da66981fd9da01a8c6816...
I am Satoshi Nakamoto15 =
>
394809fb809c5f83ce97ab554a2812c...
I am Satoshi Nakamoto16 =
>
8fa4992219df33f50834465d3047429...
I am Satoshi Nakamoto17 =
>
dca9b8b4f8d8e1521fa4eaa46f4f0cd...
I am Satoshi Nakamoto18 =
>
9989a401b2a3a318b01e9ca9a22b0f3...
I am Satoshi Nakamoto19 =
>
cda56022ecb5b67b2bc93a2d764e75f...
```
每個語句都生成了一個完全不同的哈希值。它們看起來是完全隨機的,但你在任何計算機上用Python執行上面的腳本都 能重現這些完全相同的哈希值。
?類似這樣在語句末尾的變化的數字叫做nonce(隨機數)。Nonce是用來改變加密函數輸出的,在這個示例中改變了這個語句的 SHA256指紋。
?為了使這個哈希算法變得富有挑戰,我們來設定一個具有任意性的目標:找到一個語句,使之哈希值的十六進制表示以 0開頭。幸運的是,這很容易!在例10-10中語句 "I am Satoshi Nakamoto13" 的哈希值是 0ebc56d59a34f5082aaef3d66b37a661696c2b618e62432727216ba9531041a5 ,剛好滿足條件。我們得到它用了13次。用概率的角度 來看,如果哈希函數的輸出是平均分布的,我們可以期望每16次得到一個以0開頭的哈希值(十六進制個位數字為0到 F)。從數字的角度來看,我們要找的是小于 0x1000000000000000000000000000000000000000000000000000000000000000 的哈希值。
我們稱這個為Target目標閾值,我們的目的是找到一個小于這個目標的哈希值。如果我們減小這個目標值,那找到一個小于它的哈希值會越來越難。
簡單打個比方,想象人們不斷扔一對骰子以得到小于一個特定點數的游戲。第一局,目標是12。只要你不扔出兩個6, 你就會贏。然后下一局目標為11。玩家只能扔10或更小的點數才能贏,不過也很簡單。假如幾局之后目標降低為了5。
現在有一半機率以上扔出來的骰子加起來點數會超過5,因此無效。隨著目標越來越小,要想贏的話,扔骰子的次數會 指數級的上升。最終當目標為2時(最小可能點數),只有一個人平均扔36次或2%扔的次數中,他才能贏。從一個知道骰子游戲目標為2的觀察者的角度來看,如果有人要成功中獎,假設他平均嘗試了36次。
換句話說,可以估計從實現目標難度取得成功所需的工作量。 當算法是基于諸如SHA256的確定性函數時,輸入本身就成為證據,必須要一定的工作量才能產生低于目標的結果。 因此,稱之為工作量證明。
?**提示**:盡管每次嘗試產生一個隨機的結果,但是任何可能的結果的概率可以預先計算。 因此,指定特定難度的結果構成了具體的工作量證明。
?在例10-10中,成功的nonce為13,且這個結果能被所有人獨立確認。任何人將13加到語句 "I am Satoshi Nakamoto" 后面再計算哈希值都能確認它比目標值要小。這個正確的結果同時也是工作量證明(Proof of Work),因為它證明我們的確花時間找到了這個nonce。驗證這個哈希值只需要一次計算,而我們找到它卻花了13次。如果目標值更小(難度更大),那我們需要多得多的哈希計算才能找到合適的nonce,但其他人驗證它時只需要一次哈希計算。此外,知道目標值后,任何人都可以用統計學來估算其難度,因此就能知道找到這個nonce需要多少工作。
**提示**:工作證明必須產生小于目標的哈希值。 更高的目標意味著找到低于目標的哈希是不太困難的。 較低的目標意味著在目標下方找到哈希更難。 目標和難度是成反比。
?比特幣的工作量證明和例10-10中的挑戰非常類似。礦工用一些交易構建一個候選區塊。接下來,這個礦工計算這個區塊頭信息的哈希值,看其是否小于當前目標值。如果這個哈希值不小于目標值,礦工就會修改這個nonce(通常將之加 1)然后再試一次。按當前比特幣系統的難度,礦工得試10^15次(10的15次方)才能找到一個合適的nonce使區塊頭信息哈希值足夠小。
? 例10-11是一個簡化很多的工作量證明算法的實現。
? 例10-11 簡化的工作量證明算法
```
code/proof-of-work-example.py\[]
```
你可以任意調整難度值(按二進制bit數來設定,即哈希值開頭多少個bit必須是0)。然后執行代碼,看看在你的計算機 上求解需要多久。在例10-12中,你可以看到該程序在一個普通筆記本電腦上的執行情況。
例10-12 多種難度值的工作量證明算法的運行輸出
```
$ python proof-of-work-example.py*
```
```
Difficulty: 1 (0 bits)
[...]
Difficulty: 8 (3 bits)
Starting search...
Success with nonce 9
Hash is 1c1c105e65b47142f028a8f93ddf3dabb9260491bc64474738133ce5256cb3c1
Elapsed Time: 0.0004 seconds
Hashing Power: 25065 hashes per second
Difficulty: 16 (4 bits)
Starting search...
Success with nonce 25
Hash is 0f7becfd3bcd1a82e06663c97176add89e7cae0268de46f94e7e11bc3863e148
Elapsed Time: 0.0005 seconds
Hashing Power: 52507 hashes per second
Difficulty: 32 (5 bits)
Starting search...
Success with nonce 36
Hash is 029ae6e5004302a120630adcbb808452346ab1cf0b94c5189ba8bac1d47e7903
Elapsed Time: 0.0006 seconds
Hashing Power: 58164 hashes per second
[...]
Difficulty: 4194304 (22 bits)
Starting search...
Success with nonce 1759164
Hash is 0000008bb8f0e731f0496b8e530da984e85fb3cd2bd81882fe8ba3610b6cefc3
Elapsed Time: 13.3201 seconds
Hashing Power: 132068 hashes per second
Difficulty: 8388608 (23 bits)
Starting search...
Success with nonce 14214729
Hash is 000001408cf12dbd20fcba6372a223e098d58786c6ff93488a9f74f5df4df0a3
Elapsed Time: 110.1507 seconds
Hashing Power: 129048 hashes per second
Difficulty: 16777216 (24 bits)
Starting search...
Success with nonce 24586379
Hash is 0000002c3d6b370fccd699708d1b7cb4a94388595171366b944d68b2acce8b95
Elapsed Time: 195.2991 seconds
Hashing Power: 125890 hashes per second
[...]
Difficulty: 67108864 (26 bits)
Starting search...
Success with nonce 84561291
Hash is 0000001f0ea21e676b6dde5ad429b9d131a9f2b000802ab2f169cbca22b1e21a
Elapsed Time: 665.0949 seconds
Hashing Power: 127141 hashes per second
```
?你可以看出,隨著難度位一位一位地增加,查找正確結果的時間會呈指數級增長。如果你考慮整個256bit數字空間,每次要求多一個0,你就把哈希查找空間縮減了一半。在例10-12中,為尋找一個nonce使得哈希值開頭的26位值為0,一共嘗試了8千多萬次。即使家用筆記本每秒可以達270,000多次哈希計算,這個查找依然需要10分鐘。
?在寫這本書的時候,比特幣網絡要尋找區塊頭信息哈希值小于
```
0000000000000000029AB9000000000000000000000000000000000000000000
```
?可以看出,這個目標哈希值開頭的0多了很多。這意味 著可接受的哈希值范圍大幅縮減,因而找到正確的哈希值更加困難。生成下一個區塊需要網絡每秒計算1.8 septa-hashes(\(thousand billion billion次哈希)。這看起來像是不可能的任務,但幸運的是比特幣網絡已經擁有3EH/sec的處理能力,平均每10分鐘就可以找到一個新區塊。
### 10.7.2 難度表示
?在例10-3中,我們在區塊中看到難度目標,其被標為"難度位"或簡稱"bits"。在區塊277,316中,它的值為 0x1903a30c。 這個標記的值被存為系數/指數格式,前兩位十六進制數字為冪(exponent),接下來得六位為系數(coefficient)。在這個區塊里,0x19為冪,而 0x03a30c為系數。
計算難度目標的公式為:
```
target = coefficient \* 2^\(8 \* \(exponent – 3\)\)
```
?由此公式及難度位的值 0x1903a30c,可得:
```
target = 0x03a30c * 2^(0x08 * (0x19 - 0x03))^
=
>
target = 0x03a30c * 2^(0x08 * 0x16)^
=
>
target = 0x03a30c * 2^0xB0^
```
?按十進制計算為:
```
=
>
target = 238,348 * 2^176^
=
>
target = 22,829,202,948,393,929,850,749,706,076,701,368,331,072,452,018,388,575,715,328
```
轉化為十六進制后為:
```
=
>
target = 0x0000000000000003A30C00000000000000000000000000000000000000000000
```
?也就是說高度為277,316的有效區塊的頭信息哈希值是小于這個目標值的。這個數字的二進制表示中前60位都是0。在 這個難度上,一個每秒可以處理1萬億個哈希計算的礦工(1 tera-hash per second 或 1 TH/sec)平均每8,496個區塊才能找到一個正確結果,換句話說,平均每59天,才能為某一個區塊找到正確的哈希值。
### 10.7.3 難度目標與難度調整
?如前所述,目標決定了難度,進而影響求解工作量證明算法所需要的時間。那么問題來了:為什么這個難度值是可調整 的?由誰來調整?如何調整?
?比特幣的區塊平均每10分鐘生成一個。這就是比特幣的心跳,是貨幣發行速率和交易達成速度的基礎。不僅是在短期內,而是在幾十年內它都必須要保持恒定。在此期間,計算機性能將飛速提升。此外,參與挖礦的人和計算機也會不斷 變化。為了能讓新區塊的保持10分鐘一個的產生速率,挖礦的難度必須根據這些變化進行調整。事實上,難度是一個動 態的參數,會定期調整以達到每10分鐘一個新區塊的目標。簡單地說,難度被設定在,無論挖礦能力如何,新區塊產生 速率都保持在10分鐘一個。
?那么,在一個完全去中心化的網絡中,這樣的調整是如何做到的呢?難度的調整是在每個完整節點中獨立自動發生的。 每2,016個區塊中的所有節點都會調整難度。難度的調整公式是由最新2,016個區塊的花費時長與20,160分鐘(即 這些區塊以10分鐘一個速率所期望花費的時長)比較得出的。難度是根據實際時長與期望時長的比值進行相應調整的 (或變難或變易)。簡單來說,如果網絡發現區塊產生速率比10分鐘要快時會增加難度。如果發現比10分鐘慢時則降低難度。
?這個公式可以總結為如下形式:
```
New Difficulty = Old Difficulty \* \(Actual Time of Last 2016 Blocks / 20160 minutes\)
```
? 例10-13展示了比特幣核心客戶端中的難度調整代碼。
例10-13 工作量證明的難度調整 源文件**\( pow.cpp文件鐘的CalculateNextWorkRequired\(\) 函數\)**
第43行函數 GetNextWorkRequired\(\)// Limit adjustment step
```
// Limit adjustment step
int64_t nActualTimespan = pindexLast-
>
GetBlockTime() - nFirstBlockTime;
LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan);
if (nActualTimespan
<
params.nPowTargetTimespan/4)
nActualTimespan = params.nPowTargetTimespan/4;
if (nActualTimespan
>
params.nPowTargetTimespan*4)
nActualTimespan = params.nPowTargetTimespan*4;
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew;
arith_uint256 bnOld;
bnNew.SetCompact(pindexLast-
>
nBits);
bnOld = bnNew;
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
if (bnNew
>
bnPowLimit)
bnNew = bnPowLimit;
```
**注意**:雖然目標校準每2,016個塊發生,但是由于Bitcoin Core客戶端的一個錯誤,它是基于之前的2,015個塊的總時間(不應該是2,016個),導致重定向偏差向較高難度提高0.05%。
?參數Interval\(2,016區塊\)和TargetTimespan\(1,209,600秒即兩周\)的定義在文件chainparams.cpp中。
?為了防止難度的變化過快,每個周期的調整幅度必須小于一個因子(值為4)。如果要調整的幅度大于4倍,則按4倍調 整。由于在下一個2,016區塊的周期不平衡的情況會繼續存在,所以進一步的難度調整會在下一周期進行。因此平衡哈 希計算能力和難度的巨大差異有可能需要花費幾個2,016區塊周期才會完成。
?**提示**:尋找一個比特幣區塊需要整個網絡花費10分鐘來處理,每發現2,016個區塊時會根據前2,016個區塊完成的時間對難度進行調整。
?值得注意的是目標難度與交易的數量和金額無關。這意味著哈希算力的強弱,即讓比特幣更安全的電力投入量,與交易 的數量完全無關。換句話說,當比特幣的規模變得更大,使用它的人數更多時,即使哈希算力保持當前的水平,比特幣 的安全性也不會受到影響。哈希算力的增加表明更多的人為得到比特幣回報而加入了挖礦隊伍。只要為了回報,公平正 當地從事挖礦的礦工群體保持足夠的哈希算力,"接管"攻擊就不會得逞,讓比特幣的安全無虞。
?目標難度和挖礦電力消耗與將比特幣兌換成現金以支付這些電力之間的關系密切相關。高性能挖礦系統就是要用當前硅 芯片以最高效的方式將電力轉化為哈希算力。挖礦市場的關鍵因素就是每度電轉換為比特幣后的價格。因為這決定著挖 礦活動的營利性,也因此刺激著人們選擇進入或退出挖礦市場。
## 10.8 成功構建區塊
前面已經看到,Jing的節點創建了一個候選區塊,準備拿它來挖礦。Jing有幾個安裝了ASIC(專用集成電路)的礦機, 上面有成千上萬個集成電路可以超高速地并行運行SHA256算法。這些定制的硬件通過USB連接到他的挖礦節點上。接 下來,運行在Jing的桌面電腦上的挖礦節點將區塊頭信息傳送給這些硬件,讓它們以每秒億萬次的速度進行nonce測 試。
?在對區塊277,316的挖礦工作開始大概11分鐘后,這些硬件里的其中一個求得了解并發回挖礦節點。當把這個結果放進 區塊頭時,nonce 4,215,469,401 就會產生一個區塊哈希值:
```
0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569
```
而這個值小于難度目標值:
```
0000000000000003A30C00000000000000000000000000000000000000000000
```
?Jing的挖礦節點立刻將這個區塊發給它的所有相鄰節點。這些節點在接收并驗證這個新區塊后,也會繼續傳播此區塊。當這個新區塊在網絡中擴散時,每個節點都會將它作為區塊277,316加到自身節點的區塊鏈副本中。當挖礦節點收到并驗證了這個新區塊后,它們會放棄之前對構建這個相 同高度區塊的計算,并立即開始計算區塊鏈中下一個區塊的工作。
?下節將介紹節點進行區塊驗證、最長鏈選擇、達成共識,并以此形成一個去中心化區塊鏈的過程。
## 10.9 校驗新區塊
?比特幣共識機制的第三步是通過網絡中的每個節點獨立校驗每個新區塊。當新區塊在網絡中傳播時,每一個節點在將它轉發到其節點之前,會進行一系列的測試去驗證它。這確保了只有有效的區塊會在網絡中傳播。獨立校驗還確保了誠實 的礦工生成的區塊可以被納入到區塊鏈中,從而獲得獎勵。行為不誠實的礦工所產生的區塊將被拒絕,這不但使他們失 去了獎勵,而且也浪費了本來可以去尋找工作量證明解的機會,因而導致其電費虧損。
?當一個節點接收到一個新的區塊,它將對照一個長長的標準清單對該區塊進行驗證,若沒有通過驗證,這個區塊將被拒絕。這些標準可以在比特幣核心客戶端的CheckBlock函數和CheckBlockHead函數中獲得,
它包括:
? 區塊的數據結構語法上有效
? 區塊頭的哈希值小于目標難度(確認包含足夠的工作量證明)
? 區塊時間戳早于驗證時刻未來兩個小時(允許時間錯誤)
? 區塊大小在長度限制之內
? 第一個交易(且只有第一個)是coinbase交易
? 使用檢查清單驗證區塊內的交易并確保它們的有效性
參見之前章節 “交易的獨立校驗”一節已經討論過這個清單。每一個節點對每一個新區塊的獨立校驗,確保了礦工無法欺詐。在前面的章節中,我們看到了礦工們如何去記錄一筆交 易,以獲得在此區塊中創造的新比特幣和交易費。為什么礦工不為他們自己記錄一筆交易去獲得數以千計的比特幣?這是因為每一個節點根據相同的規則對區塊進行校驗。一個無效的coinbase交易將使整個區塊無效,這將導致該區塊被拒 絕,因此,該交易就不會成為總賬的一部分。礦工們必須構建一個完美的區塊,基于所有節點共享的規則,并且根據正 確工作量證明的解決方案進行挖礦,他們要花費大量的電力挖礦才能做到這一點。如果他們作弊,所有的電力和努力都 會浪費。這就是為什么獨立校驗是去中心化共識的重要組成部分。
## 10.10 區塊鏈的組裝與選擇
?比特幣去中心化的共識機制的最后一步是將區塊集合至有最大工作量證明的鏈中。一旦一個節點驗證了一個新的區塊, 它將嘗試將新的區塊連接到到現存的區塊鏈,將它們組裝起來。
節點維護三種區塊:第一種是連接到主鏈上的,第二種是從主鏈上產生分支的(備用鏈),最后一種是在已知鏈中沒有 找到已知父區塊的。在驗證過程中,一旦發現有不符合標準的地方,驗證就會失敗,這樣區塊會被節點拒絕,所以也不 會加入到任何一條鏈中。
?任何時候,主鏈都是累計了最多難度的區塊鏈。在一般情況下,主鏈也是包含最多區塊的那個鏈,除非有兩個等長的鏈 并且其中一個有更多的工作量證明。主鏈也會有一些分支,這些分支中的區塊與主鏈上的區塊互為“兄弟”區塊。這些區 塊是有效的,但不是主鏈的一部分。 保留這些分支的目的是如果在未來的某個時刻它們中的一個延長了并在難度值上超 過了主鏈,那么后續的區塊就會引用它們。在“10.10.1 區塊鏈分叉”,我們將會看到在同樣的區塊高度,幾乎同時挖出區 塊時,候選鏈是如何產生的。
?當節點接收到新區塊,它會嘗試將這個區塊插入到現有區塊鏈中。節點會看一下這個區塊的“previous block hash”字 段,這個字段是該區塊對其父區塊的引用。同時,新的節點將嘗試在已存在的區塊鏈中找出這個父區塊。大多數情況 下,父區塊是主塊鏈的“頂點”,這就意味著這個新的區塊延長了主鏈。舉個例子,一個新的區塊——區塊277,316引用 了它的父區塊——區塊277,315。收到277316區塊的大部分節點都已經將277315最為主鏈的頂端,因此,連接這個新區塊并延長區塊鏈。
?有時候,新區塊所延長的區塊鏈并不是主鏈,這一點我們將在“10.10.1 區塊鏈分叉”中看到。在這種情況下,節點將新的區塊添加到備用鏈,同時比較備用鏈與主鏈的難度。如果備用鏈比主鏈積累了更多的難度,節點將收斂于備用鏈,意味 著節點將選擇備用鏈作為其新的主鏈,而之前那個老的主鏈則成為了備用鏈。如果節點是一個礦工,它將開始構造新的 區塊,來延長這個更新更長的區塊鏈。
?如果節點收到了一個有效的區塊,而在現有的區塊鏈中卻未找到它的父區塊,那么這個區塊被認為是“孤塊”。孤塊會被 保存在孤塊池中,直到它們的父區塊被節點收到。一旦收到了父區塊并且將其連接到現有區塊鏈上,節點就會將孤塊從 孤塊池中取出,并且連接到它的父區塊,讓它作為區塊鏈的一部分。當兩個區塊在很短的時間間隔內被挖出來,節點有 可能會以相反的順序接收到它們,這個時候孤塊現象就會出現。
選擇了最大難度的區塊鏈后,所有的節點最終在全網范圍內達成共識。隨著更多的工作量證明被添加到鏈中,鏈的暫時 性差異最終會得到解決。挖礦節點通過“投票”來選擇它們想要延長的區塊鏈,當它們挖出一個新塊并且延長了一個鏈, 新塊本身就代表它們的投票。
?相互競爭的鏈之間是存在差異的,下節我們將看到節點是怎樣通過獨立選擇最長難度鏈來解決這種差異的。
### 10.10.1 區塊鏈分叉
因為區塊鏈是去中心化的數據結構,所以不同副本之間不能總是保持一致。區塊有可能在不同時間到達不同節點,導致節點有不同的區塊鏈全貌。解決的辦法是,每一個節點總是選擇并嘗試延長代表累計了最大工作量證明的區塊鏈,也就 是最長的或最大累計工作的鏈(greatest cumulative work chain)。節點通過累加鏈上的每個區塊的工作量,得到建立這個鏈所要付出的工作量證明的總量。只要所有的節點選擇最長累計工作的區塊鏈,整個比特幣網絡最終會收斂到一致的狀態。分叉即在不同區塊鏈間發生的臨時差異,當更多的區塊添加到了某個分叉中,這個問題便會迎刃而解。
?**提示**由于全球網絡中的傳輸延遲,本節中描述的區塊鏈分叉自動會發生。 我們也將在本章稍后再看看故意引起的分叉。
在接下來的幾個圖表中,我們將通過網絡跟蹤“fork”事件的進展。 該圖是比特幣網絡的簡化表示。 為了便于描述,不同的區塊被顯示為不同的形狀(星形,三角形,倒置三角形,菱形),遍布網絡。 網絡中的每個圓表示一個節點。
每個節點都有自己的全局區塊鏈視圖。 當每個節點從其鄰居接收區塊時,它會更新其自己的區塊鏈副本,選擇最大累積工作鏈。 為便于描述,每個節點包含一個圖形形狀,表示它相信的區塊處于主鏈的頂端。 因此,如果在節點里面看到星形,那就意味著該節點認為星形區塊處于主鏈的頂端。
在第一張圖(圖10-2)中,網絡有一個統一的區塊鏈視角,以星形區塊為主鏈的“頂點”。

? 圖10-2
?當有兩個候選區塊同時想要延長最長區塊鏈時,分叉事件就會發生。正常情況下,分叉發生在兩名礦工在較短的時間 內,各自都算得了工作量證明解的時候。兩個礦工在各自的候選區塊一發現解,便立即傳播自己的“獲勝”區塊到網絡 中,先是傳播給鄰近的節點而后傳播到整個網絡。每個收到有效區塊的節點都會將其并入并延長區塊鏈。如果該節點在 隨后又收到了另一個候選區塊,而這個區塊又擁有同樣父區塊,那么節點會將這個區塊連接到候選鏈上。其結果是,一 些節點收到了一個候選區塊,而另一些節點收到了另一個候選區塊,這時兩個不同版本的區塊鏈就出現了。在圖10-3中,我們看到兩個礦工(NodeX和NodeY)幾乎同時挖到了兩個不同的區塊。這兩個區塊是頂點區塊——星形區塊的子區塊,可以 延長這個區塊鏈。為了方便查看,我們把節點X產生的區塊標記為三角形,把節點Y生產的區塊標記為倒三角形。

? 圖10-3
?例如,我們假設礦工節點X找到擴展區塊鏈工作量證明的解,即三角形區塊,構建在星形父區塊的頂端。與此同時,同樣進行星形區塊擴展的節點Y也找到了擴展區塊鏈工作量證明的解,即倒三角形區塊作為候選區塊。現在有兩個可能的塊,節點X的三角形區塊和節點Y的倒三角形區塊,這兩個區塊都是有效的,均包含有效的工作量證明解并延長同一 個父區塊。這個兩個區塊可能包含了幾乎相同的交易,只是在交易的排序上有些許不同。
?當兩個區塊開始在網絡傳播時,一些節點首先接收到三角形區塊,另外一些節點首先接收倒三角形區塊。如下圖10-4所示,比特幣網絡上的節點對于區塊鏈的頂點產生了分歧,一派以三角形區塊為頂點,而另一派以倒三角形區塊為頂點。

? 圖10-4
?在圖中,假設節點X首先接收到三角形塊,并用它擴展星形鏈。節點X選擇三角形區塊為主鏈。之后,節點X也收到倒三角區塊。由于是第二次收到,因此它判定這個倒三角形區塊是競爭失敗的產物,認為是無效區塊。然而,倒三角形的區塊不會被丟棄。它被鏈接到星形鏈的父區塊,并形成備用鏈。雖然節點X認為自己已經正確選擇了獲勝鏈,但是它還會保存“丟失”鏈,使得“丟失”鏈如果可能最終“獲勝”,它還具有重新打包的所需的信息。
?在網絡的另一端,節點Y根據自己的視角構建一個區塊鏈。首先獲得倒三角形區塊,并選擇這條鏈作為“贏家”。當它稍后收到三角形區塊時,它也將三角形區塊連接到星形鏈的父區塊作為備用鏈。
?雙方都是“正確的”或“不正確的”。兩者都是自己關于區塊鏈的有效立場。只有事后,才能理解這兩個競爭鏈如何通過額外的工作得到延伸。
節點X陣營的其他節點將立即開始挖掘候選區塊,以“三角形”作為擴展區塊鏈的頂端。通過將三角形作為候選區塊的父區塊,它們用自己的哈希算力進行投票。它們的投票標明支持自己選擇的鏈為主鏈。
?同樣,節點Y陣營的其他節點,將開始構建一個以倒三角形作為其父節點的候選節點,擴展它們認為是主鏈的鏈。比賽再次開始。分叉問題幾乎總是在一個區塊內就被解決了。網絡中的一部分算力專注于“三角形”區塊為父區塊,在其之上建立新的區 塊;另一部分算力則專注在“倒三角形”區塊上。即便算力在這兩個陣營中平均分配,也總有一個陣營搶在另一個陣營前發現工作量證明解并將其傳播出去。在這個例子中我們可以打個比方,假如工作在“三角形”區塊上的礦工找到了一個“菱形”區塊 延長了區塊鏈\(星形-三角形-菱形\),他們會立刻傳播這個新區塊,整個網絡會都會認為這個區塊是有效的,如下圖10-5所示。

? 圖10-5
?選擇“三角形”作為上一輪中勝出者的所有節點將簡單地將區塊鏈擴展一個塊。然而,選擇“倒三角”的節點現在將看到兩個鏈:星形-三角形-菱形和星型-到三角形。星形-三角形-菱形這條鏈現在比其他鏈條更長(更多累積的工作)。因此,這些節點將星形-三角形-菱形設置為主鏈,并將星型-倒三角形鏈變為備用鏈,如圖10-6所示。這是一個鏈的重新共識,因為這些節點被迫修改他們對塊鏈的立場,把自己納入更長的鏈。任何從事延伸星形-倒三角形的礦工現在都將停止這項工作,因為他們的候選人是“孤兒”,因為他們的父母“倒三角形”不再是最長的連鎖。 “倒三角形”內的交易重新插入到內存池中用來包含在下一個塊中,因為它們所在的塊不再位于主鏈中。整個網絡重新回到單一鏈狀態,星形-三角形-菱形,“菱形”成為鏈中的最后一個塊。所有礦工立即開始研究以“菱形”為父區塊的候選塊,以擴展這條星形-三角形-菱形鏈。

? 圖10-6
從理論上來說,兩個區塊的分叉是有可能的,這種情況發生在因先前分叉而相互對立起來的礦工,又幾乎同時發現了兩 個不同區塊的解。然而,這種情況發生的幾率是很低的。單區塊分叉每周都會發生,而雙塊分叉則非常罕見。比特幣將區塊間隔設計為10分鐘,是在更快速的交易確認和更低的分叉概率間作出的妥協。更短的區塊產生間隔會讓交 易清算更快地完成,也會導致更加頻繁地區塊鏈分叉。與之相對地,更長的間隔會減少分叉數量,卻會導致更長的清算時間。
## 10.11 挖礦和算力競賽
比特幣挖礦是一個極富競爭性的行業。自從比特幣存在開始,每年比特幣算力都成指數增長。一些年份的增長