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, parameters::Network};
36
37use crate::{
38 address_book_updater::{AddressBookUpdater, MIN_CHANNEL_SIZE},
39 connection_metrics::{
40 network_kind_label, record_connection_attempt_finished, record_connection_attempt_started,
41 record_inbound_connection_rejected, ConnectionDirection,
42 },
43 constants,
44 meta_addr::{MetaAddr, MetaAddrChange},
45 peer::{
46 self, address_is_valid_for_inbound_listeners, HandshakeRequest, MinimumPeerVersion,
47 OutboundConnectorRequest, PeerPreference,
48 },
49 peer_cache_updater::peer_cache_updater,
50 peer_set::{set::MorePeers, ActiveConnectionCounter, CandidateSet, ConnectionTracker, PeerSet},
51 protocol::external::{canonical_peer_addr, canonical_socket_addr},
52 AddressBook, BoxError, Config, PeerSocketAddr, Request, Response,
53};
54
55#[cfg(test)]
56mod tests;
57
58mod recent_by_ip;
59
60type DiscoveredPeer = (PeerSocketAddr, peer::Client);
69
70pub async fn init<S, C>(
103 config: Config,
104 inbound_service: S,
105 latest_chain_tip: C,
106 user_agent: String,
107) -> (
108 Buffer<BoxService<Request, Response, BoxError>, Request>,
109 Arc<std::sync::Mutex<AddressBook>>,
110 mpsc::Sender<(PeerSocketAddr, u32)>,
111)
112where
113 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
114 S::Future: Send + 'static,
115 C: ChainTip + Clone + Send + Sync + 'static,
116{
117 init_with_block_gossip_peer_ips(
118 config,
119 inbound_service,
120 latest_chain_tip,
121 user_agent,
122 Vec::new(),
123 )
124 .await
125}
126
127pub async fn init_with_block_gossip_peer_ips<S, C>(
135 config: Config,
136 inbound_service: S,
137 latest_chain_tip: C,
138 user_agent: String,
139 block_gossip_peer_ips: Vec<IpAddr>,
140) -> (
141 Buffer<BoxService<Request, Response, BoxError>, Request>,
142 Arc<std::sync::Mutex<AddressBook>>,
143 mpsc::Sender<(PeerSocketAddr, u32)>,
144)
145where
146 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
147 S::Future: Send + 'static,
148 C: ChainTip + Clone + Send + Sync + 'static,
149{
150 let (tcp_listener, listen_addr) = open_listener(&config.clone()).await;
151
152 let (
153 address_book,
154 bans_receiver,
155 address_book_updater,
156 address_metrics,
157 address_book_updater_guard,
158 ) = AddressBookUpdater::spawn(&config, listen_addr);
159
160 let (misbehavior_tx, mut misbehavior_rx) = mpsc::channel(
161 config
164 .peerset_total_connection_limit()
165 .max(MIN_CHANNEL_SIZE),
166 );
167
168 let misbehaviour_updater = address_book_updater.clone();
169 tokio::spawn(
170 async move {
171 let mut misbehaviors: HashMap<PeerSocketAddr, u32> = HashMap::new();
172 let mut flush_timer =
175 IntervalStream::new(tokio::time::interval(constants::MISBEHAVIOR_FLUSH_INTERVAL));
176
177 loop {
178 tokio::select! {
179 msg = misbehavior_rx.recv() => match msg {
180 Some((peer_addr, score_increment)) => *misbehaviors
181 .entry(peer_addr)
182 .or_default()
183 += score_increment,
184 None => break,
185 },
186
187 _ = flush_timer.next() => {
188 for (addr, score_increment) in misbehaviors.drain() {
189 let _ = misbehaviour_updater
190 .send(MetaAddr::new_misbehavior(addr, score_increment))
191 .await;
192 }
193 },
194 };
195 }
196
197 tracing::warn!("exiting misbehavior update batch task");
198 }
199 .in_current_span(),
200 );
201
202 let (inv_sender, inv_receiver) = broadcast::channel(config.peerset_total_connection_limit());
210
211 let (listen_handshaker, outbound_connector) = {
216 use tower::timeout::TimeoutLayer;
217 let hs_timeout = TimeoutLayer::new(constants::HANDSHAKE_TIMEOUT);
218 use crate::protocol::external::types::PeerServices;
219 let hs = peer::Handshake::builder()
220 .with_config(config.clone())
221 .with_inbound_service(inbound_service)
222 .with_inventory_collector(inv_sender)
223 .with_address_book_updater(address_book_updater.clone())
224 .with_advertised_services(PeerServices::NODE_NETWORK)
225 .with_user_agent(user_agent)
226 .with_latest_chain_tip(latest_chain_tip.clone())
227 .want_transactions(true)
228 .finish()
229 .expect("configured all required parameters");
230 (
231 hs_timeout.layer(hs.clone()),
232 hs_timeout.layer(peer::Connector::new(hs)),
233 )
234 };
235
236 let (peerset_tx, peerset_rx) =
242 futures::channel::mpsc::channel::<DiscoveredPeer>(config.peerset_total_connection_limit());
243
244 let discovered_peers = peerset_rx.map(|(address, client)| {
245 Result::<_, Infallible>::Ok(Change::Insert(address, client.into()))
246 });
247
248 let (mut demand_tx, demand_rx) =
251 futures::channel::mpsc::channel::<MorePeers>(config.peerset_outbound_connection_limit());
252
253 let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
255
256 let peer_set = PeerSet::new(
258 &config,
259 block_gossip_peer_ips.clone(),
260 discovered_peers,
261 demand_tx.clone(),
262 handle_rx,
263 inv_receiver,
264 bans_receiver.clone(),
265 address_metrics,
266 MinimumPeerVersion::new(latest_chain_tip, &config.network),
267 None,
268 );
269 let peer_set = Buffer::new(BoxService::new(peer_set), constants::PEERSET_BUFFER_SIZE);
270
271 let listen_fut = accept_inbound_connections(
275 config.clone(),
276 tcp_listener,
277 constants::MIN_INBOUND_PEER_CONNECTION_INTERVAL,
278 listen_handshaker,
279 peerset_tx.clone(),
280 bans_receiver,
281 block_gossip_peer_ips,
282 );
283 let listen_guard = tokio::spawn(listen_fut.in_current_span());
284
285 let initial_peers_fut = add_initial_peers(
287 config.clone(),
288 outbound_connector.clone(),
289 peerset_tx.clone(),
290 address_book_updater.clone(),
291 );
292 let initial_peers_join = tokio::spawn(initial_peers_fut.in_current_span());
293
294 let mut candidates = CandidateSet::new(address_book.clone(), peer_set.clone());
296
297 let mut active_outbound_connections = initial_peers_join
299 .wait_for_panics()
300 .await
301 .expect("unexpected error connecting to initial peers");
302 let active_initial_peer_count = active_outbound_connections.update_count();
303
304 info!(
312 ?active_initial_peer_count,
313 "sending initial request for peers"
314 );
315 let _ = candidates.update_initial(active_initial_peer_count).await;
316
317 let demand_count = config
319 .peerset_initial_target_size
320 .saturating_sub(active_outbound_connections.update_count());
321
322 for _ in 0..demand_count {
323 let _ = demand_tx.try_send(MorePeers);
324 }
325
326 let crawl_fut = crawl_and_dial(
328 config.clone(),
329 demand_tx,
330 demand_rx,
331 candidates,
332 outbound_connector,
333 peerset_tx,
334 active_outbound_connections,
335 address_book_updater,
336 );
337 let crawl_guard = tokio::spawn(crawl_fut.in_current_span());
338
339 let peer_cache_updater_fut = peer_cache_updater(config, address_book.clone());
341 let peer_cache_updater_guard = tokio::spawn(peer_cache_updater_fut.in_current_span());
342
343 handle_tx
344 .send(vec![
345 listen_guard,
346 crawl_guard,
347 address_book_updater_guard,
348 peer_cache_updater_guard,
349 ])
350 .unwrap();
351
352 (peer_set, address_book, misbehavior_tx)
353}
354
355#[instrument(skip(config, outbound_connector, peerset_tx, address_book_updater))]
360async fn add_initial_peers<S>(
361 config: Config,
362 outbound_connector: S,
363 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
364 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
365) -> Result<ActiveConnectionCounter, BoxError>
366where
367 S: Service<
368 OutboundConnectorRequest,
369 Response = (PeerSocketAddr, peer::Client),
370 Error = BoxError,
371 > + Clone
372 + Send
373 + 'static,
374 S::Future: Send + 'static,
375{
376 let initial_peers = limit_initial_peers(&config, address_book_updater).await;
377
378 let mut handshake_success_total: usize = 0;
379 let mut handshake_error_total: usize = 0;
380
381 let mut active_outbound_connections = ActiveConnectionCounter::new_counter_with(
382 config.peerset_outbound_connection_limit(),
383 "Outbound Connections",
384 );
385
386 let ipv4_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv4()).count();
388 let ipv6_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv6()).count();
389 info!(
390 ?ipv4_peer_count,
391 ?ipv6_peer_count,
392 "connecting to initial peer set"
393 );
394
395 let network = config.network.clone();
407 let mut handshakes: FuturesUnordered<_> = initial_peers
408 .into_iter()
409 .enumerate()
410 .map(|(i, addr)| {
411 let connection_tracker = active_outbound_connections.track_connection();
412 let req = OutboundConnectorRequest {
413 addr,
414 connection_tracker,
415 };
416 let outbound_connector = outbound_connector.clone();
417 let network = network.clone();
418
419 tokio::spawn(
421 async move {
422 sleep(
426 constants::MIN_OUTBOUND_PEER_CONNECTION_INTERVAL.saturating_mul(i as u32),
427 )
428 .await;
429
430 record_connection_attempt_started(
433 &network,
434 ConnectionDirection::Outbound,
435 addr,
436 );
437 let result = outbound_connector
438 .oneshot(req)
439 .map_err(move |e| (addr, e))
440 .await;
441 record_connection_attempt_finished(
442 &network,
443 ConnectionDirection::Outbound,
444 addr,
445 result.as_ref().err().map(|(_, error)| error),
446 );
447 result
448 }
449 .in_current_span(),
450 )
451 .wait_for_panics()
452 })
453 .collect();
454
455 while let Some(handshake_result) = handshakes.next().await {
456 match handshake_result {
457 Ok(change) => {
458 handshake_success_total += 1;
459 debug!(
460 ?handshake_success_total,
461 ?handshake_error_total,
462 ?change,
463 "an initial peer handshake succeeded"
464 );
465
466 peerset_tx.send(change).await?;
468 }
469 Err((addr, ref e)) => {
470 handshake_error_total += 1;
471
472 let mut expected_error = false;
474 if let Some(io_error) = e.downcast_ref::<tokio::io::Error>() {
475 if io_error.kind() == tokio::io::ErrorKind::AddrNotAvailable {
478 expected_error = true;
479 }
480 }
481
482 if expected_error {
483 debug!(
484 successes = ?handshake_success_total,
485 errors = ?handshake_error_total,
486 ?addr,
487 ?e,
488 "an initial peer connection failed"
489 );
490 } else {
491 info!(
492 successes = ?handshake_success_total,
493 errors = ?handshake_error_total,
494 ?addr,
495 %e,
496 "an initial peer connection failed"
497 );
498 }
499 }
500 }
501
502 tokio::task::yield_now().await;
506 }
507
508 let outbound_connections = active_outbound_connections.update_count();
509 info!(
510 ?handshake_success_total,
511 ?handshake_error_total,
512 ?outbound_connections,
513 "finished connecting to initial seed and disk cache peers"
514 );
515
516 Ok(active_outbound_connections)
517}
518
519async fn limit_initial_peers(
527 config: &Config,
528 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
529) -> HashSet<PeerSocketAddr> {
530 let all_peers: HashSet<PeerSocketAddr> = config.initial_peers().await;
531 let mut preferred_peers: BTreeMap<PeerPreference, Vec<PeerSocketAddr>> = BTreeMap::new();
532
533 let all_peers_count = all_peers.len();
534 if all_peers_count > config.peerset_initial_target_size {
535 info!(
536 "limiting the initial peers list from {} to {}",
537 all_peers_count, config.peerset_initial_target_size,
538 );
539 }
540
541 for peer_addr in all_peers {
544 let preference = PeerPreference::new(peer_addr, config.network.clone());
545
546 match preference {
547 Ok(preference) => preferred_peers
548 .entry(preference)
549 .or_default()
550 .push(peer_addr),
551 Err(error) => info!(
552 ?peer_addr,
553 ?error,
554 "invalid initial peer from DNS seeder, configured IP address, or disk cache",
555 ),
556 }
557 }
558
559 for peer in preferred_peers.values().flatten() {
568 let peer_addr = MetaAddr::new_initial_peer(*peer);
569 let _ = address_book_updater.send(peer_addr).await;
572 }
573
574 let mut initial_peers: HashSet<PeerSocketAddr> = HashSet::new();
577 for better_peers in preferred_peers.values() {
578 let mut better_peers = better_peers.clone();
579 let (chosen_peers, _unused_peers) = better_peers.partial_shuffle(
580 &mut rand::thread_rng(),
581 config.peerset_initial_target_size - initial_peers.len(),
582 );
583
584 initial_peers.extend(chosen_peers.iter());
585
586 if initial_peers.len() >= config.peerset_initial_target_size {
587 break;
588 }
589 }
590
591 initial_peers
592}
593
594#[instrument(skip(config), fields(addr = ?config.listen_addr))]
604pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
605 if let Err(wrong_addr) =
607 address_is_valid_for_inbound_listeners(config.listen_addr, config.network.clone())
608 {
609 warn!(
610 "We are configured with address {} on {:?}, but it could cause network issues. \
611 The default port for {:?} is {}. Error: {wrong_addr:?}",
612 config.listen_addr,
613 config.network,
614 config.network,
615 config.network.default_port(),
616 );
617 }
618
619 info!(
620 "Trying to open Zcash protocol endpoint at {}...",
621 config.listen_addr
622 );
623 let listener_result = TcpListener::bind(config.listen_addr).await;
624
625 let listener = match listener_result {
626 Ok(l) => l,
627 Err(e) => panic!(
628 "Opening Zcash network protocol listener {:?} failed: {e:?}. \
629 Hint: Check if another zebrad or zcashd process is running. \
630 Try changing the network listen_addr in the Zebra config.",
631 config.listen_addr,
632 ),
633 };
634
635 let local_addr = listener
636 .local_addr()
637 .expect("unexpected missing local addr for open listener");
638 info!("Opened Zcash protocol endpoint at {}", local_addr);
639
640 (listener, local_addr)
641}
642
643#[instrument(skip(config, listener, handshaker, peerset_tx), fields(listener_addr = ?listener.local_addr()))]
652async fn accept_inbound_connections<S>(
653 config: Config,
654 listener: TcpListener,
655 min_inbound_peer_connection_interval: Duration,
656 handshaker: S,
657 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
658 bans_receiver: watch::Receiver<Arc<IndexMap<IpAddr, std::time::Instant>>>,
659 zcashd_compat_peer_ips: Vec<IpAddr>,
660) -> Result<(), BoxError>
661where
662 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
663 + Clone,
664 S::Future: Send + 'static,
665{
666 let mut recent_inbound_connections =
667 recent_by_ip::RecentByIp::new(None, Some(config.max_connections_per_ip));
668
669 let mut active_inbound_connections = ActiveConnectionCounter::new_counter_with(
670 config.peerset_inbound_connection_limit(),
671 "Inbound Connections",
672 );
673 let zcashd_compat_peer_ips: HashSet<_> = zcashd_compat_peer_ips
674 .into_iter()
675 .map(|ip| canonical_socket_addr(SocketAddr::new(ip, 0)).ip())
676 .collect();
677 let zcashd_compat_reserved_slots = zcashd_compat_peer_ips
684 .len()
685 .min(config.peerset_inbound_connection_limit().saturating_sub(1));
686 let mut active_zcashd_compat_connections = ActiveConnectionCounter::new_counter_with(
687 zcashd_compat_reserved_slots,
688 "zcashd-compat Inbound Connections",
689 );
690 let public_inbound_connection_limit = config
691 .peerset_inbound_connection_limit()
692 .saturating_sub(zcashd_compat_reserved_slots);
693
694 let mut handshakes: FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>> =
695 FuturesUnordered::new();
696 handshakes.push(future::pending().boxed());
698
699 loop {
700 let inbound_result = tokio::select! {
702 biased;
703 next_handshake_res = handshakes.next() => match next_handshake_res {
704 Some(()) => continue,
706 None => unreachable!("handshakes never terminates, because it contains a future that never resolves"),
707 },
708
709 inbound_result = listener.accept() => inbound_result,
711 };
712
713 if let Ok((tcp_stream, addr)) = inbound_result {
714 let addr: PeerSocketAddr = canonical_peer_addr(addr);
722 record_connection_attempt_started(&config.network, ConnectionDirection::Inbound, addr);
723
724 let canonical_ip = canonical_socket_addr(addr.remove_socket_addr_privacy()).ip();
731 let bans = bans_receiver.borrow().clone();
732 if bans.contains_key(&addr.ip()) || bans.contains_key(&canonical_ip) {
733 debug!(?addr, "banned inbound connection attempt");
734 record_inbound_connection_rejected(&config.network, addr, "banned");
735 std::mem::drop(tcp_stream);
736 continue;
737 }
738
739 let active_public_inbound_connections = active_inbound_connections.update_count();
740 let active_zcashd_compat_inbound_connections =
741 active_zcashd_compat_connections.update_count();
742 let active_total_inbound_connections =
743 active_public_inbound_connections + active_zcashd_compat_inbound_connections;
744 let is_zcashd_compat_peer = zcashd_compat_peer_ips.contains(&canonical_ip);
745
746 let use_reserved_slot = is_zcashd_compat_peer
754 && active_zcashd_compat_inbound_connections < zcashd_compat_reserved_slots
755 && active_total_inbound_connections < config.peerset_inbound_connection_limit();
756
757 let connection_tracker = if use_reserved_slot {
758 active_zcashd_compat_connections.track_connection()
759 } else if active_public_inbound_connections >= public_inbound_connection_limit
760 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
761 || recent_inbound_connections.is_past_limit_or_add(addr.ip())
762 {
763 record_inbound_connection_rejected(
766 &config.network,
767 addr,
768 "capacity_or_rate_limited",
769 );
770 std::mem::drop(tcp_stream);
771 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
774 continue;
775 } else {
776 active_inbound_connections.track_connection()
777 };
778 debug!(
779 inbound_connections = ?active_total_inbound_connections,
780 ?is_zcashd_compat_peer,
781 "handshaking on an open inbound peer connection"
782 );
783
784 let handshake_task = accept_inbound_handshake(
785 config.network.clone(),
786 addr,
787 handshaker.clone(),
788 tcp_stream,
789 connection_tracker,
790 peerset_tx.clone(),
791 )
792 .await?
793 .wait_for_panics();
794
795 handshakes.push(handshake_task);
796
797 tokio::time::sleep(min_inbound_peer_connection_interval).await;
807 } else {
808 debug!(?inbound_result, "error accepting inbound connection");
811 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
812 }
813
814 tokio::task::yield_now().await;
822 }
823}
824
825#[instrument(skip(handshaker, tcp_stream, connection_tracker, peerset_tx))]
833async fn accept_inbound_handshake<S>(
834 network: Network,
835 addr: PeerSocketAddr,
836 mut handshaker: S,
837 tcp_stream: TcpStream,
838 connection_tracker: ConnectionTracker,
839 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
840) -> Result<tokio::task::JoinHandle<()>, BoxError>
841where
842 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
843 + Clone,
844 S::Future: Send + 'static,
845{
846 let connected_addr = peer::ConnectedAddr::new_inbound_direct(addr);
847
848 debug!("got incoming connection");
849
850 handshaker.ready().await?;
857
858 let handshake = handshaker.call(HandshakeRequest {
860 data_stream: tcp_stream,
861 connected_addr,
862 connection_tracker,
863 });
864 let mut peerset_tx = peerset_tx.clone();
866
867 let handshake_task = tokio::spawn(
868 async move {
869 let handshake_result = handshake.await;
870 record_connection_attempt_finished(
871 &network,
872 ConnectionDirection::Inbound,
873 addr,
874 handshake_result.as_ref().err(),
875 );
876
877 if let Ok(client) = handshake_result {
878 let _ = peerset_tx.send((addr, client)).await;
880 } else {
881 debug!(?handshake_result, "error handshaking with inbound peer");
882 }
883 }
884 .in_current_span(),
885 );
886
887 Ok(handshake_task)
888}
889
890enum CrawlerAction {
892 DemandDrop,
894 DemandHandshakeOrCrawl,
898 TimerCrawl { tick: Instant },
901 HandshakeFinished,
903 DemandCrawlFinished,
905 TimerCrawlFinished,
907}
908
909#[allow(clippy::too_many_arguments)]
932#[instrument(
933 skip(
934 config,
935 demand_tx,
936 demand_rx,
937 candidates,
938 outbound_connector,
939 peerset_tx,
940 active_outbound_connections,
941 address_book_updater,
942 ),
943 fields(
944 new_peer_interval = ?config.crawl_new_peer_interval,
945 )
946)]
947async fn crawl_and_dial<C, S>(
948 config: Config,
949 demand_tx: futures::channel::mpsc::Sender<MorePeers>,
950 mut demand_rx: futures::channel::mpsc::Receiver<MorePeers>,
951 candidates: CandidateSet<S>,
952 outbound_connector: C,
953 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
954 mut active_outbound_connections: ActiveConnectionCounter,
955 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
956) -> Result<(), BoxError>
957where
958 C: Service<
959 OutboundConnectorRequest,
960 Response = (PeerSocketAddr, peer::Client),
961 Error = BoxError,
962 > + Clone
963 + Send
964 + 'static,
965 C::Future: Send + 'static,
966 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
967 S::Future: Send + 'static,
968{
969 use CrawlerAction::*;
970
971 info!(
972 crawl_new_peer_interval = ?config.crawl_new_peer_interval,
973 outbound_connections = ?active_outbound_connections.update_count(),
974 "starting the peer address crawler",
975 );
976
977 let candidates = Arc::new(futures::lock::Mutex::new(candidates));
983
984 let mut handshakes: FuturesUnordered<
986 Pin<Box<dyn Future<Output = Result<CrawlerAction, BoxError>> + Send>>,
987 > = FuturesUnordered::new();
988 handshakes.push(future::pending().boxed());
991
992 let mut crawl_timer = tokio::time::interval(config.crawl_new_peer_interval);
993 crawl_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
996
997 let mut crawl_timer = IntervalStream::new(crawl_timer).map(|tick| TimerCrawl { tick });
998
999 loop {
1004 metrics::gauge!(
1005 "crawler.in_flight_handshakes",
1006 "network" => network_kind_label(&config.network),
1007 )
1008 .set(
1009 handshakes
1010 .len()
1011 .checked_sub(1)
1012 .expect("the pool always contains an unresolved future") as f64,
1013 );
1014
1015 let crawler_action = tokio::select! {
1016 biased;
1017 next_handshake_res = handshakes.next() => next_handshake_res.expect(
1020 "handshakes never terminates, because it contains a future that never resolves"
1021 ),
1022 next_timer = crawl_timer.next() => Ok(next_timer.expect("timers never terminate")),
1024 next_demand = demand_rx.next() => next_demand.ok_or("demand stream closed, is Zebra shutting down?".into()).map(|MorePeers|{
1030 if active_outbound_connections.update_count() >= config.peerset_outbound_connection_limit() {
1031 DemandDrop
1033 } else {
1034 DemandHandshakeOrCrawl
1035 }
1036 })
1037 };
1038
1039 match crawler_action {
1040 Ok(DemandDrop) => {
1042 trace!("too many open connections or in-flight handshakes, dropping demand signal");
1045 }
1046
1047 Ok(DemandHandshakeOrCrawl) => {
1049 let candidates = candidates.clone();
1050 let outbound_connector = outbound_connector.clone();
1051 let peerset_tx = peerset_tx.clone();
1052 let address_book_updater = address_book_updater.clone();
1053 let demand_tx = demand_tx.clone();
1054 let network = config.network.clone();
1055
1056 let outbound_connection_tracker = active_outbound_connections.track_connection();
1058 let outbound_connections = active_outbound_connections.update_count();
1059 debug!(?outbound_connections, "opening an outbound peer connection");
1060
1061 let handshake_or_crawl_handle = tokio::spawn(
1070 async move {
1071 let candidate = { candidates.lock().await.next().await };
1078
1079 if let Some(candidate) = candidate {
1080 dial(
1082 network,
1083 candidate,
1084 outbound_connector,
1085 outbound_connection_tracker,
1086 outbound_connections,
1087 peerset_tx,
1088 address_book_updater,
1089 demand_tx,
1090 )
1091 .await?;
1092
1093 Ok(HandshakeFinished)
1094 } else {
1095 debug!("demand for peers but no available candidates");
1097
1098 crawl(candidates, demand_tx).await?;
1099
1100 Ok(DemandCrawlFinished)
1101 }
1102 }
1103 .in_current_span(),
1104 )
1105 .wait_for_panics();
1106
1107 handshakes.push(handshake_or_crawl_handle);
1108 }
1109 Ok(TimerCrawl { tick }) => {
1110 let candidates = candidates.clone();
1111 let crawl_demand_tx = demand_tx.clone();
1112 let spare_capacity = config
1113 .peerset_outbound_connection_limit()
1114 .saturating_sub(active_outbound_connections.update_count());
1115
1116 let crawl_handle = tokio::spawn(
1117 async move {
1118 debug!(
1119 ?tick,
1120 spare_capacity,
1121 "crawling for more peers in response to the crawl timer"
1122 );
1123
1124 let ready_candidates = { candidates.lock().await.ready_peer_count().await };
1134 let mut fill_demand_tx = crawl_demand_tx.clone();
1135 for _ in 0..spare_capacity.min(ready_candidates) {
1136 if fill_demand_tx.try_send(MorePeers).is_err() {
1137 break;
1138 }
1139 }
1140
1141 crawl(candidates, crawl_demand_tx).await?;
1142
1143 Ok(TimerCrawlFinished)
1144 }
1145 .in_current_span(),
1146 )
1147 .wait_for_panics();
1148
1149 handshakes.push(crawl_handle);
1150 }
1151
1152 Ok(HandshakeFinished) => {
1154 }
1156 Ok(DemandCrawlFinished) => {
1157 trace!("demand-based crawl finished");
1160 }
1161 Ok(TimerCrawlFinished) => {
1162 debug!("timer-based crawl finished");
1163 }
1164
1165 Err(error) => {
1167 info!(?error, "crawler task exiting due to an error");
1168 return Err(error);
1169 }
1170 }
1171
1172 tokio::task::yield_now().await;
1176 }
1177}
1178
1179#[instrument(skip(candidates, demand_tx))]
1182async fn crawl<S>(
1183 candidates: Arc<futures::lock::Mutex<CandidateSet<S>>>,
1184 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1185) -> Result<(), BoxError>
1186where
1187 S: Service<Request, Response = Response, Error = BoxError> + Send + Sync + 'static,
1188 S::Future: Send + 'static,
1189{
1190 let result = {
1194 let result = candidates.lock().await.update().await;
1195 std::mem::drop(candidates);
1196 result
1197 };
1198 let more_peers = match result {
1199 Ok(more_peers) => more_peers,
1200 Err(e) => {
1201 info!(
1202 ?e,
1203 "candidate set returned an error, is Zebra shutting down?"
1204 );
1205 return Err(e);
1206 }
1207 };
1208
1209 if let Some(more_peers) = more_peers {
1220 if let Err(send_error) = demand_tx.try_send(more_peers) {
1221 if send_error.is_disconnected() {
1222 return Err(send_error.into());
1224 }
1225 }
1226 }
1227
1228 Ok(())
1229}
1230
1231#[allow(clippy::too_many_arguments)]
1238#[instrument(skip(
1239 outbound_connector,
1240 outbound_connection_tracker,
1241 outbound_connections,
1242 peerset_tx,
1243 address_book_updater,
1244 demand_tx
1245))]
1246async fn dial<C>(
1247 network: Network,
1248 candidate: MetaAddr,
1249 mut outbound_connector: C,
1250 outbound_connection_tracker: ConnectionTracker,
1251 outbound_connections: usize,
1252 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
1253 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1254 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1255) -> Result<(), BoxError>
1256where
1257 C: Service<
1258 OutboundConnectorRequest,
1259 Response = (PeerSocketAddr, peer::Client),
1260 Error = BoxError,
1261 > + Clone
1262 + Send
1263 + 'static,
1264 C::Future: Send + 'static,
1265{
1266 const MAX_CONNECTIONS_FOR_INFO_LOG: usize = 5;
1269
1270 debug!(?candidate.addr, "attempting outbound connection in response to demand");
1277
1278 let outbound_connector = outbound_connector.ready().await?;
1280
1281 let req = OutboundConnectorRequest {
1282 addr: candidate.addr,
1283 connection_tracker: outbound_connection_tracker,
1284 };
1285
1286 record_connection_attempt_started(&network, ConnectionDirection::Outbound, candidate.addr);
1288 let handshake_result: Result<(PeerSocketAddr, peer::Client), BoxError> =
1289 outbound_connector.call(req).map(Into::into).await;
1290 record_connection_attempt_finished(
1291 &network,
1292 ConnectionDirection::Outbound,
1293 candidate.addr,
1294 handshake_result.as_ref().err(),
1295 );
1296
1297 match handshake_result {
1298 Ok((address, client)) => {
1299 debug!(?candidate.addr, "successfully dialed new peer");
1300
1301 peerset_tx.send((address, client)).await?;
1303 }
1304 Err(error) => {
1306 if outbound_connections <= MAX_CONNECTIONS_FOR_INFO_LOG && !cfg!(test) {
1309 info!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1310 } else {
1311 debug!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1312 }
1313 report_failed(address_book_updater.clone(), candidate).await;
1314
1315 if let Err(send_error) = demand_tx.try_send(MorePeers) {
1322 if send_error.is_disconnected() {
1323 return Err(send_error.into());
1325 }
1326 }
1327 }
1328 }
1329
1330 Ok(())
1331}
1332
1333#[instrument(skip(address_book_updater))]
1335async fn report_failed(
1336 address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1337 addr: MetaAddr,
1338) {
1339 let addr = MetaAddr::new_errored(addr.addr, None);
1341
1342 let _ = address_book_updater.send(addr).await;
1344}