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 zebra_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,
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) -> (
104 Buffer<BoxService<Request, Response, BoxError>, Request>,
105 Arc<std::sync::Mutex<AddressBook>>,
106 mpsc::Sender<(PeerSocketAddr, u32)>,
107)
108where
109 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
110 S::Future: Send + 'static,
111 C: ChainTip + Clone + Send + Sync + 'static,
112{
113 init_with_block_gossip_peer_ips(
114 config,
115 inbound_service,
116 latest_chain_tip,
117 user_agent,
118 Vec::new(),
119 )
120 .await
121}
122
123pub async fn init_with_block_gossip_peer_ips<S, C>(
131 config: Config,
132 inbound_service: S,
133 latest_chain_tip: C,
134 user_agent: String,
135 block_gossip_peer_ips: Vec<IpAddr>,
136) -> (
137 Buffer<BoxService<Request, Response, BoxError>, Request>,
138 Arc<std::sync::Mutex<AddressBook>>,
139 mpsc::Sender<(PeerSocketAddr, u32)>,
140)
141where
142 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
143 S::Future: Send + 'static,
144 C: ChainTip + Clone + Send + Sync + 'static,
145{
146 let (tcp_listener, listen_addr) = open_listener(&config.clone()).await;
147
148 let (
149 address_book,
150 bans_receiver,
151 address_book_updater,
152 address_metrics,
153 address_book_updater_guard,
154 ) = AddressBookUpdater::spawn(&config, listen_addr);
155
156 let (misbehavior_tx, mut misbehavior_rx) = mpsc::channel(
157 config
160 .peerset_total_connection_limit()
161 .max(MIN_CHANNEL_SIZE),
162 );
163
164 let misbehaviour_updater = address_book_updater.clone();
165 tokio::spawn(
166 async move {
167 let mut misbehaviors: HashMap<PeerSocketAddr, u32> = HashMap::new();
168 let mut flush_timer =
171 IntervalStream::new(tokio::time::interval(Duration::from_secs(30)));
172
173 loop {
174 tokio::select! {
175 msg = misbehavior_rx.recv() => match msg {
176 Some((peer_addr, score_increment)) => *misbehaviors
177 .entry(peer_addr)
178 .or_default()
179 += score_increment,
180 None => break,
181 },
182
183 _ = flush_timer.next() => {
184 for (addr, score_increment) in misbehaviors.drain() {
185 let _ = misbehaviour_updater
186 .send(MetaAddr::new_misbehavior(addr, score_increment))
187 .await;
188 }
189 },
190 };
191 }
192
193 tracing::warn!("exiting misbehavior update batch task");
194 }
195 .in_current_span(),
196 );
197
198 let (inv_sender, inv_receiver) = broadcast::channel(config.peerset_total_connection_limit());
206
207 let (listen_handshaker, outbound_connector) = {
212 use tower::timeout::TimeoutLayer;
213 let hs_timeout = TimeoutLayer::new(constants::HANDSHAKE_TIMEOUT);
214 use crate::protocol::external::types::PeerServices;
215 let hs = peer::Handshake::builder()
216 .with_config(config.clone())
217 .with_inbound_service(inbound_service)
218 .with_inventory_collector(inv_sender)
219 .with_address_book_updater(address_book_updater.clone())
220 .with_advertised_services(PeerServices::NODE_NETWORK)
221 .with_user_agent(user_agent)
222 .with_latest_chain_tip(latest_chain_tip.clone())
223 .want_transactions(true)
224 .finish()
225 .expect("configured all required parameters");
226 (
227 hs_timeout.layer(hs.clone()),
228 hs_timeout.layer(peer::Connector::new(hs)),
229 )
230 };
231
232 let (peerset_tx, peerset_rx) =
238 futures::channel::mpsc::channel::<DiscoveredPeer>(config.peerset_total_connection_limit());
239
240 let discovered_peers = peerset_rx.map(|(address, client)| {
241 Result::<_, Infallible>::Ok(Change::Insert(address, client.into()))
242 });
243
244 let (mut demand_tx, demand_rx) =
247 futures::channel::mpsc::channel::<MorePeers>(config.peerset_outbound_connection_limit());
248
249 let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
251
252 let peer_set = PeerSet::new(
254 &config,
255 block_gossip_peer_ips.clone(),
256 discovered_peers,
257 demand_tx.clone(),
258 handle_rx,
259 inv_receiver,
260 bans_receiver.clone(),
261 address_metrics,
262 MinimumPeerVersion::new(latest_chain_tip, &config.network),
263 None,
264 );
265 let peer_set = Buffer::new(BoxService::new(peer_set), constants::PEERSET_BUFFER_SIZE);
266
267 let listen_fut = accept_inbound_connections(
271 config.clone(),
272 tcp_listener,
273 constants::MIN_INBOUND_PEER_CONNECTION_INTERVAL,
274 listen_handshaker,
275 peerset_tx.clone(),
276 bans_receiver,
277 block_gossip_peer_ips,
278 );
279 let listen_guard = tokio::spawn(listen_fut.in_current_span());
280
281 let initial_peers_fut = add_initial_peers(
283 config.clone(),
284 outbound_connector.clone(),
285 peerset_tx.clone(),
286 address_book_updater.clone(),
287 );
288 let initial_peers_join = tokio::spawn(initial_peers_fut.in_current_span());
289
290 let mut candidates = CandidateSet::new(address_book.clone(), peer_set.clone());
292
293 let mut active_outbound_connections = initial_peers_join
295 .wait_for_panics()
296 .await
297 .expect("unexpected error connecting to initial peers");
298 let active_initial_peer_count = active_outbound_connections.update_count();
299
300 info!(
308 ?active_initial_peer_count,
309 "sending initial request for peers"
310 );
311 let _ = candidates.update_initial(active_initial_peer_count).await;
312
313 let demand_count = config
315 .peerset_initial_target_size
316 .saturating_sub(active_outbound_connections.update_count());
317
318 for _ in 0..demand_count {
319 let _ = demand_tx.try_send(MorePeers);
320 }
321
322 let crawl_fut = crawl_and_dial(
324 config.clone(),
325 demand_tx,
326 demand_rx,
327 candidates,
328 outbound_connector,
329 peerset_tx,
330 active_outbound_connections,
331 address_book_updater,
332 );
333 let crawl_guard = tokio::spawn(crawl_fut.in_current_span());
334
335 let peer_cache_updater_fut = peer_cache_updater(config, address_book.clone());
337 let peer_cache_updater_guard = tokio::spawn(peer_cache_updater_fut.in_current_span());
338
339 handle_tx
340 .send(vec![
341 listen_guard,
342 crawl_guard,
343 address_book_updater_guard,
344 peer_cache_updater_guard,
345 ])
346 .unwrap();
347
348 (peer_set, address_book, misbehavior_tx)
349}
350
351#[instrument(skip(config, outbound_connector, peerset_tx, address_book_updater))]
356async fn add_initial_peers<S>(
357 config: Config,
358 outbound_connector: S,
359 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
360 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
361) -> Result<ActiveConnectionCounter, BoxError>
362where
363 S: Service<
364 OutboundConnectorRequest,
365 Response = (PeerSocketAddr, peer::Client),
366 Error = BoxError,
367 > + Clone
368 + Send
369 + 'static,
370 S::Future: Send + 'static,
371{
372 let initial_peers = limit_initial_peers(&config, address_book_updater).await;
373
374 let mut handshake_success_total: usize = 0;
375 let mut handshake_error_total: usize = 0;
376
377 let mut active_outbound_connections = ActiveConnectionCounter::new_counter_with(
378 config.peerset_outbound_connection_limit(),
379 "Outbound Connections",
380 );
381
382 let ipv4_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv4()).count();
384 let ipv6_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv6()).count();
385 info!(
386 ?ipv4_peer_count,
387 ?ipv6_peer_count,
388 "connecting to initial peer set"
389 );
390
391 let mut handshakes: FuturesUnordered<_> = initial_peers
403 .into_iter()
404 .enumerate()
405 .map(|(i, addr)| {
406 let connection_tracker = active_outbound_connections.track_connection();
407 let req = OutboundConnectorRequest {
408 addr,
409 connection_tracker,
410 };
411 let outbound_connector = outbound_connector.clone();
412
413 tokio::spawn(
415 async move {
416 sleep(
420 constants::MIN_OUTBOUND_PEER_CONNECTION_INTERVAL.saturating_mul(i as u32),
421 )
422 .await;
423
424 outbound_connector
427 .oneshot(req)
428 .map_err(move |e| (addr, e))
429 .await
430 }
431 .in_current_span(),
432 )
433 .wait_for_panics()
434 })
435 .collect();
436
437 while let Some(handshake_result) = handshakes.next().await {
438 match handshake_result {
439 Ok(change) => {
440 handshake_success_total += 1;
441 debug!(
442 ?handshake_success_total,
443 ?handshake_error_total,
444 ?change,
445 "an initial peer handshake succeeded"
446 );
447
448 peerset_tx.send(change).await?;
450 }
451 Err((addr, ref e)) => {
452 handshake_error_total += 1;
453
454 let mut expected_error = false;
456 if let Some(io_error) = e.downcast_ref::<tokio::io::Error>() {
457 if io_error.kind() == tokio::io::ErrorKind::AddrNotAvailable {
460 expected_error = true;
461 }
462 }
463
464 if expected_error {
465 debug!(
466 successes = ?handshake_success_total,
467 errors = ?handshake_error_total,
468 ?addr,
469 ?e,
470 "an initial peer connection failed"
471 );
472 } else {
473 info!(
474 successes = ?handshake_success_total,
475 errors = ?handshake_error_total,
476 ?addr,
477 %e,
478 "an initial peer connection failed"
479 );
480 }
481 }
482 }
483
484 tokio::task::yield_now().await;
488 }
489
490 let outbound_connections = active_outbound_connections.update_count();
491 info!(
492 ?handshake_success_total,
493 ?handshake_error_total,
494 ?outbound_connections,
495 "finished connecting to initial seed and disk cache peers"
496 );
497
498 Ok(active_outbound_connections)
499}
500
501async fn limit_initial_peers(
509 config: &Config,
510 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
511) -> HashSet<PeerSocketAddr> {
512 let all_peers: HashSet<PeerSocketAddr> = config.initial_peers().await;
513 let mut preferred_peers: BTreeMap<PeerPreference, Vec<PeerSocketAddr>> = BTreeMap::new();
514
515 let all_peers_count = all_peers.len();
516 if all_peers_count > config.peerset_initial_target_size {
517 info!(
518 "limiting the initial peers list from {} to {}",
519 all_peers_count, config.peerset_initial_target_size,
520 );
521 }
522
523 for peer_addr in all_peers {
526 let preference = PeerPreference::new(peer_addr, config.network.clone());
527
528 match preference {
529 Ok(preference) => preferred_peers
530 .entry(preference)
531 .or_default()
532 .push(peer_addr),
533 Err(error) => info!(
534 ?peer_addr,
535 ?error,
536 "invalid initial peer from DNS seeder, configured IP address, or disk cache",
537 ),
538 }
539 }
540
541 for peer in preferred_peers.values().flatten() {
550 let peer_addr = MetaAddr::new_initial_peer(*peer);
551 let _ = address_book_updater.send(peer_addr).await;
554 }
555
556 let mut initial_peers: HashSet<PeerSocketAddr> = HashSet::new();
559 for better_peers in preferred_peers.values() {
560 let mut better_peers = better_peers.clone();
561 let (chosen_peers, _unused_peers) = better_peers.partial_shuffle(
562 &mut rand::thread_rng(),
563 config.peerset_initial_target_size - initial_peers.len(),
564 );
565
566 initial_peers.extend(chosen_peers.iter());
567
568 if initial_peers.len() >= config.peerset_initial_target_size {
569 break;
570 }
571 }
572
573 initial_peers
574}
575
576#[instrument(skip(config), fields(addr = ?config.listen_addr))]
586pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
587 if let Err(wrong_addr) =
589 address_is_valid_for_inbound_listeners(config.listen_addr, config.network.clone())
590 {
591 warn!(
592 "We are configured with address {} on {:?}, but it could cause network issues. \
593 The default port for {:?} is {}. Error: {wrong_addr:?}",
594 config.listen_addr,
595 config.network,
596 config.network,
597 config.network.default_port(),
598 );
599 }
600
601 info!(
602 "Trying to open Zcash protocol endpoint at {}...",
603 config.listen_addr
604 );
605 let listener_result = TcpListener::bind(config.listen_addr).await;
606
607 let listener = match listener_result {
608 Ok(l) => l,
609 Err(e) => panic!(
610 "Opening Zcash network protocol listener {:?} failed: {e:?}. \
611 Hint: Check if another zebrad or zcashd process is running. \
612 Try changing the network listen_addr in the Zebra config.",
613 config.listen_addr,
614 ),
615 };
616
617 let local_addr = listener
618 .local_addr()
619 .expect("unexpected missing local addr for open listener");
620 info!("Opened Zcash protocol endpoint at {}", local_addr);
621
622 (listener, local_addr)
623}
624
625#[instrument(skip(config, listener, handshaker, peerset_tx), fields(listener_addr = ?listener.local_addr()))]
634async fn accept_inbound_connections<S>(
635 config: Config,
636 listener: TcpListener,
637 min_inbound_peer_connection_interval: Duration,
638 handshaker: S,
639 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
640 bans_receiver: watch::Receiver<Arc<IndexMap<IpAddr, std::time::Instant>>>,
641 zcashd_compat_peer_ips: Vec<IpAddr>,
642) -> Result<(), BoxError>
643where
644 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
645 + Clone,
646 S::Future: Send + 'static,
647{
648 let mut recent_inbound_connections =
649 recent_by_ip::RecentByIp::new(None, Some(config.max_connections_per_ip));
650
651 let mut active_inbound_connections = ActiveConnectionCounter::new_counter_with(
652 config.peerset_inbound_connection_limit(),
653 "Inbound Connections",
654 );
655 let zcashd_compat_peer_ips: HashSet<_> = zcashd_compat_peer_ips
656 .into_iter()
657 .map(|ip| canonical_socket_addr(SocketAddr::new(ip, 0)).ip())
658 .collect();
659 let zcashd_compat_reserved_slots = zcashd_compat_peer_ips
666 .len()
667 .min(config.peerset_inbound_connection_limit().saturating_sub(1));
668 let mut active_zcashd_compat_connections = ActiveConnectionCounter::new_counter_with(
669 zcashd_compat_reserved_slots,
670 "zcashd-compat Inbound Connections",
671 );
672 let public_inbound_connection_limit = config
673 .peerset_inbound_connection_limit()
674 .saturating_sub(zcashd_compat_reserved_slots);
675
676 let mut handshakes: FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>> =
677 FuturesUnordered::new();
678 handshakes.push(future::pending().boxed());
680
681 loop {
682 let inbound_result = tokio::select! {
684 biased;
685 next_handshake_res = handshakes.next() => match next_handshake_res {
686 Some(()) => continue,
688 None => unreachable!("handshakes never terminates, because it contains a future that never resolves"),
689 },
690
691 inbound_result = listener.accept() => inbound_result,
693 };
694
695 if let Ok((tcp_stream, addr)) = inbound_result {
696 let addr: PeerSocketAddr = addr.into();
697
698 let canonical_ip = canonical_socket_addr(addr.remove_socket_addr_privacy()).ip();
705 let bans = bans_receiver.borrow().clone();
706 if bans.contains_key(&addr.ip()) || bans.contains_key(&canonical_ip) {
707 debug!(?addr, "banned inbound connection attempt");
708 std::mem::drop(tcp_stream);
709 continue;
710 }
711
712 let active_public_inbound_connections = active_inbound_connections.update_count();
713 let active_zcashd_compat_inbound_connections =
714 active_zcashd_compat_connections.update_count();
715 let active_total_inbound_connections =
716 active_public_inbound_connections + active_zcashd_compat_inbound_connections;
717 let is_zcashd_compat_peer = zcashd_compat_peer_ips.contains(&canonical_ip);
718
719 let use_reserved_slot = is_zcashd_compat_peer
727 && active_zcashd_compat_inbound_connections < zcashd_compat_reserved_slots
728 && active_total_inbound_connections < config.peerset_inbound_connection_limit();
729
730 let connection_tracker = if use_reserved_slot {
731 active_zcashd_compat_connections.track_connection()
732 } else if active_public_inbound_connections >= public_inbound_connection_limit
733 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
734 || recent_inbound_connections.is_past_limit_or_add(addr.ip())
735 {
736 std::mem::drop(tcp_stream);
739 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
742 continue;
743 } else {
744 active_inbound_connections.track_connection()
745 };
746 debug!(
747 inbound_connections = ?active_total_inbound_connections,
748 ?is_zcashd_compat_peer,
749 "handshaking on an open inbound peer connection"
750 );
751
752 let handshake_task = accept_inbound_handshake(
753 addr,
754 handshaker.clone(),
755 tcp_stream,
756 connection_tracker,
757 peerset_tx.clone(),
758 )
759 .await?
760 .wait_for_panics();
761
762 handshakes.push(handshake_task);
763
764 tokio::time::sleep(min_inbound_peer_connection_interval).await;
774 } else {
775 debug!(?inbound_result, "error accepting inbound connection");
778 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
779 }
780
781 tokio::task::yield_now().await;
789 }
790}
791
792#[instrument(skip(handshaker, tcp_stream, connection_tracker, peerset_tx))]
800async fn accept_inbound_handshake<S>(
801 addr: PeerSocketAddr,
802 mut handshaker: S,
803 tcp_stream: TcpStream,
804 connection_tracker: ConnectionTracker,
805 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
806) -> Result<tokio::task::JoinHandle<()>, BoxError>
807where
808 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
809 + Clone,
810 S::Future: Send + 'static,
811{
812 let connected_addr = peer::ConnectedAddr::new_inbound_direct(addr);
813
814 debug!("got incoming connection");
815
816 handshaker.ready().await?;
823
824 let handshake = handshaker.call(HandshakeRequest {
826 data_stream: tcp_stream,
827 connected_addr,
828 connection_tracker,
829 });
830 let mut peerset_tx = peerset_tx.clone();
832
833 let handshake_task = tokio::spawn(
834 async move {
835 let handshake_result = handshake.await;
836
837 if let Ok(client) = handshake_result {
838 let _ = peerset_tx.send((addr, client)).await;
840 } else {
841 debug!(?handshake_result, "error handshaking with inbound peer");
842 }
843 }
844 .in_current_span(),
845 );
846
847 Ok(handshake_task)
848}
849
850enum CrawlerAction {
852 DemandDrop,
854 DemandHandshakeOrCrawl,
858 TimerCrawl { tick: Instant },
860 HandshakeFinished,
862 DemandCrawlFinished,
864 TimerCrawlFinished,
866}
867
868#[allow(clippy::too_many_arguments)]
886#[instrument(
887 skip(
888 config,
889 demand_tx,
890 demand_rx,
891 candidates,
892 outbound_connector,
893 peerset_tx,
894 active_outbound_connections,
895 address_book_updater,
896 ),
897 fields(
898 new_peer_interval = ?config.crawl_new_peer_interval,
899 )
900)]
901async fn crawl_and_dial<C, S>(
902 config: Config,
903 demand_tx: futures::channel::mpsc::Sender<MorePeers>,
904 mut demand_rx: futures::channel::mpsc::Receiver<MorePeers>,
905 candidates: CandidateSet<S>,
906 outbound_connector: C,
907 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
908 mut active_outbound_connections: ActiveConnectionCounter,
909 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
910) -> Result<(), BoxError>
911where
912 C: Service<
913 OutboundConnectorRequest,
914 Response = (PeerSocketAddr, peer::Client),
915 Error = BoxError,
916 > + Clone
917 + Send
918 + 'static,
919 C::Future: Send + 'static,
920 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
921 S::Future: Send + 'static,
922{
923 use CrawlerAction::*;
924
925 info!(
926 crawl_new_peer_interval = ?config.crawl_new_peer_interval,
927 outbound_connections = ?active_outbound_connections.update_count(),
928 "starting the peer address crawler",
929 );
930
931 let candidates = Arc::new(futures::lock::Mutex::new(candidates));
937
938 let mut handshakes: FuturesUnordered<
940 Pin<Box<dyn Future<Output = Result<CrawlerAction, BoxError>> + Send>>,
941 > = FuturesUnordered::new();
942 handshakes.push(future::pending().boxed());
945
946 let mut crawl_timer = tokio::time::interval(config.crawl_new_peer_interval);
947 crawl_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
950
951 let mut crawl_timer = IntervalStream::new(crawl_timer).map(|tick| TimerCrawl { tick });
952
953 loop {
958 metrics::gauge!("crawler.in_flight_handshakes").set(
959 handshakes
960 .len()
961 .checked_sub(1)
962 .expect("the pool always contains an unresolved future") as f64,
963 );
964
965 let crawler_action = tokio::select! {
966 biased;
967 next_handshake_res = handshakes.next() => next_handshake_res.expect(
970 "handshakes never terminates, because it contains a future that never resolves"
971 ),
972 next_timer = crawl_timer.next() => Ok(next_timer.expect("timers never terminate")),
974 next_demand = demand_rx.next() => next_demand.ok_or("demand stream closed, is Zebra shutting down?".into()).map(|MorePeers|{
980 if active_outbound_connections.update_count() >= config.peerset_outbound_connection_limit() {
981 DemandDrop
983 } else {
984 DemandHandshakeOrCrawl
985 }
986 })
987 };
988
989 match crawler_action {
990 Ok(DemandDrop) => {
992 trace!("too many open connections or in-flight handshakes, dropping demand signal");
995 }
996
997 Ok(DemandHandshakeOrCrawl) => {
999 let candidates = candidates.clone();
1000 let outbound_connector = outbound_connector.clone();
1001 let peerset_tx = peerset_tx.clone();
1002 let address_book_updater = address_book_updater.clone();
1003 let demand_tx = demand_tx.clone();
1004
1005 let outbound_connection_tracker = active_outbound_connections.track_connection();
1007 let outbound_connections = active_outbound_connections.update_count();
1008 debug!(?outbound_connections, "opening an outbound peer connection");
1009
1010 let handshake_or_crawl_handle = tokio::spawn(
1019 async move {
1020 let candidate = { candidates.lock().await.next().await };
1027
1028 if let Some(candidate) = candidate {
1029 dial(
1031 candidate,
1032 outbound_connector,
1033 outbound_connection_tracker,
1034 outbound_connections,
1035 peerset_tx,
1036 address_book_updater,
1037 demand_tx,
1038 )
1039 .await?;
1040
1041 Ok(HandshakeFinished)
1042 } else {
1043 debug!("demand for peers but no available candidates");
1045
1046 crawl(candidates, demand_tx, false).await?;
1047
1048 Ok(DemandCrawlFinished)
1049 }
1050 }
1051 .in_current_span(),
1052 )
1053 .wait_for_panics();
1054
1055 handshakes.push(handshake_or_crawl_handle);
1056 }
1057 Ok(TimerCrawl { tick }) => {
1058 let candidates = candidates.clone();
1059 let demand_tx = demand_tx.clone();
1060 let should_always_dial = active_outbound_connections.update_count() == 0;
1061
1062 let crawl_handle = tokio::spawn(
1063 async move {
1064 debug!(
1065 ?tick,
1066 "crawling for more peers in response to the crawl timer"
1067 );
1068
1069 crawl(candidates, demand_tx, should_always_dial).await?;
1070
1071 Ok(TimerCrawlFinished)
1072 }
1073 .in_current_span(),
1074 )
1075 .wait_for_panics();
1076
1077 handshakes.push(crawl_handle);
1078 }
1079
1080 Ok(HandshakeFinished) => {
1082 }
1084 Ok(DemandCrawlFinished) => {
1085 trace!("demand-based crawl finished");
1088 }
1089 Ok(TimerCrawlFinished) => {
1090 debug!("timer-based crawl finished");
1091 }
1092
1093 Err(error) => {
1095 info!(?error, "crawler task exiting due to an error");
1096 return Err(error);
1097 }
1098 }
1099
1100 tokio::task::yield_now().await;
1104 }
1105}
1106
1107#[instrument(skip(candidates, demand_tx))]
1110async fn crawl<S>(
1111 candidates: Arc<futures::lock::Mutex<CandidateSet<S>>>,
1112 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1113 should_always_dial: bool,
1114) -> Result<(), BoxError>
1115where
1116 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
1117 S::Future: Send + 'static,
1118{
1119 let result = {
1123 let result = candidates.lock().await.update().await;
1124 std::mem::drop(candidates);
1125 result
1126 };
1127 let more_peers = match result {
1128 Ok(more_peers) => more_peers.or_else(|| should_always_dial.then_some(MorePeers)),
1129 Err(e) => {
1130 info!(
1131 ?e,
1132 "candidate set returned an error, is Zebra shutting down?"
1133 );
1134 return Err(e);
1135 }
1136 };
1137
1138 if let Some(more_peers) = more_peers {
1149 if let Err(send_error) = demand_tx.try_send(more_peers) {
1150 if send_error.is_disconnected() {
1151 return Err(send_error.into());
1153 }
1154 }
1155 }
1156
1157 Ok(())
1158}
1159
1160#[instrument(skip(
1167 outbound_connector,
1168 outbound_connection_tracker,
1169 outbound_connections,
1170 peerset_tx,
1171 address_book_updater,
1172 demand_tx
1173))]
1174async fn dial<C>(
1175 candidate: MetaAddr,
1176 mut outbound_connector: C,
1177 outbound_connection_tracker: ConnectionTracker,
1178 outbound_connections: usize,
1179 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
1180 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1181 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1182) -> Result<(), BoxError>
1183where
1184 C: Service<
1185 OutboundConnectorRequest,
1186 Response = (PeerSocketAddr, peer::Client),
1187 Error = BoxError,
1188 > + Clone
1189 + Send
1190 + 'static,
1191 C::Future: Send + 'static,
1192{
1193 const MAX_CONNECTIONS_FOR_INFO_LOG: usize = 5;
1196
1197 debug!(?candidate.addr, "attempting outbound connection in response to demand");
1204
1205 let outbound_connector = outbound_connector.ready().await?;
1207
1208 let req = OutboundConnectorRequest {
1209 addr: candidate.addr,
1210 connection_tracker: outbound_connection_tracker,
1211 };
1212
1213 let handshake_result = outbound_connector.call(req).map(Into::into).await;
1215
1216 match handshake_result {
1217 Ok((address, client)) => {
1218 debug!(?candidate.addr, "successfully dialed new peer");
1219
1220 peerset_tx.send((address, client)).await?;
1222 }
1223 Err(error) => {
1225 if outbound_connections <= MAX_CONNECTIONS_FOR_INFO_LOG && !cfg!(test) {
1228 info!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1229 } else {
1230 debug!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1231 }
1232 report_failed(address_book_updater.clone(), candidate).await;
1233
1234 if let Err(send_error) = demand_tx.try_send(MorePeers) {
1241 if send_error.is_disconnected() {
1242 return Err(send_error.into());
1244 }
1245 }
1246 }
1247 }
1248
1249 Ok(())
1250}
1251
1252#[instrument(skip(address_book_updater))]
1254async fn report_failed(
1255 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1256 addr: MetaAddr,
1257) {
1258 let addr = MetaAddr::new_errored(addr.addr, None);
1260
1261 let _ = address_book_updater.send(addr).await;
1263}