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_net_nat::{NatResolver, ResolveNatInterval};
16use reth_network_api::{DiscoveredEvent, DiscoveryEvent};
17use reth_network_peers::{NodeRecord, PeerId};
18use reth_network_types::PeerAddr;
19use secp256k1::SecretKey;
20use socket2::{Domain, Protocol, Socket, Type};
21use std::{
22 collections::VecDeque,
23 net::{IpAddr, SocketAddr},
24 pin::Pin,
25 sync::Arc,
26 task::{ready, Context, Poll},
27 time::Duration,
28};
29use tokio::{net::UdpSocket, sync::mpsc, task::JoinHandle};
30use tokio_stream::{wrappers::ReceiverStream, Stream};
31use tracing::{debug, trace};
32
33pub const DEFAULT_MAX_CAPACITY_DISCOVERED_PEERS_CACHE: u32 = 10_000;
37
38const RESOLVE_EXTERNAL_IP_INTERVAL: Duration = Duration::from_secs(60 * 5);
42
43#[derive(Debug)]
48pub struct Discovery {
49 discovered_nodes: LruMap<PeerId, PeerAddr>,
53 local_enr: NodeRecord,
55 discv4: Option<Discv4>,
57 discv4_updates: Option<ReceiverStream<DiscoveryUpdate>>,
59 _discv4_service: Option<JoinHandle<()>>,
61 discv5: Option<Discv5>,
63 discv5_updates: Option<ReceiverStream<discv5::Event>>,
65 _discv5_forwarder: Option<JoinHandle<()>>,
68 _dns_discovery: Option<DnsDiscoveryHandle>,
70 dns_discovery_updates: Option<ReceiverStream<DnsNodeRecordUpdate>>,
72 _dns_disc_service: Option<JoinHandle<()>>,
74 queued_events: VecDeque<DiscoveryEvent>,
76 discovery_listeners: Vec<mpsc::UnboundedSender<DiscoveryEvent>>,
78 nat_resolver: Option<ResolveNatInterval>,
80}
81
82impl Discovery {
83 pub async fn new(
91 tcp_addr: SocketAddr,
92 discovery_v4_addr: SocketAddr,
93 sk: SecretKey,
94 discv4_config: Option<Discv4Config>,
95 mut discv5_config: Option<reth_discv5::Config>, dns_discovery_config: Option<DnsDiscoveryConfig>,
97 nat: Option<NatResolver>,
98 ) -> Result<Self, NetworkError> {
99 let local_enr =
101 NodeRecord::from_secret_key(discovery_v4_addr, &sk).with_tcp_port(tcp_addr.port());
102
103 let bind_socket = async |addr: SocketAddr| {
107 let result = match addr {
108 SocketAddr::V4(_) => UdpSocket::bind(addr).await,
109 SocketAddr::V6(_) => (|| {
110 let socket = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
111 socket.set_only_v6(true)?;
112 socket.set_nonblocking(true)?;
113 socket.bind(&addr.into())?;
114 UdpSocket::from_std(socket.into())
115 })(),
116 };
117 result
118 .map(Arc::new)
119 .map_err(|err| NetworkError::from_io_error(err, ServiceKind::Discovery(addr)))
120 };
121
122 let (discv4, discv4_updates, _discv4_service, discv4_ingress, shared_sockets) =
130 if let Some(mut config) = discv4_config {
131 if let Some(discv5_config) = &mut discv5_config &&
132 discv5_config.has_matching_socket(discovery_v4_addr)
133 {
134 let socket = bind_socket(discovery_v4_addr).await?;
135 let shared_port = socket
136 .local_addr()
137 .map_err(|err| {
138 NetworkError::from_io_error(
139 err,
140 ServiceKind::Discovery(discovery_v4_addr),
141 )
142 })?
143 .port();
144
145 let listen_config = &discv5_config.discv5_config_mut().listen_config;
146 let secondary_addr = reth_discv5::config::sibling_addr(
147 listen_config,
148 discovery_v4_addr,
149 shared_port,
150 );
151 let secondary_socket = match secondary_addr {
152 Some(addr) => Some(bind_socket(addr).await?),
153 None => None,
154 };
155
156 if secondary_socket.is_some() {
157 let (tcp4, tcp6) = if tcp_addr.is_ipv4() {
159 (tcp_addr.port(), 0)
160 } else {
161 (0, tcp_addr.port())
162 };
163 config.add_eip868_pair("tcp", tcp4);
164 config.add_eip868_pair("tcp6", tcp6);
165 }
166
167 let (discv4, mut discv4_service, ingress) = Discv4::bind_shared(
168 socket.clone(),
169 secondary_socket.clone(),
170 local_enr,
171 sk,
172 config,
173 )
174 .map_err(|err| {
175 NetworkError::from_io_error(err, ServiceKind::Discovery(discovery_v4_addr))
176 })?;
177
178 let discv4_updates = discv4_service.update_stream();
179 let discv4_service = discv4_service.spawn();
180 debug!(target:"net", ?discovery_v4_addr, "started discovery v4 (shared port)");
181 (
182 Some(discv4),
183 Some(discv4_updates),
184 Some(discv4_service),
185 Some(ingress),
186 Some((socket, secondary_socket)),
187 )
188 } else {
189 let (discv4, mut discv4_service) =
190 Discv4::bind(discovery_v4_addr, local_enr, sk, config).await.map_err(
191 |err| {
192 NetworkError::from_io_error(
193 err,
194 ServiceKind::Discovery(discovery_v4_addr),
195 )
196 },
197 )?;
198 let discv4_updates = discv4_service.update_stream();
199 let discv4_service = discv4_service.spawn();
201
202 debug!(target:"net", ?discovery_v4_addr, "started discovery v4");
203
204 (Some(discv4), Some(discv4_updates), Some(discv4_service), None, None)
205 }
206 } else {
207 (None, None, None, None, None)
208 };
209
210 let nat_resolver = if discv4.is_none() &&
212 let Some(config) = &mut discv5_config
213 {
214 let config = config.discv5_config_mut();
215 nat.filter(|nat| match nat {
216 NatResolver::None => false,
217 NatResolver::ExternalIp(_) | NatResolver::ExternalAddr(_) => true,
219 _ => config.enr_update && config.auto_nat_listen_duration.is_none(),
222 })
223 .map(|nat| ResolveNatInterval::interval(nat, RESOLVE_EXTERNAL_IP_INTERVAL))
224 } else {
225 None
226 };
227
228 let (discv5, discv5_updates) = if let Some(mut config) = discv5_config {
230 set_bound_rlpx_port_if_unset(&mut config, tcp_addr.port());
232
233 if let Some((primary, secondary)) = shared_sockets {
234 let (ipv4, ipv6) = if discovery_v4_addr.is_ipv4() {
235 (Some(primary), secondary)
236 } else {
237 (secondary, Some(primary))
238 };
239 config.discv5_config_mut().listen_config =
240 discv5::ListenConfig::FromSockets { ipv4, ipv6 };
241 }
242
243 let (discv5, discv5_updates) = Discv5::start(&sk, config).await?;
244 debug!(target:"net", discovery_v5_enr=?discv5.local_enr(), "started discovery v5");
245 (Some(discv5), Some(discv5_updates))
246 } else {
247 (None, None)
248 };
249
250 let (discv5_updates, _discv5_forwarder) = match (discv4_ingress, discv5_updates) {
255 (Some(mut ingress), Some(mut updates)) => {
256 let (tx, rx) = mpsc::channel(updates.max_capacity());
257 let handle = tokio::spawn(async move {
258 while let Some(event) = updates.recv().await {
259 if let discv5::Event::UnrecognizedFrame(frame) = &event {
260 ingress.handle_packet(&frame.packet, frame.src_address).await;
261 continue;
262 }
263 if tx.send(event).await.is_err() {
264 break;
265 }
266 }
267 });
268 (Some(ReceiverStream::new(rx)), Some(handle))
269 }
270 (_, updates) => (updates.map(ReceiverStream::new), None),
271 };
272
273 let (_dns_discovery, dns_discovery_updates, _dns_disc_service) =
275 if let Some(dns_config) = dns_discovery_config {
276 let (mut service, dns_disc) = DnsDiscoveryService::new_pair(
277 Arc::new(DnsResolver::from_system_conf()?),
278 dns_config,
279 );
280 let dns_discovery_updates = service.node_record_stream();
281 let dns_disc_service = service.spawn();
282 (Some(dns_disc), Some(dns_discovery_updates), Some(dns_disc_service))
283 } else {
284 (None, None, None)
285 };
286
287 Ok(Self {
288 discovery_listeners: Default::default(),
289 local_enr,
290 discv4,
291 discv4_updates,
292 _discv4_service,
293 discv5,
294 discv5_updates,
295 _discv5_forwarder,
296 discovered_nodes: LruMap::new(DEFAULT_MAX_CAPACITY_DISCOVERED_PEERS_CACHE),
297 queued_events: Default::default(),
298 _dns_disc_service,
299 _dns_discovery,
300 dns_discovery_updates,
301 nat_resolver,
302 })
303 }
304
305 pub(crate) fn add_listener(&mut self, tx: mpsc::UnboundedSender<DiscoveryEvent>) {
307 self.discovery_listeners.push(tx);
308 }
309
310 #[inline]
312 fn notify_listeners(&mut self, event: &DiscoveryEvent) {
313 self.discovery_listeners.retain_mut(|listener| listener.send(event.clone()).is_ok());
314 }
315
316 pub(crate) fn update_fork_id(&self, fork_id: ForkId) {
318 if let Some(discv4) = &self.discv4 {
319 discv4.set_eip868_rlp(b"eth".to_vec(), EnrForkIdEntry::from(fork_id))
321 }
322 if let Some(discv5) = &self.discv5 {
323 discv5
324 .encode_and_set_eip868_in_local_enr(b"eth".to_vec(), EnrForkIdEntry::from(fork_id))
325 }
326 }
327
328 pub(crate) fn ban_ip(&self, ip: IpAddr) {
330 if let Some(discv4) = &self.discv4 {
331 discv4.ban_ip(ip)
332 }
333 if let Some(discv5) = &self.discv5 {
334 discv5.ban_ip(ip)
335 }
336 }
337
338 pub(crate) fn ban(&self, peer_id: PeerId, ip: IpAddr) {
340 if let Some(discv4) = &self.discv4 {
341 discv4.ban(peer_id, ip)
342 }
343 if let Some(discv5) = &self.discv5 {
344 discv5.ban(peer_id, ip)
345 }
346 }
347
348 pub fn discv4(&self) -> Option<Discv4> {
350 self.discv4.clone()
351 }
352
353 pub(crate) const fn local_id(&self) -> PeerId {
355 self.local_enr.id }
357
358 pub(crate) fn add_discv4_node(&self, node: NodeRecord) {
360 if let Some(discv4) = &self.discv4 {
361 discv4.add_node(node);
362 }
363 }
364
365 pub fn discv5(&self) -> Option<Discv5> {
367 self.discv5.clone()
368 }
369
370 pub(crate) fn add_discv5_node(&self, enr: Enr<SecretKey>) -> Result<(), NetworkError> {
372 if let Some(discv5) = &self.discv5 {
373 discv5.add_node(enr).map_err(NetworkError::Discv5Error)?;
374 }
375
376 Ok(())
377 }
378
379 fn on_node_record_update(&mut self, record: NodeRecord, fork_id: Option<ForkId>) {
381 let peer_id = record.id;
382 let tcp_addr = record.tcp_addr();
383 if tcp_addr.port() == 0 {
384 return
386 }
387 let udp_addr = record.udp_addr();
388 let addr = PeerAddr::new(tcp_addr, Some(udp_addr));
389 _ =
390 self.discovered_nodes.get_or_insert(peer_id, || {
391 self.queued_events.push_back(DiscoveryEvent::NewNode(
392 DiscoveredEvent::EventQueued { peer_id, addr, fork_id },
393 ));
394
395 addr
396 })
397 }
398
399 fn on_discv4_update(&mut self, update: DiscoveryUpdate) {
400 match update {
401 DiscoveryUpdate::Added(record) | DiscoveryUpdate::DiscoveredAtCapacity(record) => {
402 self.on_node_record_update(record, None);
403 }
404 DiscoveryUpdate::EnrForkId(node, fork_id) => {
405 self.queued_events.push_back(DiscoveryEvent::EnrForkId(node, fork_id))
406 }
407 DiscoveryUpdate::Removed(peer_id) => {
408 self.discovered_nodes.remove(&peer_id);
409 }
410 DiscoveryUpdate::Batch(updates) => {
411 for update in updates {
412 self.on_discv4_update(update);
413 }
414 }
415 }
416 }
417
418 pub(crate) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<DiscoveryEvent> {
419 loop {
420 if let Some(event) = self.queued_events.pop_front() {
422 self.notify_listeners(&event);
423 return Poll::Ready(event)
424 }
425
426 while let Some(Poll::Ready(Some(update))) =
428 self.discv4_updates.as_mut().map(|updates| updates.poll_next_unpin(cx))
429 {
430 self.on_discv4_update(update)
431 }
432
433 while let Some(Poll::Ready(Some(update))) =
435 self.discv5_updates.as_mut().map(|updates| updates.poll_next_unpin(cx))
436 {
437 if let Some(discv5) = self.discv5.as_mut() &&
438 let Some(DiscoveredPeer { node_record, fork_id }) =
439 discv5.on_discv5_update(update)
440 {
441 self.on_node_record_update(node_record, fork_id);
442 }
443 }
444
445 while let Some(Poll::Ready(Some(update))) =
447 self.dns_discovery_updates.as_mut().map(|updates| updates.poll_next_unpin(cx))
448 {
449 self.add_discv4_node(update.node_record);
450 if let Err(err) = self.add_discv5_node(update.enr) {
451 trace!(target: "net::discovery",
452 %err,
453 "failed adding node discovered by dns to discv5"
454 );
455 }
456 self.on_node_record_update(update.node_record, update.fork_id);
457 }
458
459 while let Some(Poll::Ready(ip)) =
460 self.nat_resolver.as_mut().map(|resolver| resolver.poll_tick(cx))
461 {
462 if let Some(ip) = ip {
463 self.on_external_ip(ip);
464 }
465 }
466
467 if self.queued_events.is_empty() {
468 return Poll::Pending
469 }
470 }
471 }
472
473 fn on_external_ip(&self, external_ip: IpAddr) {
478 let Some(discv5) = &self.discv5 else { return };
479 let enr = discv5.local_enr();
480 let result = match external_ip {
483 IpAddr::V4(ip) if enr.udp4().is_some() && enr.ip4() != Some(ip) => {
484 discv5.with_discv5(|discv5| discv5.enr_insert("ip", &ip))
485 }
486 IpAddr::V6(ip) if enr.udp6().is_some() && enr.ip6() != Some(ip) => {
487 discv5.with_discv5(|discv5| discv5.enr_insert("ip6", &ip))
488 }
489 _ => return,
490 };
491 match result {
492 Ok(_) => debug!(target: "net::discovery", ?external_ip, "Updated external IP"),
493 Err(err) => {
494 debug!(target: "net::discovery", ?external_ip, %err, "Failed to update external IP");
495 }
496 }
497 }
498}
499
500const fn set_bound_rlpx_port_if_unset(config: &mut reth_discv5::Config, port: u16) {
501 if config.rlpx_socket().port() == 0 {
502 config.set_rlpx_port(port);
503 }
504}
505
506impl Drop for Discovery {
507 fn drop(&mut self) {
508 if let Some(discv4) = &self.discv4 {
509 discv4.terminate();
510 }
511 if let Some(handle) = self._discv4_service.take() {
512 handle.abort();
513 }
514 if let Some(handle) = self._discv5_forwarder.take() {
515 handle.abort();
516 }
517 if let Some(handle) = self._dns_disc_service.take() {
518 handle.abort();
519 }
520 }
521}
522
523impl Stream for Discovery {
524 type Item = DiscoveryEvent;
525
526 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
527 Poll::Ready(Some(ready!(self.get_mut().poll(cx))))
528 }
529}
530
531#[cfg(test)]
532impl Discovery {
533 pub(crate) fn noop() -> Self {
537 let (_discovery_listeners, _): (mpsc::UnboundedSender<DiscoveryEvent>, _) =
538 mpsc::unbounded_channel();
539
540 Self {
541 discovered_nodes: LruMap::new(0),
542 local_enr: NodeRecord {
543 address: IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED),
544 tcp_port: 0,
545 udp_port: 0,
546 id: PeerId::random(),
547 },
548 discv4: Default::default(),
549 discv4_updates: Default::default(),
550 _discv4_service: Default::default(),
551 _discv5_forwarder: None,
552 discv5: None,
553 discv5_updates: None,
554 queued_events: Default::default(),
555 _dns_discovery: None,
556 dns_discovery_updates: None,
557 _dns_disc_service: None,
558 discovery_listeners: Default::default(),
559 nat_resolver: None,
560 }
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 use super::*;
567 use secp256k1::SECP256K1;
568 use std::{
569 net::{Ipv4Addr, Ipv6Addr, SocketAddrV4},
570 time::Duration,
571 };
572
573 #[tokio::test(flavor = "multi_thread")]
574 async fn test_discovery_setup() {
575 let (secret_key, _) = SECP256K1.generate_keypair(&mut rand_08::thread_rng());
576 let discovery_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
577 let _discovery = Discovery::new(
578 discovery_addr,
579 discovery_addr,
580 secret_key,
581 Default::default(),
582 None,
583 Default::default(),
584 None,
585 )
586 .await
587 .unwrap();
588 }
589
590 use reth_discv4::Discv4ConfigBuilder;
591 use reth_discv5::{enr::EnrCombinedKeyWrapper, enr_to_discv4_id};
592 use tracing::trace;
593
594 async fn start_discovery_node(udp_port_discv4: u16, udp_port_discv5: u16) -> Discovery {
595 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
596
597 let discv4_addr = format!("127.0.0.1:{udp_port_discv4}").parse().unwrap();
598 let discv5_addr: SocketAddr = format!("127.0.0.1:{udp_port_discv5}").parse().unwrap();
599
600 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
602
603 let discv5_listen_config = discv5::ListenConfig::from(discv5_addr);
604 let discv5_config = reth_discv5::Config::builder(discv5_addr)
605 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
606 .build();
607
608 Discovery::new(
609 discv4_addr,
610 discv4_addr,
611 secret_key,
612 Some(discv4_config),
613 Some(discv5_config),
614 None,
615 None,
616 )
617 .await
618 .expect("should build discv5 with discv4 downgrade")
619 }
620
621 async fn start_discv5_only(config: reth_discv5::Config, nat: Option<NatResolver>) -> Discovery {
622 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
623 Discovery::new(
624 "127.0.0.1:30303".parse().unwrap(),
625 "127.0.0.1:0".parse().unwrap(),
626 secret_key,
627 None,
628 Some(config),
629 None,
630 nat,
631 )
632 .await
633 .expect("should start discv5")
634 }
635
636 #[tokio::test]
637 async fn discv5_only_advertises_resolved_external_ip() {
638 let socket = Arc::new(UdpSocket::bind("0.0.0.0:0").await.unwrap());
639 let udp_port = socket.local_addr().unwrap().port();
640 let external_ip = "203.0.113.7".parse().unwrap();
641 let mut config = reth_discv5::Config::builder("127.0.0.1:30309".parse().unwrap()).build();
643 let discv5_config = config.discv5_config_mut();
645 discv5_config.listen_config =
646 discv5::ListenConfig::FromSockets { ipv4: Some(socket), ipv6: None };
647 discv5_config.enr_update = false;
649 let mut discovery =
650 start_discv5_only(config, Some(NatResolver::ExternalIp(external_ip))).await;
651 let discv5 = discovery.discv5().unwrap();
652 assert!(discv5.local_enr().ip4().is_none());
653
654 tokio::time::timeout(
655 Duration::from_secs(5),
656 futures::future::poll_fn(|cx| {
657 let _ = discovery.poll(cx);
658 if discv5.node_record().is_some_and(|record| record.address == external_ip) {
659 Poll::Ready(())
660 } else {
661 Poll::Pending
662 }
663 }),
664 )
665 .await
666 .expect("external IP should be advertised");
667
668 let record = discv5.node_record().unwrap();
669 assert_eq!(record.address, external_ip);
670 assert_eq!(record.udp_port, udp_port);
671 assert_eq!(record.tcp_port, 30309);
672
673 let seq = discv5.local_enr().seq();
674 discovery.on_external_ip(external_ip);
675 assert_eq!(discv5.local_enr().seq(), seq);
676 discovery.on_external_ip("2001:db8::7".parse().unwrap());
677 assert_eq!(discv5.local_enr().seq(), seq);
678
679 discv5.with_discv5(|discv5| {
681 discv5.update_local_enr_socket((Ipv4Addr::LOCALHOST, 30310).into(), false);
682 });
683 discovery.on_external_ip(external_ip);
684 assert_eq!(discv5.node_record().unwrap(), NodeRecord { udp_port: 30310, ..record });
685 }
686
687 #[tokio::test]
688 async fn discv5_only_respects_enr_update_policy() {
689 for (enr_update, auto_nat_listen_duration) in
690 [(false, None), (true, Some(Duration::from_secs(300)))]
691 {
692 let mut config = reth_discv5::Config::builder((Ipv4Addr::LOCALHOST, 0).into()).build();
693 let discv5_config = config.discv5_config_mut();
694 discv5_config.listen_config =
695 discv5::ListenConfig::Ipv4 { ip: Ipv4Addr::LOCALHOST, port: 0 };
696 discv5_config.enr_update = enr_update;
697 discv5_config.auto_nat_listen_duration = auto_nat_listen_duration;
699 let discovery = start_discv5_only(config, Some(NatResolver::Any)).await;
700
701 assert!(discovery.nat_resolver.is_none());
703 }
704 }
705
706 #[test]
707 fn discv5_enr_advertises_bound_rlpx_port() {
708 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
709
710 let bound_rlpx_port = 30307;
711 let discv5_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
712 let mut discv5_config = reth_discv5::Config::builder((Ipv4Addr::LOCALHOST, 0).into())
713 .discv5_config(discv5::ConfigBuilder::new(discv5_addr.into()).build())
714 .build();
715
716 set_bound_rlpx_port_if_unset(&mut discv5_config, bound_rlpx_port);
717
718 let (enr, _, _, _) = reth_discv5::build_local_enr(&secret_key, &discv5_config);
719 assert_eq!(enr.tcp4(), Some(bound_rlpx_port));
720 }
721
722 #[test]
723 fn discv5_enr_preserves_configured_rlpx_port() {
724 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
725
726 let advertised_addr: SocketAddr = "127.0.0.1:30308".parse().unwrap();
727 let discv5_addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
728 let mut discv5_config = reth_discv5::Config::builder(advertised_addr)
729 .discv5_config(discv5::ConfigBuilder::new(discv5_addr.into()).build())
730 .build();
731
732 set_bound_rlpx_port_if_unset(&mut discv5_config, 30307);
733
734 let (enr, _, _, _) = reth_discv5::build_local_enr(&secret_key, &discv5_config);
735 assert_eq!(enr.tcp4(), Some(advertised_addr.port()));
736 }
737
738 #[tokio::test(flavor = "multi_thread")]
739 async fn discv5_and_discv4_same_pk() {
740 reth_tracing::init_test_tracing();
741
742 let mut node_1 = start_discovery_node(40014, 40015).await;
744 let discv4_enr_1 = node_1.discv4.as_ref().unwrap().node_record();
745 let discv5_enr_node_1 =
746 node_1.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
747 let discv4_id_1 = discv4_enr_1.id;
748 let discv5_id_1 = discv5_enr_node_1.node_id();
749
750 let mut node_2 = start_discovery_node(40024, 40025).await;
751 let discv4_enr_2 = node_2.discv4.as_ref().unwrap().node_record();
752 let discv5_enr_node_2 =
753 node_2.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
754 let discv4_id_2 = discv4_enr_2.id;
755 let discv5_id_2 = discv5_enr_node_2.node_id();
756
757 trace!(target: "net::discovery::tests",
758 node_1_node_id=format!("{:#}", discv5_id_1),
759 node_2_node_id=format!("{:#}", discv5_id_2),
760 "started nodes"
761 );
762
763 assert_eq!(discv4_id_1, enr_to_discv4_id(&discv5_enr_node_1).unwrap());
767 assert_eq!(discv4_id_2, enr_to_discv4_id(&discv5_enr_node_2).unwrap());
768
769 node_1.add_discv4_node(discv4_enr_2);
771
772 let event_node_1 = node_1.next().await.unwrap();
774 let event_node_2 = node_2.next().await.unwrap();
775
776 assert_eq!(
777 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
778 peer_id: discv4_id_2,
779 addr: PeerAddr::new(discv4_enr_2.tcp_addr(), Some(discv4_enr_2.udp_addr())),
780 fork_id: None
781 }),
782 event_node_1
783 );
784 assert_eq!(
785 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
786 peer_id: discv4_id_1,
787 addr: PeerAddr::new(discv4_enr_1.tcp_addr(), Some(discv4_enr_1.udp_addr())),
788 fork_id: None
789 }),
790 event_node_2
791 );
792
793 assert_eq!(1, node_1.discovered_nodes.len());
794 assert_eq!(1, node_2.discovered_nodes.len());
795
796 node_1.add_discv5_node(EnrCombinedKeyWrapper(discv5_enr_node_2.clone()).into()).unwrap();
798 assert!(node_1
800 .discv5
801 .as_ref()
802 .unwrap()
803 .with_discv5(|discv5| discv5.table_entries_id().contains(&discv5_id_2)));
804
805 node_1
807 .discv5
808 .as_ref()
809 .unwrap()
810 .with_discv5(|discv5| discv5.send_ping(discv5_enr_node_2.clone()))
811 .await
812 .unwrap();
813
814 assert_eq!(1, node_1.discovered_nodes.len());
817 assert_eq!(1, node_2.discovered_nodes.len());
818 }
819
820 async fn start_shared_port_node(port: u16) -> Discovery {
822 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
823 let disc_addr: SocketAddr = format!("127.0.0.1:{port}").parse().unwrap();
824 let tcp_addr: SocketAddr = "127.0.0.1:30303".parse().unwrap();
827
828 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
829
830 let discv5_listen_config = discv5::ListenConfig::from(disc_addr);
831 let discv5_config = reth_discv5::Config::builder(tcp_addr)
832 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
833 .build();
834
835 Discovery::new(
837 tcp_addr,
838 disc_addr,
839 secret_key,
840 Some(discv4_config),
841 Some(discv5_config),
842 None,
843 None,
844 )
845 .await
846 .expect("should start with shared port")
847 }
848
849 #[tokio::test(flavor = "multi_thread")]
850 async fn test_shared_port_setup() {
851 reth_tracing::init_test_tracing();
852
853 let node = start_shared_port_node(0).await;
855
856 assert!(node.discv4.is_some(), "discv4 should be running");
858 assert!(node.discv5.is_some(), "discv5 should be running");
859 }
860
861 #[tokio::test(flavor = "multi_thread")]
862 async fn test_shared_port_discv5_discovery() {
863 reth_tracing::init_test_tracing();
864
865 let mut node_1 = start_shared_port_node(0).await;
866 let mut node_2 = start_shared_port_node(0).await;
867
868 let discv5_enr_1 = node_1.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
869 let discv5_enr_2 = node_2.discv5.as_ref().unwrap().with_discv5(|discv5| discv5.local_enr());
870
871 let peer_id_1 = enr_to_discv4_id(&discv5_enr_1).unwrap();
872 let peer_id_2 = enr_to_discv4_id(&discv5_enr_2).unwrap();
873
874 node_1.add_discv5_node(EnrCombinedKeyWrapper(discv5_enr_2.clone()).into()).unwrap();
878 node_1
879 .discv5
880 .as_ref()
881 .unwrap()
882 .with_discv5(|discv5| discv5.send_ping(discv5_enr_2))
883 .await
884 .unwrap();
885
886 let mut event_1 = None;
889 let mut event_2 = None;
890 let timeout = tokio::time::sleep(std::time::Duration::from_secs(5));
891 tokio::pin!(timeout);
892 loop {
893 tokio::select! {
894 ev = node_1.next(), if event_1.is_none() => {
895 event_1 = ev;
896 }
897 ev = node_2.next(), if event_2.is_none() => {
898 event_2 = ev;
899 }
900 _ = &mut timeout => {
901 panic!("timed out waiting for discv5 discovery events");
902 }
903 }
904 if event_1.is_some() && event_2.is_some() {
905 break;
906 }
907 }
908
909 assert!(matches!(
910 event_1.unwrap(),
911 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued { peer_id, .. })
912 if peer_id == peer_id_2
913 ));
914 assert!(matches!(
915 event_2.unwrap(),
916 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued { peer_id, .. })
917 if peer_id == peer_id_1
918 ));
919 }
920
921 #[tokio::test(flavor = "multi_thread")]
922 async fn test_shared_port_discv4_discovery() {
923 reth_tracing::init_test_tracing();
924
925 let mut node_1 = start_shared_port_node(0).await;
926 let mut node_2 = start_shared_port_node(0).await;
927
928 let enr_1 = node_1.discv4.as_ref().unwrap().node_record();
929 let enr_2 = node_2.discv4.as_ref().unwrap().node_record();
930
931 node_1.add_discv4_node(enr_2);
933
934 let event_1 = node_1.next().await.unwrap();
936 let event_2 = node_2.next().await.unwrap();
937
938 assert_eq!(
939 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
940 peer_id: enr_2.id,
941 addr: PeerAddr::new(enr_2.tcp_addr(), Some(enr_2.udp_addr())),
942 fork_id: None
943 }),
944 event_1
945 );
946 assert_eq!(
947 DiscoveryEvent::NewNode(DiscoveredEvent::EventQueued {
948 peer_id: enr_1.id,
949 addr: PeerAddr::new(enr_1.tcp_addr(), Some(enr_1.udp_addr())),
950 fork_id: None
951 }),
952 event_2
953 );
954 }
955
956 #[tokio::test(flavor = "multi_thread")]
960 async fn test_shared_port_dual_stack() {
961 reth_tracing::init_test_tracing();
962
963 let probe = UdpSocket::bind("0.0.0.0:0").await.expect("probe bind");
965 let port = probe.local_addr().unwrap().port();
966 drop(probe);
967
968 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
969 let v4_addr: SocketAddr = format!("0.0.0.0:{port}").parse().unwrap();
970 let tcp_addr: SocketAddr = "0.0.0.0:30303".parse().unwrap();
971
972 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
973
974 let discv5_listen_config = discv5::ListenConfig::DualStack {
975 ipv4: std::net::Ipv4Addr::UNSPECIFIED,
976 ipv4_port: port,
977 ipv6: std::net::Ipv6Addr::UNSPECIFIED,
978 ipv6_port: port,
979 };
980 let discv5_config = reth_discv5::Config::builder(tcp_addr)
981 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
982 .build();
983
984 Discovery::new(
985 tcp_addr,
986 v4_addr,
987 secret_key,
988 Some(discv4_config),
989 Some(discv5_config),
990 None,
991 None,
992 )
993 .await
994 .expect("discovery should start with shared port + dual-stack");
995 }
996
997 #[tokio::test(flavor = "multi_thread")]
1000 async fn test_shared_port_dual_stack_answers_opposite_family_discv4() {
1001 reth_tracing::init_test_tracing();
1002
1003 let Ok(probe6) = UdpSocket::bind("[::1]:0").await else {
1004 return;
1006 };
1007 drop(probe6);
1008
1009 let ips = [IpAddr::V4(Ipv4Addr::LOCALHOST), IpAddr::V6(Ipv6Addr::LOCALHOST)];
1010 for primary_ip in ips {
1011 for tcp_ip in ips {
1012 assert_shared_port_dual_stack(primary_ip, tcp_ip).await;
1013 }
1014 }
1015 }
1016
1017 async fn assert_shared_port_dual_stack(primary_ip: IpAddr, tcp_ip: IpAddr) {
1018 let secondary_ip = if primary_ip.is_ipv4() {
1019 IpAddr::V6(Ipv6Addr::LOCALHOST)
1020 } else {
1021 IpAddr::V4(Ipv4Addr::LOCALHOST)
1022 };
1023 let probe = UdpSocket::bind((primary_ip, 0)).await.expect("probe bind");
1024 let port = probe.local_addr().unwrap().port();
1025 drop(probe);
1026
1027 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
1028 let node_id = reth_network_peers::pk2id(&secret_key.public_key(SECP256K1));
1029 let discovery_addr = SocketAddr::new(primary_ip, port);
1030 let tcp_addr = SocketAddr::new(tcp_ip, 30303);
1031
1032 let discv4_config = Discv4ConfigBuilder::default().external_ip_resolver(None).build();
1033 let other_port = if port == u16::MAX { port - 1 } else { port + 1 };
1035 let discv5_listen_config = discv5::ListenConfig::DualStack {
1036 ipv4: Ipv4Addr::LOCALHOST,
1037 ipv4_port: if primary_ip.is_ipv4() { port } else { other_port },
1038 ipv6: Ipv6Addr::LOCALHOST,
1039 ipv6_port: if primary_ip.is_ipv6() { port } else { other_port },
1040 };
1041 let discv5_config = reth_discv5::Config::builder(tcp_addr)
1042 .discv5_config(discv5::ConfigBuilder::new(discv5_listen_config).build())
1043 .build();
1044
1045 let _node = Discovery::new(
1046 tcp_addr,
1047 discovery_addr,
1048 secret_key,
1049 Some(discv4_config),
1050 Some(discv5_config),
1051 None,
1052 None,
1053 )
1054 .await
1055 .expect("discovery should start with shared port + dual-stack");
1056
1057 let probe_sk = SecretKey::new(&mut rand_08::thread_rng());
1059 let secondary_node =
1060 NodeRecord { address: secondary_ip, udp_port: port, tcp_port: port, id: node_id };
1061 let probe_config = Discv4ConfigBuilder::default()
1062 .external_ip_resolver(None)
1063 .add_boot_node(secondary_node)
1064 .build();
1065 let probe_addr = SocketAddr::new(secondary_ip, 0);
1066 let probe_enr = NodeRecord::from_secret_key(probe_addr, &probe_sk);
1067 let (_probe, mut probe_service) =
1068 Discv4::bind(probe_addr, probe_enr, probe_sk, probe_config).await.unwrap();
1069 let mut updates = probe_service.update_stream();
1070 probe_service.spawn();
1071
1072 let added = tokio::time::timeout(Duration::from_secs(5), async {
1074 while let Some(update) = updates.next().await {
1075 if let DiscoveryUpdate::Added(record) = update &&
1076 record.id == node_id
1077 {
1078 return true
1079 }
1080 }
1081 false
1082 })
1083 .await
1084 .unwrap_or(false);
1085 assert!(added, "node should answer discv4 over its secondary family");
1086
1087 for peer_ip in [primary_ip, secondary_ip] {
1088 let node = NodeRecord { address: peer_ip, ..secondary_node };
1089 let socket = UdpSocket::bind((peer_ip, 0)).await.unwrap();
1090 let secret_key = SecretKey::new(&mut rand_08::thread_rng());
1091 let record = NodeRecord::from_secret_key(socket.local_addr().unwrap(), &secret_key);
1092 let ping = reth_discv4::proto::Message::Ping(reth_discv4::proto::Ping {
1093 from: record.into(),
1094 to: node.into(),
1095 expire: u32::MAX.into(),
1096 enr_sq: None,
1097 });
1098 let (packet, _) = ping.encode(&secret_key);
1099 socket.send_to(&packet, node.udp_addr()).await.unwrap();
1100 let ping = tokio::time::timeout(Duration::from_secs(5), async {
1101 let mut buf = [0u8; 1280];
1102 loop {
1103 let (len, _) = socket.recv_from(&mut buf).await.unwrap();
1104 if let reth_discv4::proto::Message::Ping(ping) =
1105 reth_discv4::proto::Message::decode(&buf[..len]).unwrap().msg
1106 {
1107 break ping
1108 }
1109 }
1110 })
1111 .await
1112 .expect("node should ping the peer");
1113 assert_eq!(
1114 ping.from.tcp_port,
1115 if peer_ip == tcp_ip { tcp_addr.port() } else { 0 },
1116 "primary discovery IP: {primary_ip}, TCP IP: {tcp_ip}, peer IP: {peer_ip}"
1117 );
1118 }
1119 }
1120}