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 protected_peer_ips: Arc<HashSet<IpAddr>> = Arc::new(
242 block_gossip_peer_ips
243 .iter()
244 .map(|&ip| canonical_socket_addr(SocketAddr::new(ip, 0)).ip())
245 .collect(),
246 );
247
248 let (listen_handshaker, outbound_connector) = {
249 use tower::timeout::TimeoutLayer;
250 let hs_timeout = TimeoutLayer::new(constants::HANDSHAKE_TIMEOUT);
251 let zakura_handshake_connector = zakura_endpoint
252 .as_ref()
253 .map(|endpoint| endpoint.connector());
254
255 let mut hs_builder = peer::Handshake::builder()
256 .with_config(config.clone())
257 .with_inbound_service(inbound_service)
258 .with_inventory_collector(inv_sender)
259 .with_address_book_updater(address_book_updater.clone())
260 .with_advertised_services(advertised_services)
261 .with_user_agent(user_agent)
262 .with_latest_chain_tip(latest_chain_tip.clone())
263 .with_protected_peer_ips(protected_peer_ips)
264 .want_transactions(true);
265
266 if let Some(zakura_handshake_connector) = zakura_handshake_connector {
267 hs_builder = hs_builder.with_zakura_handshake_connector(zakura_handshake_connector);
268 }
269
270 let hs = hs_builder
271 .finish()
272 .expect("configured all required parameters");
273 (
274 hs_timeout.layer(hs.clone()),
275 hs_timeout.layer(peer::Connector::new(hs)),
276 )
277 };
278
279 let (peerset_tx, peerset_rx) =
285 futures::channel::mpsc::channel::<DiscoveredPeer>(config.peerset_total_connection_limit());
286
287 let discovered_peers = peerset_rx.map(|(address, client)| {
288 Result::<_, Infallible>::Ok(Change::Insert(address, client.into()))
289 });
290
291 let (mut demand_tx, demand_rx) =
294 futures::channel::mpsc::channel::<MorePeers>(config.peerset_outbound_connection_limit());
295
296 let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
298
299 let peer_set = PeerSet::new(
301 &config,
302 block_gossip_peer_ips.clone(),
303 discovered_peers,
304 demand_tx.clone(),
305 handle_rx,
306 inv_receiver,
307 bans_receiver.clone(),
308 address_metrics,
309 MinimumPeerVersion::new(latest_chain_tip, &config.network),
310 None,
311 );
312 let peer_set = Buffer::new(BoxService::new(peer_set), constants::PEERSET_BUFFER_SIZE);
313
314 let peer_cache_updater_fut = peer_cache_updater(config.clone(), address_book.clone());
316 let peer_cache_updater_guard = tokio::spawn(peer_cache_updater_fut.in_current_span());
317
318 let mut task_handles = vec![address_book_updater_guard, peer_cache_updater_guard];
319
320 if let Some(tcp_listener) = tcp_listener {
321 let listen_fut = accept_inbound_connections(
325 config.clone(),
326 tcp_listener,
327 constants::MIN_INBOUND_PEER_CONNECTION_INTERVAL,
328 listen_handshaker,
329 peerset_tx.clone(),
330 bans_receiver,
331 block_gossip_peer_ips,
332 );
333 task_handles.push(tokio::spawn(listen_fut.in_current_span()));
334
335 let initial_peers_fut = add_initial_peers(
337 config.clone(),
338 outbound_connector.clone(),
339 peerset_tx.clone(),
340 address_book_updater.clone(),
341 );
342 let initial_peers_join = tokio::spawn(initial_peers_fut.in_current_span());
343
344 let mut candidates = CandidateSet::new(address_book.clone(), peer_set.clone());
346
347 let mut active_outbound_connections = initial_peers_join
349 .wait_for_panics()
350 .await
351 .expect("unexpected error connecting to initial peers");
352 let active_initial_peer_count = active_outbound_connections.update_count();
353
354 info!(
362 ?active_initial_peer_count,
363 "sending initial request for peers"
364 );
365 let _ = candidates.update_initial(active_initial_peer_count).await;
366
367 let demand_count = config
369 .peerset_initial_target_size
370 .saturating_sub(active_outbound_connections.update_count());
371
372 for _ in 0..demand_count {
373 let _ = demand_tx.try_send(MorePeers);
374 }
375
376 let crawl_fut = crawl_and_dial(
378 config.clone(),
379 demand_tx,
380 demand_rx,
381 candidates,
382 outbound_connector,
383 peerset_tx,
384 active_outbound_connections,
385 address_book_updater,
386 );
387 task_handles.push(tokio::spawn(crawl_fut.in_current_span()));
388 } else {
389 task_handles.push(tokio::spawn(
390 async move {
391 let _peerset_tx = peerset_tx;
392 let mut demand_rx = demand_rx;
393
394 while let Some(MorePeers) = demand_rx.next().await {
395 debug!("ignoring legacy peer demand because legacy P2P is disabled");
396 }
397
398 future::pending::<Result<(), BoxError>>().await
399 }
400 .in_current_span(),
401 ));
402 }
403
404 let zakura_supervisor_and_trace = zakura_endpoint
407 .as_ref()
408 .map(|endpoint| (endpoint.supervisor(), endpoint.trace()));
409
410 let returned_zakura_endpoint = zakura_endpoint.clone();
411 if let Some(zakura_endpoint) = zakura_endpoint {
412 task_handles.push(tokio::spawn(async move {
413 let _zakura_endpoint = zakura_endpoint;
414 future::pending::<Result<(), BoxError>>().await
415 }));
416 }
417
418 handle_tx.send(task_handles).unwrap();
419
420 let peer_set = match zakura_supervisor_and_trace {
425 Some((supervisor, trace)) => {
426 let dual_stack = crate::zakura::ZakuraDualStackService::new_with_trace(
427 peer_set,
428 supervisor,
429 config.legacy_p2p(),
430 trace,
431 );
432 Buffer::new(BoxService::new(dual_stack), constants::PEERSET_BUFFER_SIZE)
433 }
434 None => peer_set,
435 };
436
437 (
438 peer_set,
439 address_book,
440 misbehavior_tx,
441 returned_zakura_endpoint,
442 )
443}
444
445#[instrument(skip(config, outbound_connector, peerset_tx, address_book_updater))]
450async fn add_initial_peers<S>(
451 config: Config,
452 outbound_connector: S,
453 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
454 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
455) -> Result<ActiveConnectionCounter, BoxError>
456where
457 S: Service<
458 OutboundConnectorRequest,
459 Response = (PeerSocketAddr, peer::Client),
460 Error = BoxError,
461 > + Clone
462 + Send
463 + 'static,
464 S::Future: Send + 'static,
465{
466 let initial_peers = limit_initial_peers(&config, address_book_updater).await;
467
468 let mut handshake_success_total: usize = 0;
469 let mut handshake_error_total: usize = 0;
470
471 let mut active_outbound_connections = ActiveConnectionCounter::new_counter_with(
472 config.peerset_outbound_connection_limit(),
473 "Outbound Connections",
474 );
475
476 let ipv4_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv4()).count();
478 let ipv6_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv6()).count();
479 info!(
480 ?ipv4_peer_count,
481 ?ipv6_peer_count,
482 "connecting to initial peer set"
483 );
484
485 let mut handshakes: FuturesUnordered<_> = initial_peers
497 .into_iter()
498 .enumerate()
499 .map(|(i, addr)| {
500 let connection_tracker = active_outbound_connections.track_connection();
501 let req = OutboundConnectorRequest {
502 addr,
503 connection_tracker,
504 };
505 let outbound_connector = outbound_connector.clone();
506
507 tokio::spawn(
509 async move {
510 sleep(
514 constants::MIN_OUTBOUND_PEER_CONNECTION_INTERVAL.saturating_mul(i as u32),
515 )
516 .await;
517
518 outbound_connector
521 .oneshot(req)
522 .map_err(move |e| (addr, e))
523 .await
524 }
525 .in_current_span(),
526 )
527 .wait_for_panics()
528 })
529 .collect();
530
531 while let Some(handshake_result) = handshakes.next().await {
532 match handshake_result {
533 Ok(change) => {
534 handshake_success_total += 1;
535 let peer_addr = change.0;
536 debug!(
537 ?handshake_success_total,
538 ?handshake_error_total,
539 peer = %peer_addr.addr_label(config.expose_peer_addresses),
540 "an initial peer handshake succeeded"
541 );
542
543 peerset_tx.send(change).await?;
545 }
546 Err((addr, ref e)) => {
547 handshake_error_total += 1;
548 let addr_label = addr.addr_label(config.expose_peer_addresses);
549
550 let mut expected_error = false;
552 if let Some(io_error) = e.downcast_ref::<tokio::io::Error>() {
553 if io_error.kind() == tokio::io::ErrorKind::AddrNotAvailable {
556 expected_error = true;
557 }
558 }
559
560 if expected_error {
561 debug!(
562 successes = ?handshake_success_total,
563 errors = ?handshake_error_total,
564 peer = %addr_label,
565 ?e,
566 "an initial peer connection failed"
567 );
568 } else {
569 info!(
570 successes = ?handshake_success_total,
571 errors = ?handshake_error_total,
572 peer = %addr_label,
573 %e,
574 "an initial peer connection failed"
575 );
576 }
577 }
578 }
579
580 tokio::task::yield_now().await;
584 }
585
586 let outbound_connections = active_outbound_connections.update_count();
587 info!(
588 ?handshake_success_total,
589 ?handshake_error_total,
590 ?outbound_connections,
591 "finished connecting to initial seed and disk cache peers"
592 );
593
594 Ok(active_outbound_connections)
595}
596
597async fn limit_initial_peers(
605 config: &Config,
606 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
607) -> HashSet<PeerSocketAddr> {
608 let all_peers: HashSet<PeerSocketAddr> = config.initial_peers().await;
609 let mut preferred_peers: BTreeMap<PeerPreference, Vec<PeerSocketAddr>> = BTreeMap::new();
610
611 let all_peers_count = all_peers.len();
612 if all_peers_count > config.peerset_initial_target_size {
613 info!(
614 "limiting the initial peers list from {} to {}",
615 all_peers_count, config.peerset_initial_target_size,
616 );
617 }
618
619 for peer_addr in all_peers {
622 let preference = PeerPreference::new(peer_addr, config.network.clone());
623
624 match preference {
625 Ok(preference) => preferred_peers
626 .entry(preference)
627 .or_default()
628 .push(peer_addr),
629 Err(error) => info!(
630 peer = %peer_addr.addr_label(config.expose_peer_addresses),
631 ?error,
632 "invalid initial peer from DNS seeder, configured IP address, or disk cache",
633 ),
634 }
635 }
636
637 for peer in preferred_peers.values().flatten() {
646 let peer_addr = MetaAddr::new_initial_peer(*peer);
647 let _ = address_book_updater.send(peer_addr).await;
650 }
651
652 let mut initial_peers: HashSet<PeerSocketAddr> = HashSet::new();
655 for better_peers in preferred_peers.values() {
656 let mut better_peers = better_peers.clone();
657 let (chosen_peers, _unused_peers) = better_peers.partial_shuffle(
658 &mut rand::thread_rng(),
659 config.peerset_initial_target_size - initial_peers.len(),
660 );
661
662 initial_peers.extend(chosen_peers.iter());
663
664 if initial_peers.len() >= config.peerset_initial_target_size {
665 break;
666 }
667 }
668
669 initial_peers
670}
671
672#[instrument(skip(config), fields(addr = ?config.listen_addr))]
682pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
683 if let Err(wrong_addr) =
685 address_is_valid_for_inbound_listeners(config.listen_addr, config.network.clone())
686 {
687 warn!(
688 "We are configured with address {} on {:?}, but it could cause network issues. \
689 The default port for {:?} is {}. Error: {wrong_addr:?}",
690 config.listen_addr,
691 config.network,
692 config.network,
693 config.network.default_port(),
694 );
695 }
696
697 info!(
698 "Trying to open Zcash protocol endpoint at {}...",
699 config.listen_addr
700 );
701 let listener_result = TcpListener::bind(config.listen_addr).await;
702
703 let listener = match listener_result {
704 Ok(l) => l,
705 Err(e) => panic!(
706 "Opening Zcash network protocol listener {:?} failed: {e:?}. \
707 Hint: Check if another zakurad or zcashd process is running. \
708 Try changing the network listen_addr in the Zebra config.",
709 config.listen_addr,
710 ),
711 };
712
713 let local_addr = listener
714 .local_addr()
715 .expect("unexpected missing local addr for open listener");
716 info!("Opened Zcash protocol endpoint at {}", local_addr);
717
718 (listener, local_addr)
719}
720
721#[instrument(skip(config, listener, handshaker, peerset_tx), fields(listener_addr = ?listener.local_addr()))]
730async fn accept_inbound_connections<S>(
731 config: Config,
732 listener: TcpListener,
733 min_inbound_peer_connection_interval: Duration,
734 handshaker: S,
735 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
736 bans_receiver: watch::Receiver<Arc<IndexMap<IpAddr, std::time::Instant>>>,
737 zcashd_compat_peer_ips: Vec<IpAddr>,
738) -> Result<(), BoxError>
739where
740 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
741 + Clone,
742 S::Future: Send + 'static,
743{
744 let mut recent_inbound_connections =
745 recent_by_ip::RecentByIp::new(None, Some(config.max_connections_per_ip));
746
747 let mut active_inbound_connections = ActiveConnectionCounter::new_counter_with(
748 config.peerset_inbound_connection_limit(),
749 "Inbound Connections",
750 );
751 let mut active_zcashd_compat_connections =
752 ActiveConnectionCounter::new_counter_with(1, "zcashd-compat Inbound Connection");
753 let zcashd_compat_peer_ips: HashSet<_> = zcashd_compat_peer_ips
754 .into_iter()
755 .map(|ip| canonical_socket_addr(SocketAddr::new(ip, 0)).ip())
756 .collect();
757 let zcashd_compat_reserved_slots = usize::from(
758 !zcashd_compat_peer_ips.is_empty() && config.peerset_inbound_connection_limit() > 0,
759 );
760 let public_inbound_connection_limit = config
761 .peerset_inbound_connection_limit()
762 .saturating_sub(zcashd_compat_reserved_slots);
763
764 let mut handshakes: FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>> =
765 FuturesUnordered::new();
766 handshakes.push(future::pending().boxed());
768
769 loop {
770 let inbound_result = tokio::select! {
772 biased;
773 next_handshake_res = handshakes.next() => match next_handshake_res {
774 Some(()) => continue,
776 None => unreachable!("handshakes never terminates, because it contains a future that never resolves"),
777 },
778
779 inbound_result = listener.accept() => inbound_result,
781 };
782
783 if let Ok((tcp_stream, addr)) = inbound_result {
784 let addr: PeerSocketAddr = addr.into();
785 let addr_label = addr.addr_label(config.expose_peer_addresses);
786
787 if bans_receiver.borrow().clone().contains_key(&addr.ip()) {
788 debug!(peer = %addr_label, "banned inbound connection attempt");
789 std::mem::drop(tcp_stream);
790 continue;
791 }
792
793 let active_public_inbound_connections = active_inbound_connections.update_count();
794 let active_zcashd_compat_inbound_connections =
795 active_zcashd_compat_connections.update_count();
796 let active_total_inbound_connections =
797 active_public_inbound_connections + active_zcashd_compat_inbound_connections;
798 let canonical_ip = canonical_socket_addr(addr.remove_socket_addr_privacy()).ip();
799 let is_zcashd_compat_peer = zcashd_compat_peer_ips.contains(&canonical_ip);
800 let connection_tracker = if is_zcashd_compat_peer {
801 if active_zcashd_compat_inbound_connections >= zcashd_compat_reserved_slots
802 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
803 {
804 std::mem::drop(tcp_stream);
807 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL)
810 .await;
811 continue;
812 }
813
814 active_zcashd_compat_connections.track_connection()
815 } else if active_public_inbound_connections >= public_inbound_connection_limit
816 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
817 || recent_inbound_connections.is_past_limit_or_add(addr.ip())
818 {
819 std::mem::drop(tcp_stream);
822 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
825 continue;
826 } else {
827 active_inbound_connections.track_connection()
828 };
829 debug!(
830 inbound_connections = ?active_total_inbound_connections,
831 ?is_zcashd_compat_peer,
832 "handshaking on an open inbound peer connection"
833 );
834
835 let handshake_task = accept_inbound_handshake(
836 addr,
837 addr_label,
838 handshaker.clone(),
839 tcp_stream,
840 connection_tracker,
841 peerset_tx.clone(),
842 )
843 .await?
844 .wait_for_panics();
845
846 handshakes.push(handshake_task);
847
848 tokio::time::sleep(min_inbound_peer_connection_interval).await;
858 } else {
859 debug!(?inbound_result, "error accepting inbound connection");
862 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
863 }
864
865 tokio::task::yield_now().await;
873 }
874}
875
876#[instrument(
884 skip(addr, addr_label, handshaker, tcp_stream, connection_tracker, peerset_tx),
885 fields(peer = %addr_label)
886)]
887async fn accept_inbound_handshake<S>(
888 addr: PeerSocketAddr,
889 addr_label: String,
890 mut handshaker: S,
891 tcp_stream: TcpStream,
892 connection_tracker: ConnectionTracker,
893 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
894) -> Result<tokio::task::JoinHandle<()>, BoxError>
895where
896 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
897 + Clone,
898 S::Future: Send + 'static,
899{
900 let connected_addr = peer::ConnectedAddr::new_inbound_direct(addr);
901
902 debug!("got incoming connection");
903
904 handshaker.ready().await?;
911
912 let handshake = handshaker.call(HandshakeRequest {
914 data_stream: tcp_stream,
915 connected_addr,
916 connection_tracker,
917 });
918 let mut peerset_tx = peerset_tx.clone();
920
921 let handshake_task = tokio::spawn(
922 async move {
923 let handshake_result = handshake.await;
924
925 if let Ok(client) = handshake_result {
926 let _ = peerset_tx.send((addr, client)).await;
928 } else {
929 debug!(?handshake_result, "error handshaking with inbound peer");
930 }
931 }
932 .in_current_span(),
933 );
934
935 Ok(handshake_task)
936}
937
938enum CrawlerAction {
940 DemandDrop,
942 DemandHandshakeOrCrawl,
946 TimerCrawl { tick: Instant },
948 HandshakeFinished,
950 DemandCrawlFinished,
952 TimerCrawlFinished,
954}
955
956#[allow(clippy::too_many_arguments)]
974#[instrument(
975 skip(
976 config,
977 demand_tx,
978 demand_rx,
979 candidates,
980 outbound_connector,
981 peerset_tx,
982 active_outbound_connections,
983 address_book_updater,
984 ),
985 fields(
986 new_peer_interval = ?config.crawl_new_peer_interval,
987 )
988)]
989async fn crawl_and_dial<C, S>(
990 config: Config,
991 demand_tx: futures::channel::mpsc::Sender<MorePeers>,
992 mut demand_rx: futures::channel::mpsc::Receiver<MorePeers>,
993 candidates: CandidateSet<S>,
994 outbound_connector: C,
995 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
996 mut active_outbound_connections: ActiveConnectionCounter,
997 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
998) -> Result<(), BoxError>
999where
1000 C: Service<
1001 OutboundConnectorRequest,
1002 Response = (PeerSocketAddr, peer::Client),
1003 Error = BoxError,
1004 > + Clone
1005 + Send
1006 + 'static,
1007 C::Future: Send + 'static,
1008 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
1009 S::Future: Send + 'static,
1010{
1011 use CrawlerAction::*;
1012
1013 info!(
1014 crawl_new_peer_interval = ?config.crawl_new_peer_interval,
1015 outbound_connections = ?active_outbound_connections.update_count(),
1016 "starting the peer address crawler",
1017 );
1018
1019 let candidates = Arc::new(futures::lock::Mutex::new(candidates));
1025
1026 let mut handshakes: FuturesUnordered<
1028 Pin<Box<dyn Future<Output = Result<CrawlerAction, BoxError>> + Send>>,
1029 > = FuturesUnordered::new();
1030 handshakes.push(future::pending().boxed());
1033
1034 let mut crawl_timer = tokio::time::interval(config.crawl_new_peer_interval);
1035 crawl_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1038
1039 let mut crawl_timer = IntervalStream::new(crawl_timer).map(|tick| TimerCrawl { tick });
1040
1041 loop {
1046 metrics::gauge!("crawler.in_flight_handshakes").set(
1047 handshakes
1048 .len()
1049 .checked_sub(1)
1050 .expect("the pool always contains an unresolved future") as f64,
1051 );
1052
1053 let crawler_action = tokio::select! {
1054 biased;
1055 next_handshake_res = handshakes.next() => next_handshake_res.expect(
1058 "handshakes never terminates, because it contains a future that never resolves"
1059 ),
1060 next_timer = crawl_timer.next() => Ok(next_timer.expect("timers never terminate")),
1062 next_demand = demand_rx.next() => next_demand.ok_or("demand stream closed, is Zebra shutting down?".into()).map(|MorePeers|{
1068 if active_outbound_connections.update_count() >= config.peerset_outbound_connection_limit() {
1069 DemandDrop
1071 } else {
1072 DemandHandshakeOrCrawl
1073 }
1074 })
1075 };
1076
1077 match crawler_action {
1078 Ok(DemandDrop) => {
1080 trace!("too many open connections or in-flight handshakes, dropping demand signal");
1083 }
1084
1085 Ok(DemandHandshakeOrCrawl) => {
1087 let candidates = candidates.clone();
1088 let outbound_connector = outbound_connector.clone();
1089 let peerset_tx = peerset_tx.clone();
1090 let address_book_updater = address_book_updater.clone();
1091 let demand_tx = demand_tx.clone();
1092 let expose_peer_addresses = config.expose_peer_addresses;
1093
1094 let outbound_connection_tracker = active_outbound_connections.track_connection();
1096 let outbound_connections = active_outbound_connections.update_count();
1097 debug!(?outbound_connections, "opening an outbound peer connection");
1098
1099 let handshake_or_crawl_handle = tokio::spawn(
1108 async move {
1109 let candidate = { candidates.lock().await.next().await };
1116
1117 if let Some(candidate) = candidate {
1118 dial(
1120 candidate,
1121 outbound_connector,
1122 outbound_connection_tracker,
1123 outbound_connections,
1124 peerset_tx,
1125 address_book_updater,
1126 demand_tx,
1127 expose_peer_addresses,
1128 )
1129 .await?;
1130
1131 Ok(HandshakeFinished)
1132 } else {
1133 debug!("demand for peers but no available candidates");
1135
1136 crawl(candidates, demand_tx, false).await?;
1137
1138 Ok(DemandCrawlFinished)
1139 }
1140 }
1141 .in_current_span(),
1142 )
1143 .wait_for_panics();
1144
1145 handshakes.push(handshake_or_crawl_handle);
1146 }
1147 Ok(TimerCrawl { tick }) => {
1148 let candidates = candidates.clone();
1149 let demand_tx = demand_tx.clone();
1150 let should_always_dial = active_outbound_connections.update_count() == 0;
1151
1152 let crawl_handle = tokio::spawn(
1153 async move {
1154 debug!(
1155 ?tick,
1156 "crawling for more peers in response to the crawl timer"
1157 );
1158
1159 crawl(candidates, demand_tx, should_always_dial).await?;
1160
1161 Ok(TimerCrawlFinished)
1162 }
1163 .in_current_span(),
1164 )
1165 .wait_for_panics();
1166
1167 handshakes.push(crawl_handle);
1168 }
1169
1170 Ok(HandshakeFinished) => {
1172 }
1174 Ok(DemandCrawlFinished) => {
1175 trace!("demand-based crawl finished");
1178 }
1179 Ok(TimerCrawlFinished) => {
1180 debug!("timer-based crawl finished");
1181 }
1182
1183 Err(error) => {
1185 info!(?error, "crawler task exiting due to an error");
1186 return Err(error);
1187 }
1188 }
1189
1190 tokio::task::yield_now().await;
1194 }
1195}
1196
1197#[instrument(skip(candidates, demand_tx))]
1200async fn crawl<S>(
1201 candidates: Arc<futures::lock::Mutex<CandidateSet<S>>>,
1202 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1203 should_always_dial: bool,
1204) -> Result<(), BoxError>
1205where
1206 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
1207 S::Future: Send + 'static,
1208{
1209 let result = {
1213 let result = candidates.lock().await.update().await;
1214 std::mem::drop(candidates);
1215 result
1216 };
1217 let more_peers = match result {
1218 Ok(more_peers) => more_peers.or_else(|| should_always_dial.then_some(MorePeers)),
1219 Err(e) => {
1220 info!(
1221 ?e,
1222 "candidate set returned an error, is Zebra shutting down?"
1223 );
1224 return Err(e);
1225 }
1226 };
1227
1228 if let Some(more_peers) = more_peers {
1239 if let Err(send_error) = demand_tx.try_send(more_peers) {
1240 if send_error.is_disconnected() {
1241 return Err(send_error.into());
1243 }
1244 }
1245 }
1246
1247 Ok(())
1248}
1249
1250#[allow(clippy::too_many_arguments)]
1257#[instrument(skip(
1258 candidate,
1259 outbound_connector,
1260 outbound_connection_tracker,
1261 outbound_connections,
1262 peerset_tx,
1263 address_book_updater,
1264 demand_tx,
1265 expose_peer_addresses,
1266), fields(peer = %candidate.addr.addr_label(expose_peer_addresses)))]
1267async fn dial<C>(
1268 candidate: MetaAddr,
1269 mut outbound_connector: C,
1270 outbound_connection_tracker: ConnectionTracker,
1271 outbound_connections: usize,
1272 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
1273 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1274 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1275 expose_peer_addresses: bool,
1276) -> Result<(), BoxError>
1277where
1278 C: Service<
1279 OutboundConnectorRequest,
1280 Response = (PeerSocketAddr, peer::Client),
1281 Error = BoxError,
1282 > + Clone
1283 + Send
1284 + 'static,
1285 C::Future: Send + 'static,
1286{
1287 const MAX_CONNECTIONS_FOR_INFO_LOG: usize = 5;
1290
1291 let addr_label = candidate.addr.addr_label(expose_peer_addresses);
1298 debug!(peer = %addr_label, "attempting outbound connection in response to demand");
1299
1300 let outbound_connector = outbound_connector.ready().await?;
1302
1303 let req = OutboundConnectorRequest {
1304 addr: candidate.addr,
1305 connection_tracker: outbound_connection_tracker,
1306 };
1307
1308 let handshake_result = outbound_connector.call(req).map(Into::into).await;
1310
1311 match handshake_result {
1312 Ok((address, client)) => {
1313 debug!(peer = %addr_label, "successfully dialed new peer");
1314
1315 peerset_tx.send((address, client)).await?;
1317 }
1318 Err(error) => {
1320 let neutral_disconnect = error
1321 .downcast_ref::<peer::HandshakeError>()
1322 .is_some_and(peer::HandshakeError::is_neutral_disconnect);
1323
1324 if outbound_connections <= MAX_CONNECTIONS_FOR_INFO_LOG && !cfg!(test) {
1327 info!(?error, peer = %addr_label, "failed to make outbound connection to peer");
1328 } else {
1329 debug!(?error, peer = %addr_label, "failed to make outbound connection to peer");
1330 }
1331
1332 if !neutral_disconnect {
1333 report_failed(
1334 address_book_updater.clone(),
1335 candidate,
1336 expose_peer_addresses,
1337 )
1338 .await;
1339 }
1340
1341 if let Err(send_error) = demand_tx.try_send(MorePeers) {
1348 if send_error.is_disconnected() {
1349 return Err(send_error.into());
1351 }
1352 }
1353 }
1354 }
1355
1356 Ok(())
1357}
1358
1359#[instrument(
1361 skip(address_book_updater, addr, expose_peer_addresses),
1362 fields(peer = %addr.addr.addr_label(expose_peer_addresses))
1363)]
1364async fn report_failed(
1365 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1366 addr: MetaAddr,
1367 expose_peer_addresses: bool,
1368) {
1369 let addr = MetaAddr::new_errored(addr.addr, None);
1371
1372 let _ = address_book_updater.send(addr).await;
1374}