P2P網(wǎng)絡(luò) - 比特幣開發(fā)指南

P2P網(wǎng)絡(luò) - 比特幣開發(fā)指南

原文鏈接: https://bitcoin.org/en/developer-guide#operating-modes

翻譯: terryc007

版本:1.0


比特幣開發(fā)指南

1. 區(qū)塊鏈

2. 交易

3.合約

4.錢包

5.支付處理

6.工作模式

7.P2P網(wǎng)絡(luò)

8.挖礦


比特幣網(wǎng)絡(luò)協(xié)議允許全節(jié)點一起協(xié)作維持p2p網(wǎng)絡(luò),實現(xiàn)區(qū)塊,交易數(shù)據(jù)的交換。全節(jié)點下載,并驗證每個個區(qū)塊,交易,然后在轉(zhuǎn)發(fā)給其他節(jié)點。檔案節(jié)點是一種全節(jié)點,它會存儲各個區(qū)塊鏈,并為其他節(jié)點提供歷史區(qū)塊服務(wù)。裁剪節(jié)點是一種不會保存這個區(qū)塊鏈的全節(jié)點。很多SPV客戶端也使用比特幣網(wǎng)絡(luò)協(xié)議來連接全節(jié)點。

因為共識規(guī)則不涉及到網(wǎng)絡(luò),因此比特幣程序可以選擇不同的網(wǎng)絡(luò),不同的協(xié)議,比如一些礦工使用高速區(qū)塊轉(zhuǎn)發(fā)網(wǎng)絡(luò),一些提供SPV級別安全的錢包,使用專業(yè)的交易信息服務(wù)器。

為提高一個可實操的比特幣P2P網(wǎng)絡(luò)例子,這個章節(jié)使用比特幣內(nèi)核作為典型的全節(jié)點,BitcoinJ作為典型SPV客戶端。這兩個程序都是靈活的,因此這里只討論默認(rèn)他們的行為。同時,考慮到隱私,下面例子中的ip地址已經(jīng)被替換成RFC5737預(yù)留的IP地址。

節(jié)點發(fā)現(xiàn)

當(dāng)程序第一啟動時,它并不知道任何活躍節(jié)點的ip地址。為了發(fā)現(xiàn)一些全節(jié)點的ip地址,他們會查詢硬編碼在比特幣內(nèi)核或BitCoinJ中的,一個或多個DNS域名,在返回的結(jié)果中應(yīng)該包含一個或多個DNS A記錄,里面有一些可接受新連接的全節(jié)點的ip地址。 比如,使用Unix命令 dig:

;;QUESTION SECTION: 
;seed.bitcoin.sipa.be. IN A 

;; ANSWER SECTION: 
seed.bitcoin.sipa.be. 60 IN A 192.0.2.113 
seed.bitcoin.sipa.be. 60 IN A 198.51.100.231 
seed.bitcoin.sipa.be. 60 IN A 203.0.113.183 
[...]

DNS 種子由比特幣社區(qū)成員維護(hù)。其中一部分提供動態(tài)DNS種子服務(wù)器,它通過掃描比特幣網(wǎng)絡(luò),自動獲取活動節(jié)點的ip地址;其他的提供一些靜態(tài)DNS種子,這需要手動更新,不過他們很有可能提供不活躍節(jié)點的ip地址。不管是動態(tài)的,還是靜態(tài)的DNS種子,如果節(jié)點在主網(wǎng)上運行在端口號8333,或在測試網(wǎng)絡(luò)運行在端口號18333,就會被加入到DNS種子。

DNS種子結(jié)果沒有被授權(quán),一個惡意的DNS種子運營者或網(wǎng)絡(luò)中間人攻擊者能返回僅被攻擊者控制的節(jié)點的ip地址,在攻擊者自己的網(wǎng)絡(luò)中,孤立節(jié)點,并給他們假的交易,區(qū)塊數(shù)據(jù)。因為這個原因,程序不應(yīng)該只依賴一個DNS種子。

一但程序連接上比特幣網(wǎng)絡(luò),它的節(jié)點就可以開始發(fā)送,帶有網(wǎng)絡(luò)中其他節(jié)點IP地址,端口號的addr消息給其他節(jié)點。這個提供了一個完整的去中心化節(jié)點發(fā)現(xiàn)方法。比特幣內(nèi)核會在本地數(shù)據(jù)庫中保存已知節(jié)點的信息,通常,等下一次程序啟動時,它就不需要使用DNS種子,直接可以跟這些節(jié)點連接即可。

然而,節(jié)點通常會離開網(wǎng)絡(luò)或者改變ip地址,這樣程序在啟動時,在需要多次嘗試才有可能連接到比特幣網(wǎng)絡(luò)。這了會增加連接到比特幣網(wǎng)絡(luò)的延遲時間,使得用戶在發(fā)送交易或檢查支付狀態(tài)前,不得不等待一段時間。

為避免這種延遲,BitcoinJ總是使用動態(tài)DNS種子,來獲取那些被確定為活躍節(jié)點的IP地址。比特幣處內(nèi)核也嘗試在降低延遲,避免使用不必要的DNS節(jié)點中權(quán)衡。如果比特幣內(nèi)核在它的節(jié)點數(shù)據(jù)庫中有記錄,它就會用11秒時間去連接至少其中一個節(jié)點,失敗后,才使用DNS節(jié)點獲取ip地址;如果在11秒內(nèi)成功建立連接,則不在向DNS種子查詢。

比特幣內(nèi)核跟BitcoinJ在其他特定版本的第一版本發(fā)布時,他們在代碼里面都硬編碼了一些節(jié)點當(dāng)時活躍節(jié)點的IP地址跟端口號。比特幣內(nèi)核內(nèi)置了一個自動回調(diào)選項,當(dāng)沒有DNS種子服務(wù)器在60秒內(nèi)回應(yīng)查詢,比特幣內(nèi)核會開始嘗試跟這些節(jié)點連接。

作為一個手段回調(diào)選項,比特幣內(nèi)核也提供了好幾個命令行連接選項,包括從一個指定節(jié)點,通過其IP地址獲取一個節(jié)點列表,或直接跟一個指定節(jié)點,通過IP地址建立持久連接。通過-help獲取命令行詳情。BitcoinJ也可以通過編程實現(xiàn)這樣的功能。

資源: Bitcoin種子, 這個程序管理了好幾個比特幣內(nèi)核,BitcoinJ都有用到的DNS種子。比特幣內(nèi)核DNS種子政策。比特幣內(nèi)核,BitcoinJ里硬編碼的節(jié)點IP地址是使用makeseeds script生成的。

連接到節(jié)點

通過給遠(yuǎn)程節(jié)點,發(fā)送version 消息跟他節(jié)點建立連接,消息里面包括軟件版本號,區(qū)塊,當(dāng)前時間。遠(yuǎn)程節(jié)點也返回一個version消息。然后,他們再給其他節(jié)點發(fā)送verack消息,表示他們已經(jīng)建立連接。

一但建立連接,客戶端就能給遠(yuǎn)程節(jié)點發(fā)送getaddraddr消息獲取其他節(jié)點信息。

客戶端為維持跟節(jié)點的連接,在其離線前30分鐘,它會給其他節(jié)點發(fā)送一個消息。如果節(jié)點在90分鐘內(nèi),沒有返回消息,那么這個客戶端就認(rèn)為連接已經(jīng)關(guān)閉。

初始化區(qū)塊下載

在全節(jié)點驗證非確認(rèn)交易,最近挖出的區(qū)塊前,它必須下載,驗證最佳區(qū)塊鏈上所有的區(qū)塊(從創(chuàng)世區(qū)塊到最頂部的區(qū)塊)。這叫做初始化區(qū)塊下載(IBD)或者叫初始化同步。

雖然“初始化” 意味著這個方法只會使用一次,但可以在任何時候,在下載大量區(qū)塊的時候,用到它,比如當(dāng)一個之前連接過的節(jié)點下線了很長一段時間。這種情況,節(jié)點能使用IBD方法去下載從它最近上線以來,所有的挖出的區(qū)塊。

在任何時候,只要比特幣內(nèi)核本地最佳區(qū)塊鏈上,它最新的區(qū)塊的區(qū)塊頭時間,以及超過了24小時,它就會使用IBD方法獲取新的區(qū)塊。如果本地最佳區(qū)塊頭鏈,比本地最佳區(qū)塊鏈多出144個區(qū)塊(這也就意味著,本地最佳區(qū)塊鏈已經(jīng)有24小時沒有更新了),比特幣內(nèi)核0.10.0也會使用IBD方法。

區(qū)塊優(yōu)先

比特幣內(nèi)核(一直到0.9.3版本為止)使用簡單的初始化區(qū)塊下載(IBD)方法,我們稱之為區(qū)塊優(yōu)先。它的目標(biāo)是從最佳區(qū)塊鏈按序下載區(qū)塊。

節(jié)點第一啟動時,在它本地最佳區(qū)塊鏈只有一個區(qū)塊 — 硬編碼的創(chuàng)世區(qū)塊(區(qū)塊0)。節(jié)點了會選擇一個遠(yuǎn)程節(jié)點(也叫同步節(jié)點),并給它發(fā)送一個getblocks消息,如下圖所示:

getblocks消息頭部哈希域,這個新節(jié)點發(fā)這個區(qū)塊僅有的頭哈希 - 創(chuàng)世區(qū)塊(6fe2...0000 內(nèi)部字節(jié)序)。同時它把停止哈希域全設(shè)置為0以獲取最大返回結(jié)果。

同步節(jié)點一但收到這個getblocks消息,它使用第一個哈希頭去它本地最佳區(qū)塊鏈搜索帶有這個哈希頭的區(qū)塊。如果找到block 0與之匹配,它就從區(qū)塊1開始,返回500個區(qū)塊清單(getblocks消息返回的最大個數(shù))。它使用inv消息來發(fā)送這些區(qū)塊清單。 如下面所示:

存貨清單是一些在比特幣網(wǎng)絡(luò)上獨一無二的身份標(biāo)識信息。每個存貨包含一個類型,一個對象實例的唯一標(biāo)識。對于區(qū)塊而言,這個唯一標(biāo)識是區(qū)塊頭的哈希值。

inv消息中區(qū)塊存貨信息的順序跟其在區(qū)塊鏈中的是一樣的,因此第一個inv消息包含了區(qū)塊1到區(qū)塊501存貨信息。(比如,如上面所示,區(qū)塊1的哈希值是4860...0000)

IBD節(jié)點使用收到存貨清單,通過getdata消息,從同步節(jié)點獲取128個區(qū)塊。如下圖所示:

對于區(qū)塊優(yōu)先的節(jié)點而言,按序請求,發(fā)送區(qū)塊是非常重要的,因為每個區(qū)塊頭會引用它前面的區(qū)塊頭。這就意味IBD節(jié)點,必須在父區(qū)塊還沒有接收完之前,是不能完全的驗證區(qū)塊的。之所以不能驗證區(qū)塊,是因為那些沒有收到父區(qū)塊的區(qū)塊是孤塊;下小節(jié)會詳細(xì)地介紹到。

一但收到getdata消息,同步節(jié)點就會把請求的區(qū)塊返回給IBD節(jié)點。每個區(qū)塊被序列化成區(qū)塊格式,并發(fā)送各自的block消息。發(fā)送的第一個block消息(block1),如下圖所示。

IBD節(jié)點下載每個區(qū)塊,并驗證,然后獲取下一個還未請求過的區(qū)塊,并維持一個128個區(qū)塊的下載隊列。當(dāng)它獲取完存貨清單中所有的區(qū)塊后,它就發(fā)送另外一個getblocks消息給同步節(jié)點,以獲取最多500個區(qū)塊的存貨清單。第二個getblocks消息包含多個區(qū)塊頭哈希,如下圖所示:

同步節(jié)點一但收到第二個getblocks消息,它就按照收到區(qū)塊頭哈希的順序,挨個在它本地最佳的區(qū)塊鏈中尋找匹配的區(qū)塊。如果它找到一個匹配的區(qū)塊,它會從該區(qū)塊的下個區(qū)塊開始,返回500個區(qū)塊存貨清單。 如果沒有找到一個匹配的哈希(除了那個截止哈希外),它會假定這個兩個節(jié)點只有block0是一樣的,因此它會發(fā)送一個從block1開始的inv消息。

通過這樣的重復(fù)查找,允許同步節(jié)點發(fā)送有用的存貨清單,即使IDB節(jié)點本地的區(qū)塊鏈?zhǔn)菑耐焦?jié)點的本地區(qū)塊鏈分叉而來的。IBD節(jié)點上的區(qū)塊離區(qū)塊鏈頂部區(qū)塊越近,分叉檢測就變的越有用。 [?]

當(dāng)IBD節(jié)點收到第二個inv消息后,它會使用getdata消息請求這些區(qū)塊。同步節(jié)點會給IBD節(jié)點返回block消息。然后IBD節(jié)點會使用getblocks消息,繼續(xù)請求更多的存貨清單。 不斷的重復(fù)這個過程直到IBD節(jié)點同步完整個區(qū)塊鏈。到這后,IBD節(jié)點通過普通的區(qū)塊廣播(后續(xù)小節(jié)會講到)來接受新的區(qū)塊。

區(qū)塊優(yōu)先的優(yōu)缺點

區(qū)塊優(yōu)先主要優(yōu)點在于簡單。其主要缺點在于只依賴于一個同步節(jié)點來下載區(qū)塊數(shù)據(jù)。這會帶來幾個影響:

  • 速度受限: 因為所有的請求都指向一個同步節(jié)點,這樣如果同步節(jié)點的上傳帶寬有限,那么IBD節(jié)點的下載速度就很慢。注意:如果同步節(jié)點離線,比特幣內(nèi)核會從另外一個同步節(jié)點下載— 但是它仍然一次只從一個同步節(jié)點下載。

  • 下載重起:同步節(jié)點可能給IBD節(jié)點發(fā)送非最佳(但是其他的都有效)區(qū)塊鏈。IBD節(jié)點是無法驗證它到底是不是最佳的區(qū)塊鏈,直到初始化區(qū)塊下載接近完成時才可以。這會強(qiáng)制IBD節(jié)點重新從另外一個節(jié)點下載區(qū)塊鏈。開發(fā)者在比特幣內(nèi)核中,在多個不同區(qū)塊高度,加了好幾個區(qū)塊鏈檢測點,來幫組IBD節(jié)點監(jiān)測它是否在下載一條非最佳區(qū)塊鏈。 這可以讓IBD節(jié)點盡早的重啟下載。

  • 硬盤充滿攻擊:這個跟下載重起關(guān)系很大,如果同步節(jié)點發(fā)送了一個非最佳區(qū)塊鏈,這條鏈會存儲在硬盤上,浪費磁盤空間,可能會使磁盤上充滿無用的數(shù)據(jù)。

  • 高內(nèi)存消耗:不管是故意的,還是意外,同步節(jié)點可能無序地發(fā)送區(qū)塊,這會導(dǎo)致一些孤塊,只有收到并驗證了其父塊后,才能驗證這些孤塊。孤塊在等待驗證期間會一直存在內(nèi)存中,這會消耗大量內(nèi)存。

在比特幣內(nèi)核0.10.0中,所有的這些問題在頭部優(yōu)先IBD方法中,部分或全部的得以解決。

資源: 下面的的表格總結(jié)了這節(jié)提到的消息。點擊消息欄的鏈接可以查看對于消息的參考頁面。

消息 getblocks inv getdata block
From→To IBD→Sync Sync→IBD IBD→Sync Sync→IBD
內(nèi)容 一個/多個頭哈希 最多500個區(qū)塊存貨(唯一id) 一個/多個區(qū)塊存貨 一個 序列化區(qū)塊

區(qū)塊頭優(yōu)先

比特幣內(nèi)核0.10.0使用區(qū)塊頭優(yōu)先IBD的初始化區(qū)塊下載方法。其目標(biāo)是先下載最佳區(qū)塊鏈頭,部分驗證,然后并行下載相應(yīng)的區(qū)塊。這解決了幾個區(qū)塊優(yōu)先IBD方法中的問題。

節(jié)點第一次啟動時,它本地最佳區(qū)塊鏈只要一個區(qū)塊 - 硬編碼的創(chuàng)世區(qū)塊(block0)。 它會選擇一個遠(yuǎn)程節(jié)點,這里我們稱之為同步節(jié)點,然后給同步節(jié)點發(fā)送getheaders消息。如下圖所示:

getheaders消息的區(qū)塊頭哈希域,新節(jié)點只發(fā)送了它本地僅有的區(qū)塊頭哈希 - 創(chuàng)世區(qū)塊哈希( 6fe2…0000 內(nèi)部字節(jié)序)。同時會截止哈希域全設(shè)為0,以獲取最多哈希值。

同步節(jié)點一但收到getheaders消息,它會取出第一個區(qū)塊頭哈希,然后用它在本地最佳區(qū)塊鏈中搜索區(qū)塊。如果block0匹配,同步節(jié)點就會從block1開始,返回2000個區(qū)塊頭哈希。它以headers消息的方式,來發(fā)送這些區(qū)塊頭。如下圖所示:

IBD節(jié)點可以部分驗證區(qū)塊頭,它是通過確保區(qū)塊頭所有字段遵循共識規(guī)則,同時區(qū)塊頭的哈希值要低于nBits字段的目標(biāo)閥值來實現(xiàn)。(要完整驗證區(qū)塊,仍需要獲得該區(qū)塊所有交易后才可以)

當(dāng)IBD節(jié)點部分驗證完區(qū)塊頭之后,它可以并行做兩件事情:

  1. 下載更多的區(qū)塊頭: IBD節(jié)點可以發(fā)送另外一個getheaders消息到同步節(jié)點,以獲取最佳區(qū)塊鏈上,下一批2000個區(qū)塊頭。這些區(qū)塊頭可以被立即驗證,同時不斷的重復(fù)批量發(fā)送請求,直到從同步節(jié)點收到的headers消息中所包含的頭少于2000個,這表示已沒有更多的區(qū)塊頭。在撰寫本文時,少于200個來回,就可以完成整個區(qū)塊頭同步,大約需要下載32MB數(shù)據(jù)。

    一但I(xiàn)BD節(jié)點收到一個少于2000個區(qū)塊頭的headers消息,它就給它所有外連的節(jié)點發(fā)送一個getheaders消息,看看他們最佳區(qū)塊鏈的情況。通過對比它們返回的消息,它很容易通過它外聯(lián)的節(jié)點,判斷它所下載的區(qū)塊頭是不是屬于最佳區(qū)塊鏈上的。這就意味一個不誠實的節(jié)點很快會被發(fā)現(xiàn),即使不用檢測點(只要IBD節(jié)點連接到至少一個誠實節(jié)點,如果找不到誠實節(jié)點,比特幣內(nèi)核會繼續(xù)提供檢查點)。

  2. 下載區(qū)塊: 當(dāng)IBD繼續(xù)下載區(qū)塊頭時,以及完成區(qū)塊頭下載后,IBD節(jié)點會請求并下載每個區(qū)塊。IBD節(jié)點通過區(qū)塊頭鏈中區(qū)塊的哈希,來創(chuàng)建getdata消息。而在區(qū)塊優(yōu)先中,getdata中的區(qū)塊頭哈希需要通過inv消息中的區(qū)塊存貨清單來提供。但在區(qū)塊頭先中,就不必從同步節(jié)點獲取區(qū)塊, 它可以從其他任何全節(jié)點獲取。(雖然并不是所有的全節(jié)點存儲所有的區(qū)塊。) 這就運行它能夠并行獲取區(qū)塊,同時避免下載速度受限于單個同步節(jié)點的帶寬速度。

為從更多的節(jié)點加載數(shù)據(jù),比特幣內(nèi)核一次最多從單個節(jié)點獲取16個區(qū)塊,最多8個外向連接。這就意味采用區(qū)塊頭優(yōu)先的比特幣內(nèi)核, 在IBD階段,同時最多可以同時發(fā)起128個區(qū)塊請求。(跟采用區(qū)塊優(yōu)先的比特幣內(nèi)核最大請求數(shù)是一樣的)

比特幣內(nèi)核采用的區(qū)塊頭優(yōu)先模式,采用1024個區(qū)塊為一移動下載時間窗口,以最大化下載速度。在下載時間窗口中最低區(qū)塊,是下一個即將被驗證的區(qū)塊。 如果輪到區(qū)塊驗證了,但區(qū)塊還未下載完成,比特幣內(nèi)核會至少會等2秒,等待區(qū)塊從失速節(jié)點下載完成,如果還沒有下載完成,比特幣內(nèi)核會斷開跟失速節(jié)點的連接,并嘗試給另外一個節(jié)點連接。比如,如上圖所示,如果在2秒后,節(jié)點A不能發(fā)送區(qū)塊3,那么節(jié)點A會被斷開連接。

一但I(xiàn)BD節(jié)點同步完整個區(qū)塊鏈,他會接收來那些通過正常區(qū)塊廣播而來的區(qū)塊,這個會在后面的小節(jié)會講到。

資源: 下面的的表格總結(jié)了這節(jié)提到的消息。點擊消息欄的鏈接可以查看對于消息的參考頁面。

消息 getheaders headers getdata block
發(fā)送→接 IBD→Sync Sync→IBD IBDMany ManyIBD
內(nèi)容 一個/多個區(qū)塊頭哈希 最大2000個區(qū)塊頭哈希 一個/多個從哈希頭派生出來的區(qū)塊存貨清單 一個序列化區(qū)塊

區(qū)塊廣播

當(dāng)?shù)V工發(fā)現(xiàn)一個新區(qū)塊后,它會使用下面的方法把區(qū)塊廣播給它的節(jié)點:

  • 主動推送區(qū)塊:礦工給它的每個一個全節(jié)點發(fā)送一個帶有新區(qū)塊的block消息。在這種情況,礦工不采用標(biāo)準(zhǔn)的轉(zhuǎn)發(fā)方法,因為它知道跟它連接的節(jié)點沒有一個正好發(fā)現(xiàn)這個區(qū)塊。

  • 標(biāo)準(zhǔn)轉(zhuǎn)發(fā)區(qū)塊: 礦工扮演一個標(biāo)準(zhǔn)的轉(zhuǎn)發(fā)節(jié)點,給它每一個節(jié)點(全節(jié)點,SPV),通過發(fā)送帶有新區(qū)塊存貨訂單的inv消息。 通常節(jié)點會有以下返回:

    1. 每個區(qū)塊優(yōu)先(BF)節(jié)點,想要從全節(jié)點通過getdata消息中獲取區(qū)塊信息。

    2. 每個區(qū)塊頭優(yōu)先(HF)節(jié)點,想要從全節(jié)點通過getheaders消息獲取區(qū)塊,消息中應(yīng)包括其最佳區(qū)塊鏈上,最高區(qū)塊頭的哈希頭,以及可能帶有一些,用于檢測分叉的,在最佳區(qū)塊鏈上的后續(xù)區(qū)塊頭。然后緊接著發(fā)送一個getdata消息去請一個完整的區(qū)塊。通過先請求區(qū)塊頭,一個區(qū)塊頭優(yōu)先的節(jié)點可能會拒絕孤塊,這會在下面小節(jié)會講到。

    3. 每個SPV客戶端,通常想通過getdata消息獲取默克爾區(qū)塊。

      礦工根據(jù)每個請求相應(yīng)的給他們返回消息。 通過block消息發(fā)送區(qū)塊,通過headers消息發(fā)送一個或多個區(qū)塊頭,在0/多個tx消息后,通過merkleblock消息,發(fā)送與SPV客戶端bloom過濾器相應(yīng)的默克爾區(qū)塊,交易。

      1. 直接公告區(qū)塊頭:中轉(zhuǎn)節(jié)點可跳過getheadersinv消息之間來回切換的方式,獲取新區(qū)塊信息,直接立即發(fā)送一個包含完整新區(qū)塊頭的headers消息。HF(區(qū)塊頭優(yōu)先)節(jié)點收到這個消息后,當(dāng)它在區(qū)塊頭優(yōu)先IBD階段時,它會部分驗證區(qū)塊頭,如果區(qū)塊頭是有限的,它就會通過getdata消息,來請求整個區(qū)塊內(nèi)容。 中轉(zhuǎn)節(jié)點會給getdata請求,相應(yīng)的以blockmerkleblock消息返回完整的,或過濾后的區(qū)塊數(shù)據(jù)。HF節(jié)點在握手連接時,可以通過發(fā)送一個特殊的sendheaders消息來發(fā)出它更喜歡接收headers消息,而非inv消息的信號。

        這個區(qū)塊廣播協(xié)議已經(jīng)在BIP130被提議,自從比特幣內(nèi)核0.12后,都實現(xiàn)了這個協(xié)議。

在默認(rèn)情況下,比特幣內(nèi)核使用直接廣播區(qū)塊的方式給那些發(fā)送了sendheaders信號的節(jié)點廣播區(qū)塊,而對其他節(jié)點使用標(biāo)準(zhǔn)區(qū)塊轉(zhuǎn)發(fā)方式。比特幣內(nèi)核接受以上所有方式轉(zhuǎn)發(fā)的區(qū)塊。

全節(jié)點驗證收到的區(qū)塊,然后使用標(biāo)準(zhǔn)區(qū)塊轉(zhuǎn)發(fā)方式轉(zhuǎn)發(fā)給它的節(jié)點。下面精簡表格,重點羅列了這個過程中用到的消息(Relay, BH, HF, SPV 分別對應(yīng) 轉(zhuǎn)發(fā)節(jié)點,區(qū)塊優(yōu)先節(jié)點,區(qū)塊頭優(yōu)先節(jié)點,SPV客戶端;Any - 表示一個使用任何獲取區(qū)塊方法的節(jié)點)

消息 inv getdata getheaders headers
From→To Relay→Any BF→Relay HF→Relay Relay→HF
內(nèi)容 新區(qū)塊清單 新區(qū)塊清單 在HF節(jié)點最佳區(qū)塊頭鏈(BHC)上,一個/多個區(qū)塊頭哈希 最多2000個區(qū)塊頭,把HF節(jié)點的BHC跟轉(zhuǎn)發(fā)節(jié)點的BHC連接起來
消息 block merkleblock tx
From→To Relay→BF/HF Relay→SPV Relay→SPV
內(nèi)容 新序列化區(qū)塊 對新區(qū)塊修改后的默克爾區(qū)塊 來自新區(qū)塊,跟bloom過濾器匹配的序列化的交易

孤塊

區(qū)塊優(yōu)先節(jié)點可能會下載孤塊。所謂的孤塊就是指之前區(qū)塊頭哈希字段,所指向的區(qū)塊還未看到。也就是說,孤塊沒有可知的父區(qū)塊(跟陳腐區(qū)塊不一樣,它們有父區(qū)塊,但是它們不屬于最近區(qū)塊鏈)。

當(dāng)區(qū)塊優(yōu)先節(jié)點下載了一個孤塊,節(jié)點不會立刻去驗證它,而是給發(fā)送孤塊的節(jié)點(廣播節(jié)點)發(fā)送一個getblocks消息,這個廣播節(jié)點會返回一個帶有區(qū)塊清單的inv消息,這個清單里面是節(jié)點丟失區(qū)塊的信息(最多500條);下載節(jié)點使用getdata消息請求這些區(qū)塊;然后廣播節(jié)點會以block消息形式發(fā)送這些區(qū)塊。然后下載節(jié)點會驗證這些區(qū)塊,一但之前孤塊的父區(qū)塊下載完成,并被驗證,下載節(jié)點就會驗證之前的孤塊。

區(qū)塊頭優(yōu)先節(jié)點為避免復(fù)雜,它在使用getdata消息請求一個區(qū)塊前,經(jīng)常先使用getheaders消息請求區(qū)塊頭。 廣播節(jié)點會給下載節(jié)點發(fā)送一個headers消息,這個消息里面包含了它認(rèn)為的,下載節(jié)點要達(dá)到最佳區(qū)塊鏈頂部,所需要的所有區(qū)塊頭(做多2000條);每個區(qū)塊頭都會指向它的父區(qū)塊,因此當(dāng)下載節(jié)點收到block消息時,該區(qū)塊不應(yīng)該是一個孤塊 — 因為它所有的父塊哈希都已經(jīng)知道(即使他們還未被驗證)。如果在block消息中,收到的區(qū)塊是孤塊,那么區(qū)塊頭優(yōu)先節(jié)點會立即丟棄它。

然而,丟棄孤塊意味著,區(qū)塊頭優(yōu)先節(jié)點會忽略掉礦工以主動推送方法發(fā)出的孤塊。

交易廣播

要發(fā)送一個交易到另外一個節(jié)點,需要發(fā)送一個inv消息。如果節(jié)點收到一個getdata消息,就會使用tx發(fā)送這個交易。節(jié)點收到這個交易后,如果是一個有效的交易,也會以同樣的方式轉(zhuǎn)發(fā)交易。

內(nèi)存池

全節(jié)點可以跟蹤那些可以打包進(jìn)下一區(qū)塊的未確認(rèn)交易。這對于那些挖取這些或全部交易的礦工來說,是非常有必要的,但是它對于任何想要跟蹤未被確認(rèn)交易的節(jié)點來說,也是很有用,比如為SPV節(jié)點提供未確認(rèn)交易信息服務(wù)的節(jié)點。

因為在比特幣中,未確認(rèn)交易沒有一種持久狀態(tài),比特幣內(nèi)核會把他們保存在內(nèi)存里,也叫做內(nèi)存池。當(dāng)一個節(jié)點關(guān)掉后,除了那些保存到錢包的交易外,其他在內(nèi)存池中的交易全部會丟失。這就意味著,但節(jié)點重起后,那些沒有被挖出的未確認(rèn)交易會慢慢的從網(wǎng)絡(luò)中消息掉,或因為內(nèi)存不足時,把其中的一些未確認(rèn)交易從內(nèi)存池中刪除掉。

那些沒有被打包進(jìn)去區(qū)塊的交易會變成陳腐區(qū)塊,它們可能會被重新加到內(nèi)存池中。如果替代區(qū)塊已包含這些交易,這些從新加入到內(nèi)存池的交易會立即被刪除掉。在比特幣內(nèi)核中,這種情況就是,從最佳區(qū)塊鏈最頂部開始(最高區(qū)塊),把鏈上的陳腐區(qū)塊一個個刪掉。當(dāng)每個區(qū)塊被刪除時,它的交易會重新加到內(nèi)存池中。刪掉完陳腐區(qū)塊后,在區(qū)塊鏈頂部,逐個加上替代區(qū)塊。當(dāng)添加完一個區(qū)塊后,區(qū)塊中確認(rèn)的交易就會從內(nèi)存中刪除掉。

SPV客戶端因為不需要轉(zhuǎn)發(fā)交易,所以他們就沒有內(nèi)存池。他們不能獨立的驗證一個交易是否包含在一個區(qū)塊里面,同時SPV客戶端只能花UTXOs,所以他們不知道哪個交易是合格的,是可以打包到下一個區(qū)塊的。

作弊節(jié)點

要注意的是,對于區(qū)塊,交易這兩種廣播,系統(tǒng)有一個機(jī)制來懲罰那些作弊節(jié)點。 他們會通過發(fā)送錯誤信息來占用帶寬,計算資源。如果一個節(jié)點的banscore值大于-banscore=<n> 設(shè)置的閥值,它就會被禁止-bantime=<n>中所設(shè)置的時長,這個默認(rèn)是86400秒(24小時)。

警告

在早期的比特幣內(nèi)核版本,是允許開發(fā)者,可信的社區(qū)成員給用戶發(fā)布比特幣警告,以通知用戶比特幣網(wǎng)絡(luò)出現(xiàn)嚴(yán)重的問題。這個消息系統(tǒng)在比特幣內(nèi)核0.13.0就已經(jīng)作廢掉;然而,內(nèi)部警告,分叉檢測警告,-alertnofity功能還保留著。


聲明:

文中帶有[?]的地方,表示我對此翻譯明顯感覺不太對的,后續(xù)會不斷修正。

有些地方可能會翻譯的不好,不地道,甚至錯誤,如果有發(fā)現(xiàn),還請留言,指出,以便我好修正,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容