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}