1use std::{
8 collections::{BTreeMap, HashMap, HashSet},
9 convert::Infallible,
10 net::{IpAddr, SocketAddr},
11 pin::Pin,
12 sync::Arc,
13 time::Duration,
14};
15
16use futures::{
17 future::{self, FutureExt},
18 sink::SinkExt,
19 stream::{FuturesUnordered, StreamExt},
20 Future, TryFutureExt,
21};
22use indexmap::IndexMap;
23use rand::seq::SliceRandom;
24use tokio::{
25 net::{TcpListener, TcpStream},
26 sync::{broadcast, mpsc, watch},
27 time::{sleep, Instant},
28};
29use tokio_stream::wrappers::IntervalStream;
30use tower::{
31 buffer::Buffer, discover::Change, layer::Layer, util::BoxService, Service, ServiceExt,
32};
33use tracing_futures::Instrument;
34
35use zakura_chain::{chain_tip::ChainTip, diagnostic::task::WaitForPanics};
36
37use crate::{
38 address_book_updater::{AddressBookUpdater, MIN_CHANNEL_SIZE},
39 constants,
40 meta_addr::{MetaAddr, MetaAddrChange},
41 peer::{
42 self, address_is_valid_for_inbound_listeners, HandshakeRequest, MinimumPeerVersion,
43 OutboundConnectorRequest, PeerPreference,
44 },
45 peer_cache_updater::peer_cache_updater,
46 peer_set::{set::MorePeers, ActiveConnectionCounter, CandidateSet, ConnectionTracker, PeerSet},
47 protocol::external::{canonical_socket_addr, types::PeerServices},
48 AddressBook, BoxError, Config, PeerSocketAddr, Request, Response,
49};
50
51#[cfg(test)]
52mod tests;
53
54mod recent_by_ip;
55
56type DiscoveredPeer = (PeerSocketAddr, peer::Client);
65
66pub async fn init<S, C>(
99 config: Config,
100 inbound_service: S,
101 latest_chain_tip: C,
102 user_agent: String,
103 advertised_services: PeerServices,
104) -> (
105 Buffer<BoxService<Request, Response, BoxError>, Request>,
106 Arc<std::sync::Mutex<AddressBook>>,
107 mpsc::Sender<(PeerSocketAddr, u32)>,
108)
109where
110 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
111 S::Future: Send + 'static,
112 C: ChainTip + Clone + Send + Sync + 'static,
113{
114 let (peer_set, address_book, misbehavior_tx, _zakura_endpoint) = init_with_zakura_header_sync(
115 config,
116 inbound_service,
117 latest_chain_tip,
118 user_agent,
119 advertised_services,
120 Vec::new(),
121 None,
122 )
123 .await;
124
125 (peer_set, address_book, misbehavior_tx)
126}
127
128pub async fn init_with_zakura_header_sync<S, C>(
130 config: Config,
131 inbound_service: S,
132 latest_chain_tip: C,
133 user_agent: String,
134 advertised_services: PeerServices,
135 block_gossip_peer_ips: Vec<IpAddr>,
136 header_sync_driver_startup: Option<crate::zakura::ZakuraHeaderSyncDriverStartup>,
137) -> (
138 Buffer<BoxService<Request, Response, BoxError>, Request>,
139 Arc<std::sync::Mutex<AddressBook>>,
140 mpsc::Sender<(PeerSocketAddr, u32)>,
141 Option<crate::zakura::ZakuraEndpoint>,
142)
143where
144 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
145 S::Future: Send + 'static,
146 C: ChainTip + Clone + Send + Sync + 'static,
147{
148 let (tcp_listener, listen_addr) = if config.legacy_p2p() {
149 let (tcp_listener, listen_addr) = open_listener(&config.clone()).await;
150 (Some(tcp_listener), listen_addr)
151 } else {
152 info!("legacy P2P disabled; not opening Zcash protocol listener");
153 (None, config.listen_addr)
154 };
155 let inbound_for_zakura_sink = inbound_service.clone();
159 let zakura_endpoint = crate::zakura::spawn_zakura_endpoint_with_header_sync_driver(
160 &config,
161 move |supervisor, trace| {
162 Arc::new(crate::zakura::LegacyGossipSink::spawn_with_trace(
163 inbound_for_zakura_sink,
164 supervisor,
165 trace,
166 )) as Arc<dyn crate::zakura::Service>
167 },
168 header_sync_driver_startup,
169 )
170 .await
171 .expect("Zakura endpoint should start when P2P v2 is enabled");
172
173 let (
174 address_book,
175 bans_receiver,
176 address_book_updater,
177 address_metrics,
178 address_book_updater_guard,
179 ) = AddressBookUpdater::spawn(&config, listen_addr, advertised_services);
180
181 let (misbehavior_tx, mut misbehavior_rx) = mpsc::channel(
182 config
185 .peerset_total_connection_limit()
186 .max(MIN_CHANNEL_SIZE),
187 );
188
189 let misbehaviour_updater = address_book_updater.clone();
190 tokio::spawn(
191 async move {
192 let mut misbehaviors: HashMap<PeerSocketAddr, u32> = HashMap::new();
193 let mut flush_timer =
196 IntervalStream::new(tokio::time::interval(Duration::from_secs(30)));
197
198 loop {
199 tokio::select! {
200 msg = misbehavior_rx.recv() => match msg {
201 Some((peer_addr, score_increment)) => *misbehaviors
202 .entry(peer_addr)
203 .or_default()
204 += score_increment,
205 None => break,
206 },
207
208 _ = flush_timer.next() => {
209 for (addr, score_increment) in misbehaviors.drain() {
210 let _ = misbehaviour_updater
211 .send(MetaAddr::new_misbehavior(addr, score_increment))
212 .await;
213 }
214 },
215 };
216 }
217
218 tracing::warn!("exiting misbehavior update batch task");
219 }
220 .in_current_span(),
221 );
222
223 let (inv_sender, inv_receiver) = broadcast::channel(config.peerset_total_connection_limit());
231
232 let (listen_handshaker, outbound_connector) = {
237 use tower::timeout::TimeoutLayer;
238 let hs_timeout = TimeoutLayer::new(constants::HANDSHAKE_TIMEOUT);
239 let zakura_handshake_connector = zakura_endpoint
240 .as_ref()
241 .map(|endpoint| endpoint.connector());
242
243 let mut hs_builder = peer::Handshake::builder()
244 .with_config(config.clone())
245 .with_inbound_service(inbound_service)
246 .with_inventory_collector(inv_sender)
247 .with_address_book_updater(address_book_updater.clone())
248 .with_advertised_services(advertised_services)
249 .with_user_agent(user_agent)
250 .with_latest_chain_tip(latest_chain_tip.clone())
251 .want_transactions(true);
252
253 if let Some(zakura_handshake_connector) = zakura_handshake_connector {
254 hs_builder = hs_builder.with_zakura_handshake_connector(zakura_handshake_connector);
255 }
256
257 let hs = hs_builder
258 .finish()
259 .expect("configured all required parameters");
260 (
261 hs_timeout.layer(hs.clone()),
262 hs_timeout.layer(peer::Connector::new(hs)),
263 )
264 };
265
266 let (peerset_tx, peerset_rx) =
272 futures::channel::mpsc::channel::<DiscoveredPeer>(config.peerset_total_connection_limit());
273
274 let discovered_peers = peerset_rx.map(|(address, client)| {
275 Result::<_, Infallible>::Ok(Change::Insert(address, client.into()))
276 });
277
278 let (mut demand_tx, demand_rx) =
281 futures::channel::mpsc::channel::<MorePeers>(config.peerset_outbound_connection_limit());
282
283 let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
285
286 let peer_set = PeerSet::new(
288 &config,
289 block_gossip_peer_ips.clone(),
290 discovered_peers,
291 demand_tx.clone(),
292 handle_rx,
293 inv_receiver,
294 bans_receiver.clone(),
295 address_metrics,
296 MinimumPeerVersion::new(latest_chain_tip, &config.network),
297 None,
298 );
299 let peer_set = Buffer::new(BoxService::new(peer_set), constants::PEERSET_BUFFER_SIZE);
300
301 let peer_cache_updater_fut = peer_cache_updater(config.clone(), address_book.clone());
303 let peer_cache_updater_guard = tokio::spawn(peer_cache_updater_fut.in_current_span());
304
305 let mut task_handles = vec![address_book_updater_guard, peer_cache_updater_guard];
306
307 if let Some(tcp_listener) = tcp_listener {
308 let listen_fut = accept_inbound_connections(
312 config.clone(),
313 tcp_listener,
314 constants::MIN_INBOUND_PEER_CONNECTION_INTERVAL,
315 listen_handshaker,
316 peerset_tx.clone(),
317 bans_receiver,
318 block_gossip_peer_ips,
319 );
320 task_handles.push(tokio::spawn(listen_fut.in_current_span()));
321
322 let initial_peers_fut = add_initial_peers(
324 config.clone(),
325 outbound_connector.clone(),
326 peerset_tx.clone(),
327 address_book_updater.clone(),
328 );
329 let initial_peers_join = tokio::spawn(initial_peers_fut.in_current_span());
330
331 let mut candidates = CandidateSet::new(address_book.clone(), peer_set.clone());
333
334 let mut active_outbound_connections = initial_peers_join
336 .wait_for_panics()
337 .await
338 .expect("unexpected error connecting to initial peers");
339 let active_initial_peer_count = active_outbound_connections.update_count();
340
341 info!(
349 ?active_initial_peer_count,
350 "sending initial request for peers"
351 );
352 let _ = candidates.update_initial(active_initial_peer_count).await;
353
354 let demand_count = config
356 .peerset_initial_target_size
357 .saturating_sub(active_outbound_connections.update_count());
358
359 for _ in 0..demand_count {
360 let _ = demand_tx.try_send(MorePeers);
361 }
362
363 let crawl_fut = crawl_and_dial(
365 config.clone(),
366 demand_tx,
367 demand_rx,
368 candidates,
369 outbound_connector,
370 peerset_tx,
371 active_outbound_connections,
372 address_book_updater,
373 );
374 task_handles.push(tokio::spawn(crawl_fut.in_current_span()));
375 } else {
376 task_handles.push(tokio::spawn(
377 async move {
378 let _peerset_tx = peerset_tx;
379 let mut demand_rx = demand_rx;
380
381 while let Some(MorePeers) = demand_rx.next().await {
382 debug!("ignoring legacy peer demand because legacy P2P is disabled");
383 }
384
385 future::pending::<Result<(), BoxError>>().await
386 }
387 .in_current_span(),
388 ));
389 }
390
391 let zakura_supervisor_and_trace = zakura_endpoint
394 .as_ref()
395 .map(|endpoint| (endpoint.supervisor(), endpoint.trace()));
396
397 let returned_zakura_endpoint = zakura_endpoint.clone();
398 if let Some(zakura_endpoint) = zakura_endpoint {
399 task_handles.push(tokio::spawn(async move {
400 let _zakura_endpoint = zakura_endpoint;
401 future::pending::<Result<(), BoxError>>().await
402 }));
403 }
404
405 handle_tx.send(task_handles).unwrap();
406
407 let peer_set = match zakura_supervisor_and_trace {
412 Some((supervisor, trace)) => {
413 let dual_stack = crate::zakura::ZakuraDualStackService::new_with_trace(
414 peer_set,
415 supervisor,
416 config.legacy_p2p(),
417 trace,
418 );
419 Buffer::new(BoxService::new(dual_stack), constants::PEERSET_BUFFER_SIZE)
420 }
421 None => peer_set,
422 };
423
424 (
425 peer_set,
426 address_book,
427 misbehavior_tx,
428 returned_zakura_endpoint,
429 )
430}
431
432#[instrument(skip(config, outbound_connector, peerset_tx, address_book_updater))]
437async fn add_initial_peers<S>(
438 config: Config,
439 outbound_connector: S,
440 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
441 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
442) -> Result<ActiveConnectionCounter, BoxError>
443where
444 S: Service<
445 OutboundConnectorRequest,
446 Response = (PeerSocketAddr, peer::Client),
447 Error = BoxError,
448 > + Clone
449 + Send
450 + 'static,
451 S::Future: Send + 'static,
452{
453 let initial_peers = limit_initial_peers(&config, address_book_updater).await;
454
455 let mut handshake_success_total: usize = 0;
456 let mut handshake_error_total: usize = 0;
457
458 let mut active_outbound_connections = ActiveConnectionCounter::new_counter_with(
459 config.peerset_outbound_connection_limit(),
460 "Outbound Connections",
461 );
462
463 let ipv4_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv4()).count();
465 let ipv6_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv6()).count();
466 info!(
467 ?ipv4_peer_count,
468 ?ipv6_peer_count,
469 "connecting to initial peer set"
470 );
471
472 let mut handshakes: FuturesUnordered<_> = initial_peers
484 .into_iter()
485 .enumerate()
486 .map(|(i, addr)| {
487 let connection_tracker = active_outbound_connections.track_connection();
488 let req = OutboundConnectorRequest {
489 addr,
490 connection_tracker,
491 };
492 let outbound_connector = outbound_connector.clone();
493
494 tokio::spawn(
496 async move {
497 sleep(
501 constants::MIN_OUTBOUND_PEER_CONNECTION_INTERVAL.saturating_mul(i as u32),
502 )
503 .await;
504
505 outbound_connector
508 .oneshot(req)
509 .map_err(move |e| (addr, e))
510 .await
511 }
512 .in_current_span(),
513 )
514 .wait_for_panics()
515 })
516 .collect();
517
518 while let Some(handshake_result) = handshakes.next().await {
519 match handshake_result {
520 Ok(change) => {
521 handshake_success_total += 1;
522 debug!(
523 ?handshake_success_total,
524 ?handshake_error_total,
525 ?change,
526 "an initial peer handshake succeeded"
527 );
528
529 peerset_tx.send(change).await?;
531 }
532 Err((addr, ref e)) => {
533 handshake_error_total += 1;
534
535 let mut expected_error = false;
537 if let Some(io_error) = e.downcast_ref::<tokio::io::Error>() {
538 if io_error.kind() == tokio::io::ErrorKind::AddrNotAvailable {
541 expected_error = true;
542 }
543 }
544
545 if expected_error {
546 debug!(
547 successes = ?handshake_success_total,
548 errors = ?handshake_error_total,
549 ?addr,
550 ?e,
551 "an initial peer connection failed"
552 );
553 } else {
554 info!(
555 successes = ?handshake_success_total,
556 errors = ?handshake_error_total,
557 ?addr,
558 %e,
559 "an initial peer connection failed"
560 );
561 }
562 }
563 }
564
565 tokio::task::yield_now().await;
569 }
570
571 let outbound_connections = active_outbound_connections.update_count();
572 info!(
573 ?handshake_success_total,
574 ?handshake_error_total,
575 ?outbound_connections,
576 "finished connecting to initial seed and disk cache peers"
577 );
578
579 Ok(active_outbound_connections)
580}
581
582async fn limit_initial_peers(
590 config: &Config,
591 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
592) -> HashSet<PeerSocketAddr> {
593 let all_peers: HashSet<PeerSocketAddr> = config.initial_peers().await;
594 let mut preferred_peers: BTreeMap<PeerPreference, Vec<PeerSocketAddr>> = BTreeMap::new();
595
596 let all_peers_count = all_peers.len();
597 if all_peers_count > config.peerset_initial_target_size {
598 info!(
599 "limiting the initial peers list from {} to {}",
600 all_peers_count, config.peerset_initial_target_size,
601 );
602 }
603
604 for peer_addr in all_peers {
607 let preference = PeerPreference::new(peer_addr, config.network.clone());
608
609 match preference {
610 Ok(preference) => preferred_peers
611 .entry(preference)
612 .or_default()
613 .push(peer_addr),
614 Err(error) => info!(
615 ?peer_addr,
616 ?error,
617 "invalid initial peer from DNS seeder, configured IP address, or disk cache",
618 ),
619 }
620 }
621
622 for peer in preferred_peers.values().flatten() {
631 let peer_addr = MetaAddr::new_initial_peer(*peer);
632 let _ = address_book_updater.send(peer_addr).await;
635 }
636
637 let mut initial_peers: HashSet<PeerSocketAddr> = HashSet::new();
640 for better_peers in preferred_peers.values() {
641 let mut better_peers = better_peers.clone();
642 let (chosen_peers, _unused_peers) = better_peers.partial_shuffle(
643 &mut rand::thread_rng(),
644 config.peerset_initial_target_size - initial_peers.len(),
645 );
646
647 initial_peers.extend(chosen_peers.iter());
648
649 if initial_peers.len() >= config.peerset_initial_target_size {
650 break;
651 }
652 }
653
654 initial_peers
655}
656
657#[instrument(skip(config), fields(addr = ?config.listen_addr))]
667pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
668 if let Err(wrong_addr) =
670 address_is_valid_for_inbound_listeners(config.listen_addr, config.network.clone())
671 {
672 warn!(
673 "We are configured with address {} on {:?}, but it could cause network issues. \
674 The default port for {:?} is {}. Error: {wrong_addr:?}",
675 config.listen_addr,
676 config.network,
677 config.network,
678 config.network.default_port(),
679 );
680 }
681
682 info!(
683 "Trying to open Zcash protocol endpoint at {}...",
684 config.listen_addr
685 );
686 let listener_result = TcpListener::bind(config.listen_addr).await;
687
688 let listener = match listener_result {
689 Ok(l) => l,
690 Err(e) => panic!(
691 "Opening Zcash network protocol listener {:?} failed: {e:?}. \
692 Hint: Check if another zakurad or zcashd process is running. \
693 Try changing the network listen_addr in the Zebra config.",
694 config.listen_addr,
695 ),
696 };
697
698 let local_addr = listener
699 .local_addr()
700 .expect("unexpected missing local addr for open listener");
701 info!("Opened Zcash protocol endpoint at {}", local_addr);
702
703 (listener, local_addr)
704}
705
706#[instrument(skip(config, listener, handshaker, peerset_tx), fields(listener_addr = ?listener.local_addr()))]
715async fn accept_inbound_connections<S>(
716 config: Config,
717 listener: TcpListener,
718 min_inbound_peer_connection_interval: Duration,
719 handshaker: S,
720 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
721 bans_receiver: watch::Receiver<Arc<IndexMap<IpAddr, std::time::Instant>>>,
722 zcashd_compat_peer_ips: Vec<IpAddr>,
723) -> Result<(), BoxError>
724where
725 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
726 + Clone,
727 S::Future: Send + 'static,
728{
729 let mut recent_inbound_connections =
730 recent_by_ip::RecentByIp::new(None, Some(config.max_connections_per_ip));
731
732 let mut active_inbound_connections = ActiveConnectionCounter::new_counter_with(
733 config.peerset_inbound_connection_limit(),
734 "Inbound Connections",
735 );
736 let mut active_zcashd_compat_connections =
737 ActiveConnectionCounter::new_counter_with(1, "zcashd-compat Inbound Connection");
738 let zcashd_compat_peer_ips: HashSet<_> = zcashd_compat_peer_ips
739 .into_iter()
740 .map(|ip| canonical_socket_addr(SocketAddr::new(ip, 0)).ip())
741 .collect();
742 let zcashd_compat_reserved_slots = usize::from(
743 !zcashd_compat_peer_ips.is_empty() && config.peerset_inbound_connection_limit() > 0,
744 );
745 let public_inbound_connection_limit = config
746 .peerset_inbound_connection_limit()
747 .saturating_sub(zcashd_compat_reserved_slots);
748
749 let mut handshakes: FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>> =
750 FuturesUnordered::new();
751 handshakes.push(future::pending().boxed());
753
754 loop {
755 let inbound_result = tokio::select! {
757 biased;
758 next_handshake_res = handshakes.next() => match next_handshake_res {
759 Some(()) => continue,
761 None => unreachable!("handshakes never terminates, because it contains a future that never resolves"),
762 },
763
764 inbound_result = listener.accept() => inbound_result,
766 };
767
768 if let Ok((tcp_stream, addr)) = inbound_result {
769 let addr: PeerSocketAddr = addr.into();
770
771 if bans_receiver.borrow().clone().contains_key(&addr.ip()) {
772 debug!(?addr, "banned inbound connection attempt");
773 std::mem::drop(tcp_stream);
774 continue;
775 }
776
777 let active_public_inbound_connections = active_inbound_connections.update_count();
778 let active_zcashd_compat_inbound_connections =
779 active_zcashd_compat_connections.update_count();
780 let active_total_inbound_connections =
781 active_public_inbound_connections + active_zcashd_compat_inbound_connections;
782 let canonical_ip = canonical_socket_addr(addr.remove_socket_addr_privacy()).ip();
783 let is_zcashd_compat_peer = zcashd_compat_peer_ips.contains(&canonical_ip);
784 let connection_tracker = if is_zcashd_compat_peer {
785 if active_zcashd_compat_inbound_connections >= zcashd_compat_reserved_slots
786 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
787 {
788 std::mem::drop(tcp_stream);
791 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL)
794 .await;
795 continue;
796 }
797
798 active_zcashd_compat_connections.track_connection()
799 } else if active_public_inbound_connections >= public_inbound_connection_limit
800 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
801 || recent_inbound_connections.is_past_limit_or_add(addr.ip())
802 {
803 std::mem::drop(tcp_stream);
806 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
809 continue;
810 } else {
811 active_inbound_connections.track_connection()
812 };
813 debug!(
814 inbound_connections = ?active_total_inbound_connections,
815 ?is_zcashd_compat_peer,
816 "handshaking on an open inbound peer connection"
817 );
818
819 let handshake_task = accept_inbound_handshake(
820 addr,
821 handshaker.clone(),
822 tcp_stream,
823 connection_tracker,
824 peerset_tx.clone(),
825 )
826 .await?
827 .wait_for_panics();
828
829 handshakes.push(handshake_task);
830
831 tokio::time::sleep(min_inbound_peer_connection_interval).await;
841 } else {
842 debug!(?inbound_result, "error accepting inbound connection");
845 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
846 }
847
848 tokio::task::yield_now().await;
856 }
857}
858
859#[instrument(skip(handshaker, tcp_stream, connection_tracker, peerset_tx))]
867async fn accept_inbound_handshake<S>(
868 addr: PeerSocketAddr,
869 mut handshaker: S,
870 tcp_stream: TcpStream,
871 connection_tracker: ConnectionTracker,
872 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
873) -> Result<tokio::task::JoinHandle<()>, BoxError>
874where
875 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
876 + Clone,
877 S::Future: Send + 'static,
878{
879 let connected_addr = peer::ConnectedAddr::new_inbound_direct(addr);
880
881 debug!("got incoming connection");
882
883 handshaker.ready().await?;
890
891 let handshake = handshaker.call(HandshakeRequest {
893 data_stream: tcp_stream,
894 connected_addr,
895 connection_tracker,
896 });
897 let mut peerset_tx = peerset_tx.clone();
899
900 let handshake_task = tokio::spawn(
901 async move {
902 let handshake_result = handshake.await;
903
904 if let Ok(client) = handshake_result {
905 let _ = peerset_tx.send((addr, client)).await;
907 } else {
908 debug!(?handshake_result, "error handshaking with inbound peer");
909 }
910 }
911 .in_current_span(),
912 );
913
914 Ok(handshake_task)
915}
916
917enum CrawlerAction {
919 DemandDrop,
921 DemandHandshakeOrCrawl,
925 TimerCrawl { tick: Instant },
927 HandshakeFinished,
929 DemandCrawlFinished,
931 TimerCrawlFinished,
933}
934
935#[allow(clippy::too_many_arguments)]
953#[instrument(
954 skip(
955 config,
956 demand_tx,
957 demand_rx,
958 candidates,
959 outbound_connector,
960 peerset_tx,
961 active_outbound_connections,
962 address_book_updater,
963 ),
964 fields(
965 new_peer_interval = ?config.crawl_new_peer_interval,
966 )
967)]
968async fn crawl_and_dial<C, S>(
969 config: Config,
970 demand_tx: futures::channel::mpsc::Sender<MorePeers>,
971 mut demand_rx: futures::channel::mpsc::Receiver<MorePeers>,
972 candidates: CandidateSet<S>,
973 outbound_connector: C,
974 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
975 mut active_outbound_connections: ActiveConnectionCounter,
976 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
977) -> Result<(), BoxError>
978where
979 C: Service<
980 OutboundConnectorRequest,
981 Response = (PeerSocketAddr, peer::Client),
982 Error = BoxError,
983 > + Clone
984 + Send
985 + 'static,
986 C::Future: Send + 'static,
987 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
988 S::Future: Send + 'static,
989{
990 use CrawlerAction::*;
991
992 info!(
993 crawl_new_peer_interval = ?config.crawl_new_peer_interval,
994 outbound_connections = ?active_outbound_connections.update_count(),
995 "starting the peer address crawler",
996 );
997
998 let candidates = Arc::new(futures::lock::Mutex::new(candidates));
1004
1005 let mut handshakes: FuturesUnordered<
1007 Pin<Box<dyn Future<Output = Result<CrawlerAction, BoxError>> + Send>>,
1008 > = FuturesUnordered::new();
1009 handshakes.push(future::pending().boxed());
1012
1013 let mut crawl_timer = tokio::time::interval(config.crawl_new_peer_interval);
1014 crawl_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1017
1018 let mut crawl_timer = IntervalStream::new(crawl_timer).map(|tick| TimerCrawl { tick });
1019
1020 loop {
1025 metrics::gauge!("crawler.in_flight_handshakes").set(
1026 handshakes
1027 .len()
1028 .checked_sub(1)
1029 .expect("the pool always contains an unresolved future") as f64,
1030 );
1031
1032 let crawler_action = tokio::select! {
1033 biased;
1034 next_handshake_res = handshakes.next() => next_handshake_res.expect(
1037 "handshakes never terminates, because it contains a future that never resolves"
1038 ),
1039 next_timer = crawl_timer.next() => Ok(next_timer.expect("timers never terminate")),
1041 next_demand = demand_rx.next() => next_demand.ok_or("demand stream closed, is Zebra shutting down?".into()).map(|MorePeers|{
1047 if active_outbound_connections.update_count() >= config.peerset_outbound_connection_limit() {
1048 DemandDrop
1050 } else {
1051 DemandHandshakeOrCrawl
1052 }
1053 })
1054 };
1055
1056 match crawler_action {
1057 Ok(DemandDrop) => {
1059 trace!("too many open connections or in-flight handshakes, dropping demand signal");
1062 }
1063
1064 Ok(DemandHandshakeOrCrawl) => {
1066 let candidates = candidates.clone();
1067 let outbound_connector = outbound_connector.clone();
1068 let peerset_tx = peerset_tx.clone();
1069 let address_book_updater = address_book_updater.clone();
1070 let demand_tx = demand_tx.clone();
1071
1072 let outbound_connection_tracker = active_outbound_connections.track_connection();
1074 let outbound_connections = active_outbound_connections.update_count();
1075 debug!(?outbound_connections, "opening an outbound peer connection");
1076
1077 let handshake_or_crawl_handle = tokio::spawn(
1086 async move {
1087 let candidate = { candidates.lock().await.next().await };
1094
1095 if let Some(candidate) = candidate {
1096 dial(
1098 candidate,
1099 outbound_connector,
1100 outbound_connection_tracker,
1101 outbound_connections,
1102 peerset_tx,
1103 address_book_updater,
1104 demand_tx,
1105 )
1106 .await?;
1107
1108 Ok(HandshakeFinished)
1109 } else {
1110 debug!("demand for peers but no available candidates");
1112
1113 crawl(candidates, demand_tx, false).await?;
1114
1115 Ok(DemandCrawlFinished)
1116 }
1117 }
1118 .in_current_span(),
1119 )
1120 .wait_for_panics();
1121
1122 handshakes.push(handshake_or_crawl_handle);
1123 }
1124 Ok(TimerCrawl { tick }) => {
1125 let candidates = candidates.clone();
1126 let demand_tx = demand_tx.clone();
1127 let should_always_dial = active_outbound_connections.update_count() == 0;
1128
1129 let crawl_handle = tokio::spawn(
1130 async move {
1131 debug!(
1132 ?tick,
1133 "crawling for more peers in response to the crawl timer"
1134 );
1135
1136 crawl(candidates, demand_tx, should_always_dial).await?;
1137
1138 Ok(TimerCrawlFinished)
1139 }
1140 .in_current_span(),
1141 )
1142 .wait_for_panics();
1143
1144 handshakes.push(crawl_handle);
1145 }
1146
1147 Ok(HandshakeFinished) => {
1149 }
1151 Ok(DemandCrawlFinished) => {
1152 trace!("demand-based crawl finished");
1155 }
1156 Ok(TimerCrawlFinished) => {
1157 debug!("timer-based crawl finished");
1158 }
1159
1160 Err(error) => {
1162 info!(?error, "crawler task exiting due to an error");
1163 return Err(error);
1164 }
1165 }
1166
1167 tokio::task::yield_now().await;
1171 }
1172}
1173
1174#[instrument(skip(candidates, demand_tx))]
1177async fn crawl<S>(
1178 candidates: Arc<futures::lock::Mutex<CandidateSet<S>>>,
1179 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1180 should_always_dial: bool,
1181) -> Result<(), BoxError>
1182where
1183 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
1184 S::Future: Send + 'static,
1185{
1186 let result = {
1190 let result = candidates.lock().await.update().await;
1191 std::mem::drop(candidates);
1192 result
1193 };
1194 let more_peers = match result {
1195 Ok(more_peers) => more_peers.or_else(|| should_always_dial.then_some(MorePeers)),
1196 Err(e) => {
1197 info!(
1198 ?e,
1199 "candidate set returned an error, is Zebra shutting down?"
1200 );
1201 return Err(e);
1202 }
1203 };
1204
1205 if let Some(more_peers) = more_peers {
1216 if let Err(send_error) = demand_tx.try_send(more_peers) {
1217 if send_error.is_disconnected() {
1218 return Err(send_error.into());
1220 }
1221 }
1222 }
1223
1224 Ok(())
1225}
1226
1227#[instrument(skip(
1234 outbound_connector,
1235 outbound_connection_tracker,
1236 outbound_connections,
1237 peerset_tx,
1238 address_book_updater,
1239 demand_tx
1240))]
1241async fn dial<C>(
1242 candidate: MetaAddr,
1243 mut outbound_connector: C,
1244 outbound_connection_tracker: ConnectionTracker,
1245 outbound_connections: usize,
1246 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
1247 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1248 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1249) -> Result<(), BoxError>
1250where
1251 C: Service<
1252 OutboundConnectorRequest,
1253 Response = (PeerSocketAddr, peer::Client),
1254 Error = BoxError,
1255 > + Clone
1256 + Send
1257 + 'static,
1258 C::Future: Send + 'static,
1259{
1260 const MAX_CONNECTIONS_FOR_INFO_LOG: usize = 5;
1263
1264 debug!(?candidate.addr, "attempting outbound connection in response to demand");
1271
1272 let outbound_connector = outbound_connector.ready().await?;
1274
1275 let req = OutboundConnectorRequest {
1276 addr: candidate.addr,
1277 connection_tracker: outbound_connection_tracker,
1278 };
1279
1280 let handshake_result = outbound_connector.call(req).map(Into::into).await;
1282
1283 match handshake_result {
1284 Ok((address, client)) => {
1285 debug!(?candidate.addr, "successfully dialed new peer");
1286
1287 peerset_tx.send((address, client)).await?;
1289 }
1290 Err(error) => {
1292 let neutral_disconnect = error
1293 .downcast_ref::<peer::HandshakeError>()
1294 .is_some_and(peer::HandshakeError::is_neutral_disconnect);
1295
1296 if outbound_connections <= MAX_CONNECTIONS_FOR_INFO_LOG && !cfg!(test) {
1299 info!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1300 } else {
1301 debug!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1302 }
1303
1304 if !neutral_disconnect {
1305 report_failed(address_book_updater.clone(), candidate).await;
1306 }
1307
1308 if let Err(send_error) = demand_tx.try_send(MorePeers) {
1315 if send_error.is_disconnected() {
1316 return Err(send_error.into());
1318 }
1319 }
1320 }
1321 }
1322
1323 Ok(())
1324}
1325
1326#[instrument(skip(address_book_updater))]
1328async fn report_failed(
1329 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1330 addr: MetaAddr,
1331) {
1332 let addr = MetaAddr::new_errored(addr.addr, None);
1334
1335 let _ = address_book_updater.send(addr).await;
1337}