libp2使用对等体的身份和位置进行区分。对等体的身份是稳定的、可验证的,并在对等体的整个生命周期内有效。
在特定网络上,在特定时间点,对等体可能具有一个或多个位置,可以用地址表示。例如,我可以通过全局IPv4地址198.51.100的TCP端口1234进行访问。
在一个只支持TCP/IP或UDP over IP的系统中,我们可以很容易地使用熟悉的:表示法编写我们的地址,并将它们存储为地址和端口的元组。然而,libp2p被设计为传输无关,这意味着我们甚至不能假设我们将使用基于IP的网络。
为了支持不断增长的传输协议集,而不是为每个寻址方案特别制定规则,libp2p使用multiaddr以一种自描述的方式对所有支持的传输协议的网络地址进行编码。
multiaddr在整个libp2p中用于编码网络地址。当需要在进程之间共享或交换地址时,它们以multiaddr的二进制表示形式进行编码。
multiaddr是一系列指令,可以被遍历到达某个目的地。
例如,/ip4/198.51.100/tcp/1234
multiaddr以ip4开头,这是请求地址的最底层协议。tcp协议运行在ip4之上,所以它紧随其后。
上述multiaddr由两个组件组成,即/ip4/198.51.100组件和/tcp/1234组件。无法进一步拆分其中任何一个。
协议地址可以在multiaddr中以一种反映网络堆栈中协议组合的方式进行组合。。通常用于描述multiaddr组合的术语是“封装”和“解封装”,它们基本上指的是在multiaddr中分别添加和移除协议组件。
当来自“内部”协议的数据被另一个“外部”协议包装时,该协议被称为“封装在”另一个协议中,通常通过将来自内部协议的数据重新组织为外部协议使用的数据包、帧或数据报类型来实现。
一些协议封装的例子包括在TCP/IP流中封装的HTTP请求,或者TCP段本身封装在IP数据报中。
multiaddr格式的设计是为了使地址以与其描述的协议相同的方式相互封装。其结果是一个地址,以网络堆栈的“最外层”开始,逐渐向“内部”推进。例如,在地址/ip4/198.51.100/tcp/80/ws中,最外层协议是IPv4,其封装了TCP流,而TCP流又封装了WebSockets。
解封装操作接受一个复合的multiaddr,并从中移除一个“内部”multiaddr,返回结果。
例如,如果我们从/ip4/198.51.100/tcp/1234/ws开始,然后解封装/ws,结果将是/ip4/198.51.100/tcp/1234。
libp2p定义了p2p multiaddr协议,其地址组件是libp2p对等节点的对等ID。 p2p multiaddr的文本表示如下:
/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
其中,QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N 是对等节点对等ID的字符串表示形式,是从其公钥派生而来的。
单独使用p2p地址并不提供足够的寻找对等节点在网络上位置的 addressing 信息;它不是一个传输地址。然而,就像ws协议用于WebSockets一样,p2p地址可以被封装在另一个multiaddr中。
例如,上述p2p地址可以与节点正在侦听的传输地址相结合:
/ip4/198.51.100/tcp/1234/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N
这个传输地址与p2地址的组合是节点在识别协议和其他核心libp2协议中通过网络传递地址的格式。
libp2p使用加密密钥对对消息进行签名并生成唯一的对等身份(或“对等id”)。
文档描述了支持的密钥类型,密钥在传输中如何进行序列化,以及如何从序列化的公钥的哈希生成对等id。
尽管私钥不会通过网络传输,但在磁盘上存储密钥所使用的序列化格式也作为参考包括在内,供希望导入现有libp2p密钥对的libp2p实现者参考。
PublicKey和PrivateKey消息包含一个带有序列化密钥的Data字段,以及指定密钥类型的Type枚举。
syntax = "proto2";
enum KeyType {
RSA = 0;
Ed25519 = 1;
Secp256k1 = 2;
ECDSA = 3;
}
message PublicKey {
required KeyType Type = 1;
required bytes Data = 2;
}
message PrivateKey {
required KeyType Type = 1;
required bytes Data = 2;
}
当前的libp2p实现将私钥存储在磁盘上,格式为序列化的PrivateKey protobuf消息。希望加载现有密钥的libp2p实现者可以使用PrivateKey消息定义来反序列化私钥文件。
对等id通过使用multihash对编码的公钥进行哈希派生而来。将序列化为超过42字节的键必须使用sha256 multihash进行哈希,将序列化为最多42字节的键必须使用“identity” multihash编解码。
具体而言,计算对等id的键:
syntax = "proto2";
message Identify {
//协议版本标识对等节点使用的协议系列。该字段是可选的,但建议用于调试和统计目的。
optional string protocolVersion = 5;
//这是一个自由格式的字符串,用于标识对等节点的实现。
optional string agentVersion = 6;
//这是对等节点的公钥,以二进制形式编组
optional bytes publicKey = 1;
//这些是对等节点作为多地址监听的地址
repeated bytes listenAddrs = 2;
//这是由正在标识的对等节点观察到的流发起对等节点的连接源地址;
//它是一个多地址。发起方可以使用此地址推断NAT的存在及其公共地址。
//例如,在TCP/IP传输的情况下,观察到的地址将具有类似/ip4/x.x.x.x/tcp/xx的形式。
//在电路中继连接的情况下,观察到的地址将具有类似/p2p/QmRelay/p2p-circuit的形式。
//在洋葱传输的情况下,没有可观察到的源地址。
optional bytes observedAddr = 4;
//这是对等节点支持的协议列表。
repeated string protocols = 3;
}
identify协议具有协议标识 /ipfs/id/1.0.0,它用于查询远程对等节点的信息。
该协议通过使用 /ipfs/id/1.0.0 作为协议标识字符串,向要查询的远程对等节点打开一个流。被识别的对等节点通过返回一个识别消息并关闭流来做出响应。
identify/push协议的协议标识为/ipfs/id/push/1.0.0,它用于通知已知对等节点关于运行时发生的更改。
当对等节点的基本信息发生变化时,例如因为它们获得了新的公共侦听地址,它们可以使用identify/push来通知其他节点有关新信息的变更。
push变体通过向要更新的每个远程对等节点打开一个流来工作,使用 /ipfs/id/push/1.0.0 作为协议标识字符串。当远程对等节点接受流时,本地对等节点将发送一个Identify消息并关闭流。
在接收到推送的Identify消息后,远程对等节点应该使用消息中的信息更新其本地元数据存储库。请注意,应该忽略缺失的字段,因为对等节点可能选择发送仅包含值已更改的字段的部分更新。