成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

以太坊源碼分析—p2p節(jié)點發(fā)現(xiàn)與協(xié)議運行

xcc3641 / 623人閱讀

摘要:前言負責(zé)以太坊底層節(jié)點間的通信,主要包括底層節(jié)點發(fā)現(xiàn)和上層協(xié)議運行兩大部分。啟動了一個定時器,定期隨機選擇一個,向其中末尾的節(jié)點發(fā)送消息,如果對方回應(yīng)了,則探活成功。

前言

p2p(peer to peer)負責(zé)以太坊底層節(jié)點間的通信,主要包括底層節(jié)點發(fā)現(xiàn)(discover)和上層協(xié)議運行兩大部分。

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

節(jié)點發(fā)現(xiàn)功能主要涉及 Server Table udp 這幾個數(shù)據(jù)結(jié)構(gòu),它們有獨自的事件響應(yīng)循環(huán),節(jié)點發(fā)現(xiàn)功能便是它們互相協(xié)作完成的。其中,每個以太坊客戶端啟動后都會在本地運行一個Server,并將網(wǎng)絡(luò)拓撲中相鄰的節(jié)點視為Node,而TableNode的容器,udp則是負責(zé)維持底層的連接。這些結(jié)構(gòu)的關(guān)系如下圖

Server
p2p/server.go
type Server struct {
    PrivateKey  *ecdsa.PrivateKey
    Protocols    []protocol
    StaticNodes[]  *discover.Node
    newTransport  func(net.Conn)  transport  
    ntab    disvocerTable    
    ourHandshake    *protoHandshake    

    addpeer    chan *conn
    ......
}

PrivateKey - 本節(jié)點的私鑰,用于與其他節(jié)點建立時的握手協(xié)商
Protocols - 支持的所有上層協(xié)議
StaticNodes - 預(yù)設(shè)的靜態(tài)Peer,節(jié)點啟動時會首先去向它們發(fā)起連接,建立鄰居關(guān)系
newTransport - 下層傳輸層實現(xiàn),定義握手過程中的數(shù)據(jù)加密解密方式,默認的傳輸層實現(xiàn)是用newRLPX()創(chuàng)建的rlpx,這不是本文的重點
ntab - 典型實現(xiàn)是Table,所有peerNode的形式存放在Table
ourHandshake - 與其他節(jié)點建立連接時的握手信息,包含本地節(jié)點的版本號以及支持的上層協(xié)議
addpeer - 連接握手完成后,連接過程通過這個通道通知Server

Server.listenLoop()

Server的監(jiān)聽循環(huán),啟動底層監(jiān)聽socket,當(dāng)收到連接請求時,Accept后調(diào)用setupConn()開始連接建立過程

Server.run()

Server的主要事件處理和功能實現(xiàn)循環(huán)

進行主動的節(jié)點發(fā)現(xiàn),詳見之后的節(jié)點發(fā)現(xiàn)部分

posthandshake channel 接收已經(jīng)完成第一階段的連接,這些連接的身份已經(jīng)被確認,但還需要驗證

addpeer channel 接收已經(jīng)完成第二階段的連接,這些連接已經(jīng)驗證,調(diào)用runPeer()運行本節(jié)點與Peer連接上的協(xié)議

Node

Node唯一表示網(wǎng)絡(luò)上的一個節(jié)點

p2p/discover/node.go
type Node struct {
    IP net.IP
    UDP, TCP uint16
    ID      NodeID
    sha    common.Hash
}

IP - IP地址
UDP/TCP - 連接使用的UDP/TCP端口號
ID - 以太坊網(wǎng)絡(luò)中唯一標(biāo)識一個節(jié)點,本質(zhì)上是一個橢圓曲線公鑰(PublicKey),與ServerPrivateKey對應(yīng)。一個節(jié)點的IP地址不一定是固定的,但ID是唯一的。
sha - 用于節(jié)點間的距離計算


Table

Table主要用來管理與本節(jié)點與其他節(jié)點的連接的建立更新刪除

p2p/discover/table.go
type Table struct {
    bucket   [nBuckets]* bucket
    refreshReq    chan chan struct{}
    ......
}

bucket - 所有peer按與本節(jié)點的距離遠近放在不同的桶(bucket)中,詳見之后的節(jié)點維護
refreshReq - 更新Table請求通道

Table.loop()

Table的主要事件循環(huán),主要負責(zé)控制refreshrevalidate過程。
refresh.C - 定時(30s)啟動Peer刷新過程的定時器
refreshReq - 接收其他線程投遞到Table刷新Peer連接的通知,當(dāng)收到該通知時啟動更新,詳見之后的更新鄰居關(guān)系
revalidate.C - 定時重新檢查以連接節(jié)點的有效性的定時器,詳見之后的探活檢測

udp

udp負責(zé)節(jié)點間通信的底層消息控制,是Table運行的Kademlia協(xié)議的底層組件

type udp struct {
    conn  conn
    addpending chan *pending
    gotreply  chan reply
    *Table
}

conn - 底層監(jiān)聽端口的連接
addpendingudp用來接收pending的channel。使用場景為:當(dāng)我們向其他節(jié)點發(fā)送數(shù)據(jù)包后(packet)后可能會期待收到它的回復(fù),pending用來記錄一次這種還沒有到來的回復(fù)。舉個例子,當(dāng)我們發(fā)送ping包時,總是期待對方回復(fù)pong包。這時就可以將構(gòu)造一個pending結(jié)構(gòu),其中包含期待接收的pong包的信息以及對應(yīng)的callback函數(shù),將這個pengding投遞到udp的這個channel。udp在收到匹配的pong后,執(zhí)行預(yù)設(shè)的callback。
gotreply - udp用來接收其他節(jié)點回復(fù)的通道,配合上面的addpending,收到回復(fù)后,遍歷已有的pending鏈表,看是否有匹配的pending。
Table - 和Server中的ntab是同一個Table

udp.loop()

udp的處理循環(huán),負責(zé)控制消息的向上遞交和收發(fā)控制

addpending 接收其他線程投遞來的pending需求

gotreply 接收udp.readLoop()投遞過來的pending的回復(fù)

udp.readLoop()

udp的底層接受數(shù)據(jù)包循環(huán),負責(zé)接收其他節(jié)點的packet

接受其他節(jié)點發(fā)送的packet并解析,如果是回復(fù)包則投遞到udp.loop()


節(jié)點維護

以太坊使用Kademlia分布式路由存儲協(xié)議來進行網(wǎng)絡(luò)拓撲維護,了解該協(xié)議建議先閱讀易懂分布式。更權(quán)威的資料可以查看wiki??偟膩碚f該協(xié)議:

使用UDP進行節(jié)點間消息通信,有 4 種消息

ping - 用于探測其他節(jié)點是否還存在

store - 接收者受到后,將信息中key/value對存儲在本節(jié)點

findnode - 接受者向發(fā)送者返回 k 個它知道的與目標(biāo)結(jié)點距離最近的節(jié)點

findvalue - 和findnode 差不多,區(qū)別是如果接收者本地存在與目標(biāo)結(jié)點對應(yīng)的value,那么就回復(fù)這個值給發(fā)送者。

每個節(jié)點根據(jù)與鄰居節(jié)點距離之間的距離(NodeID的差距),分別放到不同的桶(bucket)中。

本文說的距離,均是指兩個節(jié)點NodeID的距離,計算方式可見p2p/discover/node.gologdist()方法

源碼中由Table結(jié)構(gòu)保存所有bucketbucket結(jié)構(gòu)如下

p2p/discover/table.go
type bucket struct {
    entries  []*Node
    replacemenets   []*Node
    ips  netutil.DistinctNetSet
}

entries 數(shù)組中保存經(jīng)過bond的節(jié)點,并且其順序是越新bond通過了探活檢測(Revalidate)的節(jié)點位置越靠前。

replacemenets數(shù)組中保存候補節(jié)點,如果entries 數(shù)組數(shù)量滿了,之后的節(jié)點會被加入該數(shù)組

節(jié)點可以在entriesreplacements互相轉(zhuǎn)化,一個entries節(jié)點如果Validate失敗,那么它會被原本將一個原本在replacements數(shù)組的節(jié)點替換。

探活檢測(Revalidate)

有效性檢測就是利用ping消息進行探活操作。Table.loop()啟動了一個定時器(0~10s),定期隨機選擇一個bucket,向其entries中末尾的節(jié)點發(fā)送ping消息,如果對方回應(yīng)了pong,則探活成功。

舉個栗子,假設(shè)某個bucket, entries最多保存2個節(jié)點,replacements最多保存4個節(jié)點。初始情況下entries=[A, B], replacements =  [C, D, E],如果此時節(jié)點F加入網(wǎng)絡(luò),bond通過,由于entries已滿,只能加入到replacements =  [C, D, E, F]。 此時Revalidate定時器到期,則會對 B進行檢測,如果通過,則entries=[B, A],如果不通過,則將隨機選擇replacements中的一項(假設(shè)為D)替換B的位置,最終entries=[A, D],replacements =  [C, E, F]
更新鄰居關(guān)系

Table.loop()會定期(定時器超時)或不定期(收到refreshReq)地進行更新鄰居關(guān)系(發(fā)現(xiàn)新鄰居),兩者都調(diào)用doRefresh()方法,該方法對在網(wǎng)絡(luò)上查找離自身和三個隨機節(jié)點最近的若干個節(jié)點。

節(jié)點查找

Tablelookup()方法用來實現(xiàn)節(jié)點查找目標(biāo)節(jié)點,它的實現(xiàn)就是Kademlia協(xié)議,通過節(jié)點間的接力,一步一步接近目標(biāo)。

鄰居初始化

當(dāng)一個節(jié)點啟動后,它會首先向配置的靜態(tài)節(jié)點發(fā)起連接,發(fā)起連接的過程稱為Dial,源碼中通過創(chuàng)建dialTask跟蹤這個過程

dialTask

dialTask表示一次向其他節(jié)點主動發(fā)起連接的任務(wù)

p2p/dial.go
type dialTask struct {
    flags    connFlag
    dest    *discover.Node
    ......
}

Server啟動時,會調(diào)用newDialState()根據(jù)預(yù)配置的StaticNodes初始化一批dialTask, 并在Server.run()方法中,啟動這些這些任務(wù)。

Dial過程需要知道目標(biāo)節(jié)點(dest)的IP地址,如果不知道的話,就要先使用 recolve()解析出目標(biāo)的IP地址,怎么解析?就是先要用借助Kademlia協(xié)議在網(wǎng)絡(luò)中查找目標(biāo)節(jié)點。


當(dāng)?shù)玫侥繕?biāo)節(jié)點的IP后,下一步便是建立連接,這是通過dialTask.dial()建立連接

連接建立

連接建立的握手過程分為兩個階段,在在SetupConn()中實現(xiàn)
第一階段為ECDH密鑰建立:

sequenceDiagram
Note left of Dialer: Calc token
Note left of Dialer: Generate Random PrikeyNonce
Note left of Dialer: Sign
Dialer->>Receiver: AuthMsg
Note right of Receiver: Calc token
Note right of Receiver: Check Signature
Note right of Receiver: Generate Random PrikeyNonce
Receiver->>Dialer: AuthResp

第二階段為協(xié)議握手,互相交換支持的上層協(xié)議

sequenceDiagram
Dialer->>Receiver: protoHandshake
Receiver->>Dialer: protoHandshake

如果兩次握手都通過,dialTask將向Serveraddpeer通道發(fā)送peer的信息

sequenceDiagram
participant Server.run()
participant dialTask
participant Remote Node
dialTask->>Remote Node:EncHandshake
Remote Node->>dialTask:EncHandshake
dialTask->>Server.run(): posthandshake
dialTask->>Remote Node:ProtoHandshake
Remote Node->>dialTask:ProtoHandshake
dialTask->>Server.run(): addpeer
Note over Server.run(): go runPeer()
協(xié)議運行

協(xié)議運行并不單單指某個特定的協(xié)議,準(zhǔn)確地說應(yīng)該是若干個獨立的協(xié)議同時在兩個節(jié)點間運行。在p2p節(jié)點發(fā)現(xiàn)提到過,節(jié)點間建立連接的時候會經(jīng)過兩次握手,其中的第二次握手,節(jié)點間會交換自身所支持的協(xié)議。最終兩個節(jié)點間生效的協(xié)議為兩個節(jié)點支持的協(xié)議的交集。

功能主要涉及 Peer protoRW 這幾個數(shù)據(jù)結(jié)構(gòu),其關(guān)系如圖

Peer

rw - 節(jié)點間連接的底層信息,比如使用的socket以及對端節(jié)點支持的協(xié)議(capabilities)

running - 節(jié)點間生效運行的協(xié)議簇

Peer.run()負責(zé)連接建立后啟動運行上層協(xié)議,它自身運行在一個獨立的go routine,具有自己的事件處理循環(huán),除此之外,它還會額外創(chuàng)建2+ngo routine, 其中2包括一個用于?;畹?strong>pingLoop() go routine和一個用于接收協(xié)議數(shù)據(jù)的readLoop() go routine ,而 n 為運行于其上的n個協(xié)議的go routine,即每個協(xié)議調(diào)用自己的Run()方法運行在自己多帶帶的go routine

protoRW

Run 每種協(xié)議自身的運行入口,以新的go routine形式啟動.

總結(jié)

p2p主要由底層節(jié)點發(fā)現(xiàn)和上層協(xié)議運行兩部分組成,節(jié)點發(fā)現(xiàn)負責(zé)管理以太坊網(wǎng)絡(luò)中各個節(jié)點間的連接建立,更新和刪除,Server是p2p功能的入口,Table負責(zé)記錄peer節(jié)點信息, udp負責(zé)底層通信。而在底層的基礎(chǔ)上,節(jié)點間可以運行多個獨立的協(xié)議。

以太坊使用Kademlia分布式路由存儲協(xié)議來進行網(wǎng)絡(luò)拓撲維護,將不同距離的peer節(jié)點放在不同的bucket中。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/24380.html

相關(guān)文章

  • 以太源碼分析—Whisper

    摘要:前言是以太坊中一項非常有趣的技術(shù),它是一個基于身份的通信系統(tǒng),被設(shè)計用于之間少量數(shù)據(jù)通信。協(xié)議運行在以太坊協(xié)議框架之上,所有運行協(xié)議的節(jié)點以下簡稱節(jié)點組成一個網(wǎng)絡(luò)。 [TOC] 前言 Whisper是以太坊中一項非常有趣的技術(shù),它是一個基于身份的通信系統(tǒng),被設(shè)計用于Dapp之間少量數(shù)據(jù)通信。Whisper協(xié)議運行在以太坊p2p協(xié)議框架之上,所有運行Whisper協(xié)議的節(jié)點(以下簡稱節(jié)點...

    Doyle 評論0 收藏0
  • 區(qū)塊鏈中的P2P

    摘要:為什么區(qū)塊鏈會選擇作為網(wǎng)絡(luò)基礎(chǔ)上面介紹的時候說過,他是無中心服務(wù)器的,中心服務(wù)器就意味著,當(dāng)受到攻擊的時候,中心服務(wù)器一旦宕機,整個網(wǎng)絡(luò)和服務(wù)就會出現(xiàn)問題。區(qū)塊鏈的核心是去中心化,這和網(wǎng)絡(luò)的觀念不約而同,所以選擇的理由也就很充分。 區(qū)塊鏈中P2P介紹 p2p是什么 為什么區(qū)塊鏈需要P2P 比特幣、以太坊、超級賬本和EOS的P2P對比 P2P是什么 P2P作為區(qū)塊鏈網(wǎng)絡(luò)中去中心化...

    jkyin 評論0 收藏0
  • 區(qū)塊鏈技術(shù)學(xué)習(xí)指引

    摘要:引言給迷失在如何學(xué)習(xí)區(qū)塊鏈技術(shù)的同學(xué)一個指引,區(qū)塊鏈技術(shù)是隨比特幣誕生,因此要搞明白區(qū)塊鏈技術(shù),應(yīng)該先了解下比特幣。但區(qū)塊鏈技術(shù)不單應(yīng)用于比特幣,還有非常多的現(xiàn)實應(yīng)用場景,想做區(qū)塊鏈應(yīng)用開發(fā),可進一步閱讀以太坊系列。 本文始發(fā)于深入淺出區(qū)塊鏈社區(qū), 原文:區(qū)塊鏈技術(shù)學(xué)習(xí)指引 原文已更新,請讀者前往原文閱讀 本章的文章越來越多,本文是一個索引帖,方便找到自己感興趣的文章,你也可以使用左側(cè)...

    Cristic 評論0 收藏0
  • 基于以太的視頻直播平臺 Livepeer白皮書中文概覽

    摘要:說明的視頻片段分發(fā)現(xiàn)在沒做出什么成果作者還提了一句,協(xié)議有望成為直播內(nèi)容的傳播協(xié)議。仿佛也沒能掩飾住不知道怎么分發(fā)視頻片段的尷尬說了這么多,看了代碼發(fā)現(xiàn)視頻片段還是通過分發(fā)總結(jié)最終將建立一個可擴展的,即用即付的直播網(wǎng)絡(luò) Background Livepeer旨在構(gòu)建帶有激勵機制的視頻直播分布式網(wǎng)絡(luò) Blockchain 以太坊 智能合約和交易基于Ethereum以太坊網(wǎng)絡(luò) DP...

    Eric 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<