35个版本

0.6.0 2024年4月22日
0.4.1 2024年1月31日
0.4.0 2023年12月11日
0.3.1 2023年7月20日
0.1.0-alpha.72020年7月21日

#107 in 网络编程

Download history 6322/week @ 2024-04-29 6319/week @ 2024-05-06 5370/week @ 2024-05-13 4412/week @ 2024-05-20 5008/week @ 2024-05-27 5137/week @ 2024-06-03 5263/week @ 2024-06-10 6238/week @ 2024-06-17 7357/week @ 2024-06-24 6344/week @ 2024-07-01 6822/week @ 2024-07-08 11816/week @ 2024-07-15 8389/week @ 2024-07-22 6842/week @ 2024-07-29 6962/week @ 2024-08-05 6355/week @ 2024-08-12

30,193 每月下载量
用于 3 crate

Apache-2.0

620KB
12K SLoC

discv5

Build Status Doc Status Crates Status

在docs.rs上的文档

概述

这是Discovery v5对等发现协议的Rust实现。

Discovery v5是为加密对等发现而设计的协议。网络中的每个对等体/节点通过其ENREthereum Node Record)进行标识,它本质上是一个包含节点公钥以及可选的IP地址和端口的签名键值存储。

Discv5使用类似于kademlia的路由表来存储和管理发现的对等体和主题。该协议允许通过与发现的节点进行常规的PING/PONG来在NAT环境中进行外部IP发现。节点返回它们收到的外部IP地址,并选择一个简单的多数派作为我们的外部IP地址。如果外部IP地址更新,这将产生一个事件来通知蜂群(如果用于这种行为)。

关于简单的CLI发现服务,请参阅 discv5-cli

用法

创建此服务的简单示例如下

   use discv5::{enr, enr::{CombinedKey, NodeId}, TokioExecutor, Discv5, ConfigBuilder};
   use discv5::socket::ListenConfig;
   use std::net::SocketAddr;

   // construct a local ENR
   let enr_key = CombinedKey::generate_secp256k1();
   let enr = enr::Enr::empty(&enr_key).unwrap();

   // build the tokio executor
   let mut runtime = tokio::runtime::Builder::new_multi_thread()
       .thread_name("Discv5-example")
       .enable_all()
       .build()
       .unwrap();

   // configuration for the sockets to listen on
   let listen_config = ListenConfig::Ipv4 {
       ip: Ipv4Addr::UNSPECIFIED,
       port: 9000,
   };

   // default configuration
   let config = ConfigBuilder::new(listen_config).build();

   // construct the discv5 server
   let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();

   // In order to bootstrap the routing table an external ENR should be added
   // This can be done via add_enr. I.e.:
   // discv5.add_enr(<ENR>)

   // start the discv5 server
   runtime.block_on(discv5.start());

   // run a find_node query
   runtime.block_on(async {
      let found_nodes = discv5.find_node(NodeId::random()).await.unwrap();
      println!("Found nodes: {:?}", found_nodes);
   });

ENR中的地址

此协议将丢弃来自在ENR中宣传不可接触地址的对等体(例如,连接到非本地节点时127.0.0.1)的消息(即不响应请求)。本节解释了这一设计决策背后的原因。

ENR是一个签名记录,主要用于本协议中标识和连接对等体。ENR有可选的 ipport 字段。

如果一个节点不知道其可接触地址(即如果它位于NAT后面),它应该留空这些字段。这样做的原因如下

  1. 当我们收到一个ENR(节点记录)时,必须决定是否将其添加到我们的本地路由表中并向其他节点宣传。如果一个节点在ENR中放入了一些无法联系到的地址(例如,当连接到非本地节点时使用的地址127.0.0.1),我们无法使用此ENR来联系节点,因此我们不希望将其宣传给其他节点。因此,放置一个无法联系到的地址在功能上等同于留空这些字段。
  2. 对于每个新的入站连接,我们不希望检查我们收到的ENR中的地址是否可联系。我们不希望出现任何节点都可以给我们任何地址并强迫我们尝试连接到任意地址(以检查其有效性)的情况,因为这会消耗不必要的带宽,我们还想避免DOS攻击,恶意用户向许多节点发送消息以攻击受害者IP。

此协议如何处理ENR中的宣传IP

为了处理上述两种情况,此协议过滤掉并只宣传可联系到的ENR。对于发现协议宣传无法联系到的节点来说,这是没有意义的。

这是通过以下方式完成的

  1. 如果一个连接节点提供了一个未指定地址的ENR(这对于大多数在NAT后面或刚刚开始运行的节点应该是默认情况)我们认为是有效的。通常,这发生在节点尚未通过PONG响应确定其外部IP地址,并且尚未将其ENR更新为可联系到的地址。在这种情况下,我们回应这个节点提出的所有请求,但不会存储或将其ENR添加到我们的路由表中。
  2. 如果一个节点使用包含与接收到的数据包的源地址匹配的IP地址的ENR连接到我们,我们认为这个节点是有效的,并尝试将其添加到我们的本地路由表,因此可能向其他人宣传其ENR。
  3. 如果一个节点使用包含与连接到我们的套接字不匹配的IP地址的ENR连接到我们(例如127.0.0.1,或者可能是从我们当前网络无法访问的一些内部子网IP),我们认为这个节点是恶意或错误的,并丢弃所有数据包。这样,我们可以有效地丢弃那些可能试图让我们向任意远程IP发送消息的节点,并且我们可以确保我们路由表中的所有ENR都是可联系的(至少在我们的本地节点在某个时间点)。

依赖关系

~12–24MB
~320K SLoC