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