目前進度爲80%, 持續更新...node
在開發IPFS(星際文件系統)的過程當中,咱們遇到了不少在異構設備之上運行分佈式文件系統所帶來的若干挑戰,這些設備具備不一樣的網絡設置和能力。在這個過程當中,咱們必須從新審視整個網絡堆棧和詳細的解決方案,以克服由多個網絡層級和多種協議設計所施加的障礙,而不打破兼容性或再造技術。git
爲了構建這個庫,咱們專一於獨立解決問題,建立具備複雜抽象的複雜的解決方案,當把關注點進行組合時,能夠爲P2P對等應用程序提供一個可靠的工做環境。github
1.1 動機
libp2p 是咱們創建分佈式系統的集體經驗的結果,由於它對開發者負責,決定他們但願應用程序如何與網絡中的其餘人進行交互,並支持配置和可擴展性,而不是對網絡SETU作出假設。P.web
本質上,使用LIPP2P的對等體應該可以使用各類不一樣的傳輸來與另外一個對等體通訊,包括鏈接中繼,以及在不一樣的協議上進行協商,按需協商。編程
libp2p 規範及其實現的目標是:
容許使用各類:
傳輸協議: TCP、UDP、SCTP、UDT、UTP、QIC、SSH等
認證傳輸協議:TLS,DTLS,CurveCP,SSH等
高效使用套接字(鏈接重用)
使對等體之間的通訊在一個套接字上覆用(避免握手開銷)
使用協商過程使多種協議和不一樣協議版本在對等體之間使用
向後兼容
能在現有系統中工做
利用當前網絡技術的所有能力
有 NAT 穿透功能
容許中繼鏈接
啓用加密通道
有效利用底層傳輸(例如本地流複用、本地AUTH等)。bootstrap
本節向讀者介紹了網絡棧的可用協議和體系結構。咱們的目標是提供推斷結論的基礎,並理解爲何 libp2p 提出了這些需求和體系結構.瀏覽器
客戶端-服務器模型代表信道兩端的雙方具備不一樣的角色,它們支持不一樣的服務和/或具備不一樣的能力,或者換句話說,他們講不一樣的協議。緩存
構建客戶機-服務器應用程序成爲主流趨勢有如下幾個緣由:
數據中心內部的帶寬遠遠高於客戶端能夠互相鏈接的帶寬。
數據中心資源至關便宜,因爲有效的使用和散裝備貨。
開發人員和系統管理員更容易對應用程序進行細粒度的控制。
減小了要處理的異構系統的數量(雖然這個數字仍然至關可觀)。
像NAT這樣的系統使得客戶機很難彼此查找並進行通訊,迫使開發者執行很是聰明的hack來穿透這些障礙
Protocols started to be designed with the assumption that a developer will create a client-server application from the start.安全
咱們甚至學會了如何使用Internet上的網關隱藏分佈式系統的全部複雜性,使用協議來執行點對點操做(如HTTP),使得應用程序不透明地查看和理解每一次調用的服務調用級聯.服務器
libP2P提供了一種從客戶端-服務器偵聽器向撥號器偵聽器交互的方法,其中不隱含哪些實體、撥號器或偵聽器具備哪些能力或可以執行哪些操做。如今在兩個應用程序之間創建鏈接是一個須要解決的多層問題,而且這些鏈接不該該有目的偏向,而應該支持多個其餘協議來工做在已創建的鏈接之上。在客戶機-服務器模型中,發送來自客戶端的先前請求的服務器被稱爲推送模型,它一般會增長更多的複雜性;相比之下,在撥號偵聽器模型中,兩個實體能夠獨立地執行請求。
在深刻到LIPP2P協議以前,重要的是瞭解已經普遍使用和部署的協議的多樣性,這有助於維護今天的簡單抽象。例如,當咱們考慮HTTP鏈接時,人們可能天真地認爲HTTP/TCP/IP是涉及的主要協議,但實際上更多的協議參與進來,這取決於使用狀況、涉及的網絡等。諸如DNS、DHCP、ARP、OSPF、以太網、802.11(Wi-Fi)等許多協議都涉及進來。看看ISPs內部網絡,會發現更多的信息.
此外,值得注意的是,傳統的7層OSI模型表徵不適合於 libp2p. 做爲替代的是,咱們基於它們的角色來分類協議,即它們解決的問題。OSI模型的上層面嚮應用之間的點對點鏈路,而 libp2p 協議在各類不一樣的安全模型下更傾向於具備不一樣功能,不一樣大小的網絡。不一樣的 libp2p 協議能夠具備相同的角色(在OSI模型中,這將是「地址相同的層」),這意味着多個協議能夠同時運行,全部處理一個角色(而不是傳統的OSI堆疊中的每層協議)。例如,Bootstrap列表、mDNS、DHT發現 和 PEX 都是「對等發現」的形式,它們能夠共存甚至協同。
2.2.1 構建物理連接
Ethernet
Wi-Fi
Bluetooth
USB
2.2.2 尋址機器或進程
IPv4
IPv6
Hidden addressing, like SDP
2.2.3 發現對等節點或服務
ARP
DHCP
DNS
Onion
2.2.4 經過網絡路由消息
RIP(1, 2)
OSPF
BZGP
PPP
Tor
I2P
cjdns
2.2.5 傳輸
TCP
UDP
UDT
QUIC
WebRTC data channel
2.2.6 應用程序之間協商一致的通訊語義
RMI
Remoting
RPC
HTTP
雖然咱們目前有一系列的協議可供咱們的服務進行通訊,但解決方案的豐富性和多樣性也產生了自身的問題。目前,應用程序難以經過多種傳輸機制(例如,在瀏覽器應用程序中缺乏TCP/UDP棧)來支持和使用。
也沒有「存在性連接」,這意味着沒有一個對等體在多個傳輸中聲明本身的概念,所以其餘對等體能夠保證它老是相同的對等體。
libp2p 是不依賴具體傳輸機制的,因此它能夠運行在任何傳輸協議上。它甚至不依賴於IP,它能夠運行在NDN、XI和其餘新的互聯網體系結構之上.
爲了支持不一樣類型的傳輸機制,libp2p 使用 multiaddr,一種自描述尋址格式。這使得 libp2p 可以在系統中任意地處理地址,而且支持網絡層中的各類傳輸協議。libp2p 中地址的實際格式是
ipfs-addr,以IPFS節點ID結束的 multi-addr。例如,這些都是有效的 ipfs-addrs:
# IPFS over TCP over IPv6 (typical TCP) /ip6/fe80::8823:6dff:fee7:f172/tcp/4001/ipfs/QmYJyUMAcXEw1b5bFfbBbzYu5wyyjLMRHXGUkCXpag74Fu # IPFS over uTP over UDP over IPv4 (UDP-shimmed transport) /ip4/162.246.145.218/udp/4001/utp/ipfs/QmYJyUMAcXEw1b5bFfbBbzYu5wyyjLMRHXGUkCXpag74Fu # IPFS over IPv6 (unreliable) /ip6/fe80::8823:6dff:fee7:f172/ipfs/QmYJyUMAcXEw1b5bFfbBbzYu5wyyjLMRHXGUkCXpag74Fu # IPFS over TCP over IPv4 over TCP over IPv4 (proxy) /ip4/162.246.145.218/tcp/7650/ip4/192.168.0.1/tcp/4001/ipfs/QmYJyUMAcXEw1b5bFfbBbzYu5wyyjLMRHXGUkCXpag74Fu # IPFS over Ethernet (no IP) /ether/ac:fd:ec:0b:7c:fe/ipfs/QmYJyUMAcXEw1b5bFfbBbzYu5wyyjLMRHXGUkCXpag74Fu
注意:當前,尚不存在不可靠的實現。定義和使用不可靠傳輸的協議接口還沒有定義。有關不可靠VS可靠傳輸的更多信息,請參見此處。在WebRTC的上下文中,在 這裏 輸入 CTRL+F搜索「可靠」。
libp2p 協議是多個協議的集合。爲了節省資源,並使鏈接更容易,libp2p 能夠經過一個端口,如TCP或UDP端口,根據所使用的傳輸來執行其全部操做。libp2p 能夠經過點到點鏈接來複用它的許多協議。這種複用是用於可靠的流和不可靠的數據報。
libp2p 比較務實。它試圖在儘量多的配置中使用,以模塊化和靈活的方式來適應各類用例,並儘量少地選擇。所以,libp2p 網絡層提供了咱們鬆散地稱之爲「多重多路複用」的內容:
舉個例子,假設一個IPFS節點:
若是不提供這種級別的靈活性將不可能在各類平臺、場景或網絡配置中使用 libp2p。
全部實現都支持全部的選擇並不重要;關鍵的是,規範足夠靈活,容許實現精確地使用他們所須要的。這確保了複雜的用戶或應用程序約束不排除 libp2p 做爲選項。
在 libp2p 之上的通訊多是:
咱們同時做了安全和性能考慮. 加密通訊在數據中心內部高性能通訊場景下不是很必要.
咱們推薦:
Libp2p 採用相似TLS的加密套件.
Note: 咱們不直接使用 TLS, 由於咱們不須要 CA 系統包. 大多數 TLS 實現都很是龐大. Since libp2p model begins with keys, libp2p only needs to apply ciphers. 這只是整個 TLS 標準中很小的一部分.
網絡地址轉換在英特網上無處不在. Not only are most consumer devices behind many layers of NAT, but most data center nodes are often behind NAT for security or virtualization reasons.
As we move into containerized deployments, this is getting worse. IPFS的實現 須要 提供一個方法來穿透 NAT, 不然操做可能會受到影響. 即便要用真實IP地址運行的節點也必須實現NAT穿越技術,由於它們可能須要創建與NAT後面的對等體的鏈接.
Libp2p 採用了類 ICE 的協議完成了完整的 NAT 穿透. 他並不徹底是 ICE, 由於 IPFS 網絡提供了經過IPFS協議中繼通訊的可能性, 用於協調穿孔甚至中繼通訊.
建議在實現中使用諸多可用的NAT穿透庫之一,例如 libnice、libwebrtc 或 natty. 總而言之,NAT穿透必須是可互操做的.
然而,對於對稱的NATS,容器和VM NAT,以及其餘不可能繞過的NATS,libp2p 必須回退到中繼通訊,以創建一個完整的連通圖。要完成這些,實現必須支持中繼,雖然它應該是可選的,而且可以被終端用戶關閉.
鏈接中繼應該做爲傳輸來實現,以便對上層透明.
中繼的實現,可參考 p2p-circuit transport.
不一樣的系統有不一樣的需求,進而致使不一樣的拓撲結構。在P2P文獻中,咱們能夠發現這些拓撲被列舉爲:非結構化的、結構化的、混合的和集中式的.
集中式拓撲是在Web應用基礎設施中最多見的拓撲結構,它要求給定的服務或服務羣存在於已知的靜態地址,以便其餘服務可以訪問它們。非結構化網絡表明了一種P2P網絡類型,其中網絡拓撲結構是徹底隨機的,或者至少是非肯定性的,而結構化網絡具備隱組織的方式。混合網絡是前二者的混合體.
考慮到這一點,libp2p 必須準備好執行不一樣的路由機制和對等點發現,以便構建將使服務可以傳播消息或找到彼此的路由表.
libp2p 還經過記錄解決了網絡內部資源的可發現性問題。記錄是一個數據單元,能夠被數字簽名、時間戳和/或與其餘方法一塊兒使用,以使其具備短暫的有效性。這些記錄保存諸如網絡中存在的資源的位置或可用性等信息。這些資源能夠是數據、存儲、CPU週期和其餘類型的服務。
libp2p 不該該限制資源的位置,而應該提供在網絡中或旁路通道去方便查找資源的方式.
高效的消息傳遞協議提供以最小等待時間傳遞內容的方法和/或支持用於分發的大型和複雜拓撲。LIPP2P試圖結合多播和PUBSUB的發展來知足這些需求.
網絡的變化和應用應該讓使用者不感知具體的網絡拓撲結構,命名便解決了這個問題.
Libp2p 是遵循UNIX理念設計的, 它由不少易於建立和測試的小組件組成. 這些組件應該便於替換,以適應不一樣的技術或場景,並使其可以隨着時間的推移而升級.
雖然不一樣的對等體能夠根據它們的能力來支持不一樣的協議,可是任何對等體均可以充當撥號器和/或監聽器,用於鏈接其餘節點,一旦創建起的鏈接能夠從兩端從新使用,從而消除客戶端和服務器之間的區別.
libp2p 接口在多個子系統上充當薄的粘合劑,以便對等體可以通訊。只要它們尊重標準化的接口,這些子系統就能夠創建在其餘子系統的頂部。這些子系統適合的主要領域是:
點對點路由 - 機制來決定哪些節點用於路由特定消息。這種路由能夠遞歸地、迭代地或甚至在廣播/組播模式下完成.
Swarm - 處理全部從LBP2P中打開「流」部分的內容,從協議複用、流複用、NAT穿越和鏈接中繼,同時支持多路傳輸.
存儲和分發記錄的系統。記錄是其餘系統用於信令、創建連接、宣佈對等或內容等的小條目。它們在更普遍的互聯網上與DNS有類似的做用.
發現-發現或識別網絡中的其餘對等體.
這些子系統中的每個都公開了一個衆所周知的接口(見第6章),而且能夠相互使用以實現其目標。系統的全局概覽:
libp2p
Peer Routing
Swarm
Distributed Record Store
Discovery
4.1 Peer Routing
對等路由子系統公開一個接口,以標識消息在DHT中應該路由到哪些對等點。它接收一個密鑰而且必須返回一個或多個 PeerInfo 對象.
咱們提出了兩個可能的對等路由子系統的例子,第一個基於 Kademlia DHT,第二個基於 mDNS. 然而,只要實現相同的指望和接口,就能夠實現更多的對等路由機制.
kad-routing
mDNS-routing
Other-routing-mechanisms
Peer Routing
4.1.1 kad-routing
kad-routing 路由實現了 Kademlia 路由表,其中每一個節點持有一組k桶,每一個桶包含來自網絡中其餘對等點的多個 PeerInfo 對象.
4.1.2 mDNS-routing
mDNS路由 使用 mDNS 探針來識別局域網節點是否具備給定的密鑰,或者它們只是簡單地存在.
4.2.1 Stream Muxer
流複用器必須實現接口流複用器提供的接口.
4.2.2 Protocol Muxer
協議複用是在應用層級別處理的,而不是在端口級別(不一樣的服務/協議在不一樣端口監聽)的常規方式. 這使得咱們可以過支持多個協議在同一個套接字中進行加密,從而節省了爲多個端口進行NAT穿透的成本.
協議複用是經過多流協議來完成的, 該協議使用 multicodec 來協商不一樣類型的流(協議).
4.2.3 Transport
4.2.4 Crypto
4.2.5 Identify
Identify is one of the protocols mounted on top of Swarm, our Connection handler. However, it follows and respects the same pattern as any other protocol when it comes to mounting it on top of Swarm. Identify enables us to trade listenAddrs and observedAddrs between peers, which is crucial for the working of IPFS. Since every socket open implements REUSEPORT, an observedAddr by another peer can enable a third peer to connect to us, since the port will be already open and redirect to us on a NAT.
4.2.6 回放(Replay)
See Circuit Relay.
4.3.1 Record
Follows IPRS spec.
4.3.2 abstract-record-store 4.3.3 kad-record-store
4.3.4 mDNS-record-store
4.3.5 s3-record-store
4.4.1 mDNS-discovery
mDNS-發現 是一種在局域網上使用 mDNS 的發現協議。它發射了mDNS信標來查找是否有更多的對等體可用。局域網節點對於對等協議是很是有用的,由於它們的低延遲鏈路.
mDNS-發現是一種獨立的協議,不依賴於任何其餘的 libp2p 協議。在不依賴其餘基礎設施的狀況下,mDNS-發現 能夠產生局域網中可用的對等點. 這在內聯網、與互聯網主幹斷開的網絡以及暫時失去鏈路的網絡中尤爲有用.
mDNS-discovery 能夠針對每一個服務進行配置(i.e. 即僅發現參與特定協議的對等體,如IPFS), 還有私有網絡(發現屬於專用網絡的對等體).
咱們正在探索加密 mDNS-discovery beacons 的方式 (使得本地網絡中的其餘節點沒法識別正在使用的服務), though the nature of mDNS will always reveal local IP addresses.
隱私注意:mDNS 在局域網中進行廣告,在同一本地網絡中向聽衆顯示IP地址。不推薦使用隱私敏感的應用程序或太公開的路由協議.
4.4.2 random-walk
隨機遊走是DHTS(具備路由表的其餘協議)的發現協議。它進行隨機DHT查詢,以便快速瞭解大量的對等體。這致使DHT(或其餘協議)收斂得更快,而在初始階段須要承擔必定負載開銷.
4.4.3 bootstrap-list
Bootstrap列表是一種發現協議,它使用本地存儲來緩存網絡中可用的高度穩定的(和一些可信的)對等點的地址。這容許協議「找到網絡的其他部分」。這基本上與DNS自舉的方式相同(儘管注意到,經過設計改變DNS引導列表——「點域」地址——不容易作到).
該列表應該存儲在長期本地存儲中,不管這意味着本地節點(例如磁盤)。
協議能夠將默認列表硬編碼或採用標準代碼分發機制(如DNS)進行傳送。
在大多數狀況下(固然在IPFS的狀況下),引導列表應該是用戶可配置的,由於用戶可能但願創建單獨的網絡,(or place their reliance and trust in specific nodes)或者將它們的信任和信任放在特定的節點中.
4.5.1 PubSub
參考 https://github.com/libp2p/spe...
4.5.1.1 實現
PubSub 開發正在進行中, 用 floodsub 做爲初始協議, 隨後是 gossipsub, 這是2018年5月發佈的alpha版本.
Floodsub 的實現包括:
go-libp2p-floodsub: 參考 此篇 做爲上下文. 還有 這篇 關於 gossipsub;
rust-libp2p-floodsub: @jamesray1 正基於 gossipsub 之上研究和開發. Js-libp2p-floodsub.
4.5.2 IPNS
網絡協議主要處理如下數據結構:
PrivateKey: 節點的私鑰
PublicKey: 節點的公鑰
PeerId: 節點公鑰的hash
PeerInfo: 包含節點的 PeerId, 及其已知的 multiaddr 對象.
Transport: 用於創建與其餘對等體的鏈接. 參考 https://github.com/libp2p/int...
Connection: 節點之間的點對點鏈接. 必須實現 https://github.com/libp2p/int...
Muxed-Stream: 雙工通訊信道.
Stream-Muxer: 流複用器. 必須實現 https://github.com/libp2p/int...
Record: IPLD(IPFS連接數據) 描述了實現 IPRS 的對象.
multiaddr: 自描述的網絡地址. 參考 https://github.com/multiforma...
multicodec: 自描述的編碼類型. 參考 https://github.com/multiforma...
multihash: 自描述的hash. 參考 https://github.com/multiforma...
=====
libp2p 是多個協議的集合,它們一塊兒工做,提供一個能夠與任何其餘網絡可尋址進程對話的公共實體接口。這是經過將現有的協議和實現擺在一組顯式接口中來實現的:對等路由、發現、Stream Muxing、傳輸、鏈接等。
libp2p, 做爲頂級模塊,爲其它接口提供 libp2p 實例, must offer an interface for dialing to a peer and plugging in all of the modules (e.g. which transports) we want to support. We present the libp2p interface and UX in section 6.6, after presenting every other module interface.
A Peer Routing 模塊爲 libp2p 提供節點查找其它節點 PeerInfo 信息的方式, 以便鏈接節點. 最簡單的形式, Peer Routing 模塊須要一個接口,其接收一個 'key', 並返回一個 PeerInfo 的集合. 關於接口和測試可參考 https://github.com/libp2p/int...
Current interface available and updated at:
https://github.com/libp2p/js-...
6.3.1 Transport
https://github.com/libp2p/int...
6.3.2 Connection
https://github.com/libp2p/int...
6.3.3 Stream Muxing
https://github.com/libp2p/int...
https://github.com/libp2p/int...
A Peer Discovery module interface should return PeerInfo objects, as it finds new peers to be considered by our Peer Routing modules.
libp2p 實現應該使它可以以編程方式實例化,或者使用之前編譯過的庫,而且已經進行了一些協議決策,以便用戶能夠重用或擴展.
程序化構建一個LIPP2P實例
用JavaScript實現的例子, 能夠被映射到其餘語言:
var Libp2p = require(‘libp2p’) var node = new Libp2p() // add swarm instance node.addSwarm(swarmInstance) // add one or more Peer Routing mechanisms node.addPeerRouting(peerRoutingInstance) // add a Distributed Record Store node.addDistributedRecordStore(distriburedRecordStoreInstance)
配置 libp2p 很是簡單,由於大多數配置來自於實例化多個模塊,一個接一個.
撥號和偵聽與對等體的鏈接
理想狀況下,libp2p 使用本身的機制(Peer Routing and Record Store)找到撥號給給定對等點的方法.
node.dial(PeerInfo)
若要接收傳入鏈接,請指定要處理的一個或多個協議:
node.handleProtocol('<multicodec>', function (duplexStream) { })
查找對等節點
找到對等點是經過對等路由,因此接口是相同的.
存儲和檢索記錄
像查找對等體同樣,經過記錄存儲來完成記錄的存儲和檢索,因此接口是相同的。
Efforts like NDN and XIA are new architectures for the internet, which are closer to the model IPFS uses than what IP provides today. IPFS will be able to operate on top of these architectures trivially, as these are no assumptions made about the network stack in the protocol. Implementations will likely need to change, but changing implementations is vastly easier than changing protocols.
在實現不一樣的模塊和功能時,libp2p2 的實現應該(推薦)遵循必定級別的粒度,以便公共接口易於暴露、測試和檢查與其餘實現的互操做性.
這是當前 libp2p 存在的模塊列表:
libp2p(entry point)
Swarm
Transports
Stream Muxing
Crypto Channel
Peer Routing
Distributed Record Store
Generic
當前已知實現(WIP):
8.1.1 Swarm Dialer
集羣撥號器管理到目標對等體的成功鏈接,給定一個地址流做爲輸入,而且確保遵照速率限制等約定.
爲此,咱們設計瞭如下的撥號邏輯:
DialPeer(peerID) { if PeerIsBeingDialed(peerID) { waitForDialToComplete(peerID) return BestConnToPeer(peerID) } StartDial(peerID) waitForDialToComplete(peerID) return BestConnToPeer(peerID) } StartDial(peerID) { addrs = getAddressStream(peerID) addrs.onNewAddr(function(addr) { if rateLimitCanDial(peerID, addr) { doDialAsync(peerID, addr) } else { rateLimitScheduleDial(peerID, addr) } }) } // doDialAsync starts dialing to a specific address without blocking. // when the dial returns, it releases rate limit tokens, and if it // succeeded, will finalize the dial process. doDialAsync(peerID, addr) { go transportDial(addr, function(conn, err) { rateLimitReleaseTokens(peerID, addr) if err != null { // handle error } dialSuccess(conn) }) } // rateLimitReleaseTokens checks for any tokens the given dial // took, and then for each of them, checks if any other dial is waiting // for any of those tokens. If waiting dials are found, those dials are started // immediately. Otherwise, the tokens are released to their pools. rateLimitReleaseTokens(peerID, addr) { tokens = tokensForDial(peerID, addr) for token in tokens { dial = dialWaitingForToken(token) if dial != null { doDialAsync(dial.peer, dial.addr) } else { token.release() } } }