一、P2P網絡架構
比特幣采用了基於互聯網的點對點(P2P:peer-to-peer)分布式網絡架構。
比特幣網絡可以認為是按照比特幣P2P協議運行的一系列節點的集合。
本文來分析下比特幣網絡,了解它跟傳統中心化網絡的區別,以及比特幣網絡是如何發現相鄰節點的。

二、節點類型及分工
盡管比特幣P2P網絡中的各個節點相互對等,但是根據所提供的功能不同,各節點可能具有不同的分工。每個比特幣節點都是路由、區塊鏈數據庫、挖礦、錢包服務的功能集合。一個全節點(full node)包括下圖所示的四個功能:
下圖為一個包含四個完整功能的比特幣網絡節點:錢包、礦工、完整區塊鏈、網絡路由節點。
每個節點都參與全網絡的路由功能,同時也可能包含其他功能。每個節點都參與驗證並傳播交易及區塊信息,發現並維持與對等節點的連接。名為“網絡路由節點”的橙色圓圈即表示該路由功能。
一些節點保有一份完整的、最新的區塊鏈拷貝,這樣的節點被稱為“
全節點”。全節點能夠獨立自主地校驗所有交易,而不需借由任何外部參照。
另外還有一些節點只保留了區塊鏈的一部分,它們通過一種名為“簡易支付驗證(SPV)”的方式來完成交易驗證。這樣的節點被稱為
“SPV節點”,又叫“輕量級節點”。

三、對等節點發現
為了能夠加入到比特幣網絡,比特幣客戶端會做一下幾件事情:
a、節點會記住它最近成功連接的網絡節點,當重新啟動后它可以迅速與先前的對等節點網絡重新建立連接。
b、節點會在失去已有連接時嘗試發現新節點。
c、當建立一個或多個連接后,節點將一條包含自身IP地址消息發送給其相鄰節點。相鄰節點再將此消息依次轉發給它們各自的相鄰節點,從而保證節點信息被多個節點所接收、保證連接更穩定。
d、新接入的節點可以向它的相鄰節點發送獲取地址getaddr消息,要求它們返回其已知對等節點的IP地址列表。節點可以找到需連接到的對等節點。
e、在節點啟動時,可以給節點指定一個正活躍節點IP, 如果沒有,客戶端也維持一個列表,列出了那些長期穩定運行的節點。這樣的節點也被稱為種子節點(其實和BT下載的種子文件道理是一樣的),就可以通過種子節點來快速發現網絡中的其他節點。
1、比特幣的核心部分維護一個在啟動時可以連接的對等節點列表。當一個完整的節點第一次啟動時,它必須被自舉(bootstrapped)到網絡。這個過程如今在比特幣的核心部分通過一個短名單上的DNS種子自動執行。選項-dnsseed可以被用來定義這種行為,默認的設置是1。DNS請求返回一個可連接的IP地址列表。比特幣客戶端從那里可以連接到整個比特幣網絡。
2、自舉的另外一種方法是使用參數-seednode=<ip>。通過這個參數,用戶可以預先定義連接到哪個服務器,並在建立對等節點列表之后斷開連接。另外一個方法是在啟動比特幣核心時配置-connect=<ip>參數來選擇連接到哪些對等節點(未被配置的IP將不會被連接)。添加對等節點的最后一種方法是通過參數-addnode=<ip>添加一個單獨的節點到對等節點列表中。
自舉過程完成后,節點向其對等節點發送一個包含其自身IP地址的addr消息。其對等的每個節點向它們自己的對等節點轉發這個信息,以便進一步擴大連接池。
通過getpeerinfo命令可以查看某個節點所連接的對等節點及相關的數據。
3、連接到對等節點
節點通過發送version消息連接到一個對等節點。消息version 包含了節點的版本信息、塊信息和距離遠程節點的時間。一旦這個消息被對等節點收到,它必須回復一個verack。如果它願意建立對等關系,它將發送自己的version消息。
一旦建立對等關系,節點可以向遠程節點發送getaddr和addr消息來獲得其它的對等節點信息。為了維持與對等節點的連接,節點默認情況下每30分鍾內會給對等節點至少發送一次信息。如果超過90分鍾沒有收到回復,節點會認為連接已經斷開。
4、塊廣播
在與對等節點建立連接后,雙方互發包含最新塊哈希值的getblocks消息。如果某個節點堅信其擁有最新的塊信息或者有更長的鏈,它將發送一個inv消息,其中包含至多500個最新塊的哈希值,以此來表明它的鏈更長。收到的節點使用getdata來請求塊的詳細信息,而遠程的節點通過命令block來發送這些信息。在500個塊的信息被處理完之后,節點可以通過getblocks請求更多的塊信息。這些塊在被接收節點認證之后得到確認。
新塊的確認也可通過礦工挖到並發布的塊來發現。其擴散過程和上述類似。通過之前的連接,新塊以inv消息發布出去,而接收節點可以通過getdata請求這些塊的詳細信息。
5、交易的廣播
和對等節點的交易通過inv消息來實現。如果收到了getdata信息,那么交易通過發送tx實現。對等節點收到有效的交易的信息后會通過類似的方式將其擴散。如果交易的信息在一段時間內沒有被放進塊中,那么交易將被從內存池中清除,而原節點將重新發送交易信息。
6、行為不端的節點
對於所有的廣播,那些行為不端的節點(占用帶寬和通過發布錯誤信息來浪費計算資源)將收到懲罰。如果一個節點懲罰分數超過門限值-banscore=<n>,它將被禁止加入網絡若干秒。這個時間由參數-bantime=<n>定義,默認值是86400秒,即24小時。
7、警告
為了應對可能出現的bug和攻擊,比特幣開發者提供了比特幣警告服務RSS。比特幣用戶通過命令getinfo可以查看針對其特定客戶端版本的錯誤信息。
這些信息通過allert消息盡可能多擴散出去給每一個連接的對等節點。
錯誤信息采用特定的ECDSA私有密鑰簽名,只被極少數的開發者控制。
補充資源:如果你想了解各種消息的詳細結構和所有的消息類別,可以參考比特幣Wiki上的協議說明。
四、通過dns seeds獲取節點列表數據
1、dns seed是什么
返回比特幣網絡上完整節點IP地址的DNS服務器,用於協助發現節點。
2、哪里可以查看到
我們在bitcoin源碼中,搜索關鍵詞“seed.bitcoin.sipa.be”,目前最新的代碼位置在chainparams.cpp中。
3、怎么得到節點列表
這里需要用到一個開源項目,項目地址
下面開始進行具體操作。
我們用的dns是seed.bitcoin.sipa.be
xiaoyu@xiaoyu-Parallels-Virtual-Platform:~/bitcoin-seeder$ dig -t NS testnet-seed.bitcoin.jonasschnelli.ch
再次查看項目文件夾,發現多了兩個文件
dnsseed.dat
dnsseed.dump
查看dnsseed.dump文件,
這樣就得到了節點列表。
4、總結
dns seed數據是硬編碼到項目中,這也就解決了分布式系統的關於雞生蛋還是蛋生雞的問題。
五、節點通信簡述
比特幣節點通常采用TCP協議、使用8333端口與相鄰節點建立連接, 建立連接時也會有認證“握手”的通信過程,用來確定協議版本,軟件版本,節點IP,區塊高度等。
當節點連接到相鄰節點后,接着就開始跟相鄰節點同步區塊鏈數據(輕量級錢包應用其實不會同步所有區塊數據),節點們會交換一個getblocks消息,它包含本地區塊鏈最頂端的哈希值。如果某個節點識別出它接收到的哈希值並不屬於頂端區塊,而是屬於一個非頂端區塊的舊區塊,就說其自身的本地區塊鏈比其他節點的區塊鏈更長,並告訴其他節點需要補充區塊,其他節點發送getdata消息來請求區塊,驗證后更新到本地區塊鏈中。
參考文章: