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 ready or busy peers that have become outdated, in
65//!   [`PeerSet::disconnect_from_ineligible_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, VecDeque},
98    convert,
99    fmt::Debug,
100    marker::PhantomData,
101    net::IpAddr,
102    pin::Pin,
103    sync::Arc,
104    task::{Context, Poll, Waker},
105    time::Instant,
106};
107
108use futures::{
109    channel::{mpsc, oneshot},
110    future::{FutureExt, TryFutureExt},
111    prelude::*,
112    stream::FuturesUnordered,
113    task::{noop_waker, AtomicWaker},
114};
115use itertools::Itertools;
116use num_integer::div_ceil;
117use tokio::{
118    sync::{broadcast, mpsc as tokio_mpsc, watch, Notify},
119    task::JoinHandle,
120};
121use tower::{
122    buffer::Buffer,
123    discover::{Change, Discover},
124    load::Load,
125    util::BoxService,
126    Service, ServiceExt,
127};
128use tracing_futures::Instrument;
129
130use zebra_chain::{chain_tip::ChainTip, parameters::Network};
131
132use crate::{
133    address_book::AddressMetrics,
134    connection_metrics::network_kind_label,
135    constants::{
136        FIND_BUSY_SERVING_PEER_WAIT_TIMEOUT, INVENTORY_BUSY_PEER_WAIT_TIMEOUT,
137        MIN_PEER_SET_LOG_INTERVAL,
138    },
139    peer::{ConnectionInfo, LoadTrackedClient, MinimumPeerVersion},
140    peer_set::{
141        stall_tracker::FindResponseStallTracker,
142        unready_service::{Error as UnreadyError, UnreadyService},
143        InventoryChange, InventoryRegistry,
144    },
145    protocol::{
146        external::{connection_limit_key, types::PeerServices, InventoryHash},
147        internal::{Request, Response},
148    },
149    BanList, BoxError, Config, PeerError, PeerSocketAddr, SharedPeerError,
150};
151
152#[cfg(any(test, feature = "proptest-impl"))]
153pub(crate) mod tests;
154
155/// A signal sent by the [`PeerSet`] when it has no ready peers, and gets a request from Zebra.
156///
157/// In response to this signal, the crawler tries to open more peer connections.
158#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
159pub struct MorePeers;
160
161/// A signal sent by the [`PeerSet`] to cancel a [`Client`][1]'s current request
162/// or response.
163///
164/// When it receives this signal, the [`Client`][1] stops processing and exits.
165///
166/// [1]: crate::peer::Client
167#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
168pub struct CancelClientWork;
169
170type ResponseFuture = Pin<Box<dyn Future<Output = Result<Response, BoxError>> + Send + 'static>>;
171
172/// A block request that no ready peer could serve, waiting for a busy peer that might have the
173/// block to become ready.
174struct QueuedInvRequest {
175    /// The request to route.
176    request: Request,
177
178    /// The requested inventory.
179    hash: InventoryHash,
180
181    /// The enqueue-time refusal deadline; downloaders enforce their own operation deadline.
182    deadline: Option<tokio::time::Instant>,
183
184    /// The original request's tracing context.
185    span: tracing::Span,
186
187    /// Receives the response future of the peer the request is routed to.
188    ///
189    /// Dropping the sender refuses the request.
190    response_sender: oneshot::Sender<ResponseFuture>,
191}
192
193/// Bounds queued discovery requests; overflow falls back to any ready peer.
194const MAX_QUEUED_FIND_REQUESTS: usize = 16;
195
196/// A find request waiting for a serving peer, with a bounded serving-only preference.
197struct QueuedFindRequest {
198    /// The request to route.
199    request: Request,
200
201    /// When this request may fall back to any ready peer.
202    deadline: tokio::time::Instant,
203
204    /// Receives the selected peer's response future; cancellation abandons the queued request.
205    response_sender: oneshot::Sender<ResponseFuture>,
206}
207
208/// Where the peer set routes an inventory request.
209enum InvRoute<K> {
210    /// Send the request to this ready peer.
211    Peer(K),
212
213    /// Queue the request until a busy peer that might have the inventory becomes ready.
214    Wait,
215
216    /// Refuse the request, because no connected peer might have the inventory.
217    Refuse,
218}
219
220/// Returns the synthetic error for an inventory request that the peer set refuses.
221///
222/// # Security
223///
224/// Avoid routing requests to peers that are missing inventory.
225/// If we kept trying doomed requests, peers that are missing our requested inventory
226/// could take up a large amount of our bandwidth and retry limits.
227fn not_found_registry_error(hash: InventoryHash) -> BoxError {
228    SharedPeerError::from(PeerError::NotFoundRegistry(vec![hash])).into()
229}
230
231/// A waker for peer events, queue deadlines, and cancellations while requests are queued.
232///
233/// Wakes the task that polls the peer set, and notifies the task that runs
234/// [`poll_peer_set_on_notify`]. The peer set is behind a [`tower::buffer::Buffer`], which only
235/// polls it when it has a request, so queued requests otherwise wouldn't be routed when a
236/// busy peer becomes ready or a find request's serving-only wait expires.
237#[derive(Default)]
238struct QueuedRequestWaker {
239    /// Notified when requests are queued, peers change, waits expire, or callers cancel.
240    notify: Notify,
241
242    /// The waker of the task that polls the peer set.
243    peer_set_waker: AtomicWaker,
244}
245
246impl std::task::Wake for QueuedRequestWaker {
247    fn wake(self: Arc<Self>) {
248        self.wake_by_ref();
249    }
250
251    fn wake_by_ref(self: &Arc<Self>) {
252        self.notify.notify_one();
253        self.peer_set_waker.wake();
254    }
255}
256
257/// Polls `peer_set` with a [`Request::PollPeerSet`] each time `queued_request_waker` is notified,
258/// so queued requests are routed when peers become ready or their serving-only wait expires.
259///
260/// `peer_set` must be the service that wraps the [`PeerSet`] that owns `queued_request_waker`.
261/// See [`PeerSet::into_buffer`].
262///
263/// Only returns if the peer set fails.
264async fn poll_peer_set_on_notify<S>(
265    mut peer_set: S,
266    queued_request_waker: Arc<QueuedRequestWaker>,
267) -> Result<(), BoxError>
268where
269    S: Service<Request, Response = Response, Error = BoxError> + Send + Clone + 'static,
270    S::Future: Send + 'static,
271{
272    loop {
273        // Notifications while the peer set is being polled are combined into one poll, because
274        // `Notify` stores at most one permit.
275        //
276        // This wait has no timeout, because the peer set only notifies while requests are queued.
277        // The poll request can also wait a long time, while all peers are busy: then it is only
278        // routed once a peer becomes ready, which is exactly when queued requests can be routed.
279        queued_request_waker.notify.notified().await;
280        peer_set.ready().await?.call(Request::PollPeerSet).await?;
281    }
282}
283
284/// Classification of a `FindBlocks`/`FindHeaders` response, sent from a
285/// response-wrapping future to [`PeerSet::poll_ready`] via an mpsc channel so
286/// the stall tracker can be updated and the peer disconnected if needed.
287#[derive(Copy, Clone, Debug, PartialEq, Eq)]
288enum StallOutcome {
289    Stall,
290    Clear,
291}
292
293type StallEvent = (PeerSocketAddr, Arc<ConnectionInfo>, StallOutcome);
294
295type InvFailureEvent = (PeerSocketAddr, Arc<ConnectionInfo>, InventoryHash);
296
297/// Empty replies are valid when the locator reaches the remote tip. Neither a local wall-clock
298/// estimate nor an untrusted, frozen handshake height proves otherwise. Fair discovery routing
299/// prevents fast empty replies from monopolizing requests.
300fn classify_find_response<E>(result: &Result<Response, E>) -> Option<StallOutcome> {
301    match result {
302        Ok(Response::BlockHashes(_) | Response::BlockHeaders(_)) => Some(StallOutcome::Clear),
303        Ok(_) => None,
304        Err(_) => Some(StallOutcome::Stall),
305    }
306}
307
308/// A [`tower::Service`] that abstractly represents "the rest of the network".
309///
310/// # Security
311///
312/// The `Discover::Key` must be the transient remote address of each peer. This
313/// address may only be valid for the duration of a single connection. (For
314/// example, inbound connections have an ephemeral remote port, and proxy
315/// connections have an ephemeral local or proxy port.)
316///
317/// Otherwise, malicious peers could interfere with other peers' `PeerSet` state.
318pub struct PeerSet<D, C>
319where
320    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
321    D::Error: Into<BoxError>,
322    C: ChainTip,
323{
324    // Peer Tracking: New Peers
325    //
326    /// Provides new and deleted peer [`Change`]s to the peer set,
327    /// via the [`Discover`] trait implementation.
328    discover: D,
329
330    /// A channel that asks the peer crawler task to connect to more peers.
331    demand_signal: mpsc::Sender<MorePeers>,
332
333    /// A watch channel receiver with a snapshot of the banned peer groups.
334    bans_receiver: watch::Receiver<BanList>,
335
336    /// Tracks peers returning failed `FindBlocks`/`FindHeaders` responses.
337    /// Mutated only from [`Self::poll_ready`] via [`Self::stall_event_rx`].
338    find_response_stalls: FindResponseStallTracker,
339
340    /// Receives stall/clear events from tracked routing futures in
341    /// [`Self::call_ready_peer`]. The channel keeps the tracker single-owner (no
342    /// `Mutex`) and confines mutation to `poll_ready`, where the peer set can
343    /// call [`Self::remove`] directly.
344    stall_event_rx: tokio_mpsc::UnboundedReceiver<StallEvent>,
345
346    /// Producer clones handed to each tracked request's response wrapper.
347    stall_event_tx: tokio_mpsc::UnboundedSender<StallEvent>,
348
349    /// Receives completed block request errors from response futures and records them in the
350    /// [`InventoryRegistry`]. Caller cancellation is not evidence of a peer failure.
351    ///
352    /// Each routed block request sends at most one failure, so the channel is limited by the
353    /// number of block requests in flight.
354    inv_failure_rx: tokio_mpsc::UnboundedReceiver<InvFailureEvent>,
355
356    /// Producer clones handed to each block request's response future.
357    inv_failure_tx: tokio_mpsc::UnboundedSender<InvFailureEvent>,
358
359    // Peer Tracking: Ready Peers
360    //
361    /// Connected peers that are ready to receive requests from Zebra,
362    /// or send requests to Zebra.
363    ready_services: HashMap<D::Key, D::Service>,
364
365    // Request Routing
366    //
367    /// Connection identities in find-request order, preserved while peers are busy.
368    ///
369    /// New connections join at the back; disconnected connections are pruned in
370    /// [`Self::poll_peers`]. The metadata allocation identifies a connection even if
371    /// a later connection reuses its address.
372    find_peer_queue: VecDeque<(D::Key, Arc<ConnectionInfo>)>,
373
374    /// Find requests waiting for a serving peer, in request order.
375    ///
376    /// After [`FIND_BUSY_SERVING_PEER_WAIT_TIMEOUT`], any ready peer can receive the request.
377    queued_find_requests: VecDeque<QueuedFindRequest>,
378
379    /// Wakes the peer set at the next unexpired queued find request deadline.
380    ///
381    /// Expired requests wait for peer events rather than repeatedly waking a completed timer.
382    queued_find_request_timer: Option<Pin<Box<tokio::time::Sleep>>>,
383
384    /// Stores gossiped inventory hashes from connected peers.
385    ///
386    /// Used to route inventory requests to peers that are likely to have it.
387    inventory_registry: InventoryRegistry,
388
389    /// Stores requests that should be routed to peers once they are ready.
390    queued_broadcast_all: Option<(
391        Request,
392        tokio::sync::mpsc::Sender<ResponseFuture>,
393        HashSet<D::Key>,
394    )>,
395
396    /// Inbound peer IPs that must always receive block inventory broadcasts.
397    block_gossip_peer_ips: HashSet<IpAddr>,
398
399    /// The keys of connected peers that matched [`Self::block_gossip_peer_ips`]
400    /// when they were inserted into the peer set.
401    ///
402    /// Stale keys of disconnected peers are pruned by
403    /// [`Self::prune_disconnected_sidecar_keys`].
404    zcashd_compat_peer_keys: HashSet<D::Key>,
405
406    /// The keys of connected peers that advertised [`PeerServices::NODE_NETWORK`],
407    /// so they can serve historic blocks.
408    ///
409    /// Busy peers' services are not otherwise accessible, but block and find routing need to
410    /// know whether a busy peer can serve blocks. Stale keys of disconnected peers are pruned by
411    /// [`Self::retain_connected_keys`].
412    serving_peer_keys: HashSet<D::Key>,
413
414    /// Block requests that are waiting for a busy peer that might have the block, in the order
415    /// they were made.
416    ///
417    /// Each request is routed to the first of those peers that becomes ready, or refused once
418    /// [`INVENTORY_BUSY_PEER_WAIT_TIMEOUT`] expires, or once no busy peer might have the block.
419    queued_inv_requests: VecDeque<QueuedInvRequest>,
420
421    /// Wakes the task that polls the peer set, and the task that runs
422    /// [`poll_peer_set_on_notify`], while requests are queued.
423    queued_request_waker: Arc<QueuedRequestWaker>,
424
425    /// The most recent sidecar broadcast (a block advert or a pushed
426    /// transaction) that has not been delivered to all connected zcashd-compat
427    /// sidecar peers, and the sidecar peers that are still owed it.
428    ///
429    /// A sidecar can be busy with another request when a broadcast is routed.
430    /// The request is queued here and delivered as soon as the sidecar is
431    /// ready again, so configured sidecars never miss block gossip. A newer
432    /// broadcast replaces an older undelivered one, even one of a different
433    /// kind.
434    queued_sidecar_broadcast: Option<(Request, HashSet<D::Key>)>,
435
436    // Peer Tracking: Busy Peers
437    //
438    /// Connected peers that are handling a Zebra request,
439    /// or Zebra is handling one of their requests.
440    unready_services: FuturesUnordered<UnreadyService<D::Key, D::Service, Request>>,
441
442    /// Channels used to cancel the request that an unready service is doing.
443    cancel_handles: HashMap<D::Key, oneshot::Sender<CancelClientWork>>,
444
445    // Peer Validation
446    //
447    /// An endpoint to see the minimum peer protocol version in real time.
448    ///
449    /// The minimum version depends on the block height, and [`MinimumPeerVersion`] listens for
450    /// height changes and determines the correct minimum version.
451    minimum_peer_version: MinimumPeerVersion<C>,
452
453    /// The configured limit for inbound and outbound connections.
454    ///
455    /// The peer set panics if this size is exceeded.
456    /// If that happens, our connection limit code has a bug.
457    peerset_total_connection_limit: usize,
458
459    // Background Tasks
460    //
461    /// Channel for passing ownership of tokio JoinHandles from PeerSet's background tasks
462    ///
463    /// The join handles passed into the PeerSet are used populate the `guards` member
464    handle_rx: tokio::sync::oneshot::Receiver<Vec<JoinHandle<Result<(), BoxError>>>>,
465
466    /// Unordered set of handles to background tasks associated with the `PeerSet`
467    ///
468    /// These guards are checked for errors as part of `poll_ready` which lets
469    /// the `PeerSet` propagate errors from background tasks back to the user
470    guards: futures::stream::FuturesUnordered<JoinHandle<Result<(), BoxError>>>,
471
472    // Metrics and Logging
473    //
474    /// Address book metrics watch channel.
475    ///
476    /// Used for logging diagnostics.
477    address_metrics: watch::Receiver<AddressMetrics>,
478
479    /// The last time we logged a message about the peer set size
480    last_peer_log: Option<Instant>,
481
482    /// The configured maximum number of peers that can be in the
483    /// peer set per connection limit key, defaults to [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`]
484    max_conns_per_ip: usize,
485
486    /// The network of this peer set.
487    network: Network,
488}
489
490impl<D, C> Drop for PeerSet<D, C>
491where
492    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
493    D::Error: Into<BoxError>,
494    C: ChainTip,
495{
496    fn drop(&mut self) {
497        // We don't have access to the current task (if any), so we just drop everything we can.
498        let waker = noop_waker();
499        let mut cx = Context::from_waker(&waker);
500
501        self.shut_down_tasks_and_channels(&mut cx);
502    }
503}
504
505impl<D, C> PeerSet<D, C>
506where
507    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
508    D::Error: Into<BoxError>,
509    C: ChainTip,
510{
511    #[allow(clippy::too_many_arguments)]
512    /// Construct a peerset which uses `discover` to manage peer connections.
513    ///
514    /// Arguments:
515    /// - `config`: configures the peer set connection limit;
516    /// - `block_gossip_peer_ips`: inbound peer IPs that must always receive block inventory broadcasts.
517    /// - `discover`: handles peer connects and disconnects;
518    /// - `demand_signal`: requests more peers when all peers are busy (unready);
519    /// - `handle_rx`: receives background task handles,
520    ///   monitors them to make sure they're still running,
521    ///   and shuts down all the tasks as soon as one task exits;
522    /// - `inv_stream`: receives inventory changes from peers,
523    ///   allowing the peer set to direct inventory requests;
524    /// - `bans_receiver`: receives a map of banned IP addresses that should be dropped;
525    /// - `address_book`: when peer set is busy, it logs address book diagnostics.
526    /// - `minimum_peer_version`: endpoint to see the minimum peer protocol version in real time.
527    /// - `max_conns_per_ip`: configured maximum number of peers that can be in the
528    ///   peer set per connection limit key, defaults to the config value or to
529    ///   [`crate::constants::DEFAULT_MAX_CONNS_PER_IP`].
530    pub fn new(
531        config: &Config,
532        block_gossip_peer_ips: Vec<IpAddr>,
533        discover: D,
534        demand_signal: mpsc::Sender<MorePeers>,
535        handle_rx: tokio::sync::oneshot::Receiver<Vec<JoinHandle<Result<(), BoxError>>>>,
536        inv_stream: broadcast::Receiver<InventoryChange>,
537        bans_receiver: watch::Receiver<BanList>,
538        address_metrics: watch::Receiver<AddressMetrics>,
539        minimum_peer_version: MinimumPeerVersion<C>,
540        max_conns_per_ip: Option<usize>,
541    ) -> Self {
542        let (stall_event_tx, stall_event_rx) = tokio_mpsc::unbounded_channel();
543        let (inv_failure_tx, inv_failure_rx) = tokio_mpsc::unbounded_channel();
544        Self {
545            // New peers
546            discover,
547            demand_signal,
548            // Banned peers
549            bans_receiver,
550
551            // Stall tracking
552            find_response_stalls: FindResponseStallTracker::new(),
553            stall_event_rx,
554            stall_event_tx,
555
556            // Failed block request tracking
557            inv_failure_rx,
558            inv_failure_tx,
559
560            // Ready peers
561            ready_services: HashMap::new(),
562            // Request Routing
563            find_peer_queue: VecDeque::new(),
564            queued_find_requests: VecDeque::new(),
565            queued_find_request_timer: None,
566            inventory_registry: InventoryRegistry::new(inv_stream),
567            queued_broadcast_all: None,
568            block_gossip_peer_ips: block_gossip_peer_ips.into_iter().collect(),
569            zcashd_compat_peer_keys: HashSet::new(),
570            serving_peer_keys: HashSet::new(),
571            queued_inv_requests: VecDeque::new(),
572            queued_request_waker: Arc::default(),
573            queued_sidecar_broadcast: None,
574
575            // Busy peers
576            unready_services: FuturesUnordered::new(),
577            cancel_handles: HashMap::new(),
578
579            // Peer validation
580            minimum_peer_version,
581            peerset_total_connection_limit: config.peerset_total_connection_limit(),
582
583            // Background tasks
584            handle_rx,
585            guards: futures::stream::FuturesUnordered::new(),
586
587            // Metrics
588            last_peer_log: None,
589            address_metrics,
590
591            max_conns_per_ip: max_conns_per_ip.unwrap_or(config.max_connections_per_ip),
592
593            network: config.network.clone(),
594        }
595    }
596
597    /// Returns a waker for peer events, which also notifies [`poll_peer_set_on_notify`] while
598    /// requests are queued.
599    fn peer_event_waker(&self, cx: &Context<'_>) -> Waker {
600        if self.queued_inv_requests.is_empty() && self.queued_find_requests.is_empty() {
601            return cx.waker().clone();
602        }
603
604        self.queued_request_waker
605            .peer_set_waker
606            .register(cx.waker());
607        Waker::from(self.queued_request_waker.clone())
608    }
609
610    /// Check background task handles to make sure they're still running.
611    ///
612    /// Never returns `Ok`.
613    ///
614    /// If any background task exits, shuts down all other background tasks,
615    /// and returns an error. Otherwise, returns `Pending`, and registers a wakeup for
616    /// receiving the background tasks, or the background tasks exiting.
617    fn poll_background_errors(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
618        futures::ready!(self.receive_tasks_if_needed(cx))?;
619
620        // Return Pending if all background tasks are still running.
621        match futures::ready!(Pin::new(&mut self.guards).poll_next(cx)) {
622            Some(res) => {
623                info!(
624                    background_tasks = %self.guards.len(),
625                    "a peer set background task exited, shutting down other peer set tasks"
626                );
627
628                self.shut_down_tasks_and_channels(cx);
629
630                // Flatten the join result and inner result, and return any errors.
631                res.map_err(Into::into)
632                    // TODO: replace with Result::flatten when it stabilises (#70142)
633                    .and_then(convert::identity)?;
634
635                // Turn Ok() task exits into errors.
636                Poll::Ready(Err("a peer set background task exited".into()))
637            }
638
639            None => {
640                self.shut_down_tasks_and_channels(cx);
641                Poll::Ready(Err("all peer set background tasks have exited".into()))
642            }
643        }
644    }
645
646    /// Receive background tasks, if they've been sent on the channel, but not consumed yet.
647    ///
648    /// Returns a result representing the current task state, or `Poll::Pending` if the background
649    /// tasks should be polled again to check their state.
650    fn receive_tasks_if_needed(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
651        if self.guards.is_empty() {
652            // Return Pending if the tasks have not been sent yet.
653            let handles = futures::ready!(Pin::new(&mut self.handle_rx).poll(cx));
654
655            match handles {
656                // The tasks have been sent, but not consumed yet.
657                Ok(handles) => {
658                    // Currently, the peer set treats an empty background task set as an error.
659                    //
660                    // TODO: refactor `handle_rx` and `guards` into an enum
661                    //       for the background task state: Waiting/Running/Shutdown.
662                    assert!(
663                        !handles.is_empty(),
664                        "the peer set requires at least one background task"
665                    );
666
667                    self.guards.extend(handles);
668
669                    Poll::Ready(Ok(()))
670                }
671
672                // The sender was dropped without sending the tasks.
673                Err(_) => Poll::Ready(Err(
674                    "sender did not send peer background tasks before it was dropped".into(),
675                )),
676            }
677        } else {
678            Poll::Ready(Ok(()))
679        }
680    }
681
682    /// Shut down:
683    /// - services by dropping the service lists
684    /// - background tasks via their join handles or cancel handles
685    /// - channels by closing the channel
686    fn shut_down_tasks_and_channels(&mut self, cx: &mut Context<'_>) {
687        // Drop services and cancel their background tasks.
688        self.ready_services = HashMap::new();
689
690        for (_peer_key, handle) in self.cancel_handles.drain() {
691            let _ = handle.send(CancelClientWork);
692        }
693        self.unready_services = FuturesUnordered::new();
694
695        // Refuse queued requests, because there are no peers left to route them to.
696        self.queued_inv_requests.clear();
697        self.queued_find_requests.clear();
698        self.queued_find_request_timer = None;
699
700        // Close the MorePeers channel for all senders,
701        // so we don't add more peers to a shut down peer set.
702        self.demand_signal.close_channel();
703
704        // Shut down background tasks, ignoring pending polls.
705        self.handle_rx.close();
706        let _ = self.receive_tasks_if_needed(cx);
707        for guard in self.guards.iter() {
708            guard.abort();
709        }
710    }
711
712    /// Checks for newly ready, disconnects from outdated peers, and polls ready peer errors.
713    fn poll_peers(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
714        // Busy connections must remain eligible too: neither a ban nor an upgrade waits for
715        // their request channel to regain capacity.
716        self.disconnect_from_ineligible_peers();
717
718        // Check for newly ready peers, including newly added peers (which are added as unready).
719        // So it needs to run after `poll_discover()`. Registers a wakeup if there are any unready
720        // peers.
721        //
722        // Request-channel backpressure is bounded independently of the wire response timeout.
723        let _poll_pending_or_ready: Poll<Option<()>> = self.poll_unready(cx)?;
724
725        // Check for failures in ready peers, removing newly errored or disconnected peers.
726        // So it needs to run after `poll_unready()`.
727        let ready_peers = self.poll_ready_peer_errors(cx);
728        self.find_peer_queue.retain(|(key, _)| {
729            let connected =
730                self.ready_services.contains_key(key) || self.cancel_handles.contains_key(key);
731            if !connected {
732                self.find_response_stalls.clear(*key);
733            }
734            connected
735        });
736        ready_peers.map(Ok)
737    }
738
739    /// Check busy peer services for request completion or errors.
740    ///
741    /// Move newly ready services to the ready list if they are for peers with supported protocol
742    /// versions, otherwise they are dropped. Also drop failed services.
743    ///
744    /// Never returns an error.
745    ///
746    /// Returns `Ok(Some(())` if at least one peer became ready, `Poll::Pending` if there are
747    /// unready peers, but none became ready, and `Ok(None)` if the unready peers were empty.
748    ///
749    /// If there are any remaining unready peers, registers a wakeup for the next time one becomes
750    /// ready. If there are no unready peers, doesn't register any wakeups. (Since wakeups come
751    /// from peers, there needs to be at least one peer to register a wakeup.)
752    fn poll_unready(&mut self, cx: &mut Context<'_>) -> Poll<Result<Option<()>, BoxError>> {
753        let mut result = Poll::Pending;
754
755        // # Correctness
756        //
757        // `poll_next()` must always be called, because `self.unready_services` could have been
758        // empty before the call to `self.poll_ready()`.
759        //
760        // > When new futures are added, `poll_next` must be called in order to begin receiving
761        // > wake-ups for new futures.
762        //
763        // <https://docs.rs/futures/latest/futures/stream/futures_unordered/struct.FuturesUnordered.html>
764        //
765        // Returns Pending if we've finished processing the unready service changes,
766        // but there are still some unready services.
767        loop {
768            // No ready peers left, but there are some unready peers pending.
769            let Poll::Ready(ready_peer) = Pin::new(&mut self.unready_services).poll_next(cx) else {
770                break;
771            };
772
773            match ready_peer {
774                // No unready peers in the list.
775                None => {
776                    // If we've finished processing the unready service changes, and there are no
777                    // unready services left, it doesn't make sense to return Pending, because
778                    // their stream is terminated. But when we add more unready peers and call
779                    // `poll_next()`, its termination status will be reset, and it will receive
780                    // wakeups again.
781                    if result.is_pending() {
782                        result = Poll::Ready(Ok(None));
783                    }
784
785                    break;
786                }
787
788                // Unready -> Ready
789                Some(Ok((key, svc))) => {
790                    trace!(?key, "service became ready");
791
792                    if self.bans_receiver.borrow().is_banned(key.ip()) {
793                        warn!(?key, "service is banned, dropping service");
794                        std::mem::drop(svc);
795                        let cancel = self.cancel_handles.remove(&key);
796                        debug_assert!(
797                            cancel.is_some(),
798                            "missing cancel handle for banned unready peer"
799                        );
800                        continue;
801                    }
802
803                    self.push_ready(true, key, svc);
804
805                    // Return Ok if at least one peer became ready.
806                    result = Poll::Ready(Ok(Some(())));
807                }
808
809                // Unready -> Canceled
810                Some(Err((key, UnreadyError::Canceled))) => {
811                    // A service be canceled because we've connected to the same service twice.
812                    // In that case, there is a cancel handle for the peer address,
813                    // but it belongs to the service for the newer connection.
814                    trace!(
815                        ?key,
816                        duplicate_connection = self.cancel_handles.contains_key(&key),
817                        "service was canceled, dropping service"
818                    );
819                }
820                Some(Err((key, UnreadyError::CancelHandleDropped(_)))) => {
821                    // Similarly, services with dropped cancel handes can have duplicates.
822                    trace!(
823                        ?key,
824                        duplicate_connection = self.cancel_handles.contains_key(&key),
825                        "cancel handle was dropped, dropping service"
826                    );
827                }
828                Some(Err((key, UnreadyError::TimedOut))) => {
829                    debug!(?key, "service readiness timed out, dropping service");
830                    let cancel = self.cancel_handles.remove(&key);
831                    assert!(
832                        cancel.is_some(),
833                        "timed-out unready service has a cancel handle"
834                    );
835                }
836
837                // Unready -> Errored
838                Some(Err((key, UnreadyError::Inner(error)))) => {
839                    debug!(%error, "service failed while unready, dropping service");
840
841                    let cancel = self.cancel_handles.remove(&key);
842                    assert!(cancel.is_some(), "missing cancel handle");
843                }
844            }
845        }
846
847        result
848    }
849
850    /// Checks previously ready peer services for errors.
851    ///
852    /// The only way these peer `Client`s can become unready is when we send them a request,
853    /// because the peer set has exclusive access to send requests to each peer. (If an inbound
854    /// request is in progress, it will be handled, then our request will be sent by the connection
855    /// task.)
856    ///
857    /// Returns `Poll::Ready` if there are some ready peers, and `Poll::Pending` if there are no
858    /// ready peers. Registers a wakeup if any peer has failed due to a disconnection, hang, or protocol error.
859    ///
860    /// # Panics
861    ///
862    /// If any peers somehow became unready without being sent a request. This indicates a bug in the peer set, where requests
863    /// are sent to peers without putting them in `unready_peers`.
864    fn poll_ready_peer_errors(&mut self, cx: &mut Context<'_>) -> Poll<()> {
865        let mut previous = HashMap::new();
866        std::mem::swap(&mut previous, &mut self.ready_services);
867
868        // TODO: consider only checking some peers each poll (for performance reasons),
869        //       but make sure we eventually check all of them.
870        for (key, mut svc) in previous.drain() {
871            let Poll::Ready(peer_readiness) = Pin::new(&mut svc).poll_ready(cx) else {
872                unreachable!(
873                    "unexpected unready peer: peers must be put into the unready_peers list \
874                     after sending them a request"
875                );
876            };
877
878            match peer_readiness {
879                // Still ready, add it back to the list.
880                Ok(()) => {
881                    if self.bans_receiver.borrow().is_banned(key.ip()) {
882                        debug!(?key, "service ip is banned, dropping service");
883                        std::mem::drop(svc);
884                        continue;
885                    }
886
887                    self.push_ready(false, key, svc)
888                }
889
890                // Ready -> Errored
891                Err(error) => {
892                    debug!(%error, "service failed while ready, dropping service");
893
894                    // Ready services can just be dropped, they don't need any cleanup.
895                    std::mem::drop(svc);
896                }
897            }
898        }
899
900        if self.ready_services.is_empty() {
901            Poll::Pending
902        } else {
903            Poll::Ready(())
904        }
905    }
906
907    /// Returns the number of peer connections Zebra already has with the
908    /// provided IPv4 address, or in the same IPv6 `/64` subnet.
909    ///
910    /// Peer set admission uses this count to enforce
911    /// [`Config::max_connections_per_ip`](crate::config::Config).
912    ///
913    /// # Performance
914    ///
915    /// This method is `O(connected peers)`, so it should not be called from a loop
916    /// that is already iterating through the peer set.
917    fn num_peers_with_ip(&self, ip: IpAddr) -> usize {
918        let limit_key = connection_limit_key(ip);
919        self.ready_services
920            .keys()
921            .chain(self.cancel_handles.keys())
922            .filter(|addr| connection_limit_key(addr.ip()) == limit_key)
923            .count()
924    }
925
926    /// Returns `true` if Zebra is already connected to the IP and port in `addr`.
927    fn has_peer_with_addr(&self, addr: PeerSocketAddr) -> bool {
928        self.ready_services.contains_key(&addr) || self.cancel_handles.contains_key(&addr)
929    }
930
931    /// Processes the entire list of newly inserted or removed services.
932    ///
933    /// Puts inserted services in the unready list.
934    /// Drops removed services, after cancelling any pending requests.
935    ///
936    /// If the peer connector channel is closed, returns an error.
937    ///
938    /// Otherwise, returns `Ok` if it discovered at least one peer, or `Poll::Pending` if it didn't
939    /// discover any peers. Always registers a wakeup for new peers, even when it returns `Ok`.
940    fn poll_discover(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> {
941        // Return pending if there are no peers in the list.
942        let mut result = Poll::Pending;
943
944        loop {
945            // If we've emptied the list, finish looping, otherwise process the new peer.
946            let Poll::Ready(discovered) = Pin::new(&mut self.discover).poll_discover(cx) else {
947                break;
948            };
949
950            // If the change channel has a permanent error, return that error.
951            let change = discovered
952                .ok_or("discovery stream closed")?
953                .map_err(Into::into)?;
954
955            // Otherwise we have successfully processed a peer.
956            result = Poll::Ready(Ok(()));
957
958            // Process each change.
959            match change {
960                Change::Remove(key) => {
961                    trace!(?key, "got Change::Remove from Discover");
962                    self.remove(&key);
963                }
964                Change::Insert(key, svc) => {
965                    // We add peers as unready, so that we:
966                    // - always do the same checks on every ready peer, and
967                    // - check for any errors that happened right after the handshake
968                    trace!(?key, "got Change::Insert from Discover");
969
970                    // # Security
971                    //
972                    // Drop the new peer if we are already connected to it.
973                    // Preferring old connections avoids connection thrashing.
974                    if self.has_peer_with_addr(key) {
975                        std::mem::drop(svc);
976                        continue;
977                    }
978
979                    // Classify sidecars before the per-IP cap: a trusted sidecar
980                    // reconnecting from a new ephemeral port must not be blocked
981                    // by its own not-yet-swept dead connection still counting
982                    // against `max_conns_per_ip` (which defaults to 1), or the
983                    // wallet would silently stop following the chain.
984                    let is_sidecar = self.is_zcashd_compat_peer(&svc);
985
986                    // # Security
987                    //
988                    // drop the new peer if there are already `max_conns_per_ip` peers with
989                    // the same IPv4 address, or in the same IPv6 `/64` subnet. Sidecars are
990                    // exempt: they are trusted, and the listener already caps their inbound
991                    // slots.
992                    if !is_sidecar && self.num_peers_with_ip(key.ip()) >= self.max_conns_per_ip {
993                        std::mem::drop(svc);
994                        continue;
995                    }
996
997                    if is_sidecar {
998                        self.zcashd_compat_peer_keys.insert(key);
999                    }
1000
1001                    if svc.remote_services().contains(PeerServices::NODE_NETWORK) {
1002                        self.serving_peer_keys.insert(key);
1003                    } else {
1004                        self.serving_peer_keys.remove(&key);
1005                    }
1006                    self.inventory_registry.clear_peer_failures(key);
1007
1008                    self.find_peer_queue
1009                        .push_back((key, svc.connection_info().clone()));
1010                    self.push_unready(key, svc);
1011                }
1012            }
1013        }
1014
1015        result
1016    }
1017
1018    /// Disconnects banned and outdated connections, whether ready or backpressured.
1019    fn disconnect_from_ineligible_peers(&mut self) {
1020        let minimum_version = self.minimum_peer_version.current();
1021        let bans = self.bans_receiver.borrow();
1022        self.find_peer_queue.retain(|(key, info)| {
1023            !bans.is_banned(key.ip()) && info.remote.version >= minimum_version
1024        });
1025        self.ready_services.retain(|key, service| {
1026            let eligible = !bans.is_banned(key.ip()) && service.remote_version() >= minimum_version;
1027            if !eligible {
1028                self.find_response_stalls.clear(*key);
1029            }
1030            eligible
1031        });
1032        for pending in Pin::new(&self.unready_services).iter_pin_ref() {
1033            let pending = pending.get_ref();
1034            let key = pending
1035                .key
1036                .as_ref()
1037                .expect("unready services retain their key until completion");
1038            // Canceled futures can outlive their connection and share a replacement's address.
1039            if !self
1040                .cancel_handles
1041                .get(key)
1042                .is_some_and(|cancel| cancel.is_connected_to(&pending.cancel))
1043            {
1044                continue;
1045            }
1046            let service = pending
1047                .service
1048                .as_ref()
1049                .expect("unready services retain their client until completion");
1050            if bans.is_banned(key.ip()) || service.remote_version() < minimum_version {
1051                self.find_response_stalls.clear(*key);
1052                if let Some(cancel) = self.cancel_handles.remove(key) {
1053                    let _ = cancel.send(CancelClientWork);
1054                }
1055            }
1056        }
1057    }
1058
1059    /// Takes a ready service by key.
1060    fn take_ready_service(&mut self, key: &D::Key) -> Option<D::Service> {
1061        if let Some(svc) = self.ready_services.remove(key) {
1062            assert!(
1063                !self.cancel_handles.contains_key(key),
1064                "cancel handles are only used for unready service work"
1065            );
1066
1067            Some(svc)
1068        } else {
1069            None
1070        }
1071    }
1072
1073    /// Whether an event belongs to the currently connected incarnation of an address.
1074    fn is_current_connection(&self, addr: PeerSocketAddr, info: &Arc<ConnectionInfo>) -> bool {
1075        self.find_peer_queue
1076            .iter()
1077            .any(|(key, current)| *key == addr && Arc::ptr_eq(current, info))
1078    }
1079
1080    /// Drains pending stall/clear events from tracked routing futures and
1081    /// disconnects peers that have exceeded the stall threshold. The peer's
1082    /// TCP connection is closed when its service is dropped; address book and
1083    /// ban list are untouched, so the peer is free to reconnect.
1084    fn drain_stall_events(&mut self, cx: &mut Context<'_>) {
1085        while let Poll::Ready(Some((addr, connection_info, outcome))) =
1086            self.stall_event_rx.poll_recv(cx)
1087        {
1088            // A delayed response from an old connection must not affect its replacement.
1089            if !self.is_current_connection(addr, &connection_info) {
1090                continue;
1091            }
1092            match outcome {
1093                StallOutcome::Stall => {
1094                    if self.find_response_stalls.record_stall(addr) {
1095                        info!(
1096                            ?addr,
1097                            "dropping stalled peer: exceeded FindBlocks/FindHeaders stall threshold",
1098                        );
1099                        self.remove(&addr);
1100                    }
1101                }
1102                StallOutcome::Clear => self.find_response_stalls.clear(addr),
1103            }
1104        }
1105    }
1106
1107    /// Records block requests that peers failed to answer in the inventory registry.
1108    ///
1109    /// Peers declare their own services, and request timeouts don't mark inventory as missing. So
1110    /// otherwise a peer that falsely claims to serve blocks, and withholds them, could get every
1111    /// retry for a block.
1112    fn drain_inv_failures(&mut self, cx: &mut Context<'_>) {
1113        while let Poll::Ready(Some((peer, connection_info, hash))) =
1114            self.inv_failure_rx.poll_recv(cx)
1115        {
1116            if self.is_current_connection(peer, &connection_info) {
1117                self.inventory_registry.register_failed(hash, peer);
1118            }
1119        }
1120    }
1121
1122    /// Remove the service corresponding to `key` from the peer set.
1123    ///
1124    /// Drops the service, cancelling any pending request or response to that peer.
1125    /// If the peer does not exist, does nothing.
1126    fn remove(&mut self, key: &D::Key) {
1127        self.find_response_stalls.clear(*key);
1128        self.find_peer_queue.retain(|(addr, _)| addr != key);
1129        self.zcashd_compat_peer_keys.remove(key);
1130        self.serving_peer_keys.remove(key);
1131        if let Some((_, remaining_sidecars)) = self.queued_sidecar_broadcast.as_mut() {
1132            remaining_sidecars.remove(key);
1133            if remaining_sidecars.is_empty() {
1134                self.queued_sidecar_broadcast = None;
1135            }
1136        }
1137
1138        if let Some(ready_service) = self.take_ready_service(key) {
1139            // A ready service has no work to cancel, so just drop it.
1140            std::mem::drop(ready_service);
1141        } else if let Some(handle) = self.cancel_handles.remove(key) {
1142            // Cancel the work, implicitly dropping the cancel handle.
1143            // The service future returns a `Canceled` error,
1144            // making `poll_unready` drop the service.
1145            let _ = handle.send(CancelClientWork);
1146        }
1147    }
1148
1149    /// Adds a ready service to the ready list if it's for a peer with a supported version.
1150    /// If `was_unready` is true, also removes the peer's cancel handle.
1151    ///
1152    /// If the service is for a connection to an outdated peer, the service is dropped.
1153    fn push_ready(&mut self, was_unready: bool, key: D::Key, svc: D::Service) {
1154        let cancel = self.cancel_handles.remove(&key);
1155        assert_eq!(
1156            cancel.is_some(),
1157            was_unready,
1158            "missing or unexpected cancel handle"
1159        );
1160
1161        if svc.remote_version() >= self.minimum_peer_version.current() {
1162            self.ready_services.insert(key, svc);
1163        } else {
1164            std::mem::drop(svc);
1165        }
1166    }
1167
1168    /// Adds a busy service to the unready list if it's for a peer with a supported version,
1169    /// and adds a cancel handle for the service's current request.
1170    ///
1171    /// If the service is for a connection to an outdated peer, the request is cancelled and the
1172    /// service is dropped.
1173    fn push_unready(&mut self, key: D::Key, svc: D::Service) {
1174        let peer_version = svc.remote_version();
1175        let (tx, rx) = oneshot::channel();
1176
1177        self.unready_services.push(UnreadyService {
1178            key: Some(key),
1179            service: Some(svc),
1180            cancel: rx,
1181            readiness_timeout: tokio::time::sleep(crate::constants::REQUEST_TIMEOUT),
1182            _req: PhantomData,
1183        });
1184
1185        if peer_version >= self.minimum_peer_version.current() {
1186            self.cancel_handles.insert(key, tx);
1187        } else {
1188            self.find_response_stalls.clear(key);
1189            // Reject delayed response events before the next peer-set poll.
1190            self.find_peer_queue.retain(|(addr, _)| *addr != key);
1191            // Cancel any request made to the service because it is using an outdated protocol
1192            // version.
1193            let _ = tx.send(CancelClientWork);
1194        }
1195    }
1196
1197    /// Performs P2C on `self.ready_services` to randomly select a less-loaded ready service.
1198    fn select_ready_p2c_peer(&self) -> Option<D::Key> {
1199        self.select_p2c_peer_from_list(&self.ready_services.keys().copied().collect())
1200    }
1201
1202    /// Rotates find requests across ready connections, restricting them to serving peers until
1203    /// the queued wait expires, unless no serving peers are connected.
1204    fn select_ready_find_peer(&mut self, allow_non_serving: bool) -> Option<D::Key> {
1205        if self.ready_services.is_empty() {
1206            return None;
1207        }
1208
1209        let serving_only = !allow_non_serving && !self.serving_peer_keys.is_empty();
1210        let (index, (key, _)) = self
1211            .find_peer_queue
1212            .iter()
1213            .enumerate()
1214            .find(|(_, (key, _))| {
1215                self.ready_services.contains_key(key)
1216                    && (!serving_only || self.serving_peer_keys.contains(key))
1217            })?;
1218        let key = *key;
1219        self.find_peer_queue.rotate_left(index + 1);
1220        Some(key)
1221    }
1222
1223    /// Performs P2C on `ready_service_list` to randomly select a less-loaded ready service.
1224    #[allow(clippy::unwrap_in_result)]
1225    fn select_p2c_peer_from_list(&self, ready_service_list: &HashSet<D::Key>) -> Option<D::Key> {
1226        match ready_service_list.len() {
1227            0 => None,
1228            1 => Some(
1229                *ready_service_list
1230                    .iter()
1231                    .next()
1232                    .expect("just checked there is one service"),
1233            ),
1234            len => {
1235                // Choose 2 random peers, then return the least loaded of those 2 peers.
1236                let (a, b) = {
1237                    let idxs = rand::seq::index::sample(&mut rand::rng(), len, 2);
1238                    let a = idxs.index(0);
1239                    let b = idxs.index(1);
1240
1241                    let a = *ready_service_list
1242                        .iter()
1243                        .nth(a)
1244                        .expect("sample returns valid indexes");
1245                    let b = *ready_service_list
1246                        .iter()
1247                        .nth(b)
1248                        .expect("sample returns valid indexes");
1249
1250                    (a, b)
1251                };
1252
1253                let a_load = self.query_load(&a).expect("supplied services are ready");
1254                let b_load = self.query_load(&b).expect("supplied services are ready");
1255
1256                let selected = if a_load <= b_load { a } else { b };
1257
1258                trace!(
1259                    a.key = ?a,
1260                    a.load = ?a_load,
1261                    b.key = ?b,
1262                    b.load = ?b_load,
1263                    selected = ?selected,
1264                    ?len,
1265                    "selected service by p2c"
1266                );
1267
1268                Some(selected)
1269            }
1270        }
1271    }
1272
1273    /// Randomly chooses `max_peers` ready services, ignoring service load.
1274    ///
1275    /// The chosen peers are unique, but their order is not fully random.
1276    fn select_random_ready_peers(&self, max_peers: usize) -> Vec<D::Key> {
1277        use rand::seq::IteratorRandom;
1278
1279        self.ready_services
1280            .keys()
1281            .copied()
1282            .sample(&mut rand::rng(), max_peers)
1283    }
1284
1285    /// Randomly chooses ready peers for a sidecar broadcast, always including
1286    /// configured zcashd compat sidecar peers.
1287    fn select_sidecar_broadcast_peers(&self, max_peers: usize) -> Vec<D::Key> {
1288        use rand::seq::IteratorRandom;
1289
1290        let mut selected_peers: Vec<_> = self
1291            .zcashd_compat_peer_keys
1292            .iter()
1293            .filter(|key| self.ready_services.contains_key(*key))
1294            .copied()
1295            .collect();
1296
1297        selected_peers.extend(
1298            self.ready_services
1299                .keys()
1300                .filter(|key| !self.zcashd_compat_peer_keys.contains(key))
1301                .copied()
1302                .sample(&mut rand::rng(), max_peers),
1303        );
1304
1305        selected_peers
1306    }
1307
1308    /// Returns true if `service` is a configured zcashd sidecar peer.
1309    ///
1310    /// Only used to classify peers once, when they are inserted into the peer
1311    /// set; every later check uses the O(1) [`Self::zcashd_compat_peer_keys`]
1312    /// set instead.
1313    fn is_zcashd_compat_peer(&self, service: &D::Service) -> bool {
1314        self.block_gossip_peer_ips
1315            .iter()
1316            .any(|ip| service.is_inbound_direct_from_ip(ip))
1317    }
1318
1319    /// Forgets sidecar keys whose peer has disconnected.
1320    ///
1321    /// Sidecars reconnect from new ephemeral ports, and services are dropped on many
1322    /// paths (bans, version downgrades, cancelled requests), so this runs every
1323    /// poll cycle to keep the set from accumulating stale keys — otherwise a
1324    /// reused port could inherit a stale sidecar's stall-tracking exemption.
1325    fn prune_disconnected_sidecar_keys(&mut self) {
1326        Self::retain_connected_keys(
1327            &mut self.zcashd_compat_peer_keys,
1328            &self.ready_services,
1329            &self.cancel_handles,
1330        );
1331    }
1332
1333    /// Removes the keys of disconnected peers from `keys`.
1334    ///
1335    /// A connected peer is either ready or unready with a registered cancel
1336    /// handle; a key in neither map belongs to a dropped connection.
1337    fn retain_connected_keys(
1338        keys: &mut HashSet<D::Key>,
1339        ready_services: &HashMap<D::Key, D::Service>,
1340        cancel_handles: &HashMap<D::Key, oneshot::Sender<CancelClientWork>>,
1341    ) {
1342        if keys.is_empty() {
1343            return;
1344        }
1345
1346        keys.retain(|key| ready_services.contains_key(key) || cancel_handles.contains_key(key));
1347    }
1348
1349    /// Accesses a ready endpoint by `key` and returns its current load.
1350    ///
1351    /// Returns `None` if the service is not in the ready service list.
1352    fn query_load(&self, key: &D::Key) -> Option<<D::Service as Load>::Metric> {
1353        let svc = self.ready_services.get(key);
1354        svc.map(|svc| svc.load())
1355    }
1356
1357    /// Routes find requests fairly and other requests using P2C load-balancing.
1358    fn route_request(&mut self, req: Request) -> <Self as tower::Service<Request>>::Future {
1359        let is_find_request = matches!(
1360            &req,
1361            Request::FindBlocks { .. } | Request::FindHeaders { .. }
1362        );
1363        let peer_key = if is_find_request {
1364            // Fast empty responses must not gain a latency-based routing advantage.
1365            self.select_ready_find_peer(false)
1366        } else {
1367            self.select_ready_p2c_peer()
1368        };
1369
1370        if let Some(peer_key) = peer_key {
1371            if is_find_request && !self.serving_peer_keys.contains(&peer_key) {
1372                metrics::counter!(
1373                    "zcash.net.peer_set.find_fallback",
1374                    "reason" => "no_serving_peer",
1375                    "network" => network_kind_label(&self.network)
1376                )
1377                .increment(1);
1378            }
1379            return self.call_ready_peer(peer_key, req);
1380        }
1381
1382        if is_find_request {
1383            self.queued_find_requests
1384                .retain(|queued| !queued.response_sender.is_canceled());
1385            if self.queued_find_requests.len() < MAX_QUEUED_FIND_REQUESTS {
1386                return self.queue_find_request(req);
1387            }
1388            if let Some(peer_key) = self.select_ready_find_peer(true) {
1389                metrics::counter!(
1390                    "zcash.net.peer_set.find_fallback",
1391                    "reason" => "queue_full",
1392                    "network" => network_kind_label(&self.network)
1393                )
1394                .increment(1);
1395                return self.call_ready_peer(peer_key, req);
1396            }
1397        }
1398
1399        async move {
1400            // Let other tasks run, so a retry request might get different ready peers.
1401            tokio::task::yield_now().await;
1402            Err(SharedPeerError::from(PeerError::NoReadyPeers))
1403        }
1404        .map_err(Into::into)
1405        .boxed()
1406    }
1407
1408    /// Tries to route a request to a ready peer that advertised that inventory,
1409    /// falling back to a ready peer that isn't missing the inventory.
1410    ///
1411    /// If no ready peer can be used, but a busy peer might have the block, queues the request
1412    /// until one of those busy peers becomes ready. See [`Self::route_queued_inv_requests`].
1413    ///
1414    /// Otherwise, returns a synthetic [`NotFoundRegistry`](PeerError::NotFoundRegistry) error.
1415    ///
1416    /// See [`Self::select_inv_route`] for details.
1417    fn route_inv(
1418        &mut self,
1419        req: Request,
1420        hash: InventoryHash,
1421    ) -> <Self as tower::Service<Request>>::Future {
1422        match self.select_inv_route(&req, hash) {
1423            InvRoute::Peer(peer) => self.call_inv_peer(peer, req, hash),
1424            InvRoute::Wait => self.queue_inv_request(req, hash),
1425            InvRoute::Refuse => {
1426                tracing::debug!(
1427                    ?hash,
1428                    "no peer that might have the inventory can take the request, failing request"
1429                );
1430                metrics::counter!(
1431                    "zcash.net.peer_set.inventory_refusal",
1432                    "kind" => "instant",
1433                    "network" => network_kind_label(&self.network)
1434                )
1435                .increment(1);
1436
1437                async move {
1438                    // Let other tasks run, so a retry request might get different ready peers.
1439                    tokio::task::yield_now().await;
1440
1441                    Err(not_found_registry_error(hash))
1442                }
1443                .boxed()
1444            }
1445        }
1446    }
1447
1448    /// Returns where to route an inventory request for `hash`.
1449    ///
1450    /// Prefers a ready peer that advertised the inventory, then a ready peer that isn't missing it.
1451    /// For blocks, that fallback prefers peers that advertised [`PeerServices::NODE_NETWORK`].
1452    /// Non-serving peers usually answer `notfound` for historic blocks, so they only get a block
1453    /// if no connected serving peer might have it, or if some peer advertised it (so it is recent).
1454    ///
1455    /// Only block requests wait for busy peers: transaction downloads don't retry, so waiting
1456    /// would only slow them down.
1457    ///
1458    /// Peers that recently failed to return a block are skipped for that block, unless no other
1459    /// peer might have it.
1460    ///
1461    /// Uses P2C to choose the least loaded peer in each list.
1462    fn select_inv_route(&self, request: &Request, hash: InventoryHash) -> InvRoute<D::Key> {
1463        // # Security
1464        //
1465        // Peers that recently failed to return this block lose any preference for it, and are only
1466        // asked again as a last resort. Otherwise, a peer that falsely claims to serve blocks and
1467        // withholds them could get every retry.
1468        let failed_peer_list: HashSet<PeerSocketAddr> = self
1469            .inventory_registry
1470            .failed_peers(hash)
1471            .copied()
1472            .collect();
1473        let has_failed = |addr: &PeerSocketAddr| failed_peer_list.contains(addr);
1474        let advertiser = match request {
1475            Request::BlockDownload { advertiser, .. } => advertiser.filter(|addr| {
1476                // Registry advertisements already exclude missing peers. Apply the same rule
1477                // to carried provenance without allocating a fallback set on the fast path.
1478                !self
1479                    .inventory_registry
1480                    .missing_peers(hash)
1481                    .any(|missing| missing == addr)
1482            }),
1483            _ => None,
1484        };
1485
1486        let advertising_peers: Vec<PeerSocketAddr> = self
1487            .inventory_registry
1488            .advertising_peers(hash)
1489            .copied()
1490            .chain(advertiser)
1491            .collect();
1492        let advertising_peer_list = advertising_peers
1493            .iter()
1494            .filter(|addr| self.ready_services.contains_key(addr) && !has_failed(addr))
1495            .copied()
1496            .collect();
1497
1498        // # Security
1499        //
1500        // Choose a random, less-loaded peer with the inventory.
1501        //
1502        // If we chose the first peer in HashMap order,
1503        // peers would be able to influence our choice by switching addresses.
1504        // But we need the choice to be random,
1505        // so that a peer can't provide all our inventory responses.
1506        if let Some(peer) = self.select_p2c_peer_from_list(&advertising_peer_list) {
1507            tracing::trace!(?hash, ?peer, "routing to a peer which advertised inventory");
1508            return InvRoute::Peer(peer);
1509        }
1510
1511        let missing_peer_list: HashSet<PeerSocketAddr> = self
1512            .inventory_registry
1513            .missing_peers(hash)
1514            .copied()
1515            .collect();
1516
1517        // Only block routing depends on peer services: non-serving peers can still serve
1518        // mempool transactions.
1519        let is_block = matches!(hash, InventoryHash::Block(_));
1520        let ready_peers_with_services = |serving: bool| -> HashSet<PeerSocketAddr> {
1521            self.ready_services
1522                .keys()
1523                .filter(|addr| {
1524                    !missing_peer_list.contains(addr)
1525                        && !has_failed(addr)
1526                        && (!is_block || self.serving_peer_keys.contains(addr) == serving)
1527                })
1528                .copied()
1529                .collect()
1530        };
1531        let preferred_peer_list = ready_peers_with_services(true);
1532
1533        // Security: choose a random, less-loaded peer that might have the inventory.
1534        if let Some(peer) = self.select_p2c_peer_from_list(&preferred_peer_list) {
1535            tracing::trace!(?hash, ?peer, "routing to a peer that might have inventory");
1536            return InvRoute::Peer(peer);
1537        }
1538
1539        if !is_block {
1540            return InvRoute::Refuse;
1541        }
1542
1543        // Peers only advertise recent blocks, which non-serving peers can usually serve. So any
1544        // busy peer might have an advertised block, but only busy serving peers might have a
1545        // historic block.
1546        //
1547        // Downloaders carry their known advertiser separately from this lossy registry.
1548        //
1549        // A disconnected or failed advertiser must not enable speculative non-serving fallback.
1550        let is_advertised = advertising_peers
1551            .iter()
1552            .any(|addr| self.has_peer_with_addr(*addr) && !has_failed(addr));
1553        let busy_peer_might_have = |failed: bool| {
1554            self.cancel_handles.keys().any(|key| {
1555                !missing_peer_list.contains(key)
1556                    && has_failed(key) == failed
1557                    && (failed || is_advertised || self.serving_peer_keys.contains(key))
1558            })
1559        };
1560        let other_busy_peer_might_have = busy_peer_might_have(false);
1561
1562        // Waiting for a busy serving peer is better than asking a non-serving peer, which would
1563        // most likely answer `notfound` for a historic block, and get marked as missing it.
1564        if is_advertised || !other_busy_peer_might_have {
1565            if let Some(peer) = self.select_p2c_peer_from_list(&ready_peers_with_services(false)) {
1566                tracing::trace!(?hash, ?peer, "routing to a non-serving peer");
1567                return InvRoute::Peer(peer);
1568            }
1569        }
1570
1571        if other_busy_peer_might_have {
1572            return InvRoute::Wait;
1573        }
1574
1575        // As a last resort, ask a peer that failed to return this block again, or wait for it.
1576        let failed_ready_peer_list = self
1577            .ready_services
1578            .keys()
1579            .filter(|addr| has_failed(addr) && !missing_peer_list.contains(addr))
1580            .copied()
1581            .collect();
1582        if let Some(peer) = self.select_p2c_peer_from_list(&failed_ready_peer_list) {
1583            tracing::trace!(
1584                ?hash,
1585                ?peer,
1586                "routing to a peer that failed to return the block"
1587            );
1588            return InvRoute::Peer(peer);
1589        }
1590
1591        if busy_peer_might_have(true) {
1592            InvRoute::Wait
1593        } else {
1594            InvRoute::Refuse
1595        }
1596    }
1597
1598    /// Sends the inventory request `req` for `hash` to the ready peer `key`, and returns its
1599    /// response future.
1600    ///
1601    /// If a block request fails, reports the failure to the peer set.
1602    fn call_inv_peer(
1603        &mut self,
1604        key: D::Key,
1605        req: Request,
1606        hash: InventoryHash,
1607    ) -> <Self as tower::Service<Request>>::Future {
1608        // Transaction requests don't wait for busy peers, so there is no preference to lose.
1609        if !matches!(hash, InventoryHash::Block(_)) {
1610            return self.call_ready_peer(key, req);
1611        }
1612
1613        let connection_info = self
1614            .ready_services
1615            .get(&key)
1616            .expect("inventory routing selected a ready connection")
1617            .connection_info()
1618            .clone();
1619        let fut = self.call_ready_peer(key, req);
1620
1621        let failure_tx = self.inv_failure_tx.clone();
1622        async move {
1623            let result = fut.await;
1624            if let Err(error) = &result {
1625                let missing = error
1626                    .downcast_ref::<SharedPeerError>()
1627                    .is_some_and(|error| error.inner_debug().contains("NotFoundResponse"));
1628                if !missing {
1629                    // Only a completed peer error, including its response timeout, is failure
1630                    // evidence. Dropping a caller, losing hedge or sync reset remains neutral.
1631                    let _ = failure_tx.send((key, connection_info, hash));
1632                }
1633            }
1634            result
1635        }
1636        .boxed()
1637    }
1638
1639    /// Sends `req` to the ready peer `key`, and returns its response future.
1640    fn call_ready_peer(
1641        &mut self,
1642        key: D::Key,
1643        req: Request,
1644    ) -> <Self as tower::Service<Request>>::Future {
1645        let is_find_request = matches!(
1646            &req,
1647            Request::FindBlocks { .. } | Request::FindHeaders { .. }
1648        );
1649        tracing::trace!(peer_key = ?key, is_find_request, "routing request to peer");
1650        let mut svc = self
1651            .take_ready_service(&key)
1652            .expect("callers select `key` from the ready services just before calling");
1653
1654        // Configured sidecars are trusted downstream consumers, not upstream sync sources.
1655        let track_stalls = is_find_request && !self.zcashd_compat_peer_keys.contains(&key);
1656        let fut = svc.call(req);
1657        let response = if track_stalls {
1658            let connection_info = svc.connection_info().clone();
1659            let stall_tx = self.stall_event_tx.clone();
1660            async move {
1661                // Cancellation is not evidence of failure: a caught-up peer can
1662                // legitimately send no getblocks reply before the caller times out.
1663                let result = fut.await;
1664                if let Some(outcome) = classify_find_response(&result) {
1665                    let _ = stall_tx.send((key, connection_info, outcome));
1666                }
1667                result.map_err(Into::into)
1668            }
1669            .boxed()
1670        } else {
1671            fut.map_err(Into::into).boxed()
1672        };
1673        self.push_unready(key, svc);
1674        response
1675    }
1676
1677    /// Waits for a serving peer, falling back to any ready peer once the bounded wait expires.
1678    fn queue_find_request(&mut self, request: Request) -> ResponseFuture {
1679        metrics::counter!(
1680            "zcash.net.peer_set.find_queued",
1681            "network" => network_kind_label(&self.network)
1682        )
1683        .increment(1);
1684        let (response_sender, response_receiver) = oneshot::channel();
1685        self.queued_find_requests.push_back(QueuedFindRequest {
1686            request,
1687            deadline: tokio::time::Instant::now() + FIND_BUSY_SERVING_PEER_WAIT_TIMEOUT,
1688            response_sender,
1689        });
1690
1691        // Register peer, timer, and cancellation wakeups even if the response is never polled.
1692        self.queued_request_waker.notify.notify_one();
1693
1694        async move {
1695            let response_fut = response_receiver
1696                .await
1697                .map_err(|_| SharedPeerError::from(PeerError::NoReadyPeers))?;
1698            response_fut.await
1699        }
1700        .boxed()
1701    }
1702
1703    /// Routes queued discovery before inventory, and registers deadline and cancellation wakeups.
1704    ///
1705    /// This also runs with no ready peers, so abandoned requests are removed and expired
1706    /// serving-only waits stop arming the timer.
1707    fn route_queued_find_requests(&mut self, cx: &mut Context<'_>) {
1708        if self.queued_find_requests.is_empty() {
1709            return;
1710        }
1711
1712        let now = tokio::time::Instant::now();
1713        let mut next_deadline = None;
1714        for _ in 0..self.queued_find_requests.len() {
1715            let mut queued = self
1716                .queued_find_requests
1717                .pop_front()
1718                .expect("the loop visits only requests that were already queued");
1719            if queued.response_sender.poll_canceled(cx).is_ready() {
1720                continue;
1721            }
1722
1723            let expired = queued.deadline <= now;
1724            if let Some(peer) = self.select_ready_find_peer(expired) {
1725                if !self.serving_peer_keys.contains(&peer) {
1726                    let reason = if self.serving_peer_keys.is_empty() {
1727                        "no_serving_peer"
1728                    } else {
1729                        "expired"
1730                    };
1731                    metrics::counter!(
1732                        "zcash.net.peer_set.find_fallback",
1733                        "reason" => reason,
1734                        "network" => network_kind_label(&self.network)
1735                    )
1736                    .increment(1);
1737                }
1738                let response_fut = self.call_ready_peer(peer, queued.request);
1739                // A concurrent cancellation drops the response future, cancelling peer work.
1740                let _ = queued.response_sender.send(response_fut);
1741                // Register the newly unready service even if an unrelated peer remains ready.
1742                // Otherwise an idle Buffer can leave later queued downloads without a wakeup.
1743                cx.waker().wake_by_ref();
1744            } else {
1745                if !expired {
1746                    // Requests enter this FIFO with monotonically increasing deadlines.
1747                    next_deadline.get_or_insert(queued.deadline);
1748                }
1749                self.queued_find_requests.push_back(queued);
1750            }
1751        }
1752
1753        if let Some(deadline) = next_deadline {
1754            let timer = self
1755                .queued_find_request_timer
1756                .get_or_insert_with(|| Box::pin(tokio::time::sleep_until(deadline)));
1757            if timer.deadline() != deadline {
1758                timer.as_mut().reset(deadline);
1759            }
1760            if timer.as_mut().poll(cx).is_ready() {
1761                // The deadline might have passed while routing; retry with a fresh `now`.
1762                cx.waker().wake_by_ref();
1763            }
1764        } else {
1765            self.queued_find_request_timer = None;
1766        }
1767    }
1768
1769    /// Queues an inventory request until a busy peer that might have `hash` becomes ready.
1770    ///
1771    /// The returned future resolves to that peer's response, or to a synthetic
1772    /// [`NotFoundRegistry`](PeerError::NotFoundRegistry) error if the request is refused.
1773    fn queue_inv_request(
1774        &mut self,
1775        request: Request,
1776        hash: InventoryHash,
1777    ) -> <Self as tower::Service<Request>>::Future {
1778        tracing::debug!(
1779            ?hash,
1780            "no ready peer can serve the inventory, waiting for a busy peer"
1781        );
1782        let network = network_kind_label(&self.network);
1783        metrics::counter!("zcash.net.peer_set.inventory_queued", "network" => network).increment(1);
1784
1785        let deadline = (!matches!(request, Request::BlockDownload { .. }))
1786            .then(|| tokio::time::Instant::now() + INVENTORY_BUSY_PEER_WAIT_TIMEOUT);
1787
1788        let (response_sender, response_receiver) = oneshot::channel();
1789        self.queued_inv_requests.push_back(QueuedInvRequest {
1790            request,
1791            hash,
1792            deadline,
1793            span: tracing::Span::current(),
1794            response_sender,
1795        });
1796
1797        // Poll the peer set again, so peer events wake `poll_peer_set_on_notify()` while this
1798        // request is queued.
1799        self.queued_request_waker.notify.notify_one();
1800
1801        async move {
1802            let response = if let Some(deadline) = deadline {
1803                tokio::time::timeout_at(deadline, response_receiver)
1804                    .await
1805                    .ok()
1806                    .and_then(Result::ok)
1807            } else {
1808                // Capacity waiting is not a peer attempt. The downloader bounds the complete
1809                // operation, including readiness, queueing, retries, and the response.
1810                response_receiver.await.ok()
1811            };
1812            match response {
1813                Some(response_fut) => response_fut.await,
1814                None => {
1815                    tracing::debug!(
1816                        ?hash,
1817                        "no busy peer that might have the inventory became ready, failing request"
1818                    );
1819                    metrics::counter!(
1820                        "zcash.net.peer_set.inventory_refusal",
1821                        "kind" => "waited",
1822                        "network" => network
1823                    )
1824                    .increment(1);
1825
1826                    Err(not_found_registry_error(hash))
1827                }
1828            }
1829        }
1830        .boxed()
1831    }
1832
1833    /// Routes queued inventory requests to ready peers that might have their inventory, in the
1834    /// order they were queued. Refuses queued requests once no busy peer might have their
1835    /// inventory.
1836    ///
1837    /// Queued requests are only routed when the peer set is polled. While requests are queued,
1838    /// peer events notify [`poll_peer_set_on_notify`], which polls the peer set.
1839    ///
1840    /// Reconsiders candidates on every poll, including advertisements, failures and removals.
1841    fn route_queued_inv_requests(&mut self, cx: &mut Context<'_>) {
1842        // Register caller cancellation too, so an expired operation releases its queue entry
1843        // even when no connection or inventory event follows it.
1844        self.queued_inv_requests.retain_mut(|queued| {
1845            queued
1846                .deadline
1847                .is_none_or(|deadline| deadline > tokio::time::Instant::now())
1848                && queued.response_sender.poll_canceled(cx).is_pending()
1849        });
1850
1851        let mut routed_any = false;
1852        for _ in 0..self.queued_inv_requests.len() {
1853            let queued = self
1854                .queued_inv_requests
1855                .pop_front()
1856                .expect("each original queue entry is visited once");
1857
1858            match self.select_inv_route(&queued.request, queued.hash) {
1859                InvRoute::Peer(peer) => {
1860                    let _span = queued.span.enter();
1861                    let response_fut = self.call_inv_peer(peer, queued.request, queued.hash);
1862                    routed_any = true;
1863                    // If the receiver was dropped since the check above, dropping the response
1864                    // future cancels the peer's request.
1865                    let _ = queued.response_sender.send(response_fut);
1866                }
1867                InvRoute::Wait => self.queued_inv_requests.push_back(queued),
1868                // Dropping the sender refuses the request.
1869                InvRoute::Refuse => {}
1870            }
1871        }
1872
1873        // Poll the newly busy peer's readiness future even if another peer stays ready.
1874        if routed_any && !self.queued_inv_requests.is_empty() {
1875            self.queued_request_waker.notify.notify_one();
1876        }
1877    }
1878
1879    /// Routes the same request to up to `max_peers` ready peers, ignoring return values.
1880    ///
1881    /// `max_peers` must be at least one, and at most the number of ready peers.
1882    fn route_multiple(
1883        &mut self,
1884        req: Request,
1885        max_peers: usize,
1886    ) -> <Self as tower::Service<Request>>::Future {
1887        assert!(
1888            max_peers > 0,
1889            "requests must be routed to at least one peer"
1890        );
1891        assert!(
1892            max_peers <= self.ready_services.len(),
1893            "requests can only be routed to ready peers"
1894        );
1895
1896        let selected_peers = self.select_random_ready_peers(max_peers);
1897        self.send_multiple(req, selected_peers)
1898    }
1899
1900    /// Sends the same request to the provided ready peers, ignoring return values.
1901    ///
1902    /// # Security
1903    ///
1904    /// Callers should choose peers randomly, ignoring load.
1905    /// This avoids favouring malicious peers, because peers can influence their own load.
1906    ///
1907    /// The order of peers isn't completely random,
1908    /// but peer request order is not security-sensitive.
1909    fn send_multiple(
1910        &mut self,
1911        req: Request,
1912        peers: Vec<D::Key>,
1913    ) -> <Self as tower::Service<Request>>::Future {
1914        let futs = FuturesUnordered::new();
1915        for key in peers {
1916            let mut svc = self
1917                .take_ready_service(&key)
1918                .expect("selected peers are ready");
1919            futs.push(svc.call(req.clone()).map_err(|_| ()));
1920            self.push_unready(key, svc);
1921        }
1922
1923        async move {
1924            let results = futs.collect::<Vec<Result<_, _>>>().await;
1925            tracing::debug!(
1926                ok.len = results.iter().filter(|r| r.is_ok()).count(),
1927                err.len = results.iter().filter(|r| r.is_err()).count(),
1928                "sent peer request to multiple peers"
1929            );
1930            Ok(Response::Nil)
1931        }
1932        .boxed()
1933    }
1934
1935    /// Broadcasts the same request to lots of ready peers, ignoring return values.
1936    fn route_broadcast(&mut self, req: Request) -> <Self as tower::Service<Request>>::Future {
1937        // Broadcasts ignore the response
1938        self.route_multiple(req, self.number_of_peers_to_broadcast())
1939    }
1940
1941    /// Broadcasts a request to sampled peers and all configured sidecars.
1942    ///
1943    /// Used for requests that sidecars must always receive: block adverts and
1944    /// pushed transactions. Connected sidecars that are busy with another
1945    /// request are owed it: the request is queued and delivered by
1946    /// [`Self::send_queued_sidecar_broadcast`] as soon as they are ready again.
1947    fn route_sidecar_broadcast(
1948        &mut self,
1949        req: Request,
1950    ) -> <Self as tower::Service<Request>>::Future {
1951        self.prune_disconnected_sidecar_keys();
1952
1953        let selected_peers =
1954            self.select_sidecar_broadcast_peers(self.number_of_peers_to_broadcast());
1955
1956        // A newer broadcast supersedes any older undelivered one, even one of
1957        // a different kind. Sidecars only need the latest block advert to stay
1958        // live: they fetch any blocks in between over the same connection.
1959        let busy_sidecars: HashSet<D::Key> = self
1960            .zcashd_compat_peer_keys
1961            .iter()
1962            .filter(|key| !self.ready_services.contains_key(*key))
1963            .copied()
1964            .collect();
1965        self.queued_sidecar_broadcast =
1966            (!busy_sidecars.is_empty()).then(|| (req.clone(), busy_sidecars));
1967
1968        self.send_multiple(req, selected_peers)
1969    }
1970
1971    /// Delivers the queued sidecar broadcast to any owed sidecar peers that
1972    /// have become ready. See [`Self::route_sidecar_broadcast`].
1973    fn send_queued_sidecar_broadcast(&mut self) {
1974        let Some((req, mut remaining_sidecars)) = self.queued_sidecar_broadcast.take() else {
1975            return;
1976        };
1977
1978        // Like `broadcast_all_queued`, don't deliver to peers that were banned
1979        // while the request was queued.
1980        let bans = self.bans_receiver.borrow().clone();
1981        remaining_sidecars.retain(|key| !bans.is_banned(key.ip()));
1982
1983        let ready_sidecars: Vec<D::Key> = remaining_sidecars
1984            .iter()
1985            .filter(|key| self.ready_services.contains_key(*key))
1986            .copied()
1987            .collect();
1988        for key in ready_sidecars {
1989            remaining_sidecars.remove(&key);
1990
1991            let mut svc = self
1992                .take_ready_service(&key)
1993                .expect("sidecars are ready because they were filtered from ready_services above");
1994            let req_fut = svc.call(req.clone());
1995            self.push_unready(key, svc);
1996
1997            // Detach the response future: the connection cancels requests whose
1998            // response channel is dropped, and there is no caller left to drive
1999            // this delivery.
2000            tokio::spawn(req_fut.map(|_| ()));
2001        }
2002
2003        // Drop sidecars that disconnected while the request was queued.
2004        Self::retain_connected_keys(
2005            &mut remaining_sidecars,
2006            &self.ready_services,
2007            &self.cancel_handles,
2008        );
2009
2010        if !remaining_sidecars.is_empty() {
2011            self.queued_sidecar_broadcast = Some((req, remaining_sidecars));
2012        }
2013    }
2014
2015    /// Broadcasts the same request to all ready peers, ignoring return values.
2016    fn broadcast_all(&mut self, req: Request) -> <Self as tower::Service<Request>>::Future {
2017        let ready_peers = self.ready_services.keys().copied().collect();
2018        let send_multiple_fut = self.send_multiple(req.clone(), ready_peers);
2019        let Some(mut queued_broadcast_fut_receiver) = self.queue_broadcast_all_unready(&req) else {
2020            return send_multiple_fut;
2021        };
2022
2023        async move {
2024            let _ = send_multiple_fut.await?;
2025            while queued_broadcast_fut_receiver.recv().await.is_some() {}
2026            Ok(Response::Nil)
2027        }
2028        .boxed()
2029    }
2030
2031    /// If there are unready peers, queues a request to be broadcasted to them and
2032    /// returns a channel receiver for callers to await the broadcast_all() futures, or
2033    /// returns None if there are no unready peers.
2034    fn queue_broadcast_all_unready(
2035        &mut self,
2036        req: &Request,
2037    ) -> Option<tokio::sync::mpsc::Receiver<ResponseFuture>> {
2038        if !self.cancel_handles.is_empty() {
2039            /// How many broadcast all futures to send to the channel until the peer set should wait for the channel consumer
2040            /// to read a message before continuing to send the queued broadcast request to peers that were originally unready.
2041            const QUEUED_BROADCAST_FUTS_CHANNEL_SIZE: usize = 3;
2042
2043            let (sender, receiver) = tokio::sync::mpsc::channel(QUEUED_BROADCAST_FUTS_CHANNEL_SIZE);
2044            let unready_peers: HashSet<_> = self.cancel_handles.keys().cloned().collect();
2045            let queued = (req.clone(), sender, unready_peers);
2046
2047            // Drop the existing queued broadcast all request, if any.
2048            self.queued_broadcast_all = Some(queued);
2049
2050            Some(receiver)
2051        } else {
2052            None
2053        }
2054    }
2055
2056    /// Broadcasts the same requests to all ready peers which were unready when
2057    /// [`PeerSet::broadcast_all()`] was last called, ignoring return values.
2058    fn broadcast_all_queued(&mut self) {
2059        let Some((req, sender, mut remaining_peers)) = self.queued_broadcast_all.take() else {
2060            return;
2061        };
2062
2063        let bans = self.bans_receiver.borrow().clone();
2064        remaining_peers.retain(|addr| !bans.is_banned(addr.ip()));
2065
2066        let Ok(reserved_send_slot) = sender.try_reserve() else {
2067            self.queued_broadcast_all = Some((req, sender, remaining_peers));
2068            return;
2069        };
2070
2071        let peers: Vec<_> = self
2072            .ready_services
2073            .keys()
2074            .filter(|ready_peer| remaining_peers.remove(ready_peer))
2075            .copied()
2076            .collect();
2077
2078        reserved_send_slot.send(self.send_multiple(req.clone(), peers).boxed());
2079
2080        if !remaining_peers.is_empty() {
2081            self.queued_broadcast_all = Some((req, sender, remaining_peers));
2082        }
2083    }
2084
2085    /// Given a number of ready peers calculate to how many of them Zebra will
2086    /// actually send the request to. Return this number.
2087    pub(crate) fn number_of_peers_to_broadcast(&self) -> usize {
2088        if self.network.is_regtest() {
2089            // In regtest, we broadcast to all peers, so that we can test the
2090            // peer set with a small number of peers.
2091            self.ready_services.len()
2092        } else {
2093            // We are currently sending broadcast messages to a third of the total peers.
2094            const PEER_FRACTION_TO_BROADCAST: usize = 3;
2095
2096            // Round up, so that if we have one ready peer, it gets the request.
2097            div_ceil(self.ready_services.len(), PEER_FRACTION_TO_BROADCAST)
2098        }
2099    }
2100
2101    /// Returns the list of addresses in the peer set.
2102    fn peer_set_addresses(&self) -> Vec<PeerSocketAddr> {
2103        self.ready_services
2104            .keys()
2105            .chain(self.cancel_handles.keys())
2106            .cloned()
2107            .collect()
2108    }
2109
2110    /// Logs the peer set size, and any potential connectivity issues.
2111    fn log_peer_set_size(&mut self) {
2112        let ready_services_len = self.ready_services.len();
2113        let unready_services_len = self.unready_services.len();
2114        trace!(ready_peers = ?ready_services_len, unready_peers = ?unready_services_len);
2115
2116        let now = Instant::now();
2117
2118        // These logs are designed to be human-readable in a terminal, at the
2119        // default Zebra log level. If you need to know the peer set size for
2120        // every request, use the trace-level logs, or the metrics exporter.
2121        if let Some(last_peer_log) = self.last_peer_log {
2122            // Avoid duplicate peer set logs
2123            if now.duration_since(last_peer_log) < MIN_PEER_SET_LOG_INTERVAL {
2124                return;
2125            }
2126        } else {
2127            // Suppress initial logs until the peer set has started up.
2128            // There can be multiple initial requests before the first peer is
2129            // ready.
2130            self.last_peer_log = Some(now);
2131            return;
2132        }
2133
2134        self.last_peer_log = Some(now);
2135
2136        // Log potential duplicate connections.
2137        let peers = self.peer_set_addresses();
2138
2139        // Check for duplicates by address and port: these are unexpected and represent a bug.
2140        let duplicates: Vec<PeerSocketAddr> = peers.iter().duplicates().cloned().collect();
2141
2142        let mut peer_counts = peers.iter().counts();
2143        peer_counts.retain(|peer, _count| duplicates.contains(peer));
2144
2145        if !peer_counts.is_empty() {
2146            let duplicate_connections: usize = peer_counts.values().sum();
2147
2148            warn!(
2149                ?duplicate_connections,
2150                duplicated_peers = ?peer_counts.len(),
2151                peers = ?peers.len(),
2152                "duplicate peer connections in peer set"
2153            );
2154        }
2155
2156        // Check for duplicates by address: these can happen if there are multiple nodes
2157        // behind a NAT or on a single server.
2158        let peers: Vec<IpAddr> = peers.iter().map(|addr| addr.ip()).collect();
2159        let duplicates: Vec<IpAddr> = peers.iter().duplicates().cloned().collect();
2160
2161        let mut peer_counts = peers.iter().counts();
2162        peer_counts.retain(|peer, _count| duplicates.contains(peer));
2163
2164        if !peer_counts.is_empty() {
2165            let duplicate_connections: usize = peer_counts.values().sum();
2166
2167            info!(
2168                ?duplicate_connections,
2169                duplicated_peers = ?peer_counts.len(),
2170                peers = ?peers.len(),
2171                "duplicate IP addresses in peer set"
2172            );
2173        }
2174
2175        // Only log connectivity warnings if all our peers are busy (or there are no peers).
2176        if ready_services_len > 0 {
2177            return;
2178        }
2179
2180        let address_metrics = *self.address_metrics.borrow();
2181        if unready_services_len == 0 {
2182            warn!(
2183                ?address_metrics,
2184                "network request with no peer connections. Hint: check your network connection"
2185            );
2186        } else {
2187            info!(?address_metrics, "network request with no ready peers: finding more peers, waiting for {} peers to answer requests",
2188                  unready_services_len);
2189        }
2190    }
2191
2192    /// Updates the peer set metrics.
2193    ///
2194    /// # Panics
2195    ///
2196    /// If the peer set size exceeds the connection limit.
2197    fn update_metrics(&self) {
2198        let num_ready = self.ready_services.len();
2199        let num_unready = self.unready_services.len();
2200        let num_peers = num_ready + num_unready;
2201        let network = network_kind_label(&self.network);
2202        metrics::gauge!("pool.num_ready", "network" => network).set(num_ready as f64);
2203        metrics::gauge!("pool.num_unready", "network" => network).set(num_unready as f64);
2204        metrics::gauge!("zcash.net.peers", "network" => network).set(num_peers as f64);
2205
2206        // Security: make sure we haven't exceeded the connection limit
2207        if num_peers > self.peerset_total_connection_limit {
2208            let address_metrics = *self.address_metrics.borrow();
2209            panic!(
2210                "unexpectedly exceeded configured peer set connection limit: \n\
2211                 peers: {num_peers:?}, ready: {num_ready:?}, unready: {num_unready:?}, \n\
2212                 address_metrics: {address_metrics:?}",
2213            );
2214        }
2215    }
2216}
2217
2218impl<D, C> PeerSet<D, C>
2219where
2220    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin + Send + 'static,
2221    D::Error: Into<BoxError>,
2222    C: ChainTip + Send + 'static,
2223{
2224    /// Buffers the peer set and retains shutdown ownership only in external service handles.
2225    ///
2226    /// The worker and self-poller are aborted when the final external handle is dropped.
2227    pub(crate) fn into_buffer(
2228        self,
2229        bound: usize,
2230    ) -> (crate::NetworkService, JoinHandle<Result<(), BoxError>>) {
2231        let queued_request_waker = self.queued_request_waker.clone();
2232        let (peer_set, worker) = Buffer::pair(BoxService::new(self), bound);
2233        let worker = tokio::spawn(worker.in_current_span());
2234        let poll_task = tokio::spawn(
2235            poll_peer_set_on_notify(peer_set.clone(), queued_request_waker).in_current_span(),
2236        );
2237
2238        let network = crate::NetworkService::new(
2239            peer_set,
2240            vec![worker.abort_handle(), poll_task.abort_handle()],
2241        );
2242        (network, poll_task)
2243    }
2244}
2245
2246impl<D, C> Service<Request> for PeerSet<D, C>
2247where
2248    D: Discover<Key = PeerSocketAddr, Service = LoadTrackedClient> + Unpin,
2249    D::Error: Into<BoxError>,
2250    C: ChainTip,
2251{
2252    type Response = Response;
2253    type Error = BoxError;
2254    type Future =
2255        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
2256
2257    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
2258        // Update service and peer statuses.
2259        //
2260        // # Correctness
2261        //
2262        // All of the futures that receive a context from this method can wake the peer set buffer
2263        // task. If there are no ready peers, and no new peers, network requests will pause until:
2264        // - an unready peer becomes ready, or
2265        // - a new peer arrives.
2266
2267        // Peer events, find deadlines, and cancellations also notify the queued request task,
2268        // so the peer set gets polled even when the buffer has no other requests.
2269        let peer_event_waker = self.peer_event_waker(cx);
2270        let mut peer_cx = Context::from_waker(&peer_event_waker);
2271        // Events can change queued candidates even without a readiness transition. Register
2272        // their wakeups with the notification task as well as the Buffer worker.
2273        self.drain_stall_events(&mut peer_cx);
2274
2275        // Check for new peers, and register a task wakeup when the next new peers arrive. New peers
2276        // can be infrequent if our connection slots are full, or we're connected to all
2277        // available/useful peers.
2278        let _poll_pending_or_ready: Poll<()> = self.poll_discover(&mut peer_cx)?;
2279
2280        // These tasks don't provide new peers or newly ready peers.
2281        let _poll_pending: Poll<()> = self.poll_background_errors(cx)?;
2282        let _poll_pending_or_ready: Poll<()> =
2283            self.inventory_registry.poll_inventory(&mut peer_cx)?;
2284        // Register failures after rotation so they survive a full interval before aging.
2285        self.drain_inv_failures(&mut peer_cx);
2286
2287        let ready_peers = self.poll_peers(&mut peer_cx)?;
2288
2289        self.prune_disconnected_sidecar_keys();
2290        Self::retain_connected_keys(
2291            &mut self.serving_peer_keys,
2292            &self.ready_services,
2293            &self.cancel_handles,
2294        );
2295        // Discovery gets newly ready serving peers before queued block downloads can take them.
2296        // Even without ready peers, poll deadlines and cancellations to keep the queue live.
2297        self.route_queued_find_requests(&mut peer_cx);
2298
2299        // These metrics should run last, to report the most up-to-date information.
2300        self.log_peer_set_size();
2301        self.update_metrics();
2302
2303        if ready_peers.is_pending() {
2304            // # Correctness
2305            //
2306            // If the channel is full, drop the demand signal rather than waiting. If we waited
2307            // here, the crawler could deadlock sending a request to fetch more peers, because it
2308            // also empties the channel.
2309            trace!("no ready services, sending demand signal");
2310            let _ = self.demand_signal.try_send(MorePeers);
2311
2312            // # Correctness
2313            //
2314            // The current task must be scheduled for wakeup every time we return `Poll::Pending`.
2315            //
2316            // As long as there are unready or new peers, this task will run, because:
2317            // - `poll_discover` schedules this task for wakeup when new peers arrive.
2318            // - if there are unready peers, `poll_unready` or `poll_ready_peers` schedule this
2319            //   task for wakeup when peer services become ready.
2320            //
2321            // To avoid peers blocking on a full peer status/error channel:
2322            // - `poll_background_errors` schedules this task for wakeup when the peer status
2323            //   update task exits.
2324            return Poll::Pending;
2325        }
2326
2327        // Waiting block requests still take priority over broadcasts.
2328        self.route_queued_inv_requests(&mut peer_cx);
2329        self.broadcast_all_queued();
2330        self.send_queued_sidecar_broadcast();
2331
2332        if self.ready_services.is_empty() {
2333            if self.poll_peers(&mut peer_cx)?.is_pending() {
2334                return Poll::Pending;
2335            }
2336            // A peer can recover capacity while the first queued request is dispatched.
2337            // Do not expose that capacity to the Buffer's held request before queued work.
2338            self.route_queued_find_requests(&mut peer_cx);
2339            self.route_queued_inv_requests(&mut peer_cx);
2340            if self.ready_services.is_empty() {
2341                // Newly inserted readiness futures need a first poll even with an idle Buffer.
2342                cx.waker().wake_by_ref();
2343                Poll::Pending
2344            } else {
2345                Poll::Ready(Ok(()))
2346            }
2347        } else {
2348            Poll::Ready(Ok(()))
2349        }
2350    }
2351
2352    fn call(&mut self, req: Request) -> Self::Future {
2353        let fut = match req {
2354            Request::BlockDownload { hash, .. } => self.route_inv(req, hash.into()),
2355            // Only do inventory-aware routing on individual items.
2356            Request::BlocksByHash(ref hashes) if hashes.len() == 1 => {
2357                let hash = InventoryHash::from(*hashes.iter().next().unwrap());
2358                self.route_inv(req, hash)
2359            }
2360            Request::TransactionsById(ref hashes) if hashes.len() == 1 => {
2361                let hash = InventoryHash::from(*hashes.iter().next().unwrap());
2362                self.route_inv(req, hash)
2363            }
2364
2365            // Broadcast advertisements to lots of peers
2366            Request::AdvertiseTransactionIds(_, _) => self.route_broadcast(req),
2367            Request::AdvertiseBlockToAll(_) => self.broadcast_all(req),
2368
2369            // Broadcasts that must always reach the configured zcashd-compat sidecar peers
2370            Request::AdvertiseBlock(_, _) | Request::PushTransaction(_, _) => {
2371                self.route_sidecar_broadcast(req)
2372            }
2373
2374            // Queued requests were already routed by `poll_ready()`.
2375            Request::PollPeerSet => async { Ok(Response::Nil) }.boxed(),
2376
2377            // Find requests rotate across serving peers with a bounded wait; others use P2C.
2378            _ => self.route_request(req),
2379        };
2380        self.update_metrics();
2381
2382        fut
2383    }
2384}