### Create and Start Discv5 Server in Rust Source: https://context7.com/sigp/discv5/llms.txt Demonstrates how to create a Discv5 server instance using `Discv5::new()`. It covers key generation, ENR creation (with and without initial IP address), configuring the listening socket, building the main configuration, starting the server with `start()`, and accessing node information. ```rust use discv5::{enr, enr::{CombinedKey, NodeId}, Discv5, ConfigBuilder, ListenConfig}; use std::net::Ipv4Addr; #[tokio::main] async fn main() { // Generate a secp256k1 key pair for the node let enr_key = CombinedKey::generate_secp256k1(); // Create an empty ENR (no advertised address initially) let enr = enr::Enr::empty(&enr_key).unwrap(); // Or create an ENR with a known external address let enr_with_ip = enr::Enr::builder() .ip4("192.168.1.100".parse().unwrap()) .udp4(9000) .build(&enr_key) .unwrap(); // Configure the listening socket let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; // Build the configuration with default settings let config = ConfigBuilder::new(listen_config).build(); // Create the Discv5 server let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap(); // Start the server (required before any queries) discv5.start().await.unwrap(); println!("Node ID: {}", discv5.local_enr().node_id()); println!("ENR: {}", discv5.local_enr().to_base64()); // Shutdown when done discv5.shutdown(); } ``` -------------------------------- ### Rust: Initialize and Run Discv5 Service Source: https://github.com/sigp/discv5/blob/master/README.md Demonstrates how to construct a local ENR, set up a Tokio runtime, configure the listening socket, build the discv5 configuration, and initialize the Discv5 service. It also shows how to start the service and perform a find_node query. ```rust 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() // 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); }); ``` -------------------------------- ### Request ENR using Multiaddr with Libp2p (Rust) Source: https://context7.com/sigp/discv5/llms.txt This code snippet demonstrates how to request an Ethereum Node Record (ENR) using a multiaddr string when the 'libp2p' feature is enabled for the discv5 library. It sets up a Discv5 instance, starts it, and then uses the `request_enr` method with a multiaddr formatted as '/ip4//udp//p2p/'. The example includes error handling and prints the retrieved ENR details or an error message. ```rust // Requires: discv5 = { version = "0.10", features = ["libp2p"] } #[cfg(feature = "libp2p")] use discv5::{enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig}; #[cfg(feature = "libp2p")] use std::net::Ipv4Addr; #[cfg(feature = "libp2p")] #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = discv5::enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Request ENR using a multiaddr with peer ID // Format: /ip4//udp//p2p/ let multiaddr = "/ip4/192.168.1.100/udp/9000/p2p/16Uiu2HAkzjwA..."; match discv5.request_enr(multiaddr).await { Ok(enr) => { println!("Retrieved ENR via multiaddr:"); println!(" Node ID: {}", enr.node_id()); println!(" Base64: {}", enr.to_base64()); } Err(e) => println!("Failed to request ENR: {:?}", e), } } #[cfg(not(feature = "libp2p"))] fn main() { println!("This example requires the 'libp2p' feature"); } ``` -------------------------------- ### Manage Discv5 Routing Table Entries Source: https://context7.com/sigp/discv5/llms.txt Demonstrates how to access and manage the Kademlia routing table in Discv5. This includes retrieving node IDs, ENRs, full entries with status, finding specific ENRs, getting nodes by distance, counting connected peers, and removing or disconnecting nodes. It also shows direct access to kbuckets for advanced operations. ```rust use discv5::{enr, enr::{CombinedKey, NodeId}, Discv5, ConfigBuilder, ListenConfig}; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Get all node IDs in the routing table let node_ids: Vec = discv5.table_entries_id(); println!("Routing table contains {} nodes", node_ids.len()); // Get all ENRs in the routing table let enrs = discv5.table_entries_enr(); for enr in &enrs { println!("Node: {} at {:?}:{:?}", enr.node_id(), enr.ip4(), enr.udp4()); } // Get full entries with status information let entries = discv5.table_entries(); for (node_id, enr, status) in &entries { println!("Node {}: connected={}, direction={:?}", node_id, status.state.is_connected(), status.direction ); } // Find ENR for a specific node let some_node_id = node_ids.first().cloned(); if let Some(node_id) = some_node_id { if let Some(enr) = discv5.find_enr(&node_id) { println!("Found ENR for {}: {}", node_id, enr.to_base64()); } } // Get nodes by distance from our node let distances = vec![1, 2, 3, 4, 5]; // Buckets 1-5 let close_nodes = discv5.nodes_by_distance(distances); println!("Found {} nodes in buckets 1-5", close_nodes.len()); // Get count of connected peers let connected = discv5.connected_peers(); println!("Currently connected to {} peers", connected); // Remove a node from the routing table if let Some(node_id) = node_ids.first() { let was_removed = discv5.remove_node(node_id); println!("Node removed: {}", was_removed); } // Mark a node as disconnected (keeps in table but deprioritized) if let Some(node_id) = node_ids.get(1) { let success = discv5.disconnect_node(node_id); println!("Node marked disconnected: {}", success); } // Access kbuckets directly for advanced operations let entries = discv5.with_kbuckets(|kbuckets| { kbuckets.read() .iter_ref() .map(|entry| *entry.node.key.preimage()) .collect::>() }); println!("Direct kbuckets access found {} entries", entries.len()); } ``` -------------------------------- ### Customize Discv5 Configuration with ConfigBuilder in Rust Source: https://context7.com/sigp/discv5/llms.txt Illustrates advanced configuration options for the Discv5 protocol using `ConfigBuilder`. It demonstrates setting up different listening configurations (IPv4, IPv6, dual-stack) and fine-tuning various protocol parameters like timeouts, rate limiting, IP filtering, and ENR update behavior. ```rust use discv5::{ConfigBuilder, ListenConfig, RateLimiterBuilder, PermitBanList, Enr}; use std::net::{Ipv4Addr, Ipv6Addr}; use std::time::Duration; // IPv4-only configuration let ipv4_listen = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; // IPv6-only configuration let ipv6_listen = ListenConfig::Ipv6 { ip: Ipv6Addr::UNSPECIFIED, port: 9000, }; // Dual-stack configuration (both IPv4 and IPv6) let dual_stack_listen = ListenConfig::default() .with_ipv4(Ipv4Addr::UNSPECIFIED, 9000) .with_ipv6(Ipv6Addr::UNSPECIFIED, 9001); // Build a customized configuration let config = ConfigBuilder::new(ipv4_listen) // Timeouts and retries .request_timeout(Duration::from_secs(2)) .query_timeout(Duration::from_secs(60)) .query_peer_timeout(Duration::from_secs(3)) .request_retries(2) // Session settings .session_timeout(Duration::from_secs(86400)) // 1 day .session_cache_capacity(1000) // Query settings .query_parallelism(3) .max_nodes_response(16) // ENR update settings .enr_peer_update_min(10) // Minimum votes before updating ENR .vote_duration(Duration::from_secs(120)) // Enable IP-based filtering (mitigate eclipse attacks) .ip_limit() .incoming_bucket_limit(8) // Max incoming nodes per bucket // Enable packet filtering with rate limiting .enable_packet_filter() .filter_rate_limiter(Some( RateLimiterBuilder::new() .total_n_every(10, Duration::from_secs(1)) .node_n_every(8, Duration::from_secs(1)) .ip_n_every(9, Duration::from_secs(1)) .build() .unwrap() )) .filter_max_nodes_per_ip(Some(10)) .filter_max_bans_per_ip(Some(5)) .ban_duration(Some(Duration::from_secs(3600))) // Custom table filter (only accept nodes with IP addresses) .table_filter(|enr: &Enr| enr.ip4().is_some() || enr.ip6().is_some()) // Ping interval for connected peers .ping_interval(Duration::from_secs(300)) // Disable automatic ENR updates from PONG responses // .disable_enr_update() // Disable reporting discovered peers (reduces event stream noise) // .disable_report_discovered_peers() .build(); ``` -------------------------------- ### Rust: Send and Handle TALK RPC Requests Source: https://context7.com/sigp/discv5/llms.txt Demonstrates sending a TALK RPC request to a peer using `talk_req` and handling incoming `TalkRequest` events. It sets up a discv5 instance, defines a custom protocol ID, sends a request, and processes responses or errors. Incoming requests are filtered by protocol and a response is sent back. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig, Event}; use discv5::node_info::NodeContact; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Define a custom protocol identifier let protocol_id = b"my-protocol/1.0".to_vec(); // Send a TALK request to a peer let peer_enr = discv5.table_entries_enr().first().cloned(); if let Some(enr) = peer_enr { let node_contact = NodeContact::try_from_enr(enr, discv5.ip_mode()) .expect("ENR should be contactable"); let request_data = b"Hello, peer!".to_vec(); match discv5.talk_req(node_contact, protocol_id.clone(), request_data).await { Ok(response) => { println!("Got TALK response: {}", String::from_utf8_lossy(&response)); } Err(e) => println!("TALK request failed: {:?}", e), } } // Handle incoming TALK requests via event stream let mut events = discv5.event_stream().await.unwrap(); while let Some(event) = events.recv().await { if let Event::TalkRequest(request) = event { let protocol = String::from_utf8_lossy(request.protocol()); if protocol == "my-protocol/1.0" { println!("Received request: {}", String::from_utf8_lossy(request.body())); // Process and respond let response = b"Acknowledged!".to_vec(); let _ = request.respond(response); } else { // Unknown protocol - drop request (sends empty response) println!("Unknown protocol: {}", protocol); } } } } ``` -------------------------------- ### Handle Discovery Events in Rust Source: https://context7.com/sigp/discv5/llms.txt Illustrates how to subscribe to and process discovery events from `discv5` using `event_stream()`. This includes handling discovered peers, node insertions, session establishments, and PONG responses. It requires the `discv5` and `tokio` crates. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig, Event}; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Get the event stream let mut event_stream = discv5.event_stream().await.unwrap(); // Process events loop { match event_stream.recv().await { Some(Event::Discovered(enr)) => { // A new peer was discovered during a query println!("Discovered peer: {}", enr.node_id()); println!(" IP: {:?}, Port: {:?}", enr.ip4(), enr.udp4()); } Some(Event::NodeInserted { node_id, replaced }) => { // A node was added to our routing table println!("Node inserted: {}", node_id); if let Some(replaced_id) = replaced { println!(" Replaced: {}", replaced_id); } } Some(Event::SessionEstablished(enr, socket_addr)) => { // A new encrypted session was established println!("Session established with {} at {}", enr.node_id(), socket_addr); } Some(Event::SocketUpdated(new_addr)) => { // Our external socket address was updated based on PONG responses println!("Our external address updated to: {}", new_addr); } Some(Event::TalkRequest(talk_req)) => { // Received a TALK protocol request println!("Talk request from {}", talk_req.node_id()); println!(" Protocol: {}", String::from_utf8_lossy(talk_req.protocol())); println!(" Body length: {} bytes", talk_req.body().len()); // Respond to the request (or drop to send empty response) let response = b"Hello from discv5!".to_vec(); if let Err(e) = talk_req.respond(response) { println!("Failed to respond: {:?}", e); } } Some(Event::UnverifiableEnr { enr, socket, node_id }) => { // Received an ENR that doesn't match the source address println!("Unverifiable ENR from {} (socket: {})", node_id, socket); } Some(Event::SessionsExpired(expired)) => { // Sessions that timed out due to inactivity println!("Sessions expired: {:?}", expired); } None => { println!("Event stream closed"); break; } } } } ``` -------------------------------- ### Rust: Ping Peers and Request ENRs Source: https://context7.com/sigp/discv5/llms.txt Illustrates sending a PING request to a peer and receiving a PONG response, then requesting the peer's ENR using `find_node_designated_peer`. It also shows how to request nodes at specified distances. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig}; use discv5::node_info::NodeContact; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Get a peer from the routing table if let Some(peer_enr) = discv5.table_entries_enr().first().cloned() { // Send a PING and wait for PONG match discv5.send_ping(peer_enr.clone()).await { Ok(pong) => { println!("Received PONG from peer:"); println!(" ENR seq: {}", pong.enr_seq); println!(" Our IP as seen by peer: {}", pong.ip); println!(" Our port as seen by peer: {}", pong.port); } Err(e) => println!("PING failed: {:?}", e), } // Request the peer's own ENR (distance 0) match discv5.find_node_designated_peer(peer_enr.clone(), vec![0]).await { Ok(enrs) => { if let Some(updated_enr) = enrs.first() { println!("Peer's ENR: {}", updated_enr.to_base64()); println!(" Seq: {}", updated_enr.seq()); } } Err(e) => println!("ENR request failed: {:?}", e), } // Request nodes at specific distances (1-3) match discv5.find_node_designated_peer(peer_enr, vec![1, 2, 3]).await { Ok(nodes) => { println!("Received {} nodes at distances 1-3", nodes.len()); } Err(e) => println!("Request failed: {:?}", e), } } } ``` -------------------------------- ### TALK Protocol for Custom Subprotocols Source: https://context7.com/sigp/discv5/llms.txt The TALK RPC allows building custom subprotocols on top of discv5. Use `talk_req()` to send requests and handle `TalkRequest` events for incoming requests. ```APIDOC ## TALK Protocol for Custom Subprotocols ### Description Allows building custom subprotocols on top of discv5. Use `talk_req()` to send requests and handle `TalkRequest` events for incoming requests. ### Method `talk_req(node_contact: NodeContact, protocol_id: Vec, request_data: Vec) -> Result, discv5::error::Error>' ### Endpoint N/A (This is a programmatic interface, not a network endpoint) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust // Assuming 'discv5' is an initialized Discv5 instance and 'peer_enr' is a known ENR let protocol_id = b"my-protocol/1.0".to_vec(); let request_data = b"Hello, peer!".to_vec(); let node_contact = NodeContact::try_from_enr(peer_enr, discv5.ip_mode()).expect("ENR should be contactable"); match discv5.talk_req(node_contact, protocol_id.clone(), request_data).await { Ok(response) => { println!("Got TALK response: {}", String::from_utf8_lossy(&response)); } Err(e) => println!("TALK request failed: {:?}", e), } ``` ### Response #### Success Response (200) - **response_data** (Vec) - The byte-string response from the peer. #### Response Example ```json "Acknowledged!" ``` ### Event Handling Incoming TALK requests are handled via the `Event::TalkRequest` enum variant from the `discv5.event_stream()`. #### Event Example ```rust // Inside the event loop: if let Event::TalkRequest(request) = event { let protocol = String::from_utf8_lossy(request.protocol()); if protocol == "my-protocol/1.0" { println!("Received request: {}", String::from_utf8_lossy(request.body())); let response = b"Acknowledged!".to_vec(); let _ = request.respond(response); } } ``` ``` -------------------------------- ### Add Bootstrap Nodes using ENR in Rust Source: https://context7.com/sigp/discv5/llms.txt Demonstrates how to add known peers (bootstrap nodes) to the discv5 routing table. It includes parsing an ENR from a base64-encoded string and adding it to the table. Dependencies include the `discv5` crate and `tokio` for async operations. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig}; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap(); // Parse a bootstrap ENR from base64 string let bootstrap_enr_str = "-HW4QBzimRxkmT18hMKaAL3IcZF1UcfTMPyi3Q1pxwZZbcZVRI8DC5infUAB_UauARLOJtYTxaagKoGmIjzQxO2qUygBgmlkgnY0iXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0AHacwUAPMljNMTg"; match bootstrap_enr_str.parse::>() { Ok(bootstrap_enr) => { println!("Bootstrap ENR parsed successfully"); println!(" Node ID: {}", bootstrap_enr.node_id()); println!(" IPv4: {:?}", bootstrap_enr.ip4()); println!(" UDP Port: {:?}", bootstrap_enr.udp4()); // Add to routing table match discv5.add_enr(bootstrap_enr) { Ok(()) => println!("ENR added to routing table"), Err(e) => println!("Failed to add ENR: {}", e), } } Err(e) => println!("Failed to parse ENR: {}", e), } // Add multiple bootstrap nodes let bootstrap_enrs = vec![ "enr:-...", // Bootstrap node 1 "enr:-...", // Bootstrap node 2 ]; for enr_str in bootstrap_enrs { if let Ok(enr) = enr_str.parse::>() { let _ = discv5.add_enr(enr); } } discv5.start().await.unwrap(); } ``` -------------------------------- ### Access Discv5 Metrics in Rust Source: https://context7.com/sigp/discv5/llms.txt Shows how to retrieve various server statistics from a running discv5 instance in Rust, including active session counts, request rates, and byte transfer counters. This code periodically prints metrics to the console. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig}; use std::net::Ipv4Addr; use std::time::Duration; use tokio::time::interval; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Periodically print metrics let mut metrics_interval = interval(Duration::from_secs(30)); loop { metrics_interval.tick().await; // Get current metrics snapshot let metrics = discv5.metrics(); println!("=== Discv5 Metrics ==="); println!("Active sessions: {}", metrics.active_sessions); println!("Unsolicited requests/sec: {:.2}", metrics.unsolicited_requests_per_second); println!("Bytes sent: {} KB", metrics.bytes_sent / 1024); println!("Bytes received: {} KB", metrics.bytes_recv / 1024); println!("IPv4 contactable: {}", metrics.ipv4_contactable); println!("IPv6 contactable: {}", metrics.ipv6_contactable); println!("Connected peers: {}", discv5.connected_peers()); println!("Routing table size: {}", discv5.table_entries_id().len()); // For advanced use: access raw static metrics // let raw_metrics = Discv5::raw_metrics(); } } ``` -------------------------------- ### Sending PING and Requesting ENR Source: https://context7.com/sigp/discv5/llms.txt Send PING requests to peers to receive their PONG response, and request nodes at specific distances, including their own ENR. ```APIDOC ## Sending PING and Requesting ENR ### Description Send PING requests to peers to receive their PONG response, and request nodes at specific distances, including their own ENR. ### Method `send_ping(enr: Enr) -> Result` `find_node_designated_peer(enr: Enr, distances: Vec) -> Result, discv5::error::Error>` ### Endpoint N/A (These are programmatic interfaces, not network endpoints) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example (PING) ```rust // Assuming 'discv5' is an initialized Discv5 instance and 'peer_enr' is a known ENR match discv5.send_ping(peer_enr.clone()).await { Ok(pong) => { println!("Received PONG from peer:"); println!(" ENR seq: {}", pong.enr_seq); println!(" Our IP as seen by peer: {}", pong.ip); println!(" Our port as seen by peer: {}", pong.port); } Err(e) => println!("PING failed: {:?}", e), } ``` ### Request Example (Find Nodes / ENR) ```rust // Assuming 'discv5' is an initialized Discv5 instance and 'peer_enr' is a known ENR // Request the peer's own ENR (distance 0) match discv5.find_node_designated_peer(peer_enr.clone(), vec![0]).await { Ok(enrs) => { if let Some(updated_enr) = enrs.first() { println!("Peer's ENR: {}", updated_enr.to_base64()); println!(" Seq: {}", updated_enr.seq()); } } Err(e) => println!("ENR request failed: {:?}", e), } // Request nodes at specific distances (e.g., 1-3) match discv5.find_node_designated_peer(peer_enr, vec![1, 2, 3]).await { Ok(nodes) => { println!("Received {} nodes at distances 1-3", nodes.len()); } Err(e) => println!("Request failed: {:?}", e), } ``` ### Response (PING) #### Success Response (200) - **pong.enr_seq** (u64) - The ENR sequence number of the sender. - **pong.ip** (std::net::IpAddr) - The IP address of the sender as seen by the recipient. - **pong.port** (u16) - The port of the sender as seen by the recipient. #### Response Example (PING) ```json { "enr_seq": 12345, "ip": "192.168.1.100", "port": 9000 } ``` ### Response (Find Nodes / ENR) #### Success Response (200) - **enrs** (Vec) - A list of `Enr` records found at the specified distances. #### Response Example (Find Nodes / ENR) ```json [ { "sequence_number": 12345, "record": { "multiaddr": "/ip4/192.168.1.101/udp/5000", "public_key": "..." } } ] ``` ``` -------------------------------- ### Find Nodes using find_node() in Rust Source: https://context7.com/sigp/discv5/llms.txt Shows how to use the `find_node()` method to perform an iterative FINDNODE query. This method discovers peers closest to a target NodeId in the DHT. It requires the `discv5` crate and `tokio` for async operations. The method returns a list of found ENRs or an error. ```rust use discv5::{enr, enr::{CombinedKey, NodeId}, Discv5, ConfigBuilder, ListenConfig}; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(enr, enr_key, config).unwrap(); // Add bootstrap nodes first... discv5.start().await.unwrap(); // Find nodes closest to a random target let target = NodeId::random(); match discv5.find_node(target).await { Ok(found_nodes) => { println!("Found {} nodes closest to target", found_nodes.len()); for node_enr in &found_nodes { println!(" Node: {}", node_enr.node_id()); println!(" IP: {:?}, UDP: {:?}", node_enr.ip4(), node_enr.udp4()); } } Err(e) => println!("Query failed: {:?}", e), } // Find nodes closest to our own node (useful for bootstrapping) let own_node_id = discv5.local_enr().node_id(); match discv5.find_node(own_node_id).await { Ok(neighbors) => { println!("Found {} neighbors", neighbors.len()); } Err(e) => println!("Failed to find neighbors: {:?}", e), } } ``` -------------------------------- ### Find Nodes with Predicate Filter in Rust Source: https://context7.com/sigp/discv5/llms.txt Demonstrates how to use `find_node_predicate` to search for nodes that satisfy a custom condition, such as having an IPv4 address or a specific ENR field. It requires the `discv5` crate and sets up a basic discovery service before performing the queries. ```rust use discv5::{enr, enr::{CombinedKey, NodeId}, Discv5, ConfigBuilder, ListenConfig, Enr}; use std::net::Ipv4Addr; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Find up to 10 nodes that have an IPv4 address let predicate = Box::new(|enr: &Enr| enr.ip4().is_some()); let target = NodeId::random(); let max_nodes = 10; match discv5.find_node_predicate(target, predicate, max_nodes).await { Ok(nodes) => { println!("Found {} nodes with IPv4 addresses", nodes.len()); for enr in nodes { println!(" {}: {:?}", enr.node_id(), enr.ip4()); } } Err(e) => println!("Predicate query failed: {:?}", e), } // Find nodes that have a specific custom field in their ENR let find_eth2_nodes = Box::new(|enr: &Enr| { enr.get_raw_rlp("eth2").is_some() }); match discv5.find_node_predicate(NodeId::random(), find_eth2_nodes, 5).await { Ok(eth2_nodes) => { println!("Found {} Ethereum 2.0 nodes", eth2_nodes.len()); } Err(e) => println!("Query failed: {:?}", e), } } ``` -------------------------------- ### Ban and Permit Nodes/IPs in Discv5 Source: https://context7.com/sigp/discv5/llms.txt Shows how to control network access in Discv5 by banning and permitting nodes and IP addresses. This includes temporary and permanent bans for nodes and IPs, and removing these restrictions. The `enable_packet_filter()` configuration is required for these features to function. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig}; use std::net::{Ipv4Addr, IpAddr}; use std::time::Duration; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::empty(&enr_key).unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config) .enable_packet_filter() // Required for ban/permit to work .build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); // Get a node to ban if let Some(node_id) = discv5.table_entries_id().first().cloned() { // Ban a node for 1 hour (also removes from routing table) discv5.ban_node(&node_id, Some(Duration::from_secs(3600))); println!("Banned node {} for 1 hour", node_id); // Remove the ban discv5.ban_node_remove(&node_id); println!("Ban removed for {}", node_id); // Permit a node (bypasses packet filter) discv5.permit_node(&node_id); println!("Permitted node {}", node_id); // Remove from permit list discv5.permit_node_remove(&node_id); } // Ban an IP address permanently let malicious_ip: IpAddr = "192.168.1.100".parse().unwrap(); discv5.ban_ip(malicious_ip, None); // None = permanent ban println!("Permanently banned IP {}", malicious_ip); // Ban IP for limited time let suspicious_ip: IpAddr = "10.0.0.50".parse().unwrap(); discv5.ban_ip(suspicious_ip, Some(Duration::from_secs(600))); // 10 minutes // Remove IP ban discv5.ban_ip_remove(&suspicious_ip); // Permit an IP (bypasses all packet filtering) let trusted_ip: IpAddr = "127.0.0.1".parse().unwrap(); discv5.permit_ip(trusted_ip); // Remove IP from permit list discv5.permit_ip_remove(&trusted_ip); } ``` -------------------------------- ### Update Local ENR in Rust Source: https://context7.com/sigp/discv5/llms.txt Demonstrates how to modify the local node's ENR (Ethereum Name Record) to advertise updated information like socket addresses and custom fields using the discv5 Rust library. It covers updating UDP and TCP socket addresses and inserting custom key-value pairs into the ENR. ```rust use discv5::{enr, enr::CombinedKey, Discv5, ConfigBuilder, ListenConfig}; use std::net::{Ipv4Addr, SocketAddr}; #[tokio::main] async fn main() { let enr_key = CombinedKey::generate_secp256k1(); let local_enr = enr::Enr::builder() .ip4(Ipv4Addr::LOCALHOST) .udp4(9000) .build(&enr_key) .unwrap(); let listen_config = ListenConfig::Ipv4 { ip: Ipv4Addr::UNSPECIFIED, port: 9000, }; let config = ConfigBuilder::new(listen_config).build(); let mut discv5: Discv5 = Discv5::new(local_enr, enr_key, config).unwrap(); discv5.start().await.unwrap(); println!("Initial ENR seq: {}", discv5.local_enr().seq()); // Update UDP socket address let new_addr: SocketAddr = "192.168.1.100:9000".parse().unwrap(); let updated = discv5.update_local_enr_socket(new_addr, false); // false = UDP println!("UDP socket updated: {}", updated); // Update TCP socket address let tcp_addr: SocketAddr = "192.168.1.100:9001".parse().unwrap(); let updated = discv5.update_local_enr_socket(tcp_addr, true); // true = TCP println!("TCP socket updated: {}", updated); // Insert a custom field into the ENR // The value must implement alloy_rlp::Encodable let custom_value: u64 = 12345; match discv5.enr_insert("custom", &custom_value) { Ok(old_value) => { if let Some(old) = old_value { println!("Replaced old value: {:?}", old); } else { println!("Inserted new custom field"); } } Err(e) => println!("Failed to insert: {:?}", e), } // Insert a string field let client_name = "my-node/1.0.0"; let _ = discv5.enr_insert("client", &client_name.as_bytes().to_vec()); println!("Updated ENR seq: {}", discv5.local_enr().seq()); println!("ENR: {}", discv5.local_enr().to_base64()); // Get external view of ENR (returns Arc for sharing across threads) let external_enr = discv5.external_enr(); let enr_read = external_enr.read(); println!("External ENR node_id: {}", enr_read.node_id()); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.