目前进度为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() } } }