Skip to main content

zebra_network/peer_set/
set.rs

1//! Abstractions that represent "the rest of the network".
2//!
3//! # Implementation
4//!
5//! The [`PeerSet`] implementation is adapted from the one in [tower::Balance][tower-balance].
6//!
7//! As described in Tower's documentation, it:
8//!
9//! > Distributes requests across inner services using the [Power of Two Choices][p2c].
10//! >
11//! > As described in the [Finagle Guide][finagle]:
12//! >
13//! > > The algorithm randomly picks two services from the set of ready endpoints and
14//! > > selects the least loaded of the two. By repeatedly using this strategy, we can
15//! > > expect a manageable upper bound on the maximum load of any server.
16//! > >
17//! > > The maximum load variance between any two servers is bound by `ln(ln(n))` where
18//! > > `n` is the number of servers in the cluster.
19//!
20//! The Power of Two Choices should work well for many network requests, but not all of them.
21//! Some requests should only be made to a subset of connected peers.
22//! For example, a request for a particular inventory item
23//! should be made to a peer that has recently advertised that inventory hash.
24//! Other requests require broadcasts, such as transaction diffusion.
25//!
26//! Implementing this specialized routing logic inside the `PeerSet` -- so that
27//! it continues to abstract away "the rest of the network" into one endpoint --
28//! is not a problem, as the `PeerSet` can simply maintain more information on
29//! its peers and route requests appropriately. However, there is a problem with
30//! maintaining accurate backpressure information, because the `Service` trait
31//! requires that service readiness is independent of the data in the request.
32//!
33//! For this reason, in the future, this code will probably be refactored to
34//! address this backpressure mismatch. One possibility is to refactor the code
35//! so that one entity holds and maintains the peer set and metadata on the
36//! peers, and each "backpressure category" of request is assigned to different
37//! `Service` impls with specialized `poll_ready()` implementations. Another
38//! less-elegant solution (which might be useful as an intermediate step for the
39//! inventory case) is to provide a way to borrow a particular backing service,
40//! say by address.
41//!
42//! [finagle]: https://twitter.github.io/finagle/guide/Clients.html#power-of-two-choices-p2c-least-loaded
43//! [p2c]: http://www.eecs.harvard.edu/~michaelm/postscripts/handbook2001.pdf
44//! [tower-balance]: https://github.com/tower-rs/tower/tree/master/tower/src/balance
45//!
46//! # Behavior During Network Upgrades
47//!
48//! [ZIP-201] specifies peer behavior during network upgrades:
49//!
50//! > With scheduled network upgrades, at the activation height, nodes on each consensus branch
51//! > should disconnect from nodes on other consensus branches and only accept new incoming
52//! > connections from nodes on the same consensus branch.
53//!
54//! Zebra handles this with the help of [`MinimumPeerVersion`], which determines the minimum peer
55//! protocol version to accept based on the current best chain tip height. The minimum version is
56//! therefore automatically increased when the block height reaches a network upgrade's activation
57//! height. The helper type is then used to:
58//!
59//! - cancel handshakes to outdated peers, in `handshake::negotiate_version`
60//! - cancel requests to and disconnect from peers that have become outdated, in
61//!   [`PeerSet::push_unready`]
62//! - disconnect from peers that have just responded and became outdated, in
63//!   [`PeerSet::poll_unready`]
64//! - disconnect from idle peers that have become outdated, in
65//!   [`PeerSet::disconnect_from_outdated_peers`]
66//!
67//! ## Network Coalescence
68//!
69//! [ZIP-201] also specifies how Zcashd behaves [leading up to a activation
70//! height][1]. Since Zcashd limits the number of connections to at most eight
71//! peers, it will gradually migrate its connections to up-to-date peers as it
72//! approaches the activation height.
73//!
74//! The motivation for this behavior is to avoid an abrupt partitioning the network, which can lead
75//! to isolated peers and increases the chance of an eclipse attack on some peers of the network.
76//!
77//! Zebra does not gradually migrate its peers as it approaches an activation height. This is
78//! because Zebra by default can connect to up to 75 peers, as can be seen in [`Config::default`].
79//! Since this is a lot larger than the 8 peers Zcashd connects to, an eclipse attack becomes a lot
80//! more costly to execute, and the probability of an abrupt network partition that isolates peers
81//! is lower.
82//!
83//! Even if a Zebra node is manually configured to connect to a smaller number
84//! of peers, the [`AddressBook`][2] is configured to hold a large number of
85//! peer addresses ([`MAX_ADDRS_IN_ADDRESS_BOOK`][3]). Since the address book
86//! prioritizes addresses it trusts (like those that it has successfully
87//! connected to before), the node should be able to recover and rejoin the
88//! network by itself, as long as the address book is populated with enough
89//! entries.
90//!
91//! [1]: https://zips.z.cash/zip-0201#network-coalescence
92//! [2]: crate::AddressBook
93//! [3]: crate::constants::MAX_ADDRS_IN_ADDRESS_BOOK
94//! [ZIP-201]: https://zips.z.cash/zip-0201
95
96use std::{
97    collections::{HashMap, HashSet},
98    convert,
99    fmt::Debug,
100    marker::PhantomData,
101    net::IpAddr,
102    pin::Pin,
103    sync::Arc,
104    task::{Context, Poll},
105    time::Instant,
106};
107
108use futures::{
109    channel::{mpsc, oneshot},
110    future::{FutureExt, TryFutureExt},
111    prelude::*,
112    stream::FuturesUnordered,
113    task::noop_waker,
114};
115use indexmap::IndexMap;
116use itertools::Itertools;
117use num_integer::div_ceil;
118use tokio::{
119    sync::{broadcast, mpsc as tokio_mpsc, watch},
120    task::JoinHandle,
121};
122use tower::{
123    discover::{Change, Discover},
124    load::Load,
125    Service,
126};
127
128use zebra_chain::{chain_tip::ChainTip, parameters::Network};
129
130use crate::{
131    address_book::AddressMetrics,
132    constants::MIN_PEER_SET_LOG_INTERVAL,
133    peer::{LoadTrackedClient, MinimumPeerVersion},
134    peer_set::{
135        stall_tracker::FindResponseStallTracker,
136        unready_service::{Error as UnreadyError, UnreadyService},
137        InventoryChange, InventoryRegistry,
138    },
139    protocol::{
140        external::InventoryHash,
141        internal::{Request, Response},
142    },
143    BoxError, Config, PeerError, PeerSocketAddr, SharedPeerError,
144};
145
146#[cfg(test)]
147mod tests;
148
149/// A signal sent by the [`PeerSet`] when it has no ready peers, and gets a request from Zebra.
150///
151/// In response to this signal, the crawler tries to open more peer connections.
152#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
153pub struct MorePeers;
154
155/// A signal sent by the [`PeerSet`] to cancel a [`Client`][1]'s current request
156/// or response.
157///
158/// When it receives this signal, the [`Client`][1] stops processing and exits.
159///
160/// [1]: crate::peer::Client
161#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
162pub struct CancelClientWork;
163
164type ResponseFuture = Pin<Box<dyn Future<Output = Result<Response, BoxError>> + Send + 'static>>;
165
166/// Classification of a `FindBlocks`/`FindHeaders` response, sent from a
167/// response-wrapping future to [`PeerSet::poll_ready`] via an mpsc channel so
168/// the stall tracker can be updated and the peer disconnected if needed.
169#[derive(Copy, Clone, Debug, PartialEq, Eq)]
170enum StallOutcome {
171    Stall,
172    Clear,
173}
174
175fn classify_find_response<E>(result: &Result<Response, E>) -> Option<StallOutcome> {
176    match result {
177        Ok(Response::BlockHashes(hashes)) if hashes.is_empty() => Some(StallOutcome::Stall),
178        Ok(Response::BlockHashes(_)) => Some(StallOutcome::Clear),
179        Ok(Response::BlockHeaders(headers)) if headers.is_empty() => Some(StallOutcome::Stall),
180        Ok(Response::BlockHeaders(_)) => Some(StallOutcome::Clear),
181        Ok(_) => None,
182        Err(_) => Some(StallOutcome::Stall),
183    }
184}
185
186/// A [`tower::Service`] that abstractly represents "the rest of the network".
187///
188/// # Security
189///
190/// The `Discover::Key` must be the transient remote address of each peer. This
191/// address may only be valid for the duration of a single connection. (For
192/// example, inbound connections have an ephemeral remote port, and proxy
193/// connections have an ephemeral local or proxy port.)
194///
195/// Otherwise, malicious peers could interfere with other peers' `PeerSet` state.
196pub struct PeerSet<D, C>
197where
198    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
199    D::Error: Into<BoxError>,
200    C: ChainTip,
201{
202    // Peer Tracking: New Peers
203    //
204    /// Provides new and deleted peer [`Change`]s to the peer set,
205    /// via the [`Discover`] trait implementation.
206    discover: D,
207
208    /// A channel that asks the peer crawler task to connect to more peers.
209    demand_signal: mpsc::Sender<MorePeers>,
210
211    /// A watch channel receiver with a copy of banned IP addresses.
212    bans_receiver: watch::Receiver<Arc<IndexMap<IpAddr, std::time::Instant>>>,
213
214    /// Tracks peers returning empty `FindBlocks`/`FindHeaders` responses.
215    /// Mutated only from [`Self::poll_ready`] via [`Self::stall_event_rx`].
216    find_response_stalls: FindResponseStallTracker,
217
218    /// Receives stall/clear events from tracked routing futures in
219    /// [`Self::route_p2c`]. The channel keeps the tracker single-owner (no
220    /// `Mutex`) and confines mutation to `poll_ready`, where the peer set can
221    /// call [`Self::remove`] directly.
222    stall_event_rx: tokio_mpsc::UnboundedReceiver<(PeerSocketAddr, StallOutcome)>,
223
224    /// Producer clones handed to each tracked request's response wrapper.
225    stall_event_tx: tokio_mpsc::UnboundedSender<(PeerSocketAddr, StallOutcome)>,
226
227    // Peer Tracking: Ready Peers
228    //
229    /// Connected peers that are ready to receive requests from Zebra,
230    /// or send requests to Zebra.
231    ready_services: HashMap<D::Key, D::Service>,
232
233    // Request Routing
234    //
235    /// Stores gossiped inventory hashes from connected peers.
236    ///
237    /// Used to route inventory requests to peers that are likely to have it.
238    inventory_registry: InventoryRegistry,
239
240    /// Stores requests that should be routed to peers once they are ready.
241    queued_broadcast_all: Option<(
242        Request,
243        tokio::sync::mpsc::Sender<ResponseFuture>,
244        HashSet<D::Key>,
245    )>,
246
247    /// Inbound peer IPs that must always receive block inventory broadcasts.
248    block_gossip_peer_ips: HashSet<IpAddr>,
249
250    /// The keys of connected peers that matched [`Self::block_gossip_peer_ips`]
251    /// when they were inserted into the peer set.
252    ///
253    /// Stale keys of disconnected peers are pruned by
254    /// [`Self::prune_disconnected_sidecar_keys`].
255    zcashd_compat_peer_keys: HashSet<D::Key>,
256
257    /// The most recent sidecar broadcast (a block advert or a pushed
258    /// transaction) that has not been delivered to all connected zcashd-compat
259    /// sidecar peers, and the sidecar peers that are still owed it.
260    ///
261    /// A sidecar can be busy with another request when a broadcast is routed.
262    /// The request is queued here and delivered as soon as the sidecar is
263    /// ready again, so configured sidecars never miss block gossip. A newer
264    /// broadcast replaces an older undelivered one, even one of a different
265    /// kind.
266    queued_sidecar_broadcast: Option<(Request, HashSet<D::Key>)>,
267
268    // Peer Tracking: Busy Peers
269    //
270    /// Connected peers that are handling a Zebra request,
271    /// or Zebra is handling one of their requests.
272    unready_services: FuturesUnordered<UnreadyService<D::Key, D::Service, Request>>,
273
274    /// Channels used to cancel the request that an unready service is doing.
275    cancel_handles: HashMap<D::Key, oneshot::Sender<CancelClientWork>>,
276
277    // Peer Validation
278    //
279    /// An endpoint to see the minimum peer protocol version in real time.
280    ///
281    /// The minimum version depends on the block height, and [`MinimumPeerVersion`] listens for
282    /// height changes and determines the correct minimum version.
283    minimum_peer_version: MinimumPeerVersion<C>,
284
285    /// The configured limit for inbound and outbound connections.
286    ///
287    /// The peer set panics if this size is exceeded.
288    /// If that happens, our connection limit code has a bug.
289    peerset_total_connection_limit: usize,
290
291    // Background Tasks
292    //
293    /// Channel for passing ownership of tokio JoinHandles from PeerSet's background tasks
294    ///
295    /// The join handles passed into the PeerSet are used populate the `guards` member
296    handle_rx: tokio::sync::oneshot::Receiver<Vec<JoinHandle<Result<(), BoxError>>>>,
297
298    /// Unordered set of handles to background tasks associated with the `PeerSet`
299    ///
300    /// These guards are checked for errors as part of `poll_ready` which lets
301    /// the `PeerSet` propagate errors from background tasks back to the user
302    guards: futures::stream::FuturesUnordered<JoinHandle<Result<(), BoxError>>>,
303
304    // Metrics and Logging
305    //
306    /// Address book metrics watch channel.
307    ///
308    /// Used for logging diagnostics.
309    address_metrics: watch::Receiver<AddressMetrics>,
310
311    /// The last time we logged a message about the peer set size
312    last_peer_log: Option<Instant>,
313
314    /// The configured maximum number of peers that can be in the
315    /// peer set per IP, defaults to [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`]
316    max_conns_per_ip: usize,
317
318    /// The network of this peer set.
319    network: Network,
320}
321
322impl<D, C> Drop for PeerSet<D, C>
323where
324    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
325    D::Error: Into<BoxError>,
326    C: ChainTip,
327{
328    fn drop(&mut self) {
329        // We don't have access to the current task (if any), so we just drop everything we can.
330        let waker = noop_waker();
331        let mut cx = Context::from_waker(&waker);
332
333        self.shut_down_tasks_and_channels(&mut cx);
334    }
335}
336
337impl<D, C> PeerSet<D, C>
338where
339    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
340    D::Error: Into<BoxError>,
341    C: ChainTip,
342{
343    #[allow(clippy::too_many_arguments)]
344    /// Construct a peerset which uses `discover` to manage peer connections.
345    ///
346    /// Arguments:
347    /// - `config`: configures the peer set connection limit;
348    /// - `block_gossip_peer_ips`: inbound peer IPs that must always receive block inventory broadcasts.
349    /// - `discover`: handles peer connects and disconnects;
350    /// - `demand_signal`: requests more peers when all peers are busy (unready);
351    /// - `handle_rx`: receives background task handles,
352    ///   monitors them to make sure they're still running,
353    ///   and shuts down all the tasks as soon as one task exits;
354    /// - `inv_stream`: receives inventory changes from peers,
355    ///   allowing the peer set to direct inventory requests;
356    /// - `bans_receiver`: receives a map of banned IP addresses that should be dropped;
357    /// - `address_book`: when peer set is busy, it logs address book diagnostics.
358    /// - `minimum_peer_version`: endpoint to see the minimum peer protocol version in real time.
359    /// - `max_conns_per_ip`: configured maximum number of peers that can be in the
360    ///   peer set per IP, defaults to the config value or to
361    ///   [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`].
362    pub fn new(
363        config: &Config,
364        block_gossip_peer_ips: Vec<IpAddr>,
365        discover: D,
366        demand_signal: mpsc::Sender<MorePeers>,
367        handle_rx: tokio::sync::oneshot::Receiver<Vec<JoinHandle<Result<(), BoxError>>>>,
368        inv_stream: broadcast::Receiver<InventoryChange>,
369        bans_receiver: watch::Receiver<Arc<IndexMap<IpAddr, std::time::Instant>>>,
370        address_metrics: watch::Receiver<AddressMetrics>,
371        minimum_peer_version: MinimumPeerVersion<C>,
372        max_conns_per_ip: Option<usize>,
373    ) -> Self {
374        let (stall_event_tx, stall_event_rx) = tokio_mpsc::unbounded_channel();
375        Self {
376            // New peers
377            discover,
378            demand_signal,
379            // Banned peers
380            bans_receiver,
381
382            // Stall tracking
383            find_response_stalls: FindResponseStallTracker::new(),
384            stall_event_rx,
385            stall_event_tx,
386
387            // Ready peers
388            ready_services: HashMap::new(),
389            // Request Routing
390            inventory_registry: InventoryRegistry::new(inv_stream),
391            queued_broadcast_all: None,
392            block_gossip_peer_ips: block_gossip_peer_ips.into_iter().collect(),
393            zcashd_compat_peer_keys: HashSet::new(),
394            queued_sidecar_broadcast: None,
395
396            // Busy peers
397            unready_services: FuturesUnordered::new(),
398            cancel_handles: HashMap::new(),
399
400            // Peer validation
401            minimum_peer_version,
402            peerset_total_connection_limit: config.peerset_total_connection_limit(),
403
404            // Background tasks
405            handle_rx,
406            guards: futures::stream::FuturesUnordered::new(),
407
408            // Metrics
409            last_peer_log: None,
410            address_metrics,
411
412            max_conns_per_ip: max_conns_per_ip.unwrap_or(config.max_connections_per_ip),
413
414            network: config.network.clone(),
415        }
416    }
417
418    /// Check background task handles to make sure they're still running.
419    ///
420    /// Never returns `Ok`.
421    ///
422    /// If any background task exits, shuts down all other background tasks,
423    /// and returns an error. Otherwise, returns `Pending`, and registers a wakeup for
424    /// receiving the background tasks, or the background tasks exiting.
425    fn poll_background_errors(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
426        futures::ready!(self.receive_tasks_if_needed(cx))?;
427
428        // Return Pending if all background tasks are still running.
429        match futures::ready!(Pin::new(&mut self.guards).poll_next(cx)) {
430            Some(res) => {
431                info!(
432                    background_tasks = %self.guards.len(),
433                    "a peer set background task exited, shutting down other peer set tasks"
434                );
435
436                self.shut_down_tasks_and_channels(cx);
437
438                // Flatten the join result and inner result, and return any errors.
439                res.map_err(Into::into)
440                    // TODO: replace with Result::flatten when it stabilises (#70142)
441                    .and_then(convert::identity)?;
442
443                // Turn Ok() task exits into errors.
444                Poll::Ready(Err("a peer set background task exited".into()))
445            }
446
447            None => {
448                self.shut_down_tasks_and_channels(cx);
449                Poll::Ready(Err("all peer set background tasks have exited".into()))
450            }
451        }
452    }
453
454    /// Receive background tasks, if they've been sent on the channel, but not consumed yet.
455    ///
456    /// Returns a result representing the current task state, or `Poll::Pending` if the background
457    /// tasks should be polled again to check their state.
458    fn receive_tasks_if_needed(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
459        if self.guards.is_empty() {
460            // Return Pending if the tasks have not been sent yet.
461            let handles = futures::ready!(Pin::new(&mut self.handle_rx).poll(cx));
462
463            match handles {
464                // The tasks have been sent, but not consumed yet.
465                Ok(handles) => {
466                    // Currently, the peer set treats an empty background task set as an error.
467                    //
468                    // TODO: refactor `handle_rx` and `guards` into an enum
469                    //       for the background task state: Waiting/Running/Shutdown.
470                    assert!(
471                        !handles.is_empty(),
472                        "the peer set requires at least one background task"
473                    );
474
475                    self.guards.extend(handles);
476
477                    Poll::Ready(Ok(()))
478                }
479
480                // The sender was dropped without sending the tasks.
481                Err(_) => Poll::Ready(Err(
482                    "sender did not send peer background tasks before it was dropped".into(),
483                )),
484            }
485        } else {
486            Poll::Ready(Ok(()))
487        }
488    }
489
490    /// Shut down:
491    /// - services by dropping the service lists
492    /// - background tasks via their join handles or cancel handles
493    /// - channels by closing the channel
494    fn shut_down_tasks_and_channels(&mut self, cx: &mut Context<'_>) {
495        // Drop services and cancel their background tasks.
496        self.ready_services = HashMap::new();
497
498        for (_peer_key, handle) in self.cancel_handles.drain() {
499            let _ = handle.send(CancelClientWork);
500        }
501        self.unready_services = FuturesUnordered::new();
502
503        // Close the MorePeers channel for all senders,
504        // so we don't add more peers to a shut down peer set.
505        self.demand_signal.close_channel();
506
507        // Shut down background tasks, ignoring pending polls.
508        self.handle_rx.close();
509        let _ = self.receive_tasks_if_needed(cx);
510        for guard in self.guards.iter() {
511            guard.abort();
512        }
513    }
514
515    /// Checks for newly ready, disconnects from outdated peers, and polls ready peer errors.
516    fn poll_peers(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
517        // Check for newly ready peers, including newly added peers (which are added as unready).
518        // So it needs to run after `poll_discover()`. Registers a wakeup if there are any unready
519        // peers.
520        //
521        // Each connected peer should become ready within a few minutes, or timeout, close the
522        // connection, and release its connection slot.
523        //
524        // TODO: drop peers that overload us with inbound messages and never become ready (#7822)
525        let _poll_pending_or_ready: Poll<Option<()>> = self.poll_unready(cx)?;
526
527        // Cleanup
528
529        // Only checks the versions of ready peers, so it needs to run after `poll_unready()`.
530        self.disconnect_from_outdated_peers();
531
532        // Check for failures in ready peers, removing newly errored or disconnected peers.
533        // So it needs to run after `poll_unready()`.
534        self.poll_ready_peer_errors(cx).map(Ok)
535    }
536
537    /// Check busy peer services for request completion or errors.
538    ///
539    /// Move newly ready services to the ready list if they are for peers with supported protocol
540    /// versions, otherwise they are dropped. Also drop failed services.
541    ///
542    /// Never returns an error.
543    ///
544    /// Returns `Ok(Some(())` if at least one peer became ready, `Poll::Pending` if there are
545    /// unready peers, but none became ready, and `Ok(None)` if the unready peers were empty.
546    ///
547    /// If there are any remaining unready peers, registers a wakeup for the next time one becomes
548    /// ready. If there are no unready peers, doesn't register any wakeups. (Since wakeups come
549    /// from peers, there needs to be at least one peer to register a wakeup.)
550    fn poll_unready(&mut self, cx: &mut Context<'_>) -> Poll<Result<Option<()>, BoxError>> {
551        let mut result = Poll::Pending;
552
553        // # Correctness
554        //
555        // `poll_next()` must always be called, because `self.unready_services` could have been
556        // empty before the call to `self.poll_ready()`.
557        //
558        // > When new futures are added, `poll_next` must be called in order to begin receiving
559        // > wake-ups for new futures.
560        //
561        // <https://docs.rs/futures/latest/futures/stream/futures_unordered/struct.FuturesUnordered.html>
562        //
563        // Returns Pending if we've finished processing the unready service changes,
564        // but there are still some unready services.
565        loop {
566            // No ready peers left, but there are some unready peers pending.
567            let Poll::Ready(ready_peer) = Pin::new(&mut self.unready_services).poll_next(cx) else {
568                break;
569            };
570
571            match ready_peer {
572                // No unready peers in the list.
573                None => {
574                    // If we've finished processing the unready service changes, and there are no
575                    // unready services left, it doesn't make sense to return Pending, because
576                    // their stream is terminated. But when we add more unready peers and call
577                    // `poll_next()`, its termination status will be reset, and it will receive
578                    // wakeups again.
579                    if result.is_pending() {
580                        result = Poll::Ready(Ok(None));
581                    }
582
583                    break;
584                }
585
586                // Unready -> Ready
587                Some(Ok((key, svc))) => {
588                    trace!(?key, "service became ready");
589
590                    if self.bans_receiver.borrow().contains_key(&key.ip()) {
591                        warn!(?key, "service is banned, dropping service");
592                        std::mem::drop(svc);
593                        let cancel = self.cancel_handles.remove(&key);
594                        debug_assert!(
595                            cancel.is_some(),
596                            "missing cancel handle for banned unready peer"
597                        );
598                        continue;
599                    }
600
601                    self.push_ready(true, key, svc);
602
603                    // Return Ok if at least one peer became ready.
604                    result = Poll::Ready(Ok(Some(())));
605                }
606
607                // Unready -> Canceled
608                Some(Err((key, UnreadyError::Canceled))) => {
609                    // A service be canceled because we've connected to the same service twice.
610                    // In that case, there is a cancel handle for the peer address,
611                    // but it belongs to the service for the newer connection.
612                    trace!(
613                        ?key,
614                        duplicate_connection = self.cancel_handles.contains_key(&key),
615                        "service was canceled, dropping service"
616                    );
617                }
618                Some(Err((key, UnreadyError::CancelHandleDropped(_)))) => {
619                    // Similarly, services with dropped cancel handes can have duplicates.
620                    trace!(
621                        ?key,
622                        duplicate_connection = self.cancel_handles.contains_key(&key),
623                        "cancel handle was dropped, dropping service"
624                    );
625                }
626
627                // Unready -> Errored
628                Some(Err((key, UnreadyError::Inner(error)))) => {
629                    debug!(%error, "service failed while unready, dropping service");
630
631                    let cancel = self.cancel_handles.remove(&key);
632                    assert!(cancel.is_some(), "missing cancel handle");
633                }
634            }
635        }
636
637        result
638    }
639
640    /// Checks previously ready peer services for errors.
641    ///
642    /// The only way these peer `Client`s can become unready is when we send them a request,
643    /// because the peer set has exclusive access to send requests to each peer. (If an inbound
644    /// request is in progress, it will be handled, then our request will be sent by the connection
645    /// task.)
646    ///
647    /// Returns `Poll::Ready` if there are some ready peers, and `Poll::Pending` if there are no
648    /// ready peers. Registers a wakeup if any peer has failed due to a disconnection, hang, or protocol error.
649    ///
650    /// # Panics
651    ///
652    /// If any peers somehow became unready without being sent a request. This indicates a bug in the peer set, where requests
653    /// are sent to peers without putting them in `unready_peers`.
654    fn poll_ready_peer_errors(&mut self, cx: &mut Context<'_>) -> Poll<()> {
655        let mut previous = HashMap::new();
656        std::mem::swap(&mut previous, &mut self.ready_services);
657
658        // TODO: consider only checking some peers each poll (for performance reasons),
659        //       but make sure we eventually check all of them.
660        for (key, mut svc) in previous.drain() {
661            let Poll::Ready(peer_readiness) = Pin::new(&mut svc).poll_ready(cx) else {
662                unreachable!(
663                    "unexpected unready peer: peers must be put into the unready_peers list \
664                     after sending them a request"
665                );
666            };
667
668            match peer_readiness {
669                // Still ready, add it back to the list.
670                Ok(()) => {
671                    if self.bans_receiver.borrow().contains_key(&key.ip()) {
672                        debug!(?key, "service ip is banned, dropping service");
673                        std::mem::drop(svc);
674                        continue;
675                    }
676
677                    self.push_ready(false, key, svc)
678                }
679
680                // Ready -> Errored
681                Err(error) => {
682                    debug!(%error, "service failed while ready, dropping service");
683
684                    // Ready services can just be dropped, they don't need any cleanup.
685                    std::mem::drop(svc);
686                }
687            }
688        }
689
690        if self.ready_services.is_empty() {
691            Poll::Pending
692        } else {
693            Poll::Ready(())
694        }
695    }
696
697    /// Returns the number of peer connections Zebra already has with
698    /// the provided IP address
699    ///
700    /// # Performance
701    ///
702    /// This method is `O(connected peers)`, so it should not be called from a loop
703    /// that is already iterating through the peer set.
704    fn num_peers_with_ip(&self, ip: IpAddr) -> usize {
705        self.ready_services
706            .keys()
707            .chain(self.cancel_handles.keys())
708            .filter(|addr| addr.ip() == ip)
709            .count()
710    }
711
712    /// Returns `true` if Zebra is already connected to the IP and port in `addr`.
713    fn has_peer_with_addr(&self, addr: PeerSocketAddr) -> bool {
714        self.ready_services.contains_key(&addr) || self.cancel_handles.contains_key(&addr)
715    }
716
717    /// Processes the entire list of newly inserted or removed services.
718    ///
719    /// Puts inserted services in the unready list.
720    /// Drops removed services, after cancelling any pending requests.
721    ///
722    /// If the peer connector channel is closed, returns an error.
723    ///
724    /// Otherwise, returns `Ok` if it discovered at least one peer, or `Poll::Pending` if it didn't
725    /// discover any peers. Always registers a wakeup for new peers, even when it returns `Ok`.
726    fn poll_discover(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
727        // Return pending if there are no peers in the list.
728        let mut result = Poll::Pending;
729
730        loop {
731            // If we've emptied the list, finish looping, otherwise process the new peer.
732            let Poll::Ready(discovered) = Pin::new(&mut self.discover).poll_discover(cx) else {
733                break;
734            };
735
736            // If the change channel has a permanent error, return that error.
737            let change = discovered
738                .ok_or("discovery stream closed")?
739                .map_err(Into::into)?;
740
741            // Otherwise we have successfully processed a peer.
742            result = Poll::Ready(Ok(()));
743
744            // Process each change.
745            match change {
746                Change::Remove(key) => {
747                    trace!(?key, "got Change::Remove from Discover");
748                    self.remove(&key);
749                }
750                Change::Insert(key, svc) => {
751                    // We add peers as unready, so that we:
752                    // - always do the same checks on every ready peer, and
753                    // - check for any errors that happened right after the handshake
754                    trace!(?key, "got Change::Insert from Discover");
755
756                    // # Security
757                    //
758                    // Drop the new peer if we are already connected to it.
759                    // Preferring old connections avoids connection thrashing.
760                    if self.has_peer_with_addr(key) {
761                        std::mem::drop(svc);
762                        continue;
763                    }
764
765                    // Classify sidecars before the per-IP cap: a trusted sidecar
766                    // reconnecting from a new ephemeral port must not be blocked
767                    // by its own not-yet-swept dead connection still counting
768                    // against `max_conns_per_ip` (which defaults to 1), or the
769                    // wallet would silently stop following the chain.
770                    let is_sidecar = self.is_zcashd_compat_peer(&svc);
771
772                    // # Security
773                    //
774                    // drop the new peer if there are already `max_conns_per_ip` peers with
775                    // the same IP address in the peer set. Sidecars are exempt: they
776                    // are trusted, and the listener already caps their inbound slots.
777                    if !is_sidecar && self.num_peers_with_ip(key.ip()) >= self.max_conns_per_ip {
778                        std::mem::drop(svc);
779                        continue;
780                    }
781
782                    if is_sidecar {
783                        self.zcashd_compat_peer_keys.insert(key);
784                    }
785
786                    self.push_unready(key, svc);
787                }
788            }
789        }
790
791        result
792    }
793
794    /// Checks if the minimum peer version has changed, and disconnects from outdated peers.
795    fn disconnect_from_outdated_peers(&mut self) {
796        if let Some(minimum_version) = self.minimum_peer_version.changed() {
797            // It is ok to drop ready services, they don't need anything cancelled.
798            self.ready_services
799                .retain(|_address, peer| peer.remote_version() >= minimum_version);
800        }
801    }
802
803    /// Takes a ready service by key.
804    fn take_ready_service(&mut self, key: &D::Key) -> Option<D::Service> {
805        if let Some(svc) = self.ready_services.remove(key) {
806            assert!(
807                !self.cancel_handles.contains_key(key),
808                "cancel handles are only used for unready service work"
809            );
810
811            Some(svc)
812        } else {
813            None
814        }
815    }
816
817    /// Drains pending stall/clear events from tracked routing futures and
818    /// disconnects peers that have exceeded the stall threshold. The peer's
819    /// TCP connection is closed when its service is dropped; address book and
820    /// ban list are untouched, so the peer is free to reconnect.
821    fn drain_stall_events(&mut self, cx: &mut Context<'_>) {
822        while let Poll::Ready(Some((addr, outcome))) = self.stall_event_rx.poll_recv(cx) {
823            match outcome {
824                StallOutcome::Stall => {
825                    if self.find_response_stalls.record_stall(addr) {
826                        info!(
827                            ?addr,
828                            "dropping stalled peer: exceeded FindBlocks/FindHeaders stall threshold",
829                        );
830                        self.remove(&addr);
831                    }
832                }
833                StallOutcome::Clear => self.find_response_stalls.clear(addr),
834            }
835        }
836    }
837
838    /// Remove the service corresponding to `key` from the peer set.
839    ///
840    /// Drops the service, cancelling any pending request or response to that peer.
841    /// If the peer does not exist, does nothing.
842    fn remove(&mut self, key: &D::Key) {
843        self.find_response_stalls.clear(*key);
844        self.zcashd_compat_peer_keys.remove(key);
845        if let Some((_, remaining_sidecars)) = self.queued_sidecar_broadcast.as_mut() {
846            remaining_sidecars.remove(key);
847            if remaining_sidecars.is_empty() {
848                self.queued_sidecar_broadcast = None;
849            }
850        }
851
852        if let Some(ready_service) = self.take_ready_service(key) {
853            // A ready service has no work to cancel, so just drop it.
854            std::mem::drop(ready_service);
855        } else if let Some(handle) = self.cancel_handles.remove(key) {
856            // Cancel the work, implicitly dropping the cancel handle.
857            // The service future returns a `Canceled` error,
858            // making `poll_unready` drop the service.
859            let _ = handle.send(CancelClientWork);
860        }
861    }
862
863    /// Adds a ready service to the ready list if it's for a peer with a supported version.
864    /// If `was_unready` is true, also removes the peer's cancel handle.
865    ///
866    /// If the service is for a connection to an outdated peer, the service is dropped.
867    fn push_ready(&mut self, was_unready: bool, key: D::Key, svc: D::Service) {
868        let cancel = self.cancel_handles.remove(&key);
869        assert_eq!(
870            cancel.is_some(),
871            was_unready,
872            "missing or unexpected cancel handle"
873        );
874
875        if svc.remote_version() >= self.minimum_peer_version.current() {
876            self.ready_services.insert(key, svc);
877        } else {
878            std::mem::drop(svc);
879        }
880    }
881
882    /// Adds a busy service to the unready list if it's for a peer with a supported version,
883    /// and adds a cancel handle for the service's current request.
884    ///
885    /// If the service is for a connection to an outdated peer, the request is cancelled and the
886    /// service is dropped.
887    fn push_unready(&mut self, key: D::Key, svc: D::Service) {
888        let peer_version = svc.remote_version();
889        let (tx, rx) = oneshot::channel();
890
891        self.unready_services.push(UnreadyService {
892            key: Some(key),
893            service: Some(svc),
894            cancel: rx,
895            _req: PhantomData,
896        });
897
898        if peer_version >= self.minimum_peer_version.current() {
899            self.cancel_handles.insert(key, tx);
900        } else {
901            // Cancel any request made to the service because it is using an outdated protocol
902            // version.
903            let _ = tx.send(CancelClientWork);
904        }
905    }
906
907    /// Performs P2C on `self.ready_services` to randomly select a less-loaded ready service.
908    fn select_ready_p2c_peer(&self) -> Option<D::Key> {
909        self.select_p2c_peer_from_list(&self.ready_services.keys().copied().collect())
910    }
911
912    /// Performs P2C on `ready_service_list` to randomly select a less-loaded ready service.
913    #[allow(clippy::unwrap_in_result)]
914    fn select_p2c_peer_from_list(&self, ready_service_list: &HashSet<D::Key>) -> Option<D::Key> {
915        match ready_service_list.len() {
916            0 => None,
917            1 => Some(
918                *ready_service_list
919                    .iter()
920                    .next()
921                    .expect("just checked there is one service"),
922            ),
923            len => {
924                // Choose 2 random peers, then return the least loaded of those 2 peers.
925                let (a, b) = {
926                    let idxs = rand::seq::index::sample(&mut rand::thread_rng(), len, 2);
927                    let a = idxs.index(0);
928                    let b = idxs.index(1);
929
930                    let a = *ready_service_list
931                        .iter()
932                        .nth(a)
933                        .expect("sample returns valid indexes");
934                    let b = *ready_service_list
935                        .iter()
936                        .nth(b)
937                        .expect("sample returns valid indexes");
938
939                    (a, b)
940                };
941
942                let a_load = self.query_load(&a).expect("supplied services are ready");
943                let b_load = self.query_load(&b).expect("supplied services are ready");
944
945                let selected = if a_load <= b_load { a } else { b };
946
947                trace!(
948                    a.key = ?a,
949                    a.load = ?a_load,
950                    b.key = ?b,
951                    b.load = ?b_load,
952                    selected = ?selected,
953                    ?len,
954                    "selected service by p2c"
955                );
956
957                Some(selected)
958            }
959        }
960    }
961
962    /// Randomly chooses `max_peers` ready services, ignoring service load.
963    ///
964    /// The chosen peers are unique, but their order is not fully random.
965    fn select_random_ready_peers(&self, max_peers: usize) -> Vec<D::Key> {
966        use rand::seq::IteratorRandom;
967
968        self.ready_services
969            .keys()
970            .copied()
971            .choose_multiple(&mut rand::thread_rng(), max_peers)
972    }
973
974    /// Randomly chooses ready peers for a sidecar broadcast, always including
975    /// configured zcashd compat sidecar peers.
976    fn select_sidecar_broadcast_peers(&self, max_peers: usize) -> Vec<D::Key> {
977        use rand::seq::IteratorRandom;
978
979        let mut selected_peers: Vec<_> = self
980            .zcashd_compat_peer_keys
981            .iter()
982            .filter(|key| self.ready_services.contains_key(*key))
983            .copied()
984            .collect();
985
986        selected_peers.extend(
987            self.ready_services
988                .keys()
989                .filter(|key| !self.zcashd_compat_peer_keys.contains(key))
990                .copied()
991                .choose_multiple(&mut rand::thread_rng(), max_peers),
992        );
993
994        selected_peers
995    }
996
997    /// Returns true if `service` is a configured zcashd sidecar peer.
998    ///
999    /// Only used to classify peers once, when they are inserted into the peer
1000    /// set; every later check uses the O(1) [`Self::zcashd_compat_peer_keys`]
1001    /// set instead.
1002    fn is_zcashd_compat_peer(&self, service: &D::Service) -> bool {
1003        self.block_gossip_peer_ips
1004            .iter()
1005            .any(|ip| service.is_inbound_direct_from_ip(ip))
1006    }
1007
1008    /// Forgets sidecar keys whose peer has disconnected.
1009    ///
1010    /// A connected peer is either ready or unready with a registered cancel
1011    /// handle; a key in neither map belongs to a dropped connection. Sidecars
1012    /// reconnect from new ephemeral ports, and services are dropped on many
1013    /// paths (bans, version downgrades, cancelled requests), so this runs every
1014    /// poll cycle to keep the set from accumulating stale keys — otherwise a
1015    /// reused port could inherit a stale sidecar's stall-tracking exemption.
1016    fn prune_disconnected_sidecar_keys(&mut self) {
1017        if self.zcashd_compat_peer_keys.is_empty() {
1018            return;
1019        }
1020
1021        let ready_services = &self.ready_services;
1022        let cancel_handles = &self.cancel_handles;
1023        self.zcashd_compat_peer_keys
1024            .retain(|key| ready_services.contains_key(key) || cancel_handles.contains_key(key));
1025    }
1026
1027    /// Accesses a ready endpoint by `key` and returns its current load.
1028    ///
1029    /// Returns `None` if the service is not in the ready service list.
1030    fn query_load(&self, key: &D::Key) -> Option<<D::Service as Load>::Metric> {
1031        let svc = self.ready_services.get(key);
1032        svc.map(|svc| svc.load())
1033    }
1034
1035    /// Routes a request using P2C load-balancing.
1036    fn route_p2c(&mut self, req: Request) -> <Self as tower::Service<Request>>::Future {
1037        if let Some(p2c_key) = self.select_ready_p2c_peer() {
1038            tracing::trace!(?p2c_key, "routing based on p2c");
1039
1040            let mut svc = self
1041                .take_ready_service(&p2c_key)
1042                .expect("selected peer must be ready");
1043
1044            let is_find_request = matches!(
1045                &req,
1046                Request::FindBlocks { .. } | Request::FindHeaders { .. }
1047            );
1048            let is_syncing = || {
1049                !self
1050                    .minimum_peer_version
1051                    .chain_tip()
1052                    .is_at_or_near_network_tip(&self.network)
1053            };
1054            // zcashd-compat sidecars are exempt: they sync *from* this node,
1055            // so they can legitimately trail it without being stalled peers.
1056            let track_stalls =
1057                is_find_request && !self.zcashd_compat_peer_keys.contains(&p2c_key) && is_syncing();
1058
1059            let fut = svc.call(req);
1060            self.push_unready(p2c_key, svc);
1061
1062            if track_stalls {
1063                let stall_tx = self.stall_event_tx.clone();
1064                return async move {
1065                    let result = fut.await;
1066                    if let Some(outcome) = classify_find_response(&result) {
1067                        let _ = stall_tx.send((p2c_key, outcome));
1068                    }
1069                    result.map_err(Into::into)
1070                }
1071                .boxed();
1072            }
1073
1074            return fut.map_err(Into::into).boxed();
1075        }
1076
1077        async move {
1078            // Let other tasks run, so a retry request might get different ready peers.
1079            tokio::task::yield_now().await;
1080
1081            // # Security
1082            //
1083            // Avoid routing requests to peers that are missing inventory.
1084            // If we kept trying doomed requests, peers that are missing our requested inventory
1085            // could take up a large amount of our bandwidth and retry limits.
1086            Err(SharedPeerError::from(PeerError::NoReadyPeers))
1087        }
1088        .map_err(Into::into)
1089        .boxed()
1090    }
1091
1092    /// Tries to route a request to a ready peer that advertised that inventory,
1093    /// falling back to a ready peer that isn't missing the inventory.
1094    ///
1095    /// If all ready peers are missing the inventory,
1096    /// returns a synthetic [`NotFoundRegistry`](PeerError::NotFoundRegistry) error.
1097    ///
1098    /// Uses P2C to route requests to the least loaded peer in each list.
1099    fn route_inv(
1100        &mut self,
1101        req: Request,
1102        hash: InventoryHash,
1103    ) -> <Self as tower::Service<Request>>::Future {
1104        let advertising_peer_list = self
1105            .inventory_registry
1106            .advertising_peers(hash)
1107            .filter(|&addr| self.ready_services.contains_key(addr))
1108            .copied()
1109            .collect();
1110
1111        // # Security
1112        //
1113        // Choose a random, less-loaded peer with the inventory.
1114        //
1115        // If we chose the first peer in HashMap order,
1116        // peers would be able to influence our choice by switching addresses.
1117        // But we need the choice to be random,
1118        // so that a peer can't provide all our inventory responses.
1119        let peer = self.select_p2c_peer_from_list(&advertising_peer_list);
1120
1121        if let Some(mut svc) = peer.and_then(|key| self.take_ready_service(&key)) {
1122            let peer = peer.expect("just checked peer is Some");
1123            tracing::trace!(?hash, ?peer, "routing to a peer which advertised inventory");
1124            let fut = svc.call(req);
1125            self.push_unready(peer, svc);
1126            return fut.map_err(Into::into).boxed();
1127        }
1128
1129        let missing_peer_list: HashSet<PeerSocketAddr> = self
1130            .inventory_registry
1131            .missing_peers(hash)
1132            .copied()
1133            .collect();
1134        let maybe_peer_list = self
1135            .ready_services
1136            .keys()
1137            .filter(|addr| !missing_peer_list.contains(addr))
1138            .copied()
1139            .collect();
1140
1141        // Security: choose a random, less-loaded peer that might have the inventory.
1142        let peer = self.select_p2c_peer_from_list(&maybe_peer_list);
1143
1144        if let Some(mut svc) = peer.and_then(|key| self.take_ready_service(&key)) {
1145            let peer = peer.expect("just checked peer is Some");
1146            tracing::trace!(?hash, ?peer, "routing to a peer that might have inventory");
1147            let fut = svc.call(req);
1148            self.push_unready(peer, svc);
1149            return fut.map_err(Into::into).boxed();
1150        }
1151
1152        tracing::debug!(
1153            ?hash,
1154            "all ready peers are missing inventory, failing request"
1155        );
1156
1157        async move {
1158            // Let other tasks run, so a retry request might get different ready peers.
1159            tokio::task::yield_now().await;
1160
1161            // # Security
1162            //
1163            // Avoid routing requests to peers that are missing inventory.
1164            // If we kept trying doomed requests, peers that are missing our requested inventory
1165            // could take up a large amount of our bandwidth and retry limits.
1166            Err(SharedPeerError::from(PeerError::NotFoundRegistry(vec![
1167                hash,
1168            ])))
1169        }
1170        .map_err(Into::into)
1171        .boxed()
1172    }
1173
1174    /// Routes the same request to up to `max_peers` ready peers, ignoring return values.
1175    ///
1176    /// `max_peers` must be at least one, and at most the number of ready peers.
1177    fn route_multiple(
1178        &mut self,
1179        req: Request,
1180        max_peers: usize,
1181    ) -> <Self as tower::Service<Request>>::Future {
1182        assert!(
1183            max_peers > 0,
1184            "requests must be routed to at least one peer"
1185        );
1186        assert!(
1187            max_peers <= self.ready_services.len(),
1188            "requests can only be routed to ready peers"
1189        );
1190
1191        let selected_peers = self.select_random_ready_peers(max_peers);
1192        self.send_multiple(req, selected_peers)
1193    }
1194
1195    /// Sends the same request to the provided ready peers, ignoring return values.
1196    ///
1197    /// # Security
1198    ///
1199    /// Callers should choose peers randomly, ignoring load.
1200    /// This avoids favouring malicious peers, because peers can influence their own load.
1201    ///
1202    /// The order of peers isn't completely random,
1203    /// but peer request order is not security-sensitive.
1204    fn send_multiple(
1205        &mut self,
1206        req: Request,
1207        peers: Vec<D::Key>,
1208    ) -> <Self as tower::Service<Request>>::Future {
1209        let futs = FuturesUnordered::new();
1210        for key in peers {
1211            let mut svc = self
1212                .take_ready_service(&key)
1213                .expect("selected peers are ready");
1214            futs.push(svc.call(req.clone()).map_err(|_| ()));
1215            self.push_unready(key, svc);
1216        }
1217
1218        async move {
1219            let results = futs.collect::<Vec<Result<_, _>>>().await;
1220            tracing::debug!(
1221                ok.len = results.iter().filter(|r| r.is_ok()).count(),
1222                err.len = results.iter().filter(|r| r.is_err()).count(),
1223                "sent peer request to multiple peers"
1224            );
1225            Ok(Response::Nil)
1226        }
1227        .boxed()
1228    }
1229
1230    /// Broadcasts the same request to lots of ready peers, ignoring return values.
1231    fn route_broadcast(&mut self, req: Request) -> <Self as tower::Service<Request>>::Future {
1232        // Broadcasts ignore the response
1233        self.route_multiple(req, self.number_of_peers_to_broadcast())
1234    }
1235
1236    /// Broadcasts a request to sampled peers and all configured sidecars.
1237    ///
1238    /// Used for requests that sidecars must always receive: block adverts and
1239    /// pushed transactions. Connected sidecars that are busy with another
1240    /// request are owed it: the request is queued and delivered by
1241    /// [`Self::send_queued_sidecar_broadcast`] as soon as they are ready again.
1242    fn route_sidecar_broadcast(
1243        &mut self,
1244        req: Request,
1245    ) -> <Self as tower::Service<Request>>::Future {
1246        self.prune_disconnected_sidecar_keys();
1247
1248        let selected_peers =
1249            self.select_sidecar_broadcast_peers(self.number_of_peers_to_broadcast());
1250
1251        // A newer broadcast supersedes any older undelivered one, even one of
1252        // a different kind. Sidecars only need the latest block advert to stay
1253        // live: they fetch any blocks in between over the same connection.
1254        let busy_sidecars: HashSet<D::Key> = self
1255            .zcashd_compat_peer_keys
1256            .iter()
1257            .filter(|key| !self.ready_services.contains_key(*key))
1258            .copied()
1259            .collect();
1260        self.queued_sidecar_broadcast =
1261            (!busy_sidecars.is_empty()).then(|| (req.clone(), busy_sidecars));
1262
1263        self.send_multiple(req, selected_peers)
1264    }
1265
1266    /// Delivers the queued sidecar broadcast to any owed sidecar peers that
1267    /// have become ready. See [`Self::route_sidecar_broadcast`].
1268    fn send_queued_sidecar_broadcast(&mut self) {
1269        let Some((req, mut remaining_sidecars)) = self.queued_sidecar_broadcast.take() else {
1270            return;
1271        };
1272
1273        // Like `broadcast_all_queued`, don't deliver to peers that were banned
1274        // while the request was queued.
1275        let bans = self.bans_receiver.borrow().clone();
1276        remaining_sidecars.retain(|key| !bans.contains_key(&key.ip()));
1277
1278        let ready_sidecars: Vec<D::Key> = remaining_sidecars
1279            .iter()
1280            .filter(|key| self.ready_services.contains_key(*key))
1281            .copied()
1282            .collect();
1283        for key in ready_sidecars {
1284            remaining_sidecars.remove(&key);
1285
1286            let mut svc = self
1287                .take_ready_service(&key)
1288                .expect("sidecars are ready because they were filtered from ready_services above");
1289            let req_fut = svc.call(req.clone());
1290            self.push_unready(key, svc);
1291
1292            // Detach the response future: the connection cancels requests whose
1293            // response channel is dropped, and there is no caller left to drive
1294            // this delivery.
1295            tokio::spawn(req_fut.map(|_| ()));
1296        }
1297
1298        // Drop sidecars that disconnected while the request was queued.
1299        let ready_services = &self.ready_services;
1300        let cancel_handles = &self.cancel_handles;
1301        remaining_sidecars
1302            .retain(|key| ready_services.contains_key(key) || cancel_handles.contains_key(key));
1303
1304        if !remaining_sidecars.is_empty() {
1305            self.queued_sidecar_broadcast = Some((req, remaining_sidecars));
1306        }
1307    }
1308
1309    /// Broadcasts the same request to all ready peers, ignoring return values.
1310    fn broadcast_all(&mut self, req: Request) -> <Self as tower::Service<Request>>::Future {
1311        let ready_peers = self.ready_services.keys().copied().collect();
1312        let send_multiple_fut = self.send_multiple(req.clone(), ready_peers);
1313        let Some(mut queued_broadcast_fut_receiver) = self.queue_broadcast_all_unready(&req) else {
1314            return send_multiple_fut;
1315        };
1316
1317        async move {
1318            let _ = send_multiple_fut.await?;
1319            while queued_broadcast_fut_receiver.recv().await.is_some() {}
1320            Ok(Response::Nil)
1321        }
1322        .boxed()
1323    }
1324
1325    /// If there are unready peers, queues a request to be broadcasted to them and
1326    /// returns a channel receiver for callers to await the broadcast_all() futures, or
1327    /// returns None if there are no unready peers.
1328    fn queue_broadcast_all_unready(
1329        &mut self,
1330        req: &Request,
1331    ) -> Option<tokio::sync::mpsc::Receiver<ResponseFuture>> {
1332        if !self.cancel_handles.is_empty() {
1333            /// How many broadcast all futures to send to the channel until the peer set should wait for the channel consumer
1334            /// to read a message before continuing to send the queued broadcast request to peers that were originally unready.
1335            const QUEUED_BROADCAST_FUTS_CHANNEL_SIZE: usize = 3;
1336
1337            let (sender, receiver) = tokio::sync::mpsc::channel(QUEUED_BROADCAST_FUTS_CHANNEL_SIZE);
1338            let unready_peers: HashSet<_> = self.cancel_handles.keys().cloned().collect();
1339            let queued = (req.clone(), sender, unready_peers);
1340
1341            // Drop the existing queued broadcast all request, if any.
1342            self.queued_broadcast_all = Some(queued);
1343
1344            Some(receiver)
1345        } else {
1346            None
1347        }
1348    }
1349
1350    /// Broadcasts the same requests to all ready peers which were unready when
1351    /// [`PeerSet::broadcast_all()`] was last called, ignoring return values.
1352    fn broadcast_all_queued(&mut self) {
1353        let Some((req, sender, mut remaining_peers)) = self.queued_broadcast_all.take() else {
1354            return;
1355        };
1356
1357        let bans = self.bans_receiver.borrow().clone();
1358        remaining_peers.retain(|addr| !bans.contains_key(&addr.ip()));
1359
1360        let Ok(reserved_send_slot) = sender.try_reserve() else {
1361            self.queued_broadcast_all = Some((req, sender, remaining_peers));
1362            return;
1363        };
1364
1365        let peers: Vec<_> = self
1366            .ready_services
1367            .keys()
1368            .filter(|ready_peer| remaining_peers.remove(ready_peer))
1369            .copied()
1370            .collect();
1371
1372        reserved_send_slot.send(self.send_multiple(req.clone(), peers).boxed());
1373
1374        if !remaining_peers.is_empty() {
1375            self.queued_broadcast_all = Some((req, sender, remaining_peers));
1376        }
1377    }
1378
1379    /// Given a number of ready peers calculate to how many of them Zebra will
1380    /// actually send the request to. Return this number.
1381    pub(crate) fn number_of_peers_to_broadcast(&self) -> usize {
1382        if self.network.is_regtest() {
1383            // In regtest, we broadcast to all peers, so that we can test the
1384            // peer set with a small number of peers.
1385            self.ready_services.len()
1386        } else {
1387            // We are currently sending broadcast messages to a third of the total peers.
1388            const PEER_FRACTION_TO_BROADCAST: usize = 3;
1389
1390            // Round up, so that if we have one ready peer, it gets the request.
1391            div_ceil(self.ready_services.len(), PEER_FRACTION_TO_BROADCAST)
1392        }
1393    }
1394
1395    /// Returns the list of addresses in the peer set.
1396    fn peer_set_addresses(&self) -> Vec<PeerSocketAddr> {
1397        self.ready_services
1398            .keys()
1399            .chain(self.cancel_handles.keys())
1400            .cloned()
1401            .collect()
1402    }
1403
1404    /// Logs the peer set size, and any potential connectivity issues.
1405    fn log_peer_set_size(&mut self) {
1406        let ready_services_len = self.ready_services.len();
1407        let unready_services_len = self.unready_services.len();
1408        trace!(ready_peers = ?ready_services_len, unready_peers = ?unready_services_len);
1409
1410        let now = Instant::now();
1411
1412        // These logs are designed to be human-readable in a terminal, at the
1413        // default Zebra log level. If you need to know the peer set size for
1414        // every request, use the trace-level logs, or the metrics exporter.
1415        if let Some(last_peer_log) = self.last_peer_log {
1416            // Avoid duplicate peer set logs
1417            if now.duration_since(last_peer_log) < MIN_PEER_SET_LOG_INTERVAL {
1418                return;
1419            }
1420        } else {
1421            // Suppress initial logs until the peer set has started up.
1422            // There can be multiple initial requests before the first peer is
1423            // ready.
1424            self.last_peer_log = Some(now);
1425            return;
1426        }
1427
1428        self.last_peer_log = Some(now);
1429
1430        // Log potential duplicate connections.
1431        let peers = self.peer_set_addresses();
1432
1433        // Check for duplicates by address and port: these are unexpected and represent a bug.
1434        let duplicates: Vec<PeerSocketAddr> = peers.iter().duplicates().cloned().collect();
1435
1436        let mut peer_counts = peers.iter().counts();
1437        peer_counts.retain(|peer, _count| duplicates.contains(peer));
1438
1439        if !peer_counts.is_empty() {
1440            let duplicate_connections: usize = peer_counts.values().sum();
1441
1442            warn!(
1443                ?duplicate_connections,
1444                duplicated_peers = ?peer_counts.len(),
1445                peers = ?peers.len(),
1446                "duplicate peer connections in peer set"
1447            );
1448        }
1449
1450        // Check for duplicates by address: these can happen if there are multiple nodes
1451        // behind a NAT or on a single server.
1452        let peers: Vec<IpAddr> = peers.iter().map(|addr| addr.ip()).collect();
1453        let duplicates: Vec<IpAddr> = peers.iter().duplicates().cloned().collect();
1454
1455        let mut peer_counts = peers.iter().counts();
1456        peer_counts.retain(|peer, _count| duplicates.contains(peer));
1457
1458        if !peer_counts.is_empty() {
1459            let duplicate_connections: usize = peer_counts.values().sum();
1460
1461            info!(
1462                ?duplicate_connections,
1463                duplicated_peers = ?peer_counts.len(),
1464                peers = ?peers.len(),
1465                "duplicate IP addresses in peer set"
1466            );
1467        }
1468
1469        // Only log connectivity warnings if all our peers are busy (or there are no peers).
1470        if ready_services_len > 0 {
1471            return;
1472        }
1473
1474        let address_metrics = *self.address_metrics.borrow();
1475        if unready_services_len == 0 {
1476            warn!(
1477                ?address_metrics,
1478                "network request with no peer connections. Hint: check your network connection"
1479            );
1480        } else {
1481            info!(?address_metrics, "network request with no ready peers: finding more peers, waiting for {} peers to answer requests",
1482                  unready_services_len);
1483        }
1484    }
1485
1486    /// Updates the peer set metrics.
1487    ///
1488    /// # Panics
1489    ///
1490    /// If the peer set size exceeds the connection limit.
1491    fn update_metrics(&self) {
1492        let num_ready = self.ready_services.len();
1493        let num_unready = self.unready_services.len();
1494        let num_peers = num_ready + num_unready;
1495        metrics::gauge!("pool.num_ready").set(num_ready as f64);
1496        metrics::gauge!("pool.num_unready").set(num_unready as f64);
1497        metrics::gauge!("zcash.net.peers").set(num_peers as f64);
1498
1499        // Security: make sure we haven't exceeded the connection limit
1500        if num_peers > self.peerset_total_connection_limit {
1501            let address_metrics = *self.address_metrics.borrow();
1502            panic!(
1503                "unexpectedly exceeded configured peer set connection limit: \n\
1504                 peers: {num_peers:?}, ready: {num_ready:?}, unready: {num_unready:?}, \n\
1505                 address_metrics: {address_metrics:?}",
1506            );
1507        }
1508    }
1509}
1510
1511impl<D, C> Service<Request> for PeerSet<D, C>
1512where
1513    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
1514    D::Error: Into<BoxError>,
1515    C: ChainTip,
1516{
1517    type Response = Response;
1518    type Error = BoxError;
1519    type Future =
1520        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
1521
1522    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1523        // Update service and peer statuses.
1524        //
1525        // # Correctness
1526        //
1527        // All of the futures that receive a context from this method can wake the peer set buffer
1528        // task. If there are no ready peers, and no new peers, network requests will pause until:
1529        // - an unready peer becomes ready, or
1530        // - a new peer arrives.
1531
1532        // Drain stall events first, so disconnects free up slots that
1533        // `poll_discover` can fill in the same poll cycle.
1534        self.drain_stall_events(cx);
1535
1536        // Check for new peers, and register a task wakeup when the next new peers arrive. New peers
1537        // can be infrequent if our connection slots are full, or we're connected to all
1538        // available/useful peers.
1539        let _poll_pending_or_ready: Poll<()> = self.poll_discover(cx)?;
1540
1541        // These tasks don't provide new peers or newly ready peers.
1542        let _poll_pending: Poll<()> = self.poll_background_errors(cx)?;
1543        let _poll_pending_or_ready: Poll<()> = self.inventory_registry.poll_inventory(cx)?;
1544
1545        let ready_peers = self.poll_peers(cx)?;
1546
1547        // These metrics should run last, to report the most up-to-date information.
1548        self.log_peer_set_size();
1549        self.update_metrics();
1550
1551        if ready_peers.is_pending() {
1552            // # Correctness
1553            //
1554            // If the channel is full, drop the demand signal rather than waiting. If we waited
1555            // here, the crawler could deadlock sending a request to fetch more peers, because it
1556            // also empties the channel.
1557            trace!("no ready services, sending demand signal");
1558            let _ = self.demand_signal.try_send(MorePeers);
1559
1560            // # Correctness
1561            //
1562            // The current task must be scheduled for wakeup every time we return `Poll::Pending`.
1563            //
1564            // As long as there are unready or new peers, this task will run, because:
1565            // - `poll_discover` schedules this task for wakeup when new peers arrive.
1566            // - if there are unready peers, `poll_unready` or `poll_ready_peers` schedule this
1567            //   task for wakeup when peer services become ready.
1568            //
1569            // To avoid peers blocking on a full peer status/error channel:
1570            // - `poll_background_errors` schedules this task for wakeup when the peer status
1571            //   update task exits.
1572            return Poll::Pending;
1573        }
1574
1575        self.prune_disconnected_sidecar_keys();
1576        self.broadcast_all_queued();
1577        self.send_queued_sidecar_broadcast();
1578
1579        if self.ready_services.is_empty() {
1580            self.poll_peers(cx)
1581        } else {
1582            Poll::Ready(Ok(()))
1583        }
1584    }
1585
1586    fn call(&mut self, req: Request) -> Self::Future {
1587        let fut = match req {
1588            // Only do inventory-aware routing on individual items.
1589            Request::BlocksByHash(ref hashes) if hashes.len() == 1 => {
1590                let hash = InventoryHash::from(*hashes.iter().next().unwrap());
1591                self.route_inv(req, hash)
1592            }
1593            Request::TransactionsById(ref hashes) if hashes.len() == 1 => {
1594                let hash = InventoryHash::from(*hashes.iter().next().unwrap());
1595                self.route_inv(req, hash)
1596            }
1597
1598            // Broadcast advertisements to lots of peers
1599            Request::AdvertiseTransactionIds(_, _) => self.route_broadcast(req),
1600            Request::AdvertiseBlockToAll(_) => self.broadcast_all(req),
1601
1602            // Broadcasts that must always reach the configured zcashd-compat sidecar peers
1603            Request::AdvertiseBlock(_, _) | Request::PushTransaction(_, _) => {
1604                self.route_sidecar_broadcast(req)
1605            }
1606
1607            // Choose a random less-loaded peer for all other requests
1608            _ => self.route_p2c(req),
1609        };
1610        self.update_metrics();
1611
1612        fut
1613    }
1614}