kona_p2p/discv5/
handler.rs

1//! Handler to the [`discv5::Discv5`] service spawned in a thread.
2
3use discv5::{Enr, RequestError, enr::NodeId, kbucket::NodeStatus, metrics::Metrics};
4use libp2p::Multiaddr;
5use std::{collections::HashSet, string::String, sync::Arc, time::Duration};
6use tokio::sync::mpsc::Sender;
7
8/// Request message for communicating with the Discv5 discovery service.
9///
10/// These requests are sent from the main application thread to the discovery
11/// service running in a separate task, enabling asynchronous operations on
12/// the discovery table and peer management.
13#[derive(Debug)]
14pub enum HandlerRequest {
15    /// Request current metrics from the discovery service.
16    ///
17    /// Returns performance and operational statistics including query counts,
18    /// success rates, and table population metrics.
19    Metrics(tokio::sync::oneshot::Sender<Metrics>),
20
21    /// Get the current number of connected peers in the discovery table.
22    ///
23    /// Returns the count of peers currently maintained in the routing table,
24    /// which indicates the health and connectivity of the discovery service.
25    PeerCount(tokio::sync::oneshot::Sender<usize>),
26
27    /// Add an ENR to the discovery service's routing table.
28    ///
29    /// Manually inserts a peer record into the table, typically used for
30    /// adding bootstrap nodes or peers discovered through other channels.
31    AddEnr(Enr),
32
33    /// Request an ENR from a specific network address.
34    ///
35    /// Initiates a discovery query to retrieve the ENR for a peer at the
36    /// given address. Used for peer verification and metadata retrieval.
37    RequestEnr {
38        /// Channel to receive the result of the ENR request.
39        out: tokio::sync::oneshot::Sender<Result<Enr, RequestError>>,
40        /// Network address to query for the ENR.
41        addr: String,
42    },
43
44    /// Get the local node's ENR.
45    ///
46    /// Returns the ENR that represents this node in the discovery network,
47    /// including its network address, capabilities, and cryptographic identity.
48    LocalEnr(tokio::sync::oneshot::Sender<Enr>),
49
50    /// Get all ENRs currently stored in the routing table.
51    ///
52    /// Returns a complete dump of peer records known to the discovery service,
53    /// useful for debugging and network analysis.
54    TableEnrs(tokio::sync::oneshot::Sender<Vec<Enr>>),
55
56    /// Get detailed information about nodes in the routing table.
57    ///
58    /// Returns comprehensive information including node IDs, ENRs, and status
59    /// for all peers in the discovery table.
60    TableInfos(tokio::sync::oneshot::Sender<Vec<(NodeId, Enr, NodeStatus)>>),
61
62    /// Ban specific network addresses for a duration.
63    ///
64    /// Prevents the discovery service from interacting with the specified
65    /// addresses, useful for blocking malicious or problematic peers.
66    BanAddrs {
67        /// Set of network addresses to ban.
68        addrs_to_ban: Arc<HashSet<Multiaddr>>,
69        /// Duration for which the addresses should be banned.
70        ban_duration: Duration,
71    },
72}
73
74/// Handler to the spawned [`discv5::Discv5`] service.
75///
76/// Provides a lock-free way to access the spawned `discv5::Discv5` service
77/// by using message-passing to relay requests and responses through
78/// a channel.
79#[derive(Debug, Clone)]
80pub struct Discv5Handler {
81    /// Sends [`HandlerRequest`]s to the spawned [`discv5::Discv5`] service.
82    pub sender: Sender<HandlerRequest>,
83    /// The chain id.
84    pub chain_id: u64,
85}
86
87impl Discv5Handler {
88    /// Creates a new [`Discv5Handler`] service.
89    pub const fn new(chain_id: u64, sender: Sender<HandlerRequest>) -> Self {
90        Self { sender, chain_id }
91    }
92
93    /// Blocking request for the ENRs of the discovery service.
94    ///
95    /// Returns `None` if the request could not be sent or received.
96    pub fn table_enrs(&self) -> tokio::sync::oneshot::Receiver<Vec<Enr>> {
97        let (tx, rx) = tokio::sync::oneshot::channel();
98        let sender = self.sender.clone();
99        tokio::spawn(async move {
100            if let Err(e) = sender.send(HandlerRequest::TableEnrs(tx)).await {
101                warn!(target: "discovery", err = ?e, "Failed to send table ENRs request");
102            }
103        });
104        rx
105    }
106
107    /// Returns a [`tokio::sync::oneshot::Receiver`] that contains a vector of information about
108    /// the nodes in the discv5 table.
109    pub fn table_infos(&self) -> tokio::sync::oneshot::Receiver<Vec<(NodeId, Enr, NodeStatus)>> {
110        let (tx, rx) = tokio::sync::oneshot::channel();
111        let sender = self.sender.clone();
112        tokio::spawn(async move {
113            if let Err(e) = sender.send(HandlerRequest::TableInfos(tx)).await {
114                warn!(target: "discv5_handler", "Failed to send table infos request: {:?}", e);
115            }
116        });
117        rx
118    }
119
120    /// Blocking request for the local ENR of the node.
121    ///
122    /// Returns `None` if the request could not be sent or received.
123    pub fn local_enr(&self) -> tokio::sync::oneshot::Receiver<Enr> {
124        let (tx, rx) = tokio::sync::oneshot::channel();
125        let sender = self.sender.clone();
126        tokio::spawn(async move {
127            if let Err(e) = sender.send(HandlerRequest::LocalEnr(tx)).await {
128                warn!(target: "discovery", err = ?e, "Failed to send local ENR request");
129            }
130        });
131        rx
132    }
133
134    /// Requests an [`Enr`] from the discv5 service given a [`Multiaddr`].
135    pub fn request_enr(
136        &self,
137        addr: Multiaddr,
138    ) -> tokio::sync::oneshot::Receiver<Result<Enr, RequestError>> {
139        let (tx, rx) = tokio::sync::oneshot::channel();
140        let sender = self.sender.clone();
141        tokio::spawn(async move {
142            if let Err(e) =
143                sender.send(HandlerRequest::RequestEnr { out: tx, addr: addr.to_string() }).await
144            {
145                warn!(target: "discv5_handler", "Failed to send request ENR request: {:?}", e);
146            }
147        });
148        rx
149    }
150
151    /// Blocking request for the metrics of the discovery service.
152    ///
153    /// Returns `None` if the request could not be sent or received.
154    pub fn metrics(&self) -> tokio::sync::oneshot::Receiver<Metrics> {
155        let (tx, rx) = tokio::sync::oneshot::channel();
156        let sender = self.sender.clone();
157        tokio::spawn(async move {
158            if let Err(e) = sender.send(HandlerRequest::Metrics(tx)).await {
159                warn!(target: "discovery", err = ?e, "Failed to send metrics request");
160            }
161        });
162        rx
163    }
164
165    /// Blocking request for the discovery service peer count.
166    ///
167    /// Returns `None` if the request could not be sent or received.
168    pub fn peer_count(&self) -> tokio::sync::oneshot::Receiver<usize> {
169        let (tx, rx) = tokio::sync::oneshot::channel();
170        let sender = self.sender.clone();
171        tokio::spawn(async move {
172            if let Err(e) = sender.send(HandlerRequest::PeerCount(tx)).await {
173                warn!(target: "discovery", err = ?e, "Failed to send peer count request");
174            }
175        });
176        rx
177    }
178}