Skip to main content

zebra_network/peer_set/
initialize.rs

1//! A peer set whose size is dynamically determined by resource constraints.
2//!
3//! The [`PeerSet`] implementation is adapted from the one in [tower::Balance][tower-balance].
4//!
5//! [tower-balance]: https://github.com/tower-rs/tower/tree/master/tower/src/balance
6
7use 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
56/// A successful outbound peer connection attempt or inbound connection handshake.
57///
58/// The [`Handshake`](peer::Handshake) service returns a [`Result`]. Only successful connections
59/// should be sent on the channel. Errors should be logged or ignored.
60///
61/// We don't allow any errors in this type, because:
62/// - The connection limits don't include failed connections
63/// - tower::Discover interprets an error as stream termination
64type DiscoveredPeer = (PeerSocketAddr, peer::Client);
65
66/// Initialize a peer set, using a network `config`, `inbound_service`,
67/// and `latest_chain_tip`.
68///
69/// The peer set abstracts away peer management to provide a
70/// [`tower::Service`] representing "the network" that load-balances requests
71/// over available peers.  The peer set automatically crawls the network to
72/// find more peer addresses and opportunistically connects to new peers.
73///
74/// Each peer connection's message handling is isolated from other
75/// connections, unlike in `zcashd`.  The peer connection first attempts to
76/// interpret inbound messages as part of a response to a previously-issued
77/// request.  Otherwise, inbound messages are interpreted as requests and sent
78/// to the supplied `inbound_service`.
79///
80/// Wrapping the `inbound_service` in [`tower::load_shed`] middleware will
81/// cause the peer set to shrink when the inbound service is unable to keep up
82/// with the volume of inbound requests.
83///
84/// Use [`NoChainTip`][1] to explicitly provide no chain tip receiver.
85///
86/// In addition to returning a service for outbound requests, this method
87/// returns a shared [`AddressBook`] updated with last-seen timestamps for
88/// connected peers. The shared address book should be accessed using a
89/// [blocking thread](https://docs.rs/tokio/1.15.0/tokio/task/index.html#blocking-and-yielding),
90/// to avoid async task deadlocks.
91///
92/// # Panics
93///
94/// If `config.config.peerset_initial_target_size` is zero.
95/// (zebra-network expects to be able to connect to at least one peer.)
96///
97/// [1]: zebra_chain::chain_tip::NoChainTip
98pub 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
123/// Initialize a peer set like [`init`], additionally pinning block inventory
124/// broadcasts to the inbound peers at `block_gossip_peer_ips`.
125///
126/// Inbound peers connecting from these IP addresses always receive block
127/// inventory broadcasts, and one inbound connection slot is reserved for them.
128/// This is used by zcashd-compat mode, where a zcashd wallet sidecar makes a
129/// single P2P connection to this node and must reliably learn about new blocks.
130pub 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        // Leave enough room for a misbehaviour update on every peer connection
158        // before the channel is drained.
159        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            // Batch misbehaviour updates so peers can't keep the address book mutex locked
169            // by repeatedly sending invalid blocks or transactions.
170            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    // Create a broadcast channel for peer inventory advertisements.
199    // If it reaches capacity, this channel drops older inventory advertisements.
200    //
201    // When Zebra is at the chain tip with an up-to-date mempool,
202    // we expect to have at most 1 new transaction per connected peer,
203    // and 1-2 new blocks across the entire network.
204    // (The block syncer and mempool crawler handle bulk fetches of blocks and transactions.)
205    let (inv_sender, inv_receiver) = broadcast::channel(config.peerset_total_connection_limit());
206
207    // Construct services that handle inbound handshakes and perform outbound
208    // handshakes. These use the same handshake service internally to detect
209    // self-connection attempts. Both are decorated with a tower TimeoutLayer to
210    // enforce timeouts as specified in the Config.
211    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    // Create an mpsc channel for peer changes,
233    // based on the maximum number of inbound and outbound peers.
234    //
235    // The connection limit does not apply to errors,
236    // so they need to be handled before sending to this channel.
237    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    // Create an mpsc channel for peerset demand signaling,
245    // based on the maximum number of outbound peers.
246    let (mut demand_tx, demand_rx) =
247        futures::channel::mpsc::channel::<MorePeers>(config.peerset_outbound_connection_limit());
248
249    // Create a oneshot to send background task JoinHandles to the peer set
250    let (handle_tx, handle_rx) = tokio::sync::oneshot::channel();
251
252    // Connect the rx end to a PeerSet, wrapping new peers in load instruments.
253    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    // Connect peerset_tx to the 3 peer sources:
268    //
269    // 1. Incoming peer connections, via a listener.
270    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    // 2. Initial peers, specified in the config and cached on disk.
282    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    // 3. Outgoing peers we connect to in response to load.
291    let mut candidates = CandidateSet::new(address_book.clone(), peer_set.clone());
292
293    // Wait for the initial seed peer count
294    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    // We need to await candidates.update() here,
301    // because zcashd rate-limits `addr`/`addrv2` messages per connection,
302    // and if we only have one initial peer,
303    // we need to ensure that its `Response::Addr` is used by the crawler.
304    //
305    // TODO: this might not be needed after we added the Connection peer address cache,
306    //       try removing it in a future release?
307    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    // Compute remaining connections to open.
314    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    // Start the peer crawler
323    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    // Start the peer disk cache updater
336    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/// Use the provided `outbound_connector` to connect to the configured DNS seeder and
352/// disk cache initial peers, then send the resulting peer connections over `peerset_tx`.
353///
354/// Also sends every initial peer address to the `address_book_updater`.
355#[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    // TODO: update when we add Tor peers or other kinds of addresses.
383    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    // # Security
392    //
393    // Resists distributed denial of service attacks by making sure that
394    // new peer connections are initiated at least `MIN_OUTBOUND_PEER_CONNECTION_INTERVAL` apart.
395    //
396    // # Correctness
397    //
398    // Each `FuturesUnordered` can hold one `Buffer` or `Batch` reservation for
399    // an indefinite period. We can use `FuturesUnordered` without filling
400    // the underlying network buffers, because we immediately drive this
401    // single `FuturesUnordered` to completion, and handshakes have a short timeout.
402    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            // Spawn a new task to make the outbound connection.
414            tokio::spawn(
415                async move {
416                    // Only spawn one outbound connector per
417                    // `MIN_OUTBOUND_PEER_CONNECTION_INTERVAL`,
418                    // by sleeping for the interval multiplied by the peer's index in the list.
419                    sleep(
420                        constants::MIN_OUTBOUND_PEER_CONNECTION_INTERVAL.saturating_mul(i as u32),
421                    )
422                    .await;
423
424                    // As soon as we create the connector future,
425                    // the handshake starts running as a spawned task.
426                    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                // The connection limit makes sure this send doesn't block
449                peerset_tx.send(change).await?;
450            }
451            Err((addr, ref e)) => {
452                handshake_error_total += 1;
453
454                // this is verbose, but it's better than just hanging with no output when there are errors
455                let mut expected_error = false;
456                if let Some(io_error) = e.downcast_ref::<tokio::io::Error>() {
457                    // Some systems only have IPv4, or only have IPv6,
458                    // so these errors are not particularly interesting.
459                    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        // Security: Let other tasks run after each connection is processed.
485        //
486        // Avoids remote peers starving other Zebra tasks using initial connection successes or errors.
487        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
501/// Limit the number of `initial_peers` addresses entries to the configured
502/// `peerset_initial_target_size`.
503///
504/// Returns randomly chosen entries from the provided set of addresses,
505/// in a random order.
506///
507/// Also sends every initial peer to the `address_book_updater`.
508async 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    // Filter out invalid initial peers, and prioritise valid peers for initial connections.
524    // (This treats initial peers the same way we treat gossiped peers.)
525    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    // Send every initial peer to the address book, in preferred order.
542    // (This treats initial peers the same way we treat gossiped peers.)
543    //
544    // # Security
545    //
546    // Initial peers are limited because:
547    // - the number of initial peers is limited
548    // - this code only runs once at startup
549    for peer in preferred_peers.values().flatten() {
550        let peer_addr = MetaAddr::new_initial_peer(*peer);
551        // `send` only waits when the channel is full.
552        // The address book updater runs in its own thread, so we will only wait for a short time.
553        let _ = address_book_updater.send(peer_addr).await;
554    }
555
556    // Split out the `initial_peers` that will be shuffled and returned,
557    // choosing preferred peers first.
558    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/// Open a peer connection listener on `config.listen_addr`,
577/// returning the opened [`TcpListener`], and the address it is bound to.
578///
579/// If the listener is configured to use an automatically chosen port (port `0`),
580/// then the returned address will contain the actual port.
581///
582/// # Panics
583///
584/// If opening the listener fails.
585#[instrument(skip(config), fields(addr = ?config.listen_addr))]
586pub(crate) async fn open_listener(config: &Config) -> (TcpListener, SocketAddr) {
587    // Warn if we're configured using the wrong network port.
588    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/// Listens for peer connections on `addr`, then sets up each connection as a
626/// Zcash peer.
627///
628/// Uses `handshaker` to perform a Zcash network protocol handshake, and sends
629/// the [`peer::Client`] result over `peerset_tx`.
630///
631/// Limits the number of active inbound connections based on `config`,
632/// and waits `min_inbound_peer_connection_interval` between connections.
633#[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    // Reserve one inbound slot per configured sidecar IP, always leaving at
660    // least one public slot. Reserved slots are a fast path, not a cap:
661    // when they are taken, further connections from listed IPs compete for
662    // public slots like any other peer, so an unrelated local process (or a
663    // sidecar reconnecting before its dead connection is noticed) can never
664    // lock the real sidecar out of the node.
665    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    // Keeping an unresolved future in the pool means the stream never terminates.
679    handshakes.push(future::pending().boxed());
680
681    loop {
682        // Check for panics in finished tasks, before accepting new connections
683        let inbound_result = tokio::select! {
684            biased;
685            next_handshake_res = handshakes.next() => match next_handshake_res {
686                // The task has already sent the peer change to the peer set.
687                Some(()) => continue,
688                None => unreachable!("handshakes never terminates, because it contains a future that never resolves"),
689            },
690
691            // This future must wait until new connections are available: it can't have a timeout.
692            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            // # Security
699            //
700            // Check bans in both the raw and canonical representations: on a
701            // dual-stack listener, an IPv4 peer can connect as IPv4-mapped IPv6,
702            // and it must not dodge a ban stored for its IPv4 address and then
703            // be granted the zcashd-compat sidecar privileges below.
704            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            // The peer already opened a connection to us.
720            // So we want to increment the connection count as soon as possible.
721            //
722            // Sidecar-listed IPs use a reserved slot when one is free, and
723            // otherwise fall back to competing for a public slot (including
724            // the recent-IP rate limit), so a taken reserved slot delays a
725            // sidecar rather than locking it out.
726            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                // Too many open inbound connections or pending handshakes already.
737                // Close the connection.
738                std::mem::drop(tcp_stream);
739                // Allow invalid connections to be cleared quickly,
740                // but still put a limit on our CPU and network usage from failed connections.
741                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            // Rate-limit inbound connection handshakes.
765            // But sleep longer after a successful connection,
766            // so we can clear out failed connections at a higher rate.
767            //
768            // If there is a flood of connections,
769            // this stops Zebra overloading the network with handshake data.
770            //
771            // Zebra can't control how many queued connections are waiting,
772            // but most OSes also limit the number of queued inbound connections on a listener port.
773            tokio::time::sleep(min_inbound_peer_connection_interval).await;
774        } else {
775            // Allow invalid connections to be cleared quickly,
776            // but still put a limit on our CPU and network usage from failed connections.
777            debug!(?inbound_result, "error accepting inbound connection");
778            tokio::time::sleep(constants::MIN_INBOUND_PEER_FAILED_CONNECTION_INTERVAL).await;
779        }
780
781        // Security: Let other tasks run after each connection is processed.
782        //
783        // Avoids remote peers starving other Zebra tasks using inbound connection successes or
784        // errors.
785        //
786        // Preventing a denial of service is important in this code, so we want to sleep *and* make
787        // the next connection after other tasks have run. (Sleeps are not guaranteed to do that.)
788        tokio::task::yield_now().await;
789    }
790}
791
792/// Set up a new inbound connection as a Zcash peer.
793///
794/// Uses `handshaker` to perform a Zcash network protocol handshake, and sends
795/// the [`peer::Client`] result over `peerset_tx`.
796//
797// TODO: when we support inbound proxies, distinguish between proxied listeners and
798//       direct listeners in the span generated by this instrument macro
799#[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    // # Correctness
817    //
818    // Holding the drop guard returned by Span::enter across .await points will
819    // result in incorrect traces if it yields.
820    //
821    // This await is okay because the handshaker's `poll_ready` method always returns Ready.
822    handshaker.ready().await?;
823
824    // Construct a handshake future but do not drive it yet....
825    let handshake = handshaker.call(HandshakeRequest {
826        data_stream: tcp_stream,
827        connected_addr,
828        connection_tracker,
829    });
830    // ... instead, spawn a new task to handle this connection
831    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                // The connection limit makes sure this send doesn't block
839                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
850/// An action that the peer crawler can take.
851enum CrawlerAction {
852    /// Drop the demand signal because there are too many pending handshakes.
853    DemandDrop,
854    /// Initiate a handshake to the next candidate peer in response to demand.
855    ///
856    /// If there are no available candidates, crawl existing peers.
857    DemandHandshakeOrCrawl,
858    /// Crawl existing peers for more peers in response to a timer `tick`.
859    TimerCrawl { tick: Instant },
860    /// Clear a finished handshake.
861    HandshakeFinished,
862    /// Clear a finished demand crawl (DemandHandshakeOrCrawl with no peers).
863    DemandCrawlFinished,
864    /// Clear a finished TimerCrawl.
865    TimerCrawlFinished,
866}
867
868/// Given a channel `demand_rx` that signals a need for new peers, try to find
869/// and connect to new peers, and send the resulting `peer::Client`s through the
870/// `peerset_tx` channel.
871///
872/// Crawl for new peers every `config.crawl_new_peer_interval`.
873/// Also crawl whenever there is demand, but no new peers in `candidates`.
874/// After crawling, try to connect to one new peer using `outbound_connector`.
875///
876/// If a handshake fails, restore the unused demand signal by sending it to
877/// `demand_tx`.
878///
879/// The crawler terminates when `candidates.update()` or `peerset_tx` returns a
880/// permanent internal error. Transient errors and individual peer errors should
881/// be handled within the crawler.
882///
883/// Uses `active_outbound_connections` to limit the number of active outbound connections
884/// across both the initial peers and crawler. The limit is based on `config`.
885#[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    // # Concurrency
932    //
933    // Allow tasks using the candidate set to be spawned, so they can run concurrently.
934    // Previously, Zebra has had deadlocks and long hangs caused by running dependent
935    // candidate set futures in the same async task.
936    let candidates = Arc::new(futures::lock::Mutex::new(candidates));
937
938    // This contains both crawl and handshake tasks.
939    let mut handshakes: FuturesUnordered<
940        Pin<Box<dyn Future<Output = Result<CrawlerAction, BoxError>> + Send>>,
941    > = FuturesUnordered::new();
942    // <FuturesUnordered as Stream> returns None when empty.
943    // Keeping an unresolved future in the pool means the stream never terminates.
944    handshakes.push(future::pending().boxed());
945
946    let mut crawl_timer = tokio::time::interval(config.crawl_new_peer_interval);
947    // If the crawl is delayed, also delay all future crawls.
948    // (Shorter intervals just add load, without any benefit.)
949    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    // # Concurrency
954    //
955    // To avoid hangs and starvation, the crawler must spawn a separate task for each crawl
956    // and handshake, so they can make progress independently (and avoid deadlocking each other).
957    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            // Check for completed handshakes first, because the rest of the app needs them.
968            // Pending handshakes are limited by the connection limit.
969            next_handshake_res = handshakes.next() => next_handshake_res.expect(
970                "handshakes never terminates, because it contains a future that never resolves"
971            ),
972            // The timer is rate-limited
973            next_timer = crawl_timer.next() => Ok(next_timer.expect("timers never terminate")),
974            // Turn any new demand into an action, based on the crawler's current state.
975            //
976            // # Concurrency
977            //
978            // Demand is potentially unlimited, so it must go last in a biased select!.
979            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                    // Too many open outbound connections or pending handshakes already
982                    DemandDrop
983                } else {
984                    DemandHandshakeOrCrawl
985                }
986            })
987        };
988
989        match crawler_action {
990            // Dummy actions
991            Ok(DemandDrop) => {
992                // This is set to trace level because when the peerset is
993                // congested it can generate a lot of demand signal very rapidly.
994                trace!("too many open connections or in-flight handshakes, dropping demand signal");
995            }
996
997            // Spawned tasks
998            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                // Increment the connection count before we spawn the connection.
1006                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                // Spawn each handshake or crawl into an independent task, so handshakes can make
1011                // progress while crawls are running.
1012                //
1013                // # Concurrency
1014                //
1015                // The peer crawler must be able to make progress even if some handshakes are
1016                // rate-limited. So the async mutex and next peer timeout are awaited inside the
1017                // spawned task.
1018                let handshake_or_crawl_handle = tokio::spawn(
1019                    async move {
1020                        // Try to get the next available peer for a handshake.
1021                        //
1022                        // candidates.next() has a short timeout, and briefly holds the address
1023                        // book lock, so it shouldn't hang.
1024                        //
1025                        // Hold the lock for as short a time as possible.
1026                        let candidate = { candidates.lock().await.next().await };
1027
1028                        if let Some(candidate) = candidate {
1029                            // we don't need to spawn here, because there's nothing running concurrently
1030                            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                            // There weren't any peers, so try to get more peers.
1044                            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            // Completed spawned tasks
1081            Ok(HandshakeFinished) => {
1082                // Already logged in dial()
1083            }
1084            Ok(DemandCrawlFinished) => {
1085                // This is set to trace level because when the peerset is
1086                // congested it can generate a lot of demand signal very rapidly.
1087                trace!("demand-based crawl finished");
1088            }
1089            Ok(TimerCrawlFinished) => {
1090                debug!("timer-based crawl finished");
1091            }
1092
1093            // Fatal errors and shutdowns
1094            Err(error) => {
1095                info!(?error, "crawler task exiting due to an error");
1096                return Err(error);
1097            }
1098        }
1099
1100        // Security: Let other tasks run after each crawler action is processed.
1101        //
1102        // Avoids remote peers starving other Zebra tasks using outbound connection errors.
1103        tokio::task::yield_now().await;
1104    }
1105}
1106
1107/// Try to get more peers using `candidates`, then queue a connection attempt using `demand_tx`.
1108/// If there were no new peers and `should_always_dial` is false, the connection attempt is skipped.
1109#[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    // update() has timeouts, and briefly holds the address book
1120    // lock, so it shouldn't hang.
1121    // Try to get new peers, holding the lock for as short a time as possible.
1122    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 we got more peers, try to connect to a new peer on our next loop.
1139    //
1140    // # Security
1141    //
1142    // Update attempts are rate-limited by the candidate set,
1143    // and we only try peers if there was actually an update.
1144    //
1145    // So if all peers have had a recent attempt, and there was recent update
1146    // with no peers, the channel will drain. This prevents useless update attempt
1147    // loops.
1148    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                // Zebra is shutting down
1152                return Err(send_error.into());
1153            }
1154        }
1155    }
1156
1157    Ok(())
1158}
1159
1160/// Try to connect to `candidate` using `outbound_connector`.
1161/// Uses `outbound_connection_tracker` to track the active connection count.
1162///
1163/// On success, sends peers to `peerset_tx`.
1164/// On failure, marks the peer as failed in the address book,
1165/// then re-adds demand to `demand_tx`.
1166#[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    // If Zebra only has a few connections, we log connection failures at info level,
1194    // so users can diagnose and fix the problem. This defines the threshold for info logs.
1195    const MAX_CONNECTIONS_FOR_INFO_LOG: usize = 5;
1196
1197    // # Correctness
1198    //
1199    // To avoid hangs, the dialer must only await:
1200    // - functions that return immediately, or
1201    // - functions that have a reasonable timeout
1202
1203    debug!(?candidate.addr, "attempting outbound connection in response to demand");
1204
1205    // the connector is always ready, so this can't hang
1206    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    // the handshake has timeouts, so it shouldn't hang
1214    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            // The connection limit makes sure this send doesn't block.
1221            peerset_tx.send((address, client)).await?;
1222        }
1223        // The connection was never opened, or it failed the handshake and was dropped.
1224        Err(error) => {
1225            // Silence verbose info logs in production, but keep logs if the number of connections is low.
1226            // Also silence them completely in tests.
1227            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            // The demand signal that was taken out of the queue to attempt to connect to the
1235            // failed candidate never turned into a connection, so add it back.
1236            //
1237            // # Security
1238            //
1239            // Handshake failures are rate-limited by peer attempt timeouts.
1240            if let Err(send_error) = demand_tx.try_send(MorePeers) {
1241                if send_error.is_disconnected() {
1242                    // Zebra is shutting down
1243                    return Err(send_error.into());
1244                }
1245            }
1246        }
1247    }
1248
1249    Ok(())
1250}
1251
1252/// Mark `addr` as a failed peer to `address_book_updater`.
1253#[instrument(skip(address_book_updater))]
1254async fn report_failed(
1255    address_book_updater: tokio::sync::mpsc::Sender<MetaAddrChange>,
1256    addr: MetaAddr,
1257) {
1258    // The connection info is the same as what's already in the address book.
1259    let addr = MetaAddr::new_errored(addr.addr, None);
1260
1261    // Ignore send errors on Zebra shutdown.
1262    let _ = address_book_updater.send(addr).await;
1263}