# 第9章 區塊鏈
## 9.1 簡介
區塊鏈數據結構是一個有序的、反向鏈接的包含交易的區塊列表。它可以被存儲為平面文件(flat file),或是存儲在一個簡單數據庫中。Bitcoin Core客戶端使用Google的LevelDB數據庫存儲區塊鏈元數據。區塊被“向后”鏈接,每個區塊都指向鏈中的前一個區塊。區塊鏈經常被視為一個垂直的棧,第一個區塊作為棧底的首區塊,隨后每個區塊都被放置在之前的區塊之上。區塊彼此堆疊在一起這種形象化,可以方便使用一些術語,例如:“高度”來表示區塊與首區塊之間的距離,以及“頂部”或“頂端”來表示最新添加的區塊。
區塊鏈中的每個區塊都由一個哈希值標識,該哈希值是對區塊頭進行SHA256加密哈希算法生成。同時, 每一個區塊都可以通過其區塊頭的“父區塊哈希值”字段引用前一區塊,叫*父*區塊。也就是說,每個區塊頭都包含它的父區塊哈希值。這樣把每個區塊鏈接到各自父區塊的哈希值序列就創建了一條鏈,可以一直追溯到有史以來的第一個區塊,叫*創世區塊*。
雖然每個區塊只有一個父區塊,但可以暫時擁有多個子區塊。每個子區塊都將同一區塊作為其父區塊,并且在“父區塊哈希值”字段中具有相同的父區塊哈希值。一個區塊出現多個子區塊的情況被稱為“區塊鏈分叉”。區塊鏈分叉只是暫時狀態,只有當多個不同區塊被不同的礦工幾乎同時發現時才會發生(參見[【10.10.1 區塊鏈分叉】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch10.md#10101-區塊鏈分叉))。最終,只有一個子區塊會成為區塊鏈的一部分,區塊鏈分叉的問題就被解決了。盡管一個區塊可能會有不止一個子區塊,但每一區塊只有 一個父區塊,這是因為一個區塊只有一個“父區塊哈希值”字段可以指向它的唯一父區塊。
由于區塊頭里面包含“父區塊哈希值”字段,所以也會影響到當前區塊的哈希值。如果父區塊的標識發生變化,子區塊的標識也會跟著變化。當父區塊有任何改動時,父區塊的哈希值也發生變化。這將迫使子區塊的“父區塊哈希值”字段發生改變,從而又將導致子區塊的哈希值發生改變。而子區塊的哈希值發生改變又將迫使孫區塊的“父區塊哈希值”字段發生改變,又因此改變了孫區塊哈希值,以此類推。這種瀑布效應確保了一旦一個區塊之后有很多后代,要對其進行修改,就不得不強制重新計算該區塊所有后續的區塊。正是這樣的重新計算需要耗費巨大的計算量(以及由此帶來的能量消耗),所以一個長區塊鏈的存在可以讓區塊鏈的深度歷史無法改變,這也是比特幣安全性的一個關鍵特征。
你可以把區塊鏈想象成地質構造中的地質層或者是冰川巖芯樣品。表層可能會隨著季節而變化,甚至在沉積之前就被風吹走了。但是越往深處,地質層就變得越穩定。到了幾百英尺深的地方,你看到的將是保存了數百萬年但依然未受干擾的過去的快照。在區塊鏈里,最近的幾個區塊可能會由于區塊鏈分叉所引發的重新計算而被修改。最新的六個區塊就像幾英寸深的表土層。但是,超過這六塊后,區塊在區塊鏈中的位置越深,被改變的可能性就越小。在100個區塊以后,區塊鏈已經足夠穩定,這時Coinbase交易(包含新挖出的比特幣的交易)可以被花費了。幾千個區塊(一個月)后的區塊鏈將變成確定的歷史,實際上永遠不會改變。雖然協議總是允許一條鏈被較長的鏈取代,所以任何區塊被逆轉的可能性總是存在,但這樣的事件的概率隨著時間的流逝而減小,直到永不可能。
## 9.2 區塊結構
區塊是一種容器數據結構,用于聚合要包含在公共分類賬簿(區塊鏈)中的交易。它由一個包含元數據的區塊頭和緊跟其后的占據區塊最大空間的一長串交易列表組成。區塊頭是80字節,而平均每個交易至少是400字節,而且平均每個區塊至少包含超過1900個交易。因此,一個包含所有交易的完整區塊比區塊頭大1000倍。表7-1描述了一個區塊結構。
表7-1 區塊結構
Size| Field| Description
| ---- | ---- |----
4 bytes|Block Size|The size of the block, in bytes, following this field
80 bytes|Block Header|Several fields form the block header
1–9 bytes (VarInt)|Transaction Counter|How many transactions follow
Variable|Transactions|The transactions recorded in this block
## 9.3 區塊頭
區塊頭由三組區塊元數據組成。首先,有一個對前一個區塊哈希的引用,它將此區塊連接到區塊鏈中的前一個區塊。第二組元數據,即*難度difficulty、時間戳timestamp*和*隨機數nonce*,與挖礦競爭相關,詳見[【第10章挖礦】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch10.md)。第三組元數據是默克爾樹根,一種用來有效地匯總區塊中所有交易的數據結構。表7-2描述了區塊頭的數據結構。
表7-2 區塊頭結構
Size| Field| Description
| ---- | ---- |----
4 bytes|Version|A version number to track software/protocol upgrades
32 bytes|Previous Block Hash|A reference to the hash of the previous (parent) block in the chain
32 bytes|Merkle Root|A hash of the root of the merkle tree of this block’s transactions
4 bytes|Timestamp|The approximate creation time of this block (seconds from Unix Epoch)
4 bytes|Difficulty Target|The Proof-of-Work algorithm difficulty target for this block
4 bytes|Nonce|A counter used for the Proof-of-Work algorithm
隨機數、難度目標和時間戳會用于挖礦過程,更多細節將在[【第10章挖礦】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch10.md)討論。
## 9.4 區塊標識符:區塊頭哈希值和區塊高度
區塊標識符最主要的是它的加密哈希值,一個通過SHA256算法對區塊頭進行二次哈希計算而得到的數字指紋。產生的32字節哈希值被稱為*區塊哈希值block hash*,但是更準確的名稱是:區塊頭哈希值,因為只有區塊頭被用于計算。例如:000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f是比特幣第一個區塊的區塊哈希值。區塊哈希值可以唯一、明確地標識一個區塊,并且任何節點通過簡單地對區塊頭進行哈希計算都可以獨立地獲取該區塊哈希值。
請注意,一個區塊不管是在網絡上傳輸時,還是它作為區塊鏈的一部分被存儲在某節點的永久性存儲設備上,其區塊哈希值實際上都不包含在區塊的數據結構里。相反,區塊哈希值是由從網絡上收到該區塊的每個節點計算出來的。區塊哈希值可能會作為區塊元數據的一部分被單獨存儲在一個數據庫表中,以便于索引和更快地從磁盤檢索區塊。
第二種識別區塊的方式是通過該區塊在區塊鏈中的位置,即*區塊高度block height*。第一個區塊,其區塊高度為 0,和之前哈希值000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f所引用的區塊為同一個區塊。因此,區塊可以通過兩種方式被標識:區塊哈希值或者區塊高度。每一個隨后被存儲在第一個區塊之上的區塊在區塊鏈中都比前一區塊“高”出一個位置,就像箱子一樣,一個接一個堆疊在其他箱子之上。2017年1月1日的區塊高度大約是 446,000,說明已經有446,000個區塊被堆疊在2009年1月創建的第一個區塊之上。
和區塊哈希值不同的是,區塊高度并不是唯一的標識符。雖然一個單一的區塊總是會有一個明確的、固定的區塊高度, 但反過來卻并不成立,一個區塊高度并不總是標識一個唯一的區塊。兩個或兩個以上的區塊可能有相同的區塊高度,在區塊鏈里爭奪同一位置。這種情況在[【10.10.1 區塊鏈分叉】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch10.md#10101-區塊鏈分叉)一節中有詳細討論。區塊高度也不是區塊數據結構的一部分, 不被存儲在區塊里。當節點接收來自比特幣網絡的區塊時,會動態地識別該區塊在區塊鏈里的位置(區塊高度)。 區塊高度也可作為元數據存儲在一個索引數據庫表中以便快速檢索。
>**提示**一個區塊的區塊哈希值總是能唯一地標識出一個特定區塊。一個區塊也總是有特定的區塊高度。但是,一個特定的區塊高度并不一定總是能唯一地標識一個特定區塊。更確切地說,兩個或者更多數量的區塊也許會為了區塊鏈中的一個位置而競爭。
## 9.5 創世區塊
區塊鏈里的第一個區塊創建于2009年,被稱為創世區塊。它是區塊鏈里面所有區塊的共同祖先,這意味著你從任一區塊,循鏈向后回溯,最終都將到達創世區塊。
因為創世區塊被靜態編入到比特幣客戶端軟件里,無法改變,所以每一個節點都是從最少包括這個區塊的區塊鏈開始。每一個節點都“知道”創世區塊的哈希值、結構、被創建的時間和里面的一個交易。因此,每個節點都有了區塊鏈的起點,一個安全的“根”,從中構建一個可信的區塊鏈。
在[*chainparams.cpp*](http://bit.ly/1x6rcwP)里可以查看Bitcoin Core客戶端里創世區塊靜態編碼。
創世區塊的哈希值為:
`0000000000 19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f`
你可以在任何區塊瀏覽網站搜索這個區塊哈希值,如blockchain.info,你會發現一個描述這一區塊內容的頁面,該頁面的鏈接包含了這個區塊哈希值:
https://blockchain.info/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
在命令行使用Bitcoin Core客戶端:
```
$ bitcoin-cli getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
```
```
{ "hash" : "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
"confirmations" : 308321,
"size" : 285,
"height" : 0,
"version" : 1,
"merkleroot" : "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"tx" : [ "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b" ],
"time" : 1231006505,
"nonce" : 2083236893,
"bits" : "1d00ffff",
"difficulty" : 1.00000000,
"nextblockhash" : "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"}
```
創世區塊包含一個隱藏的信息。在其Coinbase交易的輸入中包含這樣一句話“The Times 03/Jan/2009 Chancellor on brink of second bailout forbanks.”這句話是*泰晤士報*當天的頭版文章標題,引用這句話,是證明該區塊最早的創建日期,也可視為半開玩笑地提醒人們一個獨立的貨幣制度的重要性,告訴人們與比特幣的發行,同時伴隨著一場前所未有的世界性貨幣危機。該消息是由比特幣的創立者中本聰嵌入創世區塊中的。
## 9.6 將區塊連成區塊鏈
比特幣的全節點在本地保存了區塊鏈從創世區塊起的完整副本。隨著新的區塊的產生,該區塊鏈的本地副本會不斷地更新用于擴展這個鏈條。當一個節點從網絡接收傳入的區塊時,它會驗證這些區塊,然后鏈接到現有的區塊鏈上。為建立這種鏈接,節點將檢查傳入的區塊頭并尋找該區塊的“父區塊哈希值”。
假設,例如一個節點在區塊鏈的本地副本中有277,314個區塊。該節點知道最后一個區塊為第277,314區塊,這個區塊的區塊頭哈希值為:
`00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249 `
然后該比特幣節點從網絡上接收到一個新的區塊,該區塊描述如下:
```
{
"size" : 43560,
"version" : 2,
"previousblockhash" :
"00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249",
"merkleroot" :
"5e049f4030e0ab2debb92378f53c0a6e09548aea083f3ab25e1d94ea1155e29d",
"time" : 1388185038,
"difficulty" : 1180923195.25802612,
"nonce" : 4215469401,
"tx" : [
"257e7497fb8bc68421eb2c7b699dbab234831600e7352f0d9e6522c7cf3f6c77",
#[... many more transactions omitted ...]
"05cfd38f6ae6aa83674cc99e4d75a1458c165b7ab84725eda41d018a09176634"
]
}
```
對于這一新的區塊,節點會在“父區塊哈希值”字段里找出包含它的父區塊哈希值。對于本節點來說這是已知的哈希值,也就是第 277314塊區塊的哈希值。故這個新區塊是這個鏈條里的最后一個區塊的子區塊,因此現有的區塊鏈得以擴展。節點將新的區塊添加至鏈條的尾端,使區塊鏈延長到一個新的高度277315。圖9-1顯示了通過“父區塊哈希值”字段連接三個區塊的鏈。

圖9-1 區塊通過引用前面的區塊頭哈希連接成區塊鏈
## 9.7 默克爾樹
區塊鏈中的每個區塊都使用*默克爾樹merkle tree*歸納了區塊鏈內的所有交易。
默克爾樹是一種*二叉哈希樹binary hash tree*,它是一種有效歸納和驗證數據集完整性的數據結構。默克爾樹是包含密碼哈希的二叉樹。術語“樹”在計算機學科中常被用來描述一種具有分支的數據結構,但是樹常常被倒置顯示,“根”在圖的上部而“葉子”在圖的下部,你會在后續章節中看到相應的例子。
在比特幣網絡中,默克爾樹被用來歸納一個區塊中的所有交易,生成整個交易集合的數字指紋,且提供了一種驗證某交易是否存在于某個區塊的高效方法。生成一棵完整的默克爾樹需要遞歸地對每一對節點進行哈希,直到只剩一個哈希值,它就是*根root*,或者*默克爾樹根Merkle root*。在比特幣的默克爾樹中使用的加密散列算法SHA256被應用了兩次,因此也被稱為雙哈希double-SHA256。
當N個數據元素被哈希和歸納到默克爾樹中,你至多計算2* log<sub>2</sub>(N) 次就能檢查出任意某數據元素是否在該樹中,這使得該數據結構非常高效。
Merkle樹是自底向上構建的。在如下的例子中,我們從A、B、C、D四個構成Merkle樹樹葉的交易開始,如圖9-2。這些交易都并不存儲在默克爾樹中,而是將數據哈希化,然后將哈希值存儲至相應的葉子節點。這些葉子節點分別是H<sub>A</sub>、H<sub>B</sub>、H<sub>C</sub>和H<sub>D</sub>:
H<sub>A</sub> = SHA256(SHA256(Transaction A))
然后,將兩個哈希值連接起來再將它們哈希在一起,這樣連續的葉子節點對就被歸納到父節點中。例如,要構造父節點H<sub>AB</sub>,將子節點的兩個32字節哈希值連接起來,創建1個64字節字符串。然后對該字符串進行雙哈希運算,生成父節點的哈希:
H<sub>AB</sub> = SHA256(SHA256(H<sub>A</sub> + H<sub>B</sub>))
這個過程一直持續到頂部只剩一個節點,即默克爾樹根節點。這個32字節的哈希值存儲在區塊頭中,歸納了所有4個交易中的所有數據。圖9-2顯示如何通過節點的成對哈希計算出根。

圖9-2 計算默克樹中的節點
因為默克爾樹是一個二叉樹,所以它需要偶數個葉子節點。如果要歸納的交易數為奇數,那么就復制最后一個交易哈希,湊夠偶數個葉節點,這稱為*平衡樹 balanced tree*。圖9-3中交易C就被復制了。

圖9-3 復制一個數據元素,湊夠偶數個數據元素
同樣的,從4個交易構造樹的方法可以推廣到構造任意大小的樹。在比特幣中,一個區塊中有幾百到一千多個交易是很常見的,這些交易的歸納方式完全相同,最后只產生1個32字節的數據作為唯一的默克爾樹根。在圖9-4中,將會看到一個由16個交易組成的樹。注意,盡管圖中根節點外觀看起來比葉節點大,但它的大小完全相同,只有32個字節。無論區塊中有一個交易還是十萬個交易,默克爾樹根總是將它們歸納為32字節。
為了證明一個區塊中包含了一個特定的交易,節點只需要進行log<sub>2</sub>(N)次32字節的哈希計算,就構成了一個連接特定交易到樹根的*認證路徑authentication path*或*默克爾樹路徑merkle path*。隨著交易數量的增加,這一點尤其重要,因為交易數量的以2為底的對數增長要慢得多。這使得比特幣節點能夠高效地生成10或12個哈希值(320–384字節)的路徑,這些哈希值可以在1M字節大小的區塊包括的1000多個交易中,提供某一個交易的存在證明。

圖9-4 歸納許多數據元素的默克爾樹
在下面的圖9-5中,節點可以通過生成一個只有4個32字節哈希值(總共128字節)的默克爾樹路徑來證明交易K包含在該區塊中。這條路經包括了4個哈希值(圖9-5中的陰影背景部分):H<sub>L</sub>, H<sub>IJ</sub>, H<sub>MNOP</sub>, and H<sub>ABCDEF</sub>。通過把這4個哈希值作為認證路徑,任何節點都可以通過計算另外四個成對哈希值H<sub>KL</sub>、H<sub>IJKL</sub>、H<sub>IJKLMNOP</sub>和默克爾樹根(在圖中用虛線勾勒)來證明H<sub>K</sub>(在圖的底部黑色背景)包含在默克爾樹根中。

圖9-5 用于證明包含數據元素的默克爾樹路徑
下面例9-1中的代碼演示如何使用libbitcoin庫的輔助函數,創建從葉節點哈希到根節點的默克爾樹。
例9-1 創建默克爾樹
`link:code/merkle.cpp[]`
例9-2顯示編譯和運行上面代碼的結果。
例9-2 編譯和運行默克爾樹示例代碼
```
$ # Compile the merkle.cpp code
$ g++ -o merkle merkle.cpp $(pkg-config --cflags --libs libbitcoin)
$ # Run the merkle executable
$ ./merkle
Current merkle hash list:
32650049a0418e4380db0af81788635d8b65424d397170b8499cdc28c4d27006
30861db96905c8dc8b99398ca1cd5bd5b84ac3264a4e1b3e65afa1bcee7540c4
Current merkle hash list:
d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
Result: d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
```
隨著規模的增大,默克爾樹的效率變得更為明顯。表9-3顯示用作默克爾樹路徑交換,證明交易屬于區塊的數據量。
表9-3 默克爾樹效率
Number of transactions |Approx. size of block|Path size(hashes) |Path size (bytes)
-|-|-|-
16 transactions|4 kilobytes|4 hashes|128 bytes
512 transactions|128 kilobytes|9 hashes|288 bytes
2048 transactions|512 kilobytes|11 hashes|352 bytes
65,535 transactions|16 megabytes|16 hashes|512 bytes
從表中可以看到,雖然區塊大小快速增加,從4 KB(包含16個交易)增加到16 MB(容納65535個交易),但證明包含一個交易所需的默克爾樹路徑增長就慢得多,僅僅從128字節增加到512字節。使用默克爾樹,節點可以只下載區塊頭(每個區塊只有80字節),并且仍然能夠通過從全節點檢索一個很小的默克爾樹路徑來識別區塊中包含的交易,而無需存儲或傳輸大部分區塊鏈(可能是幾GB的大小)。不維護完整區塊鏈的節點稱為簡單支付驗證(SPV)節點,它們使用默克爾樹路徑驗證交易,而無需下載完整的區塊。
## 9.8 默克爾樹和簡單支付驗證(SPV)
默克爾樹被SPV節點廣泛使用。SPV節點不保存所有交易,也沒有下載完整的區塊,只存有區塊頭。為了驗證交易是否包含在區塊中,而又無需下載區塊中的所有交易,它們可以使用驗證路徑或默克爾樹路徑。
例如,考慮一個SPV節點,該節點感興趣的是它的錢包中的收款地址收到的交易。SPV節點在它自己與對等節點的連接上建立一個布隆過濾器(參見[bloom_filters]),只接收與自己感興趣地址相關的交易。當對等節點看到與布隆過濾器匹配的交易時,會使用merkleblock消息發送這個區塊。merkleblock消息包含區塊頭以及感興趣的交易連接到區塊的默克爾樹根的默克爾樹路徑。SPV節點可以使用這個默克爾樹路徑將交易連接到區塊,并驗證該交易是否包含在區塊中。SPV節點還使用區塊頭將區塊鏈接到區塊鏈的其余部分。交易與區塊、區塊與區塊鏈這兩個環節結合起來,就能證明交易已經記錄在區塊鏈中。總而言之,SPV節點接收到的區塊頭和默克爾樹路徑的數據量將小于1KB,這個比完整區塊(目前約為1MB)少了一千倍以上的數據量。
## 9.9 比特幣測試區塊鏈
你可能會驚訝地發現不止有一個比特幣區塊鏈。“主”比特幣區塊鏈,由中本聰于2009年1月3日創建,與我們在本章研究的創世區塊一起被稱為比特幣區塊鏈*“主網mainnet”*。還有其他用于測試的比特幣區塊鏈:*testnet、segnet和regtest*。我們逐個了解下。
### 9.9.1 比特幣測試場景Testnet
Testnet是用于測試目的的測試區塊鏈、網絡和貨幣的名稱。Testnet是一個功能齊全的實時點對點網絡,同樣有錢包、測試比特幣(testnet coins)、挖礦以及主網所具備的其他功能。有兩個實質區別:Testnet上的幣沒有價值,挖礦難度也足夠低,任何人都可以相對容易地挖testnet幣(讓它們毫無價值)。
任何打算在比特幣主網上生產使用的軟件開發都應該首先在Testnet上用測試幣進行測試。這既保護了開發人員免受錯誤造成的資金損失,也保護了網絡免受錯誤造成的意外行為。
然而,讓這些幣一文不值,讓挖礦變得容易并不簡單。盡管有開發者呼吁,一些人還是使用先進的挖礦設備(GPU和ASIC)在Testnet上進行挖礦。這增加了難度,單純使用CPU已經沒辦法挖礦,最終使得難以獲得測試幣,人們開始重視測試幣,它們不再是一文不值。因此,時不時地,Testnet必須被廢棄,并從一個新的創世區塊重新啟動,重新設置難度。
當前的Testnet被稱為Testnet3,是Testnet的第三次迭代,于2011年2月重新啟動,為的是重置以前的Testnet的難度。
請記住,Testnet3是一個大型區塊鏈,在2017年初超過20GB。計算機滿負荷完全同步大約需要一天的時間。數據量雖然不如主網,但也不完全是“輕量級”的。運行Testnet節點的一個好方法是將其作為專用與此的虛擬機映像(例如VirtualBox、Docker、云服務器等)。
#### 9.9.1.1 使用Testnet
就像大多數其他客戶端軟件一樣,Bitcoin Core完全支持在Testnet而不只是在主網上運行。Bitcoin Core的所有功能都能在Testnet上工作,包括錢包、挖Testnet幣和同步一個完整的Testnet節點。
要在Testnet而不是主網上啟動Bitcoin Core,可以使用Testnet開關:
`$ bitcoind -testnet`
在日志中,您應該看到bitcoind正在默認bitcoind目錄的Testnet3子目錄中構建新的區塊鏈:
`bitcoind: Using data directory /home/username/.bitcoin/testnet3`
要連接到bitcoind,可以使用bitcoin-cli命令行工具,但還必須將其切換到Testnet模式:
```
$ bitcoin-cli -testnet getblockchaininfo
{
"chain": "test",
"blocks": 1088,
"headers": 139999,
"bestblockhash": "0000000063d29909d475a1c4ba26da64b368e56cce5d925097bf3a2084370128",
"difficulty": 1,
"mediantime": 1337966158,
"verificationprogress": 0.001644065914099759,
"chainwork": "0000000000000000000000000000000000000000000000000000044104410441",
"pruned": false,
"softforks": [
[...]
```
還可以在Testnet3上運行其他全節點實現,例如btcd(用Go編寫d的)和bcoin(用JavaScript編寫的),使用其他編程語言和框架進行實驗和學習。
在2017年初Ttestnet3支持主網的所有特性,包括隔離見證(參見[【7.8 隔離見證】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#78-隔離見證))。因此,Testnet3也可以用于測試隔離見證功能。
### 9.9.2 Segnet:隔離見證測試網
2016年,啟動了一個專用測試網,主要用于幫助開發和測試隔離見證(又名segwit,參見[【7.8 隔離見證】](https://github.com/tianmingyun/MasterBitcoin2CN/blob/master/ch07.md#78-隔離見證))。這個測試區塊鏈稱為segnet,可以通過運行特殊版本(分支)的Bitcoin Core與它連接。
由于隔離見證被添加到testnet3中,因此不再需要使用segnet來測試隔離見證的功能。
將來,就像segnet一樣,我們可能會看到其他專門設計用于測試單個功能或主要架構更改的測試網區塊鏈。
### 9.9.3 Regtest:本地區塊鏈
Regtest代表“回退測試”,是Bitcoin Core的功能,允許創建用于測試的本地區塊鏈。與Testnet3不同,Testnet3是一個公開和共享的測試區塊鏈,Regtest區塊鏈是運行在本地的封閉的測試系統。你可以使用它從頭開始啟Rregtest區塊鏈,創建本地創世區塊。還可以將其他節點添加到網絡中,或者只使用單個節點運行它,測試Bitcoin Core軟件。
要在Regtest模式下啟動Bitcoin Core,使用regtest標記:
`$ bitcoind -regtest`
與Testnet一樣,Bitcoin Core將在比特幣默認目錄的regtest子目錄下初始化一個新的區塊鏈:
`bitcoind: Using data directory /home/username/.bitcoin/regtest`
使用命令行工具時,還需要指定regtest標記。下面嘗試getblockchaininfo命令來檢查regtest區塊鏈:
```
$ bitcoin-cli -regtest getblockchaininfo
{
"chain": "regtest",
"blocks": 0,
"headers": 0,
"bestblockhash": "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
"difficulty": 4.656542373906925e-10,
"mediantime": 1296688602,
"verificationprogress": 1,
"chainwork": "0000000000000000000000000000000000000000000000000000000000000002",
"pruned": false,
[...]
```
就像你看到的,這里還沒有區塊。可以挖礦一些區塊(500個區塊)并獲得獎勵:
```
$ bitcoin-cli -regtest generate 500
[
"7afed70259f22c2bf11e406cb12ed5c0657b6e16a6477a9f8b28e2046b5ba1ca",
"1aca2f154a80a9863a9aac4c72047a6d3f385c4eec5441a4aafa6acaa1dada14",
"4334ecf6fb022f30fbd764c3ee778fabbd53b4a4d1950eae8a91f1f5158ed2d1",
"5f951d34065efeaf64e54e91d00b260294fcdfc7f05dbb5599aec84b957a7766",
"43744b5e77c1dfece9d05ab5f0e6796ebe627303163547e69e27f55d0f2b9353",
[...]
"6c31585a48d4fc2b3fd25521f4515b18aefb59d0def82bd9c2185c4ecb754327"
]
```
只需幾秒鐘就可以挖出所有這些區塊,這無疑使測試變得容易。如果查看錢包余額,會看到在前400個區塊中獲得了獎勵(創幣交易的獎勵必須是區塊深度達到100個區塊才能使用):
```
$ bitcoin-cli -regtest getbalance
12462.50000000
```
## 9.10 使用測試區塊鏈進行開發
比特幣的各種區塊鏈(Regtest、segnet、Testnet3、主網)為比特幣開發提供了一系列測試環境。無論是針對 Bitcoin Core開發,還是另一個全節點共識客戶端,都可以使用測試區塊鏈。可以是應用程序,如錢包、交易所、電子商務網站,甚至是開發最新的智能合約和復雜腳本。
可以使用測試區塊鏈建立開發管道。開發時,可以在Regtest上本地測試代碼。一旦具備在公網上進行嘗試,可切換到Testnet,將代碼公開到一個具有代碼和應用程序多樣性的動態環境中。最后,確信代碼能夠按預期工作,就可以切換到主網部署到生產環境中。如果要進行更改、改進、修復錯誤等,可以再次啟動管道,還是先在Regtest上部署每個更改,然后在Testnet上測試,最后部署到生產環境中。