discv5/
lib.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2//! An implementation of [Discovery V5](https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md).
3//!
4//! # Overview
5//!
6//! Discovery v5 is a protocol designed for encrypted peer discovery and topic advertisement. Each
7//! peer/node on the network is identified via it's ENR ([Ethereum Name
8//! Record](https://eips.ethereum.org/EIPS/eip-778)), which is essentially a signed key-value store
9//! containing the node's public key and optionally IP address and port.
10//!
11//! Discv5 employs a kademlia-like routing table to store and manage discovered peers. The protocol
12//! allows for external IP discovery in NAT environments through regular PING/PONG's with
13//! discovered nodes. Nodes return the external IP address that they have received and a simple
14//! majority is chosen as our external IP address. If an external IP address is updated, this is
15//! produced as an event.
16//!
17//! For a simple CLI discovery service see [discv5-cli](https://github.com/AgeManning/discv5-cli)
18//!
19//! This protocol is split into four main layers:
20//!
21//! - [`socket`]: Responsible for opening the underlying UDP socket. It creates individual tasks
22//!   for sending/encoding and receiving/decoding packets from the UDP socket.
23//! - [`handler`]: The protocol's communication is encrypted with `AES_GCM`. All node communication
24//!   undergoes a handshake, which results in a `Session`. These are established when needed and get
25//!   dropped after a timeout. The creation and maintenance of sessions between nodes and the
26//!   encryption/decryption of packets from the socket is realised by the [`handler::Handler`] struct
27//!   runnning in its own task.
28//! - [`service`]: Contains the protocol-level logic. The [`service::Service`] manages the routing
29//!   table of known ENR's, and performs parallel queries for peer discovery. It also runs in it's
30//!   own task.
31//! - [`Discv5`]: The application level. Manages the user-facing API. It starts/stops the underlying
32//!   tasks, allows initiating queries and obtain metrics about the underlying server.
33//!
34//! ## Event Stream
35//!
36//! The [`Discv5`] struct provides access to an event-stream which allows the user to listen to
37//! [`Event`] that get generated from the underlying server. The stream can be obtained from the
38//! [`Discv5::event_stream`] function.
39//!
40//! ## Runtimes
41//!
42//! Discv5 requires a tokio runtime with timing and io enabled. An explicit runtime can be given
43//! via the configuration. See the [`ConfigBuilder`] for further details. Such a runtime must
44//! implement the [`Executor`] trait.
45//!
46//! If an explicit runtime is not provided via the configuration parameters, it is assumed that a
47//! tokio runtime is present when creating the [`Discv5`] struct. The struct will use the existing
48//! runtime for spawning the underlying server tasks. If a runtime is not present, the creation of
49//! the [`Discv5`] struct will panic.
50//!
51//! # Usage
52//!
53//! A simple example of creating this service is as follows:
54//!
55//! ```rust
56//!    use discv5::{enr, enr::{CombinedKey, Enr, NodeId}, TokioExecutor, Discv5, ConfigBuilder};
57//!    use discv5::socket::ListenConfig;
58//!    use std::net::{Ipv4Addr, SocketAddr};
59//!
60//!    // construct a local ENR
61//!    let enr_key = CombinedKey::generate_secp256k1();
62//!    let enr = enr::Enr::empty(&enr_key).unwrap();
63//!
64//!    // build the tokio executor
65//!    let mut runtime = tokio::runtime::Builder::new_multi_thread()
66//!        .thread_name("Discv5-example")
67//!        .enable_all()
68//!        .build()
69//!        .unwrap();
70//!
71//!    // configuration for the sockets to listen on
72//!    let listen_config = ListenConfig::Ipv4 {
73//!        ip: Ipv4Addr::UNSPECIFIED,
74//!        port: 9000,
75//!    };
76//!
77//!    // default configuration
78//!    let config = ConfigBuilder::new(listen_config).build();
79//!
80//!    // construct the discv5 server
81//!    let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap();
82//!
83//!    // In order to bootstrap the routing table an external ENR should be added
84//!    // This can be done via add_enr. I.e.:
85//!    // discv5.add_enr(<ENR>)
86//!
87//!    // start the discv5 server
88//!    runtime.block_on(discv5.start());
89//!
90//!    // run a find_node query
91//!    runtime.block_on(async {
92//!       let found_nodes = discv5.find_node(NodeId::random()).await.unwrap();
93//!       println!("Found nodes: {:?}", found_nodes);
94//!    });
95//! ```
96
97mod config;
98mod discv5;
99mod error;
100mod executor;
101pub mod handler;
102mod ipmode;
103pub mod kbucket;
104mod lru_time_cache;
105pub mod metrics;
106mod node_info;
107pub mod packet;
108pub mod permit_ban;
109mod query_pool;
110pub mod rpc;
111pub mod service;
112pub mod socket;
113
114#[macro_use]
115extern crate lazy_static;
116
117pub type Enr = enr::Enr<enr::CombinedKey>;
118
119pub use crate::discv5::{Discv5, Event};
120pub use config::{Config, ConfigBuilder};
121pub use error::{Error, QueryError, RequestError, ResponseError};
122pub use executor::{Executor, TokioExecutor};
123pub use ipmode::IpMode;
124pub use kbucket::{ConnectionDirection, ConnectionState, Key};
125pub use packet::ProtocolIdentity;
126pub use permit_ban::PermitBanList;
127pub use service::TalkRequest;
128pub use socket::{ListenConfig, RateLimiter, RateLimiterBuilder};
129// Re-export the ENR crate
130pub use enr;
131
132// Re-export libp2p-identity and multiaddr
133#[cfg(feature = "libp2p")]
134pub use libp2p_identity;
135#[cfg(feature = "libp2p")]
136pub use multiaddr;