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 rand::seq::SliceRandom;
23use tokio::{
24 net::{TcpListener, TcpStream},
25 sync::{broadcast, mpsc, watch},
26 time::{sleep, Instant},
27};
28use tokio_stream::wrappers::IntervalStream;
29use tower::{
30 buffer::Buffer, discover::Change, layer::Layer, util::BoxService, Service, ServiceExt,
31};
32use tracing_futures::Instrument;
33
34use zebra_chain::{chain_tip::ChainTip, diagnostic::task::WaitForPanics, parameters::Network};
35
36use crate::{
37 address_book_updater::{
38 AddressBookChangeSender, AddressBookService, AddressBookUpdater, MIN_CHANNEL_SIZE,
39 },
40 connection_metrics::{
41 network_kind_label, record_connection_attempt_finished, record_connection_attempt_started,
42 record_inbound_connection_rejected, ConnectionDirection,
43 },
44 constants,
45 meta_addr::MetaAddr,
46 peer::{
47 self, address_is_valid_for_inbound_listeners, HandshakeRequest, MinimumPeerVersion,
48 OutboundConnectorRequest, PeerPreference,
49 },
50 peer_cache_updater::peer_cache_updater,
51 peer_set::{
52 crawl_once, crawler_services, next_reconnect_peer, ready_peer_count, set::MorePeers,
53 ActiveConnectionCounter, ConnectionTracker, CrawlService, NextPeerService, PeerSet,
54 },
55 protocol::external::{canonical_peer_addr, canonical_socket_addr},
56 AddressBook, BanList, BoxError, Config, PeerSocketAddr, Request, Response,
57};
58
59#[cfg(test)]
60mod tests;
61
62mod recent_by_ip;
63
64type DiscoveredPeer = (PeerSocketAddr, peer::Client);
73
74pub async fn init<S, C>(
107 config: Config,
108 inbound_service: S,
109 latest_chain_tip: C,
110 user_agent: String,
111) -> (
112 Buffer<BoxService<Request, Response, BoxError>, Request>,
113 Arc<std::sync::Mutex<AddressBook>>,
114 mpsc::Sender<(PeerSocketAddr, u32)>,
115)
116where
117 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
118 S::Future: Send + 'static,
119 C: ChainTip + Clone + Send + Sync + 'static,
120{
121 init_with_block_gossip_peer_ips(
122 config,
123 inbound_service,
124 latest_chain_tip,
125 user_agent,
126 Vec::new(),
127 )
128 .await
129}
130
131pub async fn init_with_block_gossip_peer_ips<S, C>(
139 config: Config,
140 inbound_service: S,
141 latest_chain_tip: C,
142 user_agent: String,
143 block_gossip_peer_ips: Vec<IpAddr>,
144) -> (
145 Buffer<BoxService<Request, Response, BoxError>, Request>,
146 Arc<std::sync::Mutex<AddressBook>>,
147 mpsc::Sender<(PeerSocketAddr, u32)>,
148)
149where
150 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
151 S::Future: Send + 'static,
152 C: ChainTip + Clone + Send + Sync + 'static,
153{
154 let (tcp_listener, listen_addr) = open_listener(&config.clone()).await;
155
156 let (
157 address_book,
158 bans_receiver,
159 address_book_updater,
160 address_book_service,
161 address_metrics,
162 address_book_updater_guard,
163 ) = AddressBookUpdater::spawn(&config, listen_addr);
164
165 let (misbehavior_tx, mut misbehavior_rx) = mpsc::channel(
166 config
169 .peerset_total_connection_limit()
170 .max(MIN_CHANNEL_SIZE),
171 );
172
173 let misbehaviour_updater = address_book_updater.clone();
174 tokio::spawn(
175 async move {
176 let mut misbehaviors: HashMap<PeerSocketAddr, u32> = HashMap::new();
177 let mut flush_timer =
180 IntervalStream::new(tokio::time::interval(constants::MISBEHAVIOR_FLUSH_INTERVAL));
181
182 loop {
183 tokio::select! {
184 msg = misbehavior_rx.recv() => match msg {
185 Some((peer_addr, score_increment)) => *misbehaviors
186 .entry(peer_addr)
187 .or_default()
188 += score_increment,
189 None => break,
190 },
191
192 _ = flush_timer.next() => {
193 for (addr, score_increment) in misbehaviors.drain() {
194 let _ = misbehaviour_updater
195 .send(MetaAddr::new_misbehavior(addr, score_increment))
196 .await;
197 }
198 },
199 };
200 }
201
202 tracing::warn!("exiting misbehavior update batch task");
203 }
204 .in_current_span(),
205 );
206
207 let (inv_sender, inv_receiver) = broadcast::channel(config.peerset_total_connection_limit());
215
216 let (listen_handshaker, outbound_connector) = {
221 use tower::timeout::TimeoutLayer;
222 let hs_timeout = TimeoutLayer::new(constants::HANDSHAKE_TIMEOUT);
223 use crate::protocol::external::types::PeerServices;
224 let hs = peer::Handshake::builder()
225 .with_config(config.clone())
226 .with_inbound_service(inbound_service)
227 .with_inventory_collector(inv_sender)
228 .with_address_book_updater(address_book_updater.clone())
229 .with_advertised_services(PeerServices::NODE_NETWORK)
230 .with_user_agent(user_agent)
231 .with_latest_chain_tip(latest_chain_tip.clone())
232 .want_transactions(true)
233 .finish()
234 .expect("configured all required parameters");
235 (
236 hs_timeout.layer(hs.clone()),
237 hs_timeout.layer(peer::Connector::new(hs)),
238 )
239 };
240
241 let (peerset_tx, peerset_rx) =
247 futures::channel::mpsc::channel::<DiscoveredPeer>(config.peerset_total_connection_limit());
248
249 let discovered_peers = peerset_rx.map(|(address, client)| {
250 Result::<_, Infallible>::Ok(Change::Insert(address, client.into()))
251 });
252
253 let (mut demand_tx, demand_rx) =
256 futures::channel::mpsc::channel::<MorePeers>(config.peerset_outbound_connection_limit());
257
258 let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
260
261 let peer_set = PeerSet::new(
263 &config,
264 block_gossip_peer_ips.clone(),
265 discovered_peers,
266 demand_tx.clone(),
267 handle_rx,
268 inv_receiver,
269 bans_receiver.clone(),
270 address_metrics,
271 MinimumPeerVersion::new(latest_chain_tip, &config.network),
272 None,
273 );
274 let peer_set = Buffer::new(BoxService::new(peer_set), constants::PEERSET_BUFFER_SIZE);
275
276 let listen_fut = accept_inbound_connections(
280 config.clone(),
281 tcp_listener,
282 constants::MIN_INBOUND_PEER_CONNECTION_INTERVAL,
283 listen_handshaker,
284 peerset_tx.clone(),
285 bans_receiver,
286 block_gossip_peer_ips,
287 );
288 let listen_guard = tokio::spawn(listen_fut.in_current_span());
289
290 let initial_peers_fut = add_initial_peers(
292 config.clone(),
293 outbound_connector.clone(),
294 peerset_tx.clone(),
295 address_book_updater.clone(),
296 );
297 let initial_peers_join = tokio::spawn(initial_peers_fut.in_current_span());
298
299 let (next_peer_service, mut crawl_service) =
301 crawler_services(address_book_service.clone(), peer_set.clone());
302
303 let mut active_outbound_connections = initial_peers_join
305 .wait_for_panics()
306 .await
307 .expect("unexpected error connecting to initial peers");
308 let active_initial_peer_count = active_outbound_connections.update_count();
309
310 info!(
318 ?active_initial_peer_count,
319 "sending initial request for peers"
320 );
321 let _ = crawl_once(&mut crawl_service, Some(active_initial_peer_count)).await;
322
323 let demand_count = config
325 .peerset_initial_target_size
326 .saturating_sub(active_outbound_connections.update_count());
327
328 for _ in 0..demand_count {
329 let _ = demand_tx.try_send(MorePeers);
330 }
331
332 let crawl_fut = crawl_and_dial(
334 config.clone(),
335 demand_tx,
336 demand_rx,
337 next_peer_service,
338 crawl_service,
339 address_book_service.clone(),
340 outbound_connector,
341 peerset_tx,
342 active_outbound_connections,
343 address_book_updater,
344 );
345 let crawl_guard = tokio::spawn(crawl_fut.in_current_span());
346
347 let peer_cache_updater_fut = peer_cache_updater(config, address_book_service);
349 let peer_cache_updater_guard = tokio::spawn(peer_cache_updater_fut.in_current_span());
350
351 handle_tx
352 .send(vec![
353 listen_guard,
354 crawl_guard,
355 address_book_updater_guard,
356 peer_cache_updater_guard,
357 ])
358 .unwrap();
359
360 (peer_set, address_book, misbehavior_tx)
361}
362
363#[instrument(skip(config, outbound_connector, peerset_tx, address_book_updater))]
368async fn add_initial_peers<S>(
369 config: Config,
370 outbound_connector: S,
371 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
372 address_book_updater: AddressBookChangeSender,
373) -> Result<ActiveConnectionCounter, BoxError>
374where
375 S: Service<
376 OutboundConnectorRequest,
377 Response = (PeerSocketAddr, peer::Client),
378 Error = BoxError,
379 > + Clone
380 + Send
381 + 'static,
382 S::Future: Send + 'static,
383{
384 let initial_peers = limit_initial_peers(&config, address_book_updater).await;
385
386 let mut handshake_success_total: usize = 0;
387 let mut handshake_error_total: usize = 0;
388
389 let mut active_outbound_connections = ActiveConnectionCounter::new_counter_with(
390 config.peerset_outbound_connection_limit(),
391 "Outbound Connections",
392 );
393
394 let ipv4_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv4()).count();
396 let ipv6_peer_count = initial_peers.iter().filter(|ip| ip.is_ipv6()).count();
397 info!(
398 ?ipv4_peer_count,
399 ?ipv6_peer_count,
400 "connecting to initial peer set"
401 );
402
403 let network = config.network.clone();
415 let mut handshakes: FuturesUnordered<_> = initial_peers
416 .into_iter()
417 .enumerate()
418 .map(|(i, addr)| {
419 let connection_tracker = active_outbound_connections.track_connection();
420 let req = OutboundConnectorRequest {
421 addr,
422 connection_tracker,
423 };
424 let outbound_connector = outbound_connector.clone();
425 let network = network.clone();
426
427 tokio::spawn(
429 async move {
430 sleep(
434 constants::MIN_OUTBOUND_PEER_CONNECTION_INTERVAL.saturating_mul(i as u32),
435 )
436 .await;
437
438 record_connection_attempt_started(
441 &network,
442 ConnectionDirection::Outbound,
443 addr,
444 );
445 let result = outbound_connector
446 .oneshot(req)
447 .map_err(move |e| (addr, e))
448 .await;
449 record_connection_attempt_finished(
450 &network,
451 ConnectionDirection::Outbound,
452 addr,
453 result.as_ref().err().map(|(_, error)| error),
454 );
455 result
456 }
457 .in_current_span(),
458 )
459 .wait_for_panics()
460 })
461 .collect();
462
463 while let Some(handshake_result) = handshakes.next().await {
464 match handshake_result {
465 Ok(change) => {
466 handshake_success_total += 1;
467 debug!(
468 ?handshake_success_total,
469 ?handshake_error_total,
470 ?change,
471 "an initial peer handshake succeeded"
472 );
473
474 peerset_tx.send(change).await?;
476 }
477 Err((addr, ref e)) => {
478 handshake_error_total += 1;
479
480 let mut expected_error = false;
482 if let Some(io_error) = e.downcast_ref::<tokio::io::Error>() {
483 if io_error.kind() == tokio::io::ErrorKind::AddrNotAvailable {
486 expected_error = true;
487 }
488 }
489
490 if expected_error {
491 debug!(
492 successes = ?handshake_success_total,
493 errors = ?handshake_error_total,
494 ?addr,
495 ?e,
496 "an initial peer connection failed"
497 );
498 } else {
499 info!(
500 successes = ?handshake_success_total,
501 errors = ?handshake_error_total,
502 ?addr,
503 %e,
504 "an initial peer connection failed"
505 );
506 }
507 }
508 }
509
510 tokio::task::yield_now().await;
514 }
515
516 let outbound_connections = active_outbound_connections.update_count();
517 info!(
518 ?handshake_success_total,
519 ?handshake_error_total,
520 ?outbound_connections,
521 "finished connecting to initial seed and disk cache peers"
522 );
523
524 Ok(active_outbound_connections)
525}
526
527async fn limit_initial_peers(
535 config: &Config,
536 address_book_updater: AddressBookChangeSender,
537) -> HashSet<PeerSocketAddr> {
538 let all_peers: HashSet<PeerSocketAddr> = config.initial_peers().await;
539 let mut preferred_peers: BTreeMap<PeerPreference, Vec<PeerSocketAddr>> = BTreeMap::new();
540
541 let all_peers_count = all_peers.len();
542 if all_peers_count > config.peerset_initial_target_size {
543 info!(
544 "limiting the initial peers list from {} to {}",
545 all_peers_count, config.peerset_initial_target_size,
546 );
547 }
548
549 for peer_addr in all_peers {
552 let preference = PeerPreference::new(peer_addr, config.network.clone());
553
554 match preference {
555 Ok(preference) => preferred_peers
556 .entry(preference)
557 .or_default()
558 .push(peer_addr),
559 Err(error) => info!(
560 ?peer_addr,
561 ?error,
562 "invalid initial peer from DNS seeder, configured IP address, or disk cache",
563 ),
564 }
565 }
566
567 for peer in preferred_peers.values().flatten() {
576 let peer_addr = MetaAddr::new_initial_peer(*peer);
577 let _ = address_book_updater.send(peer_addr).await;
580 }
581
582 let mut initial_peers: HashSet<PeerSocketAddr> = HashSet::new();
585 for better_peers in preferred_peers.values() {
586 let mut better_peers = better_peers.clone();
587 let (chosen_peers, _unused_peers) = better_peers.partial_shuffle(
588 &mut rand::thread_rng(),
589 config.peerset_initial_target_size - initial_peers.len(),
590 );
591
592 initial_peers.extend(chosen_peers.iter());
593
594 if initial_peers.len() >= config.peerset_initial_target_size {
595 break;
596 }
597 }
598
599 initial_peers
600}
601
602#[instrument(skip(config), fields(addr = ?config.listen_addr))]
612pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
613 if let Err(wrong_addr) =
615 address_is_valid_for_inbound_listeners(config.listen_addr, config.network.clone())
616 {
617 warn!(
618 "We are configured with address {} on {:?}, but it could cause network issues. \
619 The default port for {:?} is {}. Error: {wrong_addr:?}",
620 config.listen_addr,
621 config.network,
622 config.network,
623 config.network.default_port(),
624 );
625 }
626
627 info!(
628 "Trying to open Zcash protocol endpoint at {}...",
629 config.listen_addr
630 );
631 let listener_result = TcpListener::bind(config.listen_addr).await;
632
633 let listener = match listener_result {
634 Ok(l) => l,
635 Err(e) => panic!(
636 "Opening Zcash network protocol listener {:?} failed: {e:?}. \
637 Hint: Check if another zebrad or zcashd process is running. \
638 Try changing the network listen_addr in the Zebra config.",
639 config.listen_addr,
640 ),
641 };
642
643 let local_addr = listener
644 .local_addr()
645 .expect("unexpected missing local addr for open listener");
646 info!("Opened Zcash protocol endpoint at {}", local_addr);
647
648 (listener, local_addr)
649}
650
651#[instrument(skip(config, listener, handshaker, peerset_tx), fields(listener_addr = ?listener.local_addr()))]
660async fn accept_inbound_connections<S>(
661 config: Config,
662 listener: TcpListener,
663 min_inbound_peer_connection_interval: Duration,
664 handshaker: S,
665 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
666 bans_receiver: watch::Receiver<BanList>,
667 zcashd_compat_peer_ips: Vec<IpAddr>,
668) -> Result<(), BoxError>
669where
670 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
671 + Clone,
672 S::Future: Send + 'static,
673{
674 let mut recent_inbound_connections =
675 recent_by_ip::RecentByIp::new(None, Some(config.max_connections_per_ip));
676
677 let mut active_inbound_connections = ActiveConnectionCounter::new_counter_with(
678 config.peerset_inbound_connection_limit(),
679 "Inbound Connections",
680 );
681 let zcashd_compat_peer_ips: HashSet<_> = zcashd_compat_peer_ips
682 .into_iter()
683 .map(|ip| canonical_socket_addr(SocketAddr::new(ip, 0)).ip())
684 .collect();
685 let zcashd_compat_reserved_slots = zcashd_compat_peer_ips
692 .len()
693 .min(config.peerset_inbound_connection_limit().saturating_sub(1));
694 let mut active_zcashd_compat_connections = ActiveConnectionCounter::new_counter_with(
695 zcashd_compat_reserved_slots,
696 "zcashd-compat Inbound Connections",
697 );
698 let public_inbound_connection_limit = config
699 .peerset_inbound_connection_limit()
700 .saturating_sub(zcashd_compat_reserved_slots);
701
702 let mut handshakes: FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>> =
703 FuturesUnordered::new();
704 handshakes.push(future::pending().boxed());
706
707 loop {
708 let inbound_result = tokio::select! {
710 biased;
711 next_handshake_res = handshakes.next() => match next_handshake_res {
712 Some(()) => continue,
714 None => unreachable!("handshakes never terminates, because it contains a future that never resolves"),
715 },
716
717 inbound_result = listener.accept() => inbound_result,
719 };
720
721 if let Ok((tcp_stream, addr)) = inbound_result {
722 let addr: PeerSocketAddr = canonical_peer_addr(addr);
730 record_connection_attempt_started(&config.network, ConnectionDirection::Inbound, addr);
731
732 let canonical_ip = canonical_socket_addr(addr.remove_socket_addr_privacy()).ip();
740 let bans = bans_receiver.borrow().clone();
741 if bans.is_banned(addr.ip()) {
742 debug!(?addr, "banned inbound connection attempt");
743 record_inbound_connection_rejected(&config.network, addr, "banned");
744 std::mem::drop(tcp_stream);
745 continue;
746 }
747
748 let active_public_inbound_connections = active_inbound_connections.update_count();
749 let active_zcashd_compat_inbound_connections =
750 active_zcashd_compat_connections.update_count();
751 let active_total_inbound_connections =
752 active_public_inbound_connections + active_zcashd_compat_inbound_connections;
753 let is_zcashd_compat_peer = zcashd_compat_peer_ips.contains(&canonical_ip);
754
755 let use_reserved_slot = is_zcashd_compat_peer
763 && active_zcashd_compat_inbound_connections < zcashd_compat_reserved_slots
764 && active_total_inbound_connections < config.peerset_inbound_connection_limit();
765
766 let connection_tracker = if use_reserved_slot {
767 active_zcashd_compat_connections.track_connection()
768 } else if active_public_inbound_connections >= public_inbound_connection_limit
769 || active_total_inbound_connections >= config.peerset_inbound_connection_limit()
770 || recent_inbound_connections.is_past_limit_or_add(addr.ip())
771 {
772 record_inbound_connection_rejected(
775 &config.network,
776 addr,
777 "capacity_or_rate_limited",
778 );
779 std::mem::drop(tcp_stream);
780 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
783 continue;
784 } else {
785 active_inbound_connections.track_connection()
786 };
787 debug!(
788 inbound_connections = ?active_total_inbound_connections,
789 ?is_zcashd_compat_peer,
790 "handshaking on an open inbound peer connection"
791 );
792
793 let handshake_task = accept_inbound_handshake(
794 config.network.clone(),
795 addr,
796 handshaker.clone(),
797 tcp_stream,
798 connection_tracker,
799 peerset_tx.clone(),
800 )
801 .await?
802 .wait_for_panics();
803
804 handshakes.push(handshake_task);
805
806 tokio::time::sleep(min_inbound_peer_connection_interval).await;
816 } else {
817 debug!(?inbound_result, "error accepting inbound connection");
820 tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
821 }
822
823 tokio::task::yield_now().await;
831 }
832}
833
834#[instrument(skip(handshaker, tcp_stream, connection_tracker, peerset_tx))]
842async fn accept_inbound_handshake<S>(
843 network: Network,
844 addr: PeerSocketAddr,
845 mut handshaker: S,
846 tcp_stream: TcpStream,
847 connection_tracker: ConnectionTracker,
848 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
849) -> Result<tokio::task::JoinHandle<()>, BoxError>
850where
851 S: Service<peer::HandshakeRequest<TcpStream>, Response = peer::Client, Error = BoxError>
852 + Clone,
853 S::Future: Send + 'static,
854{
855 let connected_addr = peer::ConnectedAddr::new_inbound_direct(addr);
856
857 debug!("got incoming connection");
858
859 handshaker.ready().await?;
866
867 let handshake = handshaker.call(HandshakeRequest {
869 data_stream: tcp_stream,
870 connected_addr,
871 connection_tracker,
872 });
873 let mut peerset_tx = peerset_tx.clone();
875
876 let handshake_task = tokio::spawn(
877 async move {
878 let handshake_result = handshake.await;
879 record_connection_attempt_finished(
880 &network,
881 ConnectionDirection::Inbound,
882 addr,
883 handshake_result.as_ref().err(),
884 );
885
886 if let Ok(client) = handshake_result {
887 let _ = peerset_tx.send((addr, client)).await;
889 } else {
890 debug!(?handshake_result, "error handshaking with inbound peer");
891 }
892 }
893 .in_current_span(),
894 );
895
896 Ok(handshake_task)
897}
898
899enum CrawlerAction {
901 DemandDrop,
903 DemandHandshakeOrCrawl,
907 TimerCrawl { tick: Instant },
910 HandshakeFinished,
912 DemandCrawlFinished,
914 TimerCrawlFinished,
916}
917
918#[allow(clippy::too_many_arguments)]
941#[instrument(
942 skip(
943 config,
944 demand_tx,
945 demand_rx,
946 next_peer_service,
947 crawl_service,
948 address_book_service,
949 outbound_connector,
950 peerset_tx,
951 active_outbound_connections,
952 address_book_updater,
953 ),
954 fields(
955 new_peer_interval = ?config.crawl_new_peer_interval,
956 )
957)]
958async fn crawl_and_dial<C, S>(
959 config: Config,
960 demand_tx: futures::channel::mpsc::Sender<MorePeers>,
961 mut demand_rx: futures::channel::mpsc::Receiver<MorePeers>,
962 next_peer_service: NextPeerService,
963 crawl_service: CrawlService<S>,
964 address_book_service: AddressBookService,
965 outbound_connector: C,
966 peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
967 mut active_outbound_connections: ActiveConnectionCounter,
968 address_book_updater: AddressBookChangeSender,
969) -> Result<(), BoxError>
970where
971 C: Service<
972 OutboundConnectorRequest,
973 Response = (PeerSocketAddr, peer::Client),
974 Error = BoxError,
975 > + Clone
976 + Send
977 + 'static,
978 C::Future: Send + 'static,
979 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
980 S::Future: Send + 'static,
981{
982 use CrawlerAction::*;
983
984 info!(
985 crawl_new_peer_interval = ?config.crawl_new_peer_interval,
986 outbound_connections = ?active_outbound_connections.update_count(),
987 "starting the peer address crawler",
988 );
989
990 let mut handshakes: FuturesUnordered<
1000 Pin<Box<dyn Future<Output = Result<CrawlerAction, BoxError>> + Send>>,
1001 > = FuturesUnordered::new();
1002 handshakes.push(future::pending().boxed());
1005
1006 let mut crawl_timer = tokio::time::interval(config.crawl_new_peer_interval);
1007 crawl_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
1010
1011 let mut crawl_timer = IntervalStream::new(crawl_timer).map(|tick| TimerCrawl { tick });
1012
1013 loop {
1018 metrics::gauge!(
1019 "crawler.in_flight_handshakes",
1020 "network" => network_kind_label(&config.network),
1021 )
1022 .set(
1023 handshakes
1024 .len()
1025 .checked_sub(1)
1026 .expect("the pool always contains an unresolved future") as f64,
1027 );
1028
1029 let crawler_action = tokio::select! {
1030 biased;
1031 next_handshake_res = handshakes.next() => next_handshake_res.expect(
1034 "handshakes never terminates, because it contains a future that never resolves"
1035 ),
1036 next_timer = crawl_timer.next() => Ok(next_timer.expect("timers never terminate")),
1038 next_demand = demand_rx.next() => next_demand.ok_or("demand stream closed, is Zebra shutting down?".into()).map(|MorePeers|{
1044 if active_outbound_connections.update_count() >= config.peerset_outbound_connection_limit() {
1045 DemandDrop
1047 } else {
1048 DemandHandshakeOrCrawl
1049 }
1050 })
1051 };
1052
1053 match crawler_action {
1054 Ok(DemandDrop) => {
1056 trace!("too many open connections or in-flight handshakes, dropping demand signal");
1059 }
1060
1061 Ok(DemandHandshakeOrCrawl) => {
1063 let mut next_peer_service = next_peer_service.clone();
1064 let crawl_service = crawl_service.clone();
1065 let outbound_connector = outbound_connector.clone();
1066 let peerset_tx = peerset_tx.clone();
1067 let address_book_updater = address_book_updater.clone();
1068 let demand_tx = demand_tx.clone();
1069 let network = config.network.clone();
1070
1071 let outbound_connection_tracker = active_outbound_connections.track_connection();
1073 let outbound_connections = active_outbound_connections.update_count();
1074 debug!(?outbound_connections, "opening an outbound peer connection");
1075
1076 let handshake_or_crawl_handle = tokio::spawn(
1084 async move {
1085 let candidate = next_reconnect_peer(&mut next_peer_service).await;
1090
1091 if let Some(candidate) = candidate {
1092 dial(
1094 network,
1095 candidate,
1096 outbound_connector,
1097 outbound_connection_tracker,
1098 outbound_connections,
1099 peerset_tx,
1100 address_book_updater,
1101 demand_tx,
1102 )
1103 .await?;
1104
1105 Ok(HandshakeFinished)
1106 } else {
1107 debug!("demand for peers but no available candidates");
1109
1110 crawl(crawl_service, demand_tx).await?;
1111
1112 Ok(DemandCrawlFinished)
1113 }
1114 }
1115 .in_current_span(),
1116 )
1117 .wait_for_panics();
1118
1119 handshakes.push(handshake_or_crawl_handle);
1120 }
1121 Ok(TimerCrawl { tick }) => {
1122 let crawl_service = crawl_service.clone();
1123 let address_book_service = address_book_service.clone();
1124 let crawl_demand_tx = demand_tx.clone();
1125 let spare_capacity = config
1126 .peerset_outbound_connection_limit()
1127 .saturating_sub(active_outbound_connections.update_count());
1128
1129 let crawl_handle = tokio::spawn(
1130 async move {
1131 debug!(
1132 ?tick,
1133 spare_capacity,
1134 "crawling for more peers in response to the crawl timer"
1135 );
1136
1137 let ready_candidates = ready_peer_count(&address_book_service).await;
1147 let mut fill_demand_tx = crawl_demand_tx.clone();
1148 for _ in 0..spare_capacity.min(ready_candidates) {
1149 if fill_demand_tx.try_send(MorePeers).is_err() {
1150 break;
1151 }
1152 }
1153
1154 crawl(crawl_service, crawl_demand_tx).await?;
1155
1156 Ok(TimerCrawlFinished)
1157 }
1158 .in_current_span(),
1159 )
1160 .wait_for_panics();
1161
1162 handshakes.push(crawl_handle);
1163 }
1164
1165 Ok(HandshakeFinished) => {
1167 }
1169 Ok(DemandCrawlFinished) => {
1170 trace!("demand-based crawl finished");
1173 }
1174 Ok(TimerCrawlFinished) => {
1175 debug!("timer-based crawl finished");
1176 }
1177
1178 Err(error) => {
1180 info!(?error, "crawler task exiting due to an error");
1181 return Err(error);
1182 }
1183 }
1184
1185 tokio::task::yield_now().await;
1189 }
1190}
1191
1192#[instrument(skip(crawl_service, demand_tx))]
1195async fn crawl<S>(
1196 mut crawl_service: CrawlService<S>,
1197 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1198) -> Result<(), BoxError>
1199where
1200 S: Service<Request, Response = Response, Error = BoxError> + Clone + Send + Sync + 'static,
1201 S::Future: Send + 'static,
1202{
1203 let more_peers = match crawl_once(&mut crawl_service, None).await {
1206 Ok(more_peers) => more_peers,
1207 Err(e) => {
1208 info!(
1209 ?e,
1210 "crawl service returned an error, is Zebra shutting down?"
1211 );
1212 return Err(e);
1213 }
1214 };
1215
1216 if let Some(more_peers) = more_peers {
1227 if let Err(send_error) = demand_tx.try_send(more_peers) {
1228 if send_error.is_disconnected() {
1229 return Err(send_error.into());
1231 }
1232 }
1233 }
1234
1235 Ok(())
1236}
1237
1238#[allow(clippy::too_many_arguments)]
1245#[instrument(skip(
1246 outbound_connector,
1247 outbound_connection_tracker,
1248 outbound_connections,
1249 peerset_tx,
1250 address_book_updater,
1251 demand_tx
1252))]
1253async fn dial<C>(
1254 network: Network,
1255 candidate: MetaAddr,
1256 mut outbound_connector: C,
1257 outbound_connection_tracker: ConnectionTracker,
1258 outbound_connections: usize,
1259 mut peerset_tx: futures::channel::mpsc::Sender<DiscoveredPeer>,
1260 address_book_updater: AddressBookChangeSender,
1261 mut demand_tx: futures::channel::mpsc::Sender<MorePeers>,
1262) -> Result<(), BoxError>
1263where
1264 C: Service<
1265 OutboundConnectorRequest,
1266 Response = (PeerSocketAddr, peer::Client),
1267 Error = BoxError,
1268 > + Clone
1269 + Send
1270 + 'static,
1271 C::Future: Send + 'static,
1272{
1273 const MAX_CONNECTIONS_FOR_INFO_LOG: usize = 5;
1276
1277 debug!(?candidate.addr, "attempting outbound connection in response to demand");
1284
1285 let outbound_connector = outbound_connector.ready().await?;
1287
1288 let req = OutboundConnectorRequest {
1289 addr: candidate.addr,
1290 connection_tracker: outbound_connection_tracker,
1291 };
1292
1293 record_connection_attempt_started(&network, ConnectionDirection::Outbound, candidate.addr);
1295 let handshake_result: Result<(PeerSocketAddr, peer::Client), BoxError> =
1296 outbound_connector.call(req).map(Into::into).await;
1297 record_connection_attempt_finished(
1298 &network,
1299 ConnectionDirection::Outbound,
1300 candidate.addr,
1301 handshake_result.as_ref().err(),
1302 );
1303
1304 match handshake_result {
1305 Ok((address, client)) => {
1306 debug!(?candidate.addr, "successfully dialed new peer");
1307
1308 peerset_tx.send((address, client)).await?;
1310 }
1311 Err(error) => {
1313 if outbound_connections <= MAX_CONNECTIONS_FOR_INFO_LOG && !cfg!(test) {
1316 info!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1317 } else {
1318 debug!(?error, ?candidate.addr, "failed to make outbound connection to peer");
1319 }
1320 report_failed(address_book_updater.clone(), candidate).await;
1321
1322 if let Err(send_error) = demand_tx.try_send(MorePeers) {
1329 if send_error.is_disconnected() {
1330 return Err(send_error.into());
1332 }
1333 }
1334 }
1335 }
1336
1337 Ok(())
1338}
1339
1340#[instrument(skip(address_book_updater))]
1342async fn report_failed(address_book_updater: AddressBookChangeSender, addr: MetaAddr) {
1343 let addr = MetaAddr::new_errored(addr.addr, None);
1345
1346 let _ = address_book_updater.send(addr).await;
1348}