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}