以太坊原始碼P2P網路及節點發現機制
http://www.cnblogs.com/blockchain/p/7943962.html
目錄
1 分散式網路介紹
1.1 Kad網介紹
1.2 Kad網路節點距離
1.3 K桶
1.4 Kad通訊協議
2 鄰居節點
2.1 NodeTable類主要成員
2.2 鄰居節點發現方法
2.3 鄰居節點網路拓撲及重新整理機制。
1 分散式網路介紹
以太坊底層分散式網路即P2P網路,使用了經典的Kademlia網路,簡稱kad。
1.1 Kad網介紹
Kademlia在2002年由美國紐約大學的PetarP.Manmounkov和DavidMazieres提出,是一種分散式散列表(DHT)技術,以異或運算為距離度量基礎,已經在BitTorrent、BitComet、Emule等軟體中得到應用。
1.2 Kad網路節點距離
以太坊網路節點距離計算方法:
-
Node1:節點1 NodeId
-
Node2:節點2 NodeId
1.3 K桶
Kad的路由表是通過稱為K桶的資料構造而成,K桶記錄了節點NodeId,distance,endpoint,ip等資訊。以太坊K桶按照與target節點距離進行排序,共256個K桶,每個K桶包含16個節點。
圖1.1
1.4 Kad通訊協議
以太坊Kad網路中節點間通訊基於UDP,主要由以下幾個命令構成,若兩個節點間PING-PONG握手通過,則認為相應節點線上。
Kad通訊協議,基於UDP |
|||
序號 |
分類 |
功能描述 |
構成 |
1 |
PING |
探測一個節點,判斷其是否線上 |
struct PingNode { h256 version = 0x3; Endpoint from; Endpoint to; uint32_t timestamp; }; |
2 |
PONG |
PING命令響應 |
struct Pong { Endpoint to; h256 echo; uint32_t timestamp; }; |
3 |
FINDNODE |
向節點查詢某個與目標節點ID距離接近的節點 |
struct FindNeighbours { NodeId target; uint32_t timestamp; }; |
4 |
NEIGHBORS |
FIND_NODE命令響應,傳送與目標節點ID距離接近的K桶中的節點 |
struct Neighbours { list nodes: struct Neighbour { inline Endpoint endpoint; NodeId node; }; uint32_t timestamp; }; |
2 鄰居節點
2.1 NodeTable類主要成員
C++版本以太坊原始碼中,NodeTable是以太坊 P2P網路的關鍵類,所有與鄰居節點相關的資料和方法均由NodeTable類實現。
序號 |
成員名稱 |
說明 |
備註 |
1 |
m_node |
本節點,包含NodeId、endpoint、ip等 |
|
2 |
m_state |
K桶,包含鄰居節點的NodeId、distance、endpoint、ip |
|
3 |
m_nodes |
已知的節點資訊,但並沒有加入到K桶 |
序號 |
函式名 |
路徑 |
功能 |
1 |
NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled) |
cpp-ethereum /libp2p/NodeTable.cpp |
NodeTable類建構函式,初始化K桶,發起鄰居節點發現過程 |
2 |
void NodeTable::doDiscovery() |
Cpp-ethereum /libp2p/NodeTable.cpp |
具體發現函式 |
3 |
shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relation) |
cpp-ethereum /libp2p/NodeTable.cpp |
將節點加入m_nodes,併發起ping握手 |
4 |
void NodeTable::doDiscover(NodeID _node, unsigned _round, shared_ptr<set<shared_ptr<NodeEntry>>> _tried) |
cpp-ethereum /libp2p/NodeTable.cpp |
底層發現函式,從k桶中選出節點,傳送FINDNODE命令 |
5 |
vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeID _target) |
cpp-ethereum /libp2p/NodeTable.cpp |
從K桶中選出節點 |
6 |
void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet) |
cpp-ethereum /libp2p/NodeTable.cpp |
Kad協議處理 |
7 |
void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _endpoint) |
cpp-ethereum /libp2p/NodeTable.cpp |
將新節點加入到K桶 |
2.2 鄰居節點發現方法
鄰居節點是指加入到K桶,並通過PING-PONG握手的節點。
圖2.1
鄰居節點發現流程說明:
-
系統第一次啟動隨機生成本機節點NodeId,記為LocalId,生成後將固定不變,本地節點記為local-eth。
-
系統讀取公共節點資訊,ping-pong握手完成後,將其寫入K桶。
-
系統每隔7200ms重新整理一次K桶。
-
重新整理K桶流程如下:
a. 隨機生成目標節點Id,記為TargetId,從1開始記錄發現次數和重新整理時間。
b. 計算TargetId與LocalId的距離,記為Dlt
c. K桶中節點的NodeId記為KadId,計算KadId與TargetId的距離,記為Dkt
d. 找出K桶中Dlt大於Dkt的節點,記為k桶節點,向k桶節點發送FindNODE命令,FindNODE命令包含TargetId
e. K桶節點收到FindNODE命令後,同樣執行b-d的過程,將從K桶中找到的節點使用Neighbours命令發回給本機節點。
f. 本機節點收到Neighbours後,將收到的節點寫入到K桶中。
g. 若搜尋次數不超過8次,重新整理時間不超過600ms,則返回到b步驟迴圈執行。
2.3 鄰居節點網路拓撲及重新整理機制。
圖2.2
1 TargetId為隨機生成的虛擬節點ID。
2 以太坊Kad網路與傳統Kad網路的區別:
-
以太坊節點在發現鄰居節點的8次迴圈中,所查詢的節點均在距離上向隨機生成的TargetId收斂。
-
傳統Kad網路發現節點時,在距離上向節點本身收斂。
本文由HPB(芯鏈)團隊整理。