1use crate::{
4 cache::LruMap,
5 error::{NetworkError, ServiceKind},
6};
7use enr::Enr;
8use futures::StreamExt;
9use reth_discv4::{DiscoveryUpdate, Discv4, Discv4Config};
10use reth_discv5::{DiscoveredPeer, Discv5};
11use reth_dns_discovery::{
12 DnsDiscoveryConfig, DnsDiscoveryHandle, DnsDiscoveryService, DnsNodeRecordUpdate, DnsResolver,
13};
14use reth_ethereum_forks::{EnrForkIdEntry, ForkId};
15use reth_network_api::{DiscoveredEvent, DiscoveryEvent};
16use reth_network_peers::{NodeRecord, PeerId};
17use reth_network_types::PeerAddr;
18use secp256k1::SecretKey;
19use std::{
20 collections::VecDeque,
21 net::{IpAddr, SocketAddr},
22 pin::Pin,
23 sync::Arc,
24 task::{ready, Context, Poll},
25};
26use tokio::{net::UdpSocket, sync::mpsc, task::JoinHandle};
27use tokio_stream::{wrappers::ReceiverStream, Stream};
28use tracing::{debug, trace};
29
30pub const DEFAULT_MAX_CAPACITY_DISCOVERED_PEERS_CACHE: u32 = 10_000;
34
35#[derive(Debug)]
40pub struct Discovery {
41 discovered_nodes: LruMap<PeerId, PeerAddr>,
45 local_enr: NodeRecord,
47 discv4: Option<Discv4>,
49 discv4_updates: Option<ReceiverStream<DiscoveryUpdate>>,
51 _discv4_service: Option<JoinHandle<()>>,
53 discv5: Option<Discv5>,
55 discv5_updates: Option<ReceiverStream<discv5::Event>>,
57 _discv5_forwarder: Option<JoinHandle<()>>,
60 _dns_discovery: Option<DnsDiscoveryHandle>,
62 dns_discovery_updates: Option<ReceiverStream<DnsNodeRecordUpdate>>,
64 _dns_disc_service: Option<JoinHandle<()>>,
66 queued_events: VecDeque<DiscoveryEvent>,
68 discovery_listeners: Vec<mpsc::UnboundedSender<DiscoveryEvent>>,
70}
71
72impl Discovery {
73 pub async fn new(
78 tcp_addr: SocketAddr,
79 discovery_v4_addr: SocketAddr,
80 sk: SecretKey,
81 discv4_config: Option<Discv4Config>,
82 mut discv5_config: Option<reth_discv5::Config>, dns_discovery_config: Option<DnsDiscoveryConfig>,
84 ) -> Result<Self, NetworkError> {
85 let local_enr =
87 NodeRecord::from_secret_key(discovery_v4_addr, &sk).with_tcp_port(tcp_addr.port());
88
89 let bind_socket = async |addr: SocketAddr| {
93 let result = match addr {
94 SocketAddr::V4(_) => UdpSocket::bind(addr).await,
95 SocketAddr::V6(_) => {
96 use socket2::{Domain, Protocol, Socket, Type};
97 (|| {
98 let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
99 socket.set_only_v6(true)?;
100 socket.set_nonblocking(true)?;
101 socket.bind(&addr.into())?;
102 UdpSocket::from_std(socket.into())
103 })()
104 }
105 };
106 result
107 .map(Arc::new)
108 .map_err(|err| NetworkError::from_io_error(err, ServiceKind::Discovery(addr)))
109 };
110
111 let (discv4, discv4_updates, _discv4_service, discv4_ingress, shared_socket) =
114 if let Some(config) = discv4_config {
115 if let Some(discv5_config) = &mut discv5_config &&
116 discv5_config.has_matching_socket(discovery_v4_addr)
117 {
118 let socket = bind_socket(discovery_v4_addr).await?;
119
120 let (discv4, mut discv4_service, ingress) = Discv4::bind_shared(
121 socket.clone(),
122 local_enr,
123 sk,
124 config,
125 )
126 .map_err(|err| {
127 NetworkError::from_io_error(err, ServiceKind::Discovery(discovery_v4_addr))
128 })?;
129
130 let discv4_updates = discv4_service.update_stream();
131 let discv4_service = discv4_service.spawn();
132 debug!(target:"net", ?discovery_v4_addr, "started discovery v4 (shared port)");
133 (
134 Some(discv4),
135 Some(discv4_updates),
136 Some(discv4_service),
137 Some(ingress),
138 Some(socket),
139 )
140 } else {
141 let (discv4, mut discv4_service) =
142 Discv4::bind(discovery_v4_addr, local_enr, sk, config).await.map_err(
143 |err| {
144 NetworkError::from_io_error(
145 err,
146 ServiceKind::Discovery(discovery_v4_addr),
147 )
148 },
149 )?;
150 let discv4_updates = discv4_service.update_stream();
151 let discv4_service = discv4_service.spawn();
153
154 debug!(target:"net", ?discovery_v4_addr, "started discovery v4");
155
156 (Some(discv4), Some(discv4_updates), Some(discv4_service), None, None)
157 }
158 } else {
159 (None, None, None, None, None)
160 };
161
162 let (discv5, discv5_updates) = if let Some(mut config) = discv5_config {
164 set_bound_rlpx_port_if_unset(&mut config, tcp_addr.port());
166
167 if let Some(socket) = shared_socket {
168 let discv5_cfg = config.discv5_config_mut();
169
170 let (mut ipv4, mut ipv6) = (None, None);
173 if discovery_v4_addr.is_ipv4() {
174 ipv4 = Some(socket);
175 if let Some(addr) = reth_discv5::config::ipv6(&discv5_cfg.listen_config) {
176 ipv6 = Some(bind_socket(SocketAddr::V6(addr)).await?);
177 }
178 } else {
179 ipv6 = Some(socket);
180 if let Some(addr) = reth_discv5::config::ipv4(&discv5_cfg.listen_config) {
181 ipv4 = Some(bind_socket(SocketAddr::V4(addr)).await?);
182 }
183 }
184
185 discv5_cfg.listen_config = discv5::ListenConfig::FromSockets { ipv4, ipv6 };
186 }
187
188 let (discv5, discv5_updates) = Discv5::start(&sk, config).await?;
189 debug!(target:"net", discovery_v5_enr=?discv5.local_enr(), "started discovery v5");
190 (Some(discv5), Some(discv5_updates))
191 } else {
192 (None, None)
193 };
194
195 let (discv5_updates, _discv5_forwarder) = match (discv4_ingress, discv5_updates) {
200 (Some(mut ingress), Some(mut updates)) => {
201 let (tx, rx) = mpsc::channel(updates.max_capacity());
202 let handle = tokio::spawn(async move {
203 while let Some(event) = updates.recv().await {
204 if let discv5::Event::UnrecognizedFrame(frame) = &event {
205 ingress.handle_packet(&frame.packet, frame.src_address).await;
206 continue;
207 }
208 if tx.send(event).await.is_err() {
209 break;
210 }
211 }
212 });
213 (Some(ReceiverStream::new(rx)), Some(handle))
214 }
215 (_, updates) => (updates.map(ReceiverStream::new), None),
216 };
217
218 let (_dns_discovery, dns_discovery_updates, _dns_disc_service) =
220 if let Some(dns_config) = dns_discovery_config {
221 let (mut service, dns_disc) = DnsDiscoveryService::new_pair(
222 Arc::new(DnsResolver::from_system_conf()?),
223 dns_config,
224 );
225 let dns_discovery_updates = service.node_record_stream();
226 let dns_disc_service = service.spawn();
227 (Some(dns_disc), Some(dns_discovery_updates), Some(dns_disc_service))
228 } else {
229 (None, None, None)
230 };
231
232 Ok(Self {
233 discovery_listeners: Default::default(),
234 local_enr,
235 discv4,
236 discv4_updates,
237 _discv4_service,
238 discv5,
239 discv5_updates,
240 _discv5_forwarder,
241 discovered_nodes: LruMap::new(DEFAULT_MAX_CAPACITY_DISCOVERED_PEERS_CACHE),
242 queued_events: Default::default(),
243 _dns_disc_service,
244 _dns_discovery,
245 dns_discovery_updates,
246 })
247 }
248
249 pub(crate) fn add_listener(&mut self, tx: mpsc::UnboundedSender<DiscoveryEvent>) {
251 self.discovery_listeners.push(tx);
252 }
253
254 #[inline]
256 fn notify_listeners(&mut self, event: &DiscoveryEvent) {
257 self.discovery_listeners.retain_mut(|listener| listener.send(event.clone()).is_ok());
258 }
259
260 pub(crate) fn update_fork_id(&self, fork_id: ForkId) {
262 if let Some(discv4) = &self.discv4 {
263 discv4.set_eip868_rlp(b"eth".to_vec(), EnrForkIdEntry::from(fork_id))
265 }
266 if let Some(discv5) = &self.discv5 {
267 discv5
268 .encode_and_set_eip868_in_local_enr(b"eth".to_vec(), EnrForkIdEntry::from(fork_id))
269 }
270 }
271
272 pub(crate) fn ban_ip(&self, ip: IpAddr) {
274 if let Some(discv4) = &self.discv4 {
275 discv4.ban_ip(ip)
276 }
277 if let Some(discv5) = &self.discv5 {
278 discv5.ban_ip(ip)
279 }
280 }
281
282 pub(crate) fn ban(&self, peer_id: PeerId, ip: IpAddr) {
284 if let Some(discv4) = &self.discv4 {
285 discv4.ban(peer_id, ip)
286 }
287 if let Some(discv5) = &self.discv5 {
288 discv5.ban(peer_id, ip)
289 }
290 }
291
292 pub fn discv4(&self) -> Option<Discv4> {
294 self.discv4.clone()
295 }
296
297 pub(crate) const fn local_id(&self) -> PeerId {
299 self.local_enr.id }
301
302 pub(crate) fn add_discv4_node(&self, node: NodeRecord) {
304 if let Some(discv4) = &self.discv4 {
305 discv4.add_node(node);
306 }
307 }
308
309 pub fn discv5(&self) -> Option<Discv5> {
311 self.discv5.clone()
312 }
313
314 pub(crate) fn add_discv5_node(&self, enr: Enr<SecretKey>) -> Result<(), NetworkError> {
316 if let Some(discv5) = &self.discv5 {
317 discv5.add_node(enr).map_err(NetworkError::Discv5Error)?;
318 }
319
320 Ok(())
321 }
322
323 fn on_node_record_update(&mut self, record: NodeRecord, fork_id: Option<ForkId>) {
325 let peer_id = record.id;
326 let tcp_addr = record.tcp_addr();
327 if tcp_addr.port() == 0 {
328 return
330 }
331 let udp_addr = record.udp_addr();
332 let addr = PeerAddr::new(tcp_addr, Some(udp_addr));
333 _ =
334 self.discovered_nodes.get_or_insert(peer_id, || {
335 self.queued_events.push_back(DiscoveryEvent::NewNode(
336 DiscoveredEvent::EventQueued { peer_id, addr, fork_id },
337 ));
338
339 addr
340 })
341 }
342
343 fn on_discv4_update(&mut self, update: DiscoveryUpdate) {
344 match update {
345 DiscoveryUpdate::Added(record) | DiscoveryUpdate::DiscoveredAtCapacity(record) => {
346 self.on_node_record_update(record, None);
347 }
348 DiscoveryUpdate::EnrForkId(node, fork_id) => {
349 self.queued_events.push_back(DiscoveryEvent::EnrForkId(node, fork_id))
350 }
351 DiscoveryUpdate::Removed(peer_id) => {
352 self.discovered_nodes.remove(&peer_id);
353 }
354 DiscoveryUpdate::Batch(updates) => {
355 for update in updates {
356 self.on_discv4_update(update);
357 }
358 }
359 }
360 }
361
362 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<DiscoveryEvent> {
363 loop {
364 if let Some(event) = self.queued_events.pop_front() {
366 self.notify_listeners(&event);
367 return Poll::Ready(event)
368 }
369
370 while let Some(Poll::Ready(Some(update))) =
372 self.discv4_updates.as_mut().map(|updates| updates.poll_next_unpin(cx))
373 {
374 self.on_discv4_update(update)
375 }
376
377 while let Some(Poll::Ready(Some(update))) =
379 self.discv5_updates.as_mut().map(|updates| updates.poll_next_unpin(cx))
380 {
381 if let Some(discv5) = self.discv5.as_mut() &&
382 let Some(DiscoveredPeer { node_record, fork_id }) =
383 discv5.on_discv5_update(update)
384 {
385 self.on_node_record_update(node_record, fork_id);
386 }
387 }
388
389 while let Some(Poll::Ready(Some(update))) =
391 self.dns_discovery_updates.as_mut().map(|updates| updates.poll_next_unpin(cx))
392 {
393 self.add_discv4_node(update.node_record);
394 if let Err(err) = self.add_discv5_node(update.enr) {
395 trace!(target: "net::discovery",
396 %err,
397 "failed adding node discovered by dns to discv5"
398 );
399 }
400 self.on_node_record_update(update.node_record, update.fork_id);
401 }
402
403 if self.queued_events.is_empty() {
404 return Poll::Pending
405 }
406 }
407 }
408}
409
410const fn set_bound_rlpx_port_if_unset(config: &mut reth_discv5::Config, port: u16) {
411 if config.rlpx_socket().port() == 0 {
412 config.set_rlpx_port(port);
413 }
414}
415
416impl Drop for Discovery {
417 fn drop(&mut self) {
418 if let Some(discv4) = &self.discv4 {
419 discv4.terminate();
420 }
421 if let Some(handle) = self._discv4_service.take() {
422 handle.abort();
423 }
424 if let Some(handle) = self._discv5_forwarder.take() {
425 handle.abort();
426 }
427 if let Some(handle) = self._dns_disc_service.take() {
428 handle.abort();
429 }
430 }
431}
432
433impl Stream for Discovery {
434 type Item = DiscoveryEvent;
435
436 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
437 Poll::Ready(Some(ready!(self.get_mut().poll(cx))))
438 }
439}
440
441#[cfg(test)]
442impl Discovery {
443 pub(crate) fn noop() -> Self {
447 let (_discovery_listeners, _): (mpsc::UnboundedSender<DiscoveryEvent>, _) =
448 mpsc::unbounded_channel();
449
450 Self {
451 discovered_nodes: LruMap::new(0),
452 local_enr: NodeRecord {
453 address: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED),
454 tcp_port: 0,
455 udp_port: 0,
456 id: PeerId::random(),
457 },
458 discv4: Default::default(),
459 discv4_updates: Default::default(),
460 _discv4_service: Default::default(),
461 _discv5_forwarder: None,
462 discv5: None,
463 discv5_updates: None,
464 queued_events: Default::default(),
465 _dns_discovery: None,
466 dns_discovery_updates: None,
467 _dns_disc_service: None,
468 discovery_listeners: Default::default(),
469 }
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use secp256k1::SECP256K1;
477 use std::net::{Ipv4Addr, SocketAddrV4};
478
479 #[tokio::test(flavor = "multi_thread")]
480 async fn test_discovery_setup() {
481 let (secret_key, _) = SECP256K1.generate_keypair(&mut rand_08::thread_rng());
482 let discovery_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
483 let _discovery = Discovery::new(
484 discovery_addr,
485 discovery_addr,
486 secret_key,
487 Default::default(),
488 None,
489 Default::default(),
490 )
491 .await
492 .unwrap();
493 }
494
495 use reth_discv4::Discv4ConfigBuilder;
496 use reth_discv5::{enr::EnrCombinedKeyWrapper, enr_to_discv4_id};
497 use tracing::trace;
498
499 async fn start_discovery_node(udp_port_discv4: u16, udp_port_discv5: u16) -> Discovery {
500 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
501
502 let discv4_addr = format!("127.0.0.1:{udp_port_discv4}").parse().unwrap();
503 let discv5_addr: SocketAddr = format!("127.0.0.1:{udp_port_discv5}").parse().unwrap();
504
505 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
507
508 let discv5_listen_config = discv5::ListenConfig::from(discv5_addr);
509 let discv5_config = reth_discv5::Config::builder(discv5_addr)
510 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
511 .build();
512
513 Discovery::new(
514 discv4_addr,
515 discv4_addr,
516 secret_key,
517 Some(discv4_config),
518 Some(discv5_config),
519 None,
520 )
521 .await
522 .expect("should build discv5 with discv4 downgrade")
523 }
524
525 #[test]
526 fn discv5_enr_advertises_bound_rlpx_port() {
527 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
528
529 let bound_rlpx_port = 30307;
530 let discv5_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
531 let mut discv5_config = reth_discv5::Config::builder((Ipv4Addr::LOCALHOST, 0).into())
532 .discv5_config(discv5::ConfigBuilder::new(discv5_addr.into()).build())
533 .build();
534
535 set_bound_rlpx_port_if_unset(&mut discv5_config, bound_rlpx_port);
536
537 let (enr, _, _, _) = reth_discv5::build_local_enr(&secret_key, &discv5_config);
538 assert_eq!(enr.tcp4(), Some(bound_rlpx_port));
539 }
540
541 #[test]
542 fn discv5_enr_preserves_configured_rlpx_port() {
543 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
544
545 let advertised_addr: SocketAddr = "127.0.0.1:30308".parse().unwrap();
546 let discv5_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
547 let mut discv5_config = reth_discv5::Config::builder(advertised_addr)
548 .discv5_config(discv5::ConfigBuilder::new(discv5_addr.into()).build())
549 .build();
550
551 set_bound_rlpx_port_if_unset(&mut discv5_config, 30307);
552
553 let (enr, _, _, _) = reth_discv5::build_local_enr(&secret_key, &discv5_config);
554 assert_eq!(enr.tcp4(), Some(advertised_addr.port()));
555 }
556
557 #[tokio::test(flavor = "multi_thread")]
558 async fn discv5_and_discv4_same_pk() {
559 reth_tracing::init_test_tracing();
560
561 let mut node_1 = start_discovery_node(40014, 40015).await;
563 let discv4_enr_1 = node_1.discv4.as_ref().unwrap().node_record();
564 let discv5_enr_node_1 =
565 node_1.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
566 let discv4_id_1 = discv4_enr_1.id;
567 let discv5_id_1 = discv5_enr_node_1.node_id();
568
569 let mut node_2 = start_discovery_node(40024, 40025).await;
570 let discv4_enr_2 = node_2.discv4.as_ref().unwrap().node_record();
571 let discv5_enr_node_2 =
572 node_2.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
573 let discv4_id_2 = discv4_enr_2.id;
574 let discv5_id_2 = discv5_enr_node_2.node_id();
575
576 trace!(target: "net::discovery::tests",
577 node_1_node_id=format!("{:#}", discv5_id_1),
578 node_2_node_id=format!("{:#}", discv5_id_2),
579 "started nodes"
580 );
581
582 assert_eq!(discv4_id_1, enr_to_discv4_id(&discv5_enr_node_1).unwrap());
586 assert_eq!(discv4_id_2, enr_to_discv4_id(&discv5_enr_node_2).unwrap());
587
588 node_1.add_discv4_node(discv4_enr_2);
590
591 let event_node_1 = node_1.next().await.unwrap();
593 let event_node_2 = node_2.next().await.unwrap();
594
595 assert_eq!(
596 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
597 peer_id: discv4_id_2,
598 addr: PeerAddr::new(discv4_enr_2.tcp_addr(), Some(discv4_enr_2.udp_addr())),
599 fork_id: None
600 }),
601 event_node_1
602 );
603 assert_eq!(
604 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
605 peer_id: discv4_id_1,
606 addr: PeerAddr::new(discv4_enr_1.tcp_addr(), Some(discv4_enr_1.udp_addr())),
607 fork_id: None
608 }),
609 event_node_2
610 );
611
612 assert_eq!(1, node_1.discovered_nodes.len());
613 assert_eq!(1, node_2.discovered_nodes.len());
614
615 node_1.add_discv5_node(EnrCombinedKeyWrapper(discv5_enr_node_2.clone()).into()).unwrap();
617 assert!(node_1
619 .discv5
620 .as_ref()
621 .unwrap()
622 .with_discv5(|discv5| discv5.table_entries_id().contains(&discv5_id_2)));
623
624 node_1
626 .discv5
627 .as_ref()
628 .unwrap()
629 .with_discv5(|discv5| discv5.send_ping(discv5_enr_node_2.clone()))
630 .await
631 .unwrap();
632
633 assert_eq!(1, node_1.discovered_nodes.len());
636 assert_eq!(1, node_2.discovered_nodes.len());
637 }
638
639 async fn start_shared_port_node(port: u16) -> Discovery {
641 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
642 let disc_addr: SocketAddr = format!("127.0.0.1:{port}").parse().unwrap();
643 let tcp_addr: SocketAddr = "127.0.0.1:30303".parse().unwrap();
646
647 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
648
649 let discv5_listen_config = discv5::ListenConfig::from(disc_addr);
650 let discv5_config = reth_discv5::Config::builder(tcp_addr)
651 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
652 .build();
653
654 Discovery::new(
656 tcp_addr,
657 disc_addr,
658 secret_key,
659 Some(discv4_config),
660 Some(discv5_config),
661 None,
662 )
663 .await
664 .expect("should start with shared port")
665 }
666
667 #[tokio::test(flavor = "multi_thread")]
668 async fn test_shared_port_setup() {
669 reth_tracing::init_test_tracing();
670
671 let node = start_shared_port_node(0).await;
673
674 assert!(node.discv4.is_some(), "discv4 should be running");
676 assert!(node.discv5.is_some(), "discv5 should be running");
677 }
678
679 #[tokio::test(flavor = "multi_thread")]
680 async fn test_shared_port_discv5_discovery() {
681 reth_tracing::init_test_tracing();
682
683 let mut node_1 = start_shared_port_node(0).await;
684 let mut node_2 = start_shared_port_node(0).await;
685
686 let discv5_enr_1 = node_1.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
687 let discv5_enr_2 = node_2.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
688
689 let peer_id_1 = enr_to_discv4_id(&discv5_enr_1).unwrap();
690 let peer_id_2 = enr_to_discv4_id(&discv5_enr_2).unwrap();
691
692 node_1.add_discv5_node(EnrCombinedKeyWrapper(discv5_enr_2.clone()).into()).unwrap();
696 node_1
697 .discv5
698 .as_ref()
699 .unwrap()
700 .with_discv5(|discv5| discv5.send_ping(discv5_enr_2))
701 .await
702 .unwrap();
703
704 let mut event_1 = None;
707 let mut event_2 = None;
708 let timeout = tokio::time::sleep(std::time::Duration::from_secs(5));
709 tokio::pin!(timeout);
710 loop {
711 tokio::select! {
712 ev = node_1.next(), if event_1.is_none() => {
713 event_1 = ev;
714 }
715 ev = node_2.next(), if event_2.is_none() => {
716 event_2 = ev;
717 }
718 _ = &mut timeout => {
719 panic!("timed out waiting for discv5 discovery events");
720 }
721 }
722 if event_1.is_some() && event_2.is_some() {
723 break;
724 }
725 }
726
727 assert!(matches!(
728 event_1.unwrap(),
729 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued { peer_id, .. })
730 if peer_id == peer_id_2
731 ));
732 assert!(matches!(
733 event_2.unwrap(),
734 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued { peer_id, .. })
735 if peer_id == peer_id_1
736 ));
737 }
738
739 #[tokio::test(flavor = "multi_thread")]
740 async fn test_shared_port_discv4_discovery() {
741 reth_tracing::init_test_tracing();
742
743 let mut node_1 = start_shared_port_node(0).await;
744 let mut node_2 = start_shared_port_node(0).await;
745
746 let enr_1 = node_1.discv4.as_ref().unwrap().node_record();
747 let enr_2 = node_2.discv4.as_ref().unwrap().node_record();
748
749 node_1.add_discv4_node(enr_2);
751
752 let event_1 = node_1.next().await.unwrap();
754 let event_2 = node_2.next().await.unwrap();
755
756 assert_eq!(
757 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
758 peer_id: enr_2.id,
759 addr: PeerAddr::new(enr_2.tcp_addr(), Some(enr_2.udp_addr())),
760 fork_id: None
761 }),
762 event_1
763 );
764 assert_eq!(
765 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
766 peer_id: enr_1.id,
767 addr: PeerAddr::new(enr_1.tcp_addr(), Some(enr_1.udp_addr())),
768 fork_id: None
769 }),
770 event_2
771 );
772 }
773
774 #[tokio::test(flavor = "multi_thread")]
778 async fn test_shared_port_dual_stack() {
779 reth_tracing::init_test_tracing();
780
781 let probe = UdpSocket::bind("0.0.0.0:0").await.expect("probe bind");
783 let port = probe.local_addr().unwrap().port();
784 drop(probe);
785
786 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
787 let v4_addr: SocketAddr = format!("0.0.0.0:{port}").parse().unwrap();
788 let tcp_addr: SocketAddr = "0.0.0.0:30303".parse().unwrap();
789
790 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
791
792 let discv5_listen_config = discv5::ListenConfig::DualStack {
793 ipv4: std::net::Ipv4Addr::UNSPECIFIED,
794 ipv4_port: port,
795 ipv6: std::net::Ipv6Addr::UNSPECIFIED,
796 ipv6_port: port,
797 };
798 let discv5_config = reth_discv5::Config::builder(tcp_addr)
799 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
800 .build();
801
802 Discovery::new(
803 tcp_addr,
804 v4_addr,
805 secret_key,
806 Some(discv4_config),
807 Some(discv5_config),
808 None,
809 )
810 .await
811 .expect("discovery should start with shared port + dual-stack");
812 }
813}