karyon_p2p/discovery/kademlia/
lookup.rs1use std::{sync::Arc, time::Duration};
2
3use futures_util::stream::{FuturesUnordered, StreamExt};
4use log::{error, trace};
5use rand::{rngs::OsRng, seq::IndexedRandom, TryRngCore};
6
7use karyon_core::{async_runtime::Executor, async_util::timeout, crypto::KeyPair};
8
9use karyon_net::Endpoint;
10
11use crate::{
12 access_control::{Action, Subject},
13 bloom::BloomRef,
14 connector::Connector,
15 discovery::kademlia::{
16 messages::{
17 FindPeerMsg, KadNetCmd, KadNetMsg, KadNetMsgCodec, PeerMsg, PeersMsg, PingMsg, PongMsg,
18 },
19 routing_table::RoutingTable,
20 SUPPORTED_LOOKUP_PROTOCOLS,
21 },
22 listener::Listener,
23 message::{pick_endpoint, PeerAddr, Protocol, ShutdownMsg},
24 monitor::{ConnectionKind, DiscoveryKind, Monitor},
25 peer::ConnDirection,
26 slots::ConnectionSlots,
27 util::decode,
28 version::version_match,
29 Config, Error, PeerID, Result,
30};
31
32type KadConnRef = karyon_net::FramedConn<KadNetMsgCodec>;
34
35pub const MAX_PEERS_IN_PEERSMSG: usize = 10;
37
38pub const MAX_ADDRS_PER_PEER: usize = 4;
41
42pub const MAX_DISCOVERY_ADDRS_PER_PEER: usize = 3;
45
46pub struct LookupEndpoints {
48 pub listen: Vec<Endpoint>,
50 pub lookup: Option<Endpoint>,
52 pub refresh: Option<Endpoint>,
54}
55
56pub struct LookupService {
57 id: PeerID,
59
60 table: Arc<RoutingTable>,
62
63 listener: Arc<Listener<KadNetMsgCodec>>,
65 connector: Arc<Connector<KadNetMsgCodec>>,
67
68 outbound_slots: Arc<ConnectionSlots>,
70
71 endpoints: LookupEndpoints,
73
74 config: Arc<Config>,
76
77 monitor: Arc<Monitor>,
79
80 bloom: BloomRef,
82}
83
84impl LookupService {
85 pub fn new(
87 key_pair: &KeyPair,
88 table: Arc<RoutingTable>,
89 config: Arc<Config>,
90 monitor: Arc<Monitor>,
91 bloom: BloomRef,
92 endpoints: LookupEndpoints,
93 ex: Executor,
94 ) -> Self {
95 let inbound_slots = Arc::new(ConnectionSlots::new(config.lookup_inbound_slots));
96 let outbound_slots = Arc::new(ConnectionSlots::new(config.lookup_outbound_slots));
97
98 let listener = Listener::new(
99 key_pair,
100 inbound_slots.clone(),
101 monitor.clone(),
102 config.handshake_timeout,
103 config.access_control.clone(),
104 ex.clone(),
105 );
106
107 let connector = Connector::new(
108 key_pair,
109 config.lookup_connect_retries,
110 outbound_slots.clone(),
111 monitor.clone(),
112 ex,
113 );
114
115 let id = key_pair
116 .public()
117 .try_into()
118 .expect("Get PeerID from KeyPair");
119 Self {
120 id,
121 table,
122 listener,
123 connector,
124 outbound_slots,
125 endpoints,
126 config,
127 monitor,
128 bloom,
129 }
130 }
131
132 pub async fn start(self: &Arc<Self>) -> Result<()> {
134 self.start_listener().await?;
135 Ok(())
136 }
137
138 pub fn lookup_endpoint(&self) -> Option<&Endpoint> {
139 self.endpoints.lookup.as_ref()
140 }
141
142 pub async fn shutdown(&self) {
144 self.connector.shutdown().await;
145 self.listener.shutdown().await;
146 }
147
148 pub async fn start_lookup(&self, endpoint: &Endpoint, peer_id: Option<PeerID>) -> Result<()> {
156 trace!("Lookup started {endpoint}");
157 self.monitor
158 .notify(DiscoveryKind::LookupStarted(endpoint.clone()))
159 .await;
160
161 let mut random_peers = vec![];
162 if let Err(err) = self
163 .random_lookup(endpoint, peer_id, &mut random_peers)
164 .await
165 {
166 self.monitor
167 .notify(DiscoveryKind::LookupFailed(endpoint.clone()))
168 .await;
169 return Err(err);
170 };
171
172 let mut peer_buffer = vec![];
173 if let Err(err) = self.self_lookup(&random_peers, &mut peer_buffer).await {
174 self.monitor
175 .notify(DiscoveryKind::LookupFailed(endpoint.clone()))
176 .await;
177 return Err(err);
178 }
179
180 while peer_buffer.len() < MAX_PEERS_IN_PEERSMSG {
181 match random_peers.pop() {
182 Some(p) => peer_buffer.push(p),
183 None => break,
184 }
185 }
186
187 for peer in peer_buffer.iter() {
188 let result = self.table.add_entry(peer.clone().into());
189 trace!("Add entry {result:?}");
190 }
191
192 self.monitor
193 .notify(DiscoveryKind::LookupSucceeded(
194 endpoint.clone(),
195 peer_buffer.len(),
196 ))
197 .await;
198
199 Ok(())
200 }
201
202 async fn random_lookup(
206 &self,
207 endpoint: &Endpoint,
208 peer_id: Option<PeerID>,
209 random_peers: &mut Vec<PeerMsg>,
210 ) -> Result<()> {
211 for _ in 0..2 {
212 let random_peer_id = PeerID::random()?;
213 let peers = self
214 .connect(endpoint.clone(), peer_id.clone(), &random_peer_id)
215 .await?;
216
217 for peer in peers {
218 if random_peers.contains(&peer)
219 || peer.peer_id == self.id
220 || self.table.contains_key(&peer.peer_id.0)
221 {
222 continue;
223 }
224
225 random_peers.push(peer);
226 }
227 }
228
229 Ok(())
230 }
231
232 async fn self_lookup(
234 &self,
235 random_peers: &[PeerMsg],
236 peer_buffer: &mut Vec<PeerMsg>,
237 ) -> Result<()> {
238 let mut results = FuturesUnordered::new();
239 for peer in random_peers.choose_multiple(&mut rand::rng(), random_peers.len()) {
240 let endpoint = match pick_endpoint(&peer.discovery_addrs, SUPPORTED_LOOKUP_PROTOCOLS) {
241 Some(ep) => ep,
242 None => continue,
243 };
244 results.push(self.connect(endpoint, Some(peer.peer_id.clone()), &self.id))
245 }
246
247 while let Some(result) = results.next().await {
248 match result {
249 Ok(peers) => peer_buffer.extend(peers),
250 Err(err) => {
251 error!("Failed to do self lookup: {err}");
252 }
253 }
254 }
255
256 Ok(())
257 }
258
259 async fn connect(
262 &self,
263 endpoint: Endpoint,
264 peer_id: Option<PeerID>,
265 target_peer_id: &PeerID,
266 ) -> Result<Vec<PeerMsg>> {
267 if !self.config.access_control.allow(
268 &Subject::Endpoint(&endpoint),
269 Action::Lookup(ConnDirection::Outbound),
270 ) {
271 return Err(Error::AccessDenied);
272 }
273
274 let conn = self.connector.connect(&endpoint, &peer_id).await?;
275 let result = self.handle_outbound(conn, target_peer_id).await;
276
277 self.monitor
278 .notify(ConnectionKind::Disconnected(endpoint))
279 .await;
280 self.outbound_slots.remove().await;
281
282 result
283 }
284
285 async fn handle_outbound(
287 &self,
288 mut conn: KadConnRef,
289 target_peer_id: &PeerID,
290 ) -> Result<Vec<PeerMsg>> {
291 trace!("Send Ping msg");
292 let mut peers;
293
294 let ping_msg = self.send_ping_msg(&mut conn).await?;
295
296 loop {
297 let t = Duration::from_secs(self.config.lookup_response_timeout);
298 let msg: KadNetMsg = timeout(t, conn.recv_msg()).await??;
299 match msg.header.command {
300 KadNetCmd::Pong => {
301 let (pong_msg, _) = decode::<PongMsg>(&msg.payload)?;
302 if ping_msg.nonce != pong_msg.0 {
303 return Err(Error::InvalidPongMsg);
304 }
305 trace!("Send FindPeer msg");
306 self.send_findpeer_msg(&mut conn, target_peer_id).await?;
307 }
308 KadNetCmd::Peers => {
309 peers = decode::<PeersMsg>(&msg.payload)?.0.peers;
310 if peers.len() > MAX_PEERS_IN_PEERSMSG {
311 return Err(Error::Lookup(
312 "Received too many peers in PeersMsg".to_string(),
313 ));
314 }
315 for p in &mut peers {
316 validate_peer_msg(p)?;
317 }
318 break;
319 }
320 c => return Err(Error::InvalidMsg(format!("Unexpected msg: {c:?}"))),
321 };
322 }
323
324 trace!("Send Peer msg");
325 self.send_peer_msg(&mut conn).await?;
326
327 trace!("Send Shutdown msg");
328 self.send_shutdown_msg(&mut conn).await?;
329
330 Ok(peers)
331 }
332
333 async fn start_listener(self: &Arc<Self>) -> Result<()> {
335 let endpoint = match self.lookup_endpoint() {
336 Some(e) => e.clone(),
337 None => return Ok(()),
338 };
339
340 if !endpoint.is_tcp() {
341 return Err(Error::Config(format!(
342 "lookup endpoint must be tcp://..., got {endpoint}"
343 )));
344 }
345
346 let callback = {
347 let this = self.clone();
348 |conn: KadConnRef| async move {
349 let t = Duration::from_secs(this.config.lookup_connection_lifespan);
350 timeout(t, this.handle_inbound(conn)).await??;
351 Ok(())
352 }
353 };
354
355 self.listener
356 .start_with_callback(endpoint, callback)
357 .await?;
358 Ok(())
359 }
360
361 async fn handle_inbound(self: &Arc<Self>, mut conn: KadConnRef) -> Result<()> {
363 loop {
364 let msg: KadNetMsg = conn.recv_msg().await?;
365 trace!("Receive msg {:?}", msg.header.command);
366
367 if let KadNetCmd::Shutdown = msg.header.command {
368 return Ok(());
369 }
370
371 match &msg.header.command {
372 KadNetCmd::Ping => {
373 let (ping_msg, _) = decode::<PingMsg>(&msg.payload)?;
374 if !version_match(&self.config.version.req, &ping_msg.version) {
375 return Err(Error::IncompatibleVersion("system: {}".into()));
376 }
377 self.send_pong_msg(ping_msg.nonce, &mut conn).await?;
378 }
379 KadNetCmd::FindPeer => {
380 let (findpeer_msg, _) = decode::<FindPeerMsg>(&msg.payload)?;
381 let peer_id = findpeer_msg.0;
382 self.send_peers_msg(&peer_id, &mut conn).await?;
383 }
384 KadNetCmd::Peer => {
385 let (mut peer, _) = decode::<PeerMsg>(&msg.payload)?;
386 validate_peer_msg(&mut peer)?;
387 let result = self.table.add_entry(peer.into());
388 trace!("Add entry result: {result:?}");
389 }
390 c => return Err(Error::InvalidMsg(format!("Unexpected msg: {c:?}"))),
391 }
392 }
393 }
394
395 async fn send_ping_msg(&self, conn: &mut KadConnRef) -> Result<PingMsg> {
397 trace!("Send Pong msg");
398 let mut nonce: [u8; 32] = [0; 32];
399 OsRng.try_fill_bytes(&mut nonce)?;
400
401 let ping_msg = PingMsg {
402 version: self.config.version.v.clone(),
403 nonce,
404 };
405 conn.send_msg(KadNetMsg::new(KadNetCmd::Ping, &ping_msg)?)
406 .await?;
407 Ok(ping_msg)
408 }
409
410 async fn send_pong_msg(&self, nonce: [u8; 32], conn: &mut KadConnRef) -> Result<()> {
412 trace!("Send Pong msg");
413 conn.send_msg(KadNetMsg::new(KadNetCmd::Pong, PongMsg(nonce))?)
414 .await?;
415 Ok(())
416 }
417
418 async fn send_findpeer_msg(&self, conn: &mut KadConnRef, peer_id: &PeerID) -> Result<()> {
420 trace!("Send FindPeer msg");
421 conn.send_msg(KadNetMsg::new(
422 KadNetCmd::FindPeer,
423 FindPeerMsg(peer_id.clone()),
424 )?)
425 .await?;
426 Ok(())
427 }
428
429 async fn send_peers_msg(&self, peer_id: &PeerID, conn: &mut KadConnRef) -> Result<()> {
431 trace!("Send Peers msg");
432 let entries = self
433 .table
434 .closest_entries(&peer_id.0, MAX_PEERS_IN_PEERSMSG);
435
436 let peers: Vec<PeerMsg> = entries.into_iter().map(|e| e.into()).collect();
437 conn.send_msg(KadNetMsg::new(KadNetCmd::Peers, PeersMsg { peers })?)
438 .await?;
439 Ok(())
440 }
441
442 async fn send_peer_msg(&self, conn: &mut KadConnRef) -> Result<()> {
446 trace!("Send Peer msg");
447
448 let mut addrs = Vec::new();
449 for ep in &self.endpoints.listen {
450 if let Some(pa) = PeerAddr::from_endpoint(ep, 0) {
451 addrs.push(pa);
452 }
453 }
454
455 let mut discovery_addrs = Vec::new();
456 if let Some(ep) = self.endpoints.lookup.as_ref() {
457 if let Some(pa) = PeerAddr::from_endpoint(ep, 0) {
458 discovery_addrs.push(pa);
459 }
460 }
461 if let Some(ep) = self.endpoints.refresh.as_ref() {
462 if let Some(pa) = PeerAddr::from_endpoint(ep, 0) {
463 discovery_addrs.push(pa);
464 }
465 }
466
467 let peer_msg = PeerMsg {
468 peer_id: self.id.clone(),
469 addrs,
470 discovery_addrs,
471 protocols: *self.bloom.read(),
472 };
473 conn.send_msg(KadNetMsg::new(KadNetCmd::Peer, &peer_msg)?)
474 .await?;
475 Ok(())
476 }
477
478 async fn send_shutdown_msg(&self, conn: &mut KadConnRef) -> Result<()> {
480 trace!("Send Shutdown msg");
481 conn.send_msg(KadNetMsg::new(KadNetCmd::Shutdown, ShutdownMsg(0))?)
482 .await?;
483 Ok(())
484 }
485}
486
487fn validate_peer_msg(p: &mut PeerMsg) -> Result<()> {
490 if p.addrs.len() > MAX_ADDRS_PER_PEER {
491 return Err(Error::InvalidMsg(format!(
492 "PeerMsg.addrs has {} entries, max {MAX_ADDRS_PER_PEER}",
493 p.addrs.len()
494 )));
495 }
496 p.discovery_addrs
497 .retain(|a| !matches!(a.protocol, Protocol::Tls));
498 if p.discovery_addrs.len() > MAX_DISCOVERY_ADDRS_PER_PEER {
499 return Err(Error::InvalidMsg(format!(
500 "PeerMsg.discovery_addrs has {} entries, max {MAX_DISCOVERY_ADDRS_PER_PEER}",
501 p.discovery_addrs.len()
502 )));
503 }
504 Ok(())
505}