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
#![warn(rust_2018_idioms)] #![deny(intra_doc_link_resolution_failure)] #![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. 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). //! //! This protocol is split into three main sections/layers: //! //! * Transport - The transport for this protocol is currently fixed to UDP and is realised by a //! [`Transport`]. It encodes/decodes [Packet]'s to and from the specified UDP //! socket. //! * Session - 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. It is realised by the [`Service`] struct. //! * Application - This section contains the protocol-level logic. In particular it manages the //! routing table of known ENR's, topic registration/advertisement and performs various queries //! such as peer discovery. This section is realised by the [`Discv5`] struct. //! //! *Note* - Currently only `secp256k1` keys are supported. //! //! For a simple CLI discovery service see [discv5-cli](https://github.com/AgeManning/discv5-cli) //! //! //! # Usage //! //! The [`Discv5`] service implements `Stream` which emits [`Discv5Event`] events. Running a //! discv5 service is as simple as initialising a [`Discv5`] struct and driving the stream. //! //! The service can be configured via [`Discv5Config`] which can be created using the //! [`Discv5ConfigBuilder`]. //! //! A simple example of creating this service is as follows: //! //! ```rust //! use enr::{Enr,EnrBuilder, CombinedKey}; //! use std::net::Ipv4Addr; //! use discv5::{Discv5, Discv5Config, Discv5Event}; //! use futures::prelude::*; //! //! #[tokio::main] //! async fn main() { //! // generate a key for the node //! let enr_key = CombinedKey::generate_secp256k1(); //! //! // construct a local ENR //! let enr = EnrBuilder::new("v4") //! .ip("127.0.0.1".parse::<Ipv4Addr>().expect("valid address").into()) //! .udp(9000) //! .build(&enr_key) //! .unwrap(); //! //! // display the ENR's node id and base64 encoding //! println!("Node Id: {}", enr.node_id()); //! println!("Base64 ENR: {}", enr.to_base64()); //! //! // listen on the UDP socket of the ENR //! let listen_address = enr.udp_socket().unwrap(); //! //! // use default settings for the discv5 service //! let config = Discv5Config::default(); //! //! // construct the discv5 service //! let mut discv5 = Discv5::new(enr, enr_key, config, listen_address).unwrap(); //! //! // add another node's ENR to connect to and join an existing DHT //! discv5.add_enr("-IS4QKXYSAtVY5dwZneGdrMnuvjnhG3TQM8P8RHW1ZMbdOBsMfKQoZvEe9PqsYgKAb5afYVffn8iCxptuwUamV98d8IBgmlkgnY0gmlwhAAAAACJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCIyg".parse::<Enr<CombinedKey>>().unwrap()); //! //! // search peers closest to a target //! let target_random_node_id = enr::NodeId::random(); //! let request_query_id = discv5.find_node(target_random_node_id); //! //! // poll the stream for the next FindNoeResult event //! while let Some(event) = discv5.next().await { //! match event { //! Discv5Event::FindNodeResult { closer_peers, query_id, .. } => { //! println!("Query with id {} completed. Found {} peers", query_id.0, closer_peers.len()); //! break; //! } //! _ => {} // handle other discv5 events //! } //! } //! } //! ``` //! //! To see a usage in a runtime environment, see the `find_nodes` example in `/examples`. //! //! [`Discv5`]: struct.Discv5.html //! [`Discv5Event`]: enum.Discv5Event.html //! [`Discv5Config`]: config/struct.Discv5Config.html //! [`Discv5ConfigBuilder`]: config/struct.Discv5ConfigBuilder.html //! [`Transport`]: transport/struct.Transport.html //! [Packet]: packet/enum.Packet.html //! [`Service`]: service/struct.Service.html //! [`Session`]: session/struct.Session.html mod config; mod discv5; mod error; mod kbucket; pub mod packet; mod query_pool; mod rpc; pub mod service; mod session; mod transport; pub use crate::discv5::{Discv5, Discv5Event}; pub use config::{Discv5Config, Discv5ConfigBuilder}; pub use error::Discv5Error; // re-export the ENR crate pub use enr; pub use query_pool::QueryId;