Skip to main content

zebra_network/peer/
connection.rs

1//! Zebra's per-peer connection state machine.
2//!
3//! Maps the external Zcash/Bitcoin protocol to Zebra's internal request/response
4//! protocol.
5//!
6//! This module contains a lot of undocumented state, assumptions and invariants.
7//! And it's unclear if these assumptions match the `zcashd` implementation.
8//! It should be refactored into a cleaner set of request/response pairs (#1515).
9
10use std::{borrow::Cow, collections::HashSet, fmt, pin::Pin, sync::Arc, time::Instant};
11
12use futures::{future::Either, prelude::*};
13use rand::{seq::SliceRandom, RngExt};
14use tokio::time::{sleep, Sleep};
15use tower::{Service, ServiceExt};
16use tracing_futures::Instrument;
17
18use zebra_chain::{
19    block::{self, Block},
20    serialization::SerializationError,
21    transaction::{UnminedTx, UnminedTxId},
22};
23
24use crate::{
25    constants::{
26        self, MAX_ADDRS_IN_MESSAGE, MAX_OVERLOAD_DROP_PROBABILITY, MIN_OVERLOAD_DROP_PROBABILITY,
27        OVERLOAD_PROTECTION_INTERVAL, PEER_ADDR_RESPONSE_LIMIT,
28    },
29    meta_addr::MetaAddr,
30    peer::{
31        connection::peer_tx::PeerTx, error::AlreadyErrored, ClientRequest, ClientRequestReceiver,
32        ConnectionInfo, ErrorSlot, InProgressClientRequest, MustUseClientResponseSender, PeerError,
33        SharedPeerError,
34    },
35    peer_set::ConnectionTracker,
36    protocol::{
37        external::{types::Nonce, InventoryHash, Message},
38        internal::{InventoryResponse, Request, Response},
39    },
40    BoxError, PeerSocketAddr, MAX_TX_INV_IN_SENT_MESSAGE,
41};
42
43use InventoryResponse::*;
44
45mod peer_tx;
46
47#[cfg(test)]
48mod tests;
49
50#[derive(Debug)]
51pub(super) enum Handler {
52    /// Indicates that the handler has finished processing the request.
53    /// An error here is scoped to the request.
54    Finished(Result<Response, PeerError>),
55    Ping {
56        nonce: Nonce,
57        ping_sent_at: Instant,
58    },
59    Peers,
60    FindBlocks,
61    FindHeaders,
62    BlocksByHash {
63        pending_hashes: HashSet<block::Hash>,
64        blocks: Vec<Arc<Block>>,
65    },
66    TransactionsById {
67        pending_ids: HashSet<UnminedTxId>,
68        transactions: Vec<UnminedTx>,
69    },
70    MempoolTransactionIds,
71}
72
73impl fmt::Display for Handler {
74    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        f.write_str(&match self {
76            Handler::Finished(Ok(response)) => format!("Finished({response})"),
77            Handler::Finished(Err(error)) => format!("Finished({error})"),
78
79            Handler::Ping { .. } => "Ping".to_string(),
80            Handler::Peers => "Peers".to_string(),
81
82            Handler::FindBlocks => "FindBlocks".to_string(),
83            Handler::FindHeaders => "FindHeaders".to_string(),
84            Handler::BlocksByHash {
85                pending_hashes,
86                blocks,
87            } => format!(
88                "BlocksByHash {{ pending_hashes: {}, blocks: {} }}",
89                pending_hashes.len(),
90                blocks.len()
91            ),
92
93            Handler::TransactionsById {
94                pending_ids,
95                transactions,
96            } => format!(
97                "TransactionsById {{ pending_ids: {}, transactions: {} }}",
98                pending_ids.len(),
99                transactions.len()
100            ),
101            Handler::MempoolTransactionIds => "MempoolTransactionIds".to_string(),
102        })
103    }
104}
105
106impl Handler {
107    /// Returns the Zebra internal handler type as a string.
108    pub fn command(&self) -> Cow<'static, str> {
109        match self {
110            Handler::Finished(Ok(response)) => format!("Finished({})", response.command()).into(),
111            Handler::Finished(Err(error)) => format!("Finished({})", error.kind()).into(),
112
113            Handler::Ping { .. } => "Ping".into(),
114            Handler::Peers => "Peers".into(),
115
116            Handler::FindBlocks => "FindBlocks".into(),
117            Handler::FindHeaders => "FindHeaders".into(),
118
119            Handler::BlocksByHash { .. } => "BlocksByHash".into(),
120            Handler::TransactionsById { .. } => "TransactionsById".into(),
121
122            Handler::MempoolTransactionIds => "MempoolTransactionIds".into(),
123        }
124    }
125
126    /// Try to handle `msg` as a response to a client request, possibly consuming
127    /// it in the process.
128    ///
129    /// This function is where we statefully interpret Bitcoin/Zcash messages
130    /// into responses to messages in the internal request/response protocol.
131    /// This conversion is done by a sequence of (request, message) match arms,
132    /// each of which contains the conversion logic for that pair.
133    ///
134    /// Taking ownership of the message means that we can pass ownership of its
135    /// contents to responses without additional copies.  If the message is not
136    /// interpretable as a response, we return ownership to the caller.
137    ///
138    /// Unexpected messages are left unprocessed, and may be rejected later.
139    ///
140    /// `addr` responses are limited to avoid peer set takeover. Any excess
141    /// addresses are stored in `cached_addrs`.
142    fn process_message(
143        &mut self,
144        msg: Message,
145        cached_addrs: &mut Vec<MetaAddr>,
146        transient_addr: Option<PeerSocketAddr>,
147    ) -> Option<Message> {
148        let mut ignored_msg = None;
149        // TODO: can this be avoided?
150        let tmp_state = std::mem::replace(self, Handler::Finished(Ok(Response::Nil)));
151
152        debug!(handler = %tmp_state, %msg, "received peer response to Zebra request");
153
154        *self = match (tmp_state, msg) {
155            (
156                Handler::Ping {
157                    nonce,
158                    ping_sent_at,
159                },
160                Message::Pong(rsp_nonce),
161            ) => {
162                if nonce == rsp_nonce {
163                    let duration = ping_sent_at.elapsed();
164                    Handler::Finished(Ok(Response::Pong(duration)))
165                } else {
166                    Handler::Ping {
167                        nonce,
168                        ping_sent_at,
169                    }
170                }
171            }
172
173            (Handler::Peers, Message::Addr(new_addrs)) => {
174                // Security: This method performs security-sensitive operations, see its comments
175                // for details.
176                let response_addrs =
177                    Handler::update_addr_cache(cached_addrs, &new_addrs, PEER_ADDR_RESPONSE_LIMIT);
178
179                debug!(
180                    new_addrs = new_addrs.len(),
181                    response_addrs = response_addrs.len(),
182                    remaining_addrs = cached_addrs.len(),
183                    PEER_ADDR_RESPONSE_LIMIT,
184                    "responding to Peers request using new and cached addresses",
185                );
186
187                Handler::Finished(Ok(Response::Peers(response_addrs)))
188            }
189
190            // `zcashd` returns requested transactions in a single batch of messages.
191            // Other transaction or non-transaction messages can come before or after the batch.
192            // After the transaction batch, `zcashd` sends `notfound` if any transactions are missing:
193            // https://github.com/zcash/zcash/blob/e7b425298f6d9a54810cb7183f00be547e4d9415/src/main.cpp#L5617
194            (
195                Handler::TransactionsById {
196                    mut pending_ids,
197                    mut transactions,
198                },
199                Message::Tx(transaction),
200            ) => {
201                // assumptions:
202                //   - the transaction messages are sent in a single continuous batch
203                //   - missing transactions are silently skipped
204                //     (there is no `notfound` message at the end of the batch)
205                if pending_ids.remove(&transaction.id) {
206                    // we are in the middle of the continuous transaction messages
207                    transactions.push(transaction);
208                } else {
209                    // We got a transaction we didn't ask for. If the caller doesn't know any of the
210                    // transactions, they should have sent a `notfound` with all the hashes, rather
211                    // than an unsolicited transaction.
212                    //
213                    // So either:
214                    // 1. The peer implements the protocol badly, skipping `notfound`.
215                    //    We should cancel the request, so we don't hang waiting for transactions
216                    //    that will never arrive.
217                    // 2. The peer sent an unsolicited transaction.
218                    //    We should ignore the transaction, and wait for the actual response.
219                    //
220                    // We end the request, so we don't hang on bad peers (case 1). But we keep the
221                    // connection open, so the inbound service can process transactions from good
222                    // peers (case 2).
223                    ignored_msg = Some(Message::Tx(transaction));
224                }
225
226                if ignored_msg.is_some() && transactions.is_empty() {
227                    // If we didn't get anything we wanted, retry the request.
228                    let missing_transaction_ids = pending_ids.into_iter().map(Into::into).collect();
229                    Handler::Finished(Err(PeerError::NotFoundResponse(missing_transaction_ids)))
230                } else if pending_ids.is_empty() || ignored_msg.is_some() {
231                    // If we got some of what we wanted, let the internal client know.
232                    let available = transactions
233                        .into_iter()
234                        .map(|t| InventoryResponse::Available((t, transient_addr)));
235                    let missing = pending_ids.into_iter().map(InventoryResponse::Missing);
236
237                    Handler::Finished(Ok(Response::Transactions(
238                        available.chain(missing).collect(),
239                    )))
240                } else {
241                    // Keep on waiting for more.
242                    Handler::TransactionsById {
243                        pending_ids,
244                        transactions,
245                    }
246                }
247            }
248            // `zcashd` peers actually return this response.
249            // A `notfound` naming none of the pending IDs is for a cancelled request or
250            // unsolicited, so it isn't a response to this request.
251            (
252                Handler::TransactionsById {
253                    pending_ids,
254                    transactions,
255                },
256                Message::NotFound(missing_invs),
257            ) if transaction_ids(&missing_invs).any(|id| pending_ids.contains(&id)) => {
258                // assumptions:
259                //   - the peer eventually returns a transaction or a `notfound` entry
260                //     for each hash
261                //   - all `notfound` entries are contained in a single message
262                //   - the `notfound` message comes after the transaction messages
263                //
264                // If we're in sync with the peer, then the `notfound` should contain the remaining
265                // hashes from the handler. If we're not in sync with the peer, we should return
266                // what we got so far.
267                let missing_transaction_ids: HashSet<_> = transaction_ids(&missing_invs).collect();
268                if missing_transaction_ids != pending_ids {
269                    trace!(?missing_invs, ?missing_transaction_ids, ?pending_ids);
270                    // if these errors are noisy, we should replace them with debugs
271                    debug!("unexpected notfound message from peer: all remaining transaction hashes should be listed in the notfound. Using partial received transactions as the peer response");
272                }
273                if missing_transaction_ids.len() != missing_invs.len() {
274                    trace!(?missing_invs, ?missing_transaction_ids, ?pending_ids);
275                    debug!("unexpected notfound message from peer: notfound contains duplicate hashes or non-transaction hashes. Using partial received transactions as the peer response");
276                }
277
278                if transactions.is_empty() {
279                    // If we didn't get anything we wanted, retry the request.
280                    let missing_transaction_ids = pending_ids.into_iter().map(Into::into).collect();
281                    Handler::Finished(Err(PeerError::NotFoundResponse(missing_transaction_ids)))
282                } else {
283                    // If we got some of what we wanted, let the internal client know.
284                    let available = transactions
285                        .into_iter()
286                        .map(|t| InventoryResponse::Available((t, transient_addr)));
287                    let missing = pending_ids.into_iter().map(InventoryResponse::Missing);
288
289                    Handler::Finished(Ok(Response::Transactions(
290                        available.chain(missing).collect(),
291                    )))
292                }
293            }
294
295            // `zcashd` returns requested blocks in a single batch of messages.
296            // Other blocks or non-blocks messages can come before or after the batch.
297            // `zcashd` silently skips missing blocks, rather than sending a final `notfound` message.
298            // https://github.com/zcash/zcash/blob/e7b425298f6d9a54810cb7183f00be547e4d9415/src/main.cpp#L5523
299            (
300                Handler::BlocksByHash {
301                    mut pending_hashes,
302                    mut blocks,
303                },
304                Message::Block(block),
305            ) => {
306                // assumptions:
307                //   - the block messages are sent in a single continuous batch
308                //   - missing blocks are silently skipped
309                //     (there is no `notfound` message at the end of the batch)
310                if pending_hashes.remove(&block.hash()) {
311                    // we are in the middle of the continuous block messages
312                    blocks.push(block);
313                } else {
314                    // We got a block we didn't ask for.
315                    //
316                    // So either:
317                    // 1. The response is for a previously cancelled block request.
318                    //    We should treat that block as an inbound gossiped block,
319                    //    and wait for the actual response.
320                    // 2. The peer doesn't know any of the blocks we asked for.
321                    //    We should cancel the request, so we don't hang waiting for blocks that
322                    //    will never arrive.
323                    // 3. The peer sent an unsolicited block.
324                    //    We should treat that block as an inbound gossiped block,
325                    //    and wait for the actual response.
326                    //
327                    // We ignore the message, so we don't desynchronize with the peer. This happens
328                    // when we cancel a request and send a second different request, but receive a
329                    // response for the first request. If we ended the request then, we could send
330                    // a third request to the peer, and end up having to end that request as well
331                    // when the response for the second request arrives.
332                    //
333                    // Ignoring the message gives us a chance to synchronize back to the correct
334                    // request. If that doesn't happen, this request times out.
335                    //
336                    // In case 2, if peers respond with a `notfound` message,
337                    // the cascading errors don't happen. The `notfound` message cancels our request,
338                    // and we know we are in sync with the peer.
339                    //
340                    // Zebra sends `notfound` in response to block requests, but `zcashd` doesn't.
341                    // So we need this message workaround, and the related inventory workarounds.
342                    ignored_msg = Some(Message::Block(block));
343                }
344
345                if pending_hashes.is_empty() {
346                    // If we got everything we wanted, let the internal client know.
347                    let available = blocks
348                        .into_iter()
349                        .map(|block| InventoryResponse::Available((block, transient_addr)));
350                    Handler::Finished(Ok(Response::Blocks(available.collect())))
351                } else {
352                    // Keep on waiting for all the blocks we wanted, until we get them or time out.
353                    Handler::BlocksByHash {
354                        pending_hashes,
355                        blocks,
356                    }
357                }
358            }
359            // Peers are allowed to return this response, but `zcashd` never does.
360            // A `notfound` naming none of the pending hashes is for a cancelled request or
361            // unsolicited, so it isn't a response to this request.
362            (
363                Handler::BlocksByHash {
364                    pending_hashes,
365                    blocks,
366                },
367                Message::NotFound(missing_invs),
368            ) if block_hashes(&missing_invs).any(|hash| pending_hashes.contains(&hash)) => {
369                // assumptions:
370                //   - the peer eventually returns a block or a `notfound` entry
371                //     for each hash
372                //   - all `notfound` entries are contained in a single message
373                //   - the `notfound` message comes after the block messages
374                //
375                // If we're in sync with the peer, then the `notfound` should contain the remaining
376                // hashes from the handler. If we're not in sync with the peer, we should return
377                // what we got so far, and log an error.
378                let missing_blocks: HashSet<_> = block_hashes(&missing_invs).collect();
379                if missing_blocks != pending_hashes {
380                    trace!(?missing_invs, ?missing_blocks, ?pending_hashes);
381                    // if these errors are noisy, we should replace them with debugs
382                    debug!("unexpected notfound message from peer: all remaining block hashes should be listed in the notfound. Using partial received blocks as the peer response");
383                }
384                if missing_blocks.len() != missing_invs.len() {
385                    trace!(?missing_invs, ?missing_blocks, ?pending_hashes);
386                    debug!("unexpected notfound message from peer: notfound contains duplicate hashes or non-block hashes. Using partial received blocks as the peer response");
387                }
388
389                if blocks.is_empty() {
390                    // If we didn't get anything we wanted, retry the request.
391                    let missing_block_hashes = pending_hashes.into_iter().map(Into::into).collect();
392                    Handler::Finished(Err(PeerError::NotFoundResponse(missing_block_hashes)))
393                } else {
394                    // If we got some of what we wanted, let the internal client know.
395                    let available = blocks
396                        .into_iter()
397                        .map(|block| InventoryResponse::Available((block, transient_addr)));
398                    let missing = pending_hashes.into_iter().map(InventoryResponse::Missing);
399
400                    Handler::Finished(Ok(Response::Blocks(available.chain(missing).collect())))
401                }
402            }
403
404            // TODO:
405            // - use `any(inv)` rather than `all(inv)`?
406            (Handler::FindBlocks, Message::Inv(items))
407                if items
408                    .iter()
409                    .all(|item| matches!(item, InventoryHash::Block(_))) =>
410            {
411                Handler::Finished(Ok(Response::BlockHashes(
412                    block_hashes(&items[..]).collect(),
413                )))
414            }
415            (Handler::FindHeaders, Message::Headers(headers)) => {
416                Handler::Finished(Ok(Response::BlockHeaders(headers)))
417            }
418
419            (Handler::MempoolTransactionIds, Message::Inv(items))
420                if items.iter().all(|item| item.unmined_tx_id().is_some()) =>
421            {
422                Handler::Finished(Ok(Response::TransactionIds(
423                    transaction_ids(&items).collect(),
424                )))
425            }
426
427            // By default, messages are not responses.
428            (state, msg) => {
429                trace!(?msg, "did not interpret message as response");
430                ignored_msg = Some(msg);
431                state
432            }
433        };
434
435        ignored_msg
436    }
437
438    /// Adds `new_addrs` to the `cached_addrs` cache, then takes and returns `response_size`
439    /// addresses from that cache.
440    ///
441    /// `cached_addrs` can be empty if the cache is empty. `new_addrs` can be empty or `None` if
442    /// there are no new addresses. `response_size` can be zero or `None` if there is no response
443    /// needed.
444    fn update_addr_cache<'new>(
445        cached_addrs: &mut Vec<MetaAddr>,
446        new_addrs: impl IntoIterator<Item = &'new MetaAddr>,
447        response_size: impl Into<Option<usize>>,
448    ) -> Vec<MetaAddr> {
449        // # Peer Set Reliability
450        //
451        // Newly received peers are added to the cache, so that we can use them if the connection
452        // doesn't respond to our getaddr requests.
453        //
454        // Add the new addresses to the end of the cache.
455        cached_addrs.extend(new_addrs.into_iter().cloned());
456
457        // # Security
458        //
459        // We limit how many peer addresses we take from each peer, so that our address book
460        // and outbound connections aren't controlled by a single peer (#1869). We randomly select
461        // peers, so the remote peer can't control which addresses we choose by changing the order
462        // in the messages they send.
463        let response_size = response_size.into().unwrap_or_default();
464
465        let mut temp_cache = Vec::new();
466        std::mem::swap(cached_addrs, &mut temp_cache);
467
468        // The response is fully shuffled, remaining is partially shuffled.
469        let (response, remaining) = temp_cache.partial_shuffle(&mut rand::rng(), response_size);
470
471        // # Security
472        //
473        // The cache size is limited to avoid memory denial of service.
474        //
475        // It's ok to just partially shuffle the cache, because it doesn't actually matter which
476        // peers we drop. Having excess peers is rare, because most peers only send one large
477        // unsolicited peer message when they first connect.
478        *cached_addrs = remaining.to_vec();
479        cached_addrs.truncate(MAX_ADDRS_IN_MESSAGE);
480
481        response.to_vec()
482    }
483}
484
485#[derive(Debug)]
486#[must_use = "AwaitingResponse.tx.send() must be called before drop"]
487pub(super) enum State {
488    /// Awaiting a client request or a peer message.
489    AwaitingRequest,
490    /// Awaiting a peer message we can interpret as a response to a client request.
491    AwaitingResponse {
492        handler: Handler,
493        tx: MustUseClientResponseSender,
494        span: tracing::Span,
495    },
496    /// A failure has occurred and we are shutting down the connection.
497    Failed,
498}
499
500impl fmt::Display for State {
501    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
502        f.write_str(&match self {
503            State::AwaitingRequest => "AwaitingRequest".to_string(),
504            State::AwaitingResponse { handler, .. } => {
505                format!("AwaitingResponse({handler})")
506            }
507            State::Failed => "Failed".to_string(),
508        })
509    }
510}
511
512impl State {
513    /// Returns the Zebra internal state as a string.
514    pub fn command(&self) -> Cow<'static, str> {
515        match self {
516            State::AwaitingRequest => "AwaitingRequest".into(),
517            State::AwaitingResponse { handler, .. } => {
518                format!("AwaitingResponse({})", handler.command()).into()
519            }
520            State::Failed => "Failed".into(),
521        }
522    }
523}
524
525/// The outcome of mapping an inbound [`Message`] to a [`Request`].
526#[derive(Clone, Debug, Eq, PartialEq)]
527#[must_use = "inbound messages must be handled"]
528pub enum InboundMessage {
529    /// The message was mapped to an inbound [`Request`].
530    AsRequest(Request),
531
532    /// The message was consumed by the mapping method.
533    ///
534    /// For example, it could be cached, treated as an error,
535    /// or an internally handled [`Message::Ping`].
536    Consumed,
537
538    /// The message was not used by the inbound message handler.
539    Unused,
540}
541
542impl From<Request> for InboundMessage {
543    fn from(request: Request) -> Self {
544        InboundMessage::AsRequest(request)
545    }
546}
547
548/// The channels, services, and associated state for a peer connection.
549pub struct Connection<S, Tx>
550where
551    Tx: Sink<Message, Error = SerializationError> + Unpin,
552{
553    /// The metadata for the connected peer `service`.
554    ///
555    /// This field is used for debugging.
556    pub connection_info: Arc<ConnectionInfo>,
557
558    /// The state of this connection's current request or response.
559    pub(super) state: State,
560
561    /// A timeout for a client request. This is stored separately from
562    /// State so that we can move the future out of it independently of
563    /// other state handling.
564    pub(super) request_timer: Option<Pin<Box<Sleep>>>,
565
566    /// Unused peers from recent `addr` or `addrv2` messages from this peer.
567    /// Also holds the initial addresses sent in `version` messages, or guessed from the remote IP.
568    ///
569    /// When peers send solicited or unsolicited peer advertisements, Zebra puts them in this cache.
570    ///
571    /// When Zebra's components request peers, some cached peers are randomly selected,
572    /// consumed, and returned as a modified response. This works around `zcashd`'s address
573    /// response rate-limit.
574    ///
575    /// The cache size is limited to avoid denial of service attacks.
576    pub(super) cached_addrs: Vec<MetaAddr>,
577
578    /// The `inbound` service, used to answer requests from this connection's peer.
579    pub(super) svc: S,
580
581    /// A channel for requests that Zebra's internal services want to send to remote peers.
582    ///
583    /// This channel accepts [`Request`]s, and produces [`InProgressClientRequest`]s.
584    pub(super) client_rx: ClientRequestReceiver,
585
586    /// A slot for an error shared between the Connection and the Client that uses it.
587    ///
588    /// `None` unless the connection or client have errored.
589    pub(super) error_slot: ErrorSlot,
590
591    /// A channel for sending Zcash messages to the connected peer.
592    ///
593    /// This channel accepts [`Message`]s.
594    ///
595    /// The corresponding peer message receiver is passed to [`Connection::run`].
596    pub(super) peer_tx: PeerTx<Tx>,
597
598    /// A connection tracker that reduces the open connection count when dropped.
599    /// Used to limit the number of open connections in Zebra.
600    ///
601    /// This field does nothing until it is dropped.
602    ///
603    /// # Security
604    ///
605    /// If this connection tracker or `Connection`s are leaked,
606    /// the number of active connections will appear higher than it actually is.
607    /// If enough connections leak, Zebra will stop making new connections.
608    #[allow(dead_code)]
609    pub(super) connection_tracker: ConnectionTracker,
610
611    /// The metrics label for this peer. Usually the remote IP and port.
612    pub(super) metrics_label: String,
613
614    /// The state for this peer, when the metrics were last updated.
615    pub(super) last_metrics_state: Option<Cow<'static, str>>,
616
617    /// The time of the last overload error response from the inbound
618    /// service to a request from this connection,
619    /// or None if this connection hasn't yet received an overload error.
620    last_overload_time: Option<Instant>,
621}
622
623impl<S, Tx> fmt::Debug for Connection<S, Tx>
624where
625    Tx: Sink<Message, Error = SerializationError> + Unpin,
626{
627    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
628        // skip the channels, they don't tell us anything useful
629        f.debug_struct(std::any::type_name::<Connection<S, Tx>>())
630            .field("connection_info", &self.connection_info)
631            .field("state", &self.state)
632            .field("request_timer", &self.request_timer)
633            .field("cached_addrs", &self.cached_addrs.len())
634            .field("error_slot", &self.error_slot)
635            .field("metrics_label", &self.metrics_label)
636            .field("last_metrics_state", &self.last_metrics_state)
637            .field("last_overload_time", &self.last_overload_time)
638            .finish()
639    }
640}
641
642impl<S, Tx> Connection<S, Tx>
643where
644    Tx: Sink<Message, Error = SerializationError> + Unpin,
645{
646    /// Return a new connection from its channels, services, shared state, and metadata.
647    pub(crate) fn new(
648        inbound_service: S,
649        client_rx: futures::channel::mpsc::Receiver<ClientRequest>,
650        error_slot: ErrorSlot,
651        peer_tx: Tx,
652        connection_tracker: ConnectionTracker,
653        connection_info: Arc<ConnectionInfo>,
654        initial_cached_addrs: Vec<MetaAddr>,
655    ) -> Self {
656        let metrics_label = connection_info.connected_addr.get_transient_addr_label();
657
658        Connection {
659            connection_info,
660            state: State::AwaitingRequest,
661            request_timer: None,
662            cached_addrs: initial_cached_addrs,
663            svc: inbound_service,
664            client_rx: client_rx.into(),
665            error_slot,
666            peer_tx: peer_tx.into(),
667            connection_tracker,
668            metrics_label,
669            last_metrics_state: None,
670            last_overload_time: None,
671        }
672    }
673}
674
675impl<S, Tx> Connection<S, Tx>
676where
677    S: Service<Request, Response = Response, Error = BoxError>,
678    S::Error: Into<BoxError>,
679    Tx: Sink<Message, Error = SerializationError> + Unpin,
680{
681    /// Consume this `Connection` to form a spawnable future containing its event loop.
682    ///
683    /// `peer_rx` is a channel for receiving Zcash [`Message`]s from the connected peer.
684    /// The corresponding peer message receiver is [`Connection::peer_tx`].
685    pub async fn run<Rx>(mut self, mut peer_rx: Rx)
686    where
687        Rx: Stream<Item = Result<Message, SerializationError>> + Unpin,
688    {
689        // At a high level, the event loop we want is as follows: we check for any
690        // incoming messages from the remote peer, check if they should be interpreted
691        // as a response to a pending client request, and if not, interpret them as a
692        // request from the remote peer to our node.
693        //
694        // We also need to handle those client requests in the first place. The client
695        // requests are received from the corresponding `peer::Client` over a bounded
696        // channel (with bound 1, to minimize buffering), but there is no relationship
697        // between the stream of client requests and the stream of peer messages, so we
698        // cannot ignore one kind while waiting on the other. Moreover, we cannot accept
699        // a second client request while the first one is still pending.
700        //
701        // To do this, we inspect the current request state.
702        //
703        // If there is no pending request, we wait on either an incoming peer message or
704        // an incoming request, whichever comes first.
705        //
706        // If there is a pending request, we wait only on an incoming peer message, and
707        // check whether it can be interpreted as a response to the pending request.
708        //
709        // TODO: turn this comment into a module-level comment, after splitting the module.
710        loop {
711            self.update_state_metrics(None);
712
713            match self.state {
714                State::AwaitingRequest => {
715                    trace!("awaiting client request or peer message");
716                    // # Correctness
717                    //
718                    // Currently, select prefers the first future if multiple futures are ready.
719                    // We use this behaviour to prioritise messages on each individual peer
720                    // connection in this order:
721                    // - incoming messages from the remote peer, then
722                    // - outgoing messages to the remote peer.
723                    //
724                    // This improves the performance of peer responses to Zebra requests, and new
725                    // peer requests to Zebra's inbound service.
726                    //
727                    // `futures::StreamExt::next()` is cancel-safe:
728                    // <https://docs.rs/tokio/latest/tokio/macro.select.html#cancellation-safety>
729                    // This means that messages from the future that isn't selected stay in the stream,
730                    // and they will be returned next time the future is checked.
731                    //
732                    // If an inbound peer message arrives at a ready peer that also has a pending
733                    // request from Zebra, we want to process the peer's message first.
734                    // If we process the Zebra request first:
735                    // - we could misinterpret the inbound peer message as a response to the Zebra
736                    //   request, or
737                    // - if the peer message is a request to Zebra, and we put the peer in the
738                    //   AwaitingResponse state, then we'll correctly ignore the simultaneous Zebra
739                    //   request. (Zebra services make multiple requests or retry, so this is ok.)
740                    //
741                    // # Security
742                    //
743                    // If a peer sends an uninterrupted series of messages, it will delay any new
744                    // requests from Zebra to that individual peer. This is behaviour we want,
745                    // because:
746                    // - any responses to Zebra's requests to that peer would be slow or timeout,
747                    // - the peer will eventually fail a Zebra keepalive check and get disconnected,
748                    // - if there are too many inbound messages overall, the inbound service will
749                    //   return an overload error and the peer will be disconnected.
750                    //
751                    // Messages to other peers will continue to be processed concurrently. Some
752                    // Zebra services might be temporarily delayed until the peer times out, if a
753                    // request to that peer is sent by the service, and the service blocks until
754                    // the request completes (or times out).
755                    match future::select(peer_rx.next(), self.client_rx.next()).await {
756                        Either::Left((None, _)) => {
757                            self.fail_with(PeerError::ConnectionClosed).await;
758                        }
759                        Either::Left((Some(Err(e)), _)) => self.fail_with(e).await,
760                        Either::Left((Some(Ok(msg)), _)) => {
761                            let unhandled_msg = self.handle_message_as_request(msg).await;
762
763                            if let Some(unhandled_msg) = unhandled_msg {
764                                debug!(
765                                    %unhandled_msg,
766                                    "ignoring unhandled request while awaiting a request"
767                                );
768                            }
769                        }
770                        Either::Right((None, _)) => {
771                            trace!("client_rx closed, ending connection");
772
773                            // There are no requests to be flushed,
774                            // but we need to set an error and update metrics.
775                            // (We don't want to log this error, because it's normal behaviour.)
776                            self.shutdown_async(PeerError::ClientDropped).await;
777                            break;
778                        }
779                        Either::Right((Some(req), _)) => {
780                            let span = req.span.clone();
781                            self.handle_client_request(req).instrument(span).await
782                        }
783                    }
784                }
785
786                // Check whether the handler is finished before waiting for a response message,
787                // because the response might be `Nil` or synthetic.
788                State::AwaitingResponse {
789                    handler: Handler::Finished(_),
790                    ref span,
791                    ..
792                } => {
793                    // We have to get rid of the span reference so we can tamper with the state.
794                    let span = span.clone();
795                    trace!(
796                        parent: &span,
797                        "returning completed response to client request"
798                    );
799
800                    // Replace the state with a temporary value,
801                    // so we can take ownership of the response sender.
802                    let tmp_state = std::mem::replace(&mut self.state, State::Failed);
803
804                    if let State::AwaitingResponse {
805                        handler: Handler::Finished(response),
806                        tx,
807                        ..
808                    } = tmp_state
809                    {
810                        if let Ok(response) = response.as_ref() {
811                            debug!(%response, "finished receiving peer response to Zebra request");
812                            // Add a metric for inbound responses to outbound requests.
813                            metrics::counter!(
814                                "zebra.net.in.responses",
815                                "command" => response.command(),
816                                "addr" => self.metrics_label.clone(),
817                            )
818                            .increment(1);
819                        } else {
820                            debug!(error = ?response, "error in peer response to Zebra request");
821                        }
822
823                        let _ = tx.send(response.map_err(Into::into));
824                    } else {
825                        unreachable!("already checked for AwaitingResponse");
826                    }
827
828                    self.state = State::AwaitingRequest;
829                }
830
831                // We're awaiting a response to a client request,
832                // so wait on either a peer message, or on a request cancellation.
833                State::AwaitingResponse {
834                    ref span,
835                    ref mut tx,
836                    ..
837                } => {
838                    // we have to get rid of the span reference so we can tamper with the state
839                    let span = span.clone();
840                    trace!(parent: &span, "awaiting response to client request");
841                    let timer_ref = self
842                        .request_timer
843                        .as_mut()
844                        .expect("timeout must be set while awaiting response");
845
846                    // # Security
847                    //
848                    // select() prefers the first future if multiple futures are ready.
849                    //
850                    // If multiple futures are ready, we want the priority for each individual
851                    // connection to be:
852                    // - cancellation, then
853                    // - timeout, then
854                    // - peer responses.
855                    //
856                    // (Messages to other peers are processed concurrently.)
857                    //
858                    // This makes sure a peer can't block disconnection or timeouts by sending too
859                    // many messages. It also avoids doing work to process messages after a
860                    // connection has failed.
861                    let cancel = future::select(tx.cancellation(), timer_ref);
862                    match future::select(cancel, peer_rx.next())
863                        .instrument(span.clone())
864                        .await
865                    {
866                        Either::Right((None, _)) => {
867                            self.fail_with(PeerError::ConnectionClosed).await
868                        }
869                        Either::Right((Some(Err(e)), _)) => self.fail_with(e).await,
870                        Either::Right((Some(Ok(peer_msg)), _cancel)) => {
871                            self.update_state_metrics(format!("Out::Rsp::{}", peer_msg.command()));
872
873                            // Try to process the message using the handler.
874                            // This extremely awkward construction avoids
875                            // keeping a live reference to handler across the
876                            // call to handle_message_as_request, which takes
877                            // &mut self. This is a sign that we don't properly
878                            // factor the state required for inbound and
879                            // outbound requests.
880                            let request_msg = match self.state {
881                                State::AwaitingResponse {
882                                    ref mut handler, ..
883                                } => span.in_scope(|| handler.process_message(peer_msg, &mut self.cached_addrs, self.connection_info.connected_addr.get_transient_addr())),
884                                _ => unreachable!("unexpected state after AwaitingResponse: {:?}, peer_msg: {:?}, client_receiver: {:?}",
885                                                  self.state,
886                                                  peer_msg,
887                                                  self.client_rx,
888                                ),
889                            };
890
891                            self.update_state_metrics(None);
892
893                            // If the message was not consumed as a response,
894                            // check whether it can be handled as a request.
895                            let unused_msg = if let Some(request_msg) = request_msg {
896                                // do NOT instrument with the request span, this is
897                                // independent work
898                                self.handle_message_as_request(request_msg).await
899                            } else {
900                                None
901                            };
902
903                            if let Some(unused_msg) = unused_msg {
904                                debug!(
905                                    %unused_msg,
906                                    %self.state,
907                                    "ignoring peer message: not a response or a request",
908                                );
909                            }
910                        }
911                        Either::Left((Either::Right(_), _peer_fut)) => {
912                            trace!(parent: &span, "client request timed out");
913                            let e = PeerError::ConnectionReceiveTimeout;
914
915                            // Replace the state with a temporary value,
916                            // so we can take ownership of the response sender.
917                            self.state = match std::mem::replace(&mut self.state, State::Failed) {
918                                // Special case: ping timeouts fail the connection.
919                                State::AwaitingResponse {
920                                    handler: Handler::Ping { .. },
921                                    tx,
922                                    ..
923                                } => {
924                                    // We replaced the original state, which means `fail_with` won't see it.
925                                    // So we do the state request cleanup manually.
926                                    let e = SharedPeerError::from(e);
927                                    let _ = tx.send(Err(e.clone()));
928                                    self.fail_with(e).await;
929                                    State::Failed
930                                }
931                                // Other request timeouts fail the request.
932                                State::AwaitingResponse { tx, .. } => {
933                                    let _ = tx.send(Err(e.into()));
934                                    State::AwaitingRequest
935                                }
936                                _ => unreachable!(
937                                    "unexpected failed connection state while AwaitingResponse: client_receiver: {:?}",
938                                    self.client_rx
939                                ),
940                            };
941                        }
942                        Either::Left((Either::Left(_), _peer_fut)) => {
943                            // The client receiver was dropped, so we don't need to send on `tx` here.
944                            trace!(parent: &span, "client request was cancelled");
945                            self.state = State::AwaitingRequest;
946                        }
947                    }
948                }
949
950                // This connection has failed: stop the event loop, and complete the future.
951                State::Failed => break,
952            }
953        }
954
955        // TODO: close peer_rx here, after changing it from a stream to a channel
956
957        let error = self.error_slot.try_get_error();
958        assert!(
959            error.is_some(),
960            "closing connections must call fail_with() or shutdown() to set the error slot"
961        );
962
963        self.update_state_metrics(error.expect("checked is_some").to_string());
964    }
965
966    /// Fail this connection, log the failure, and shut it down.
967    /// See [`Self::shutdown_async()`] for details.
968    ///
969    /// Use [`Self::shutdown_async()`] to avoid logging the failure,
970    /// and [`Self::shutdown()`] from non-async code.
971    async fn fail_with(&mut self, error: impl Into<SharedPeerError>) {
972        let error = error.into();
973
974        debug!(
975            %error,
976            client_receiver = ?self.client_rx,
977            "failing peer service with error"
978        );
979
980        self.shutdown_async(error).await;
981    }
982
983    /// Handle an internal client request, possibly generating outgoing messages to the
984    /// remote peer.
985    ///
986    /// NOTE: the caller should use .instrument(msg.span) to instrument the function.
987    async fn handle_client_request(&mut self, req: InProgressClientRequest) {
988        trace!(?req.request);
989        use Request::*;
990        use State::*;
991        let InProgressClientRequest { request, tx, span } = req;
992
993        if tx.is_canceled() {
994            metrics::counter!("peer.canceled").increment(1);
995            debug!(state = %self.state, %request, "ignoring canceled request");
996
997            metrics::counter!(
998                "zebra.net.out.requests.canceled",
999                "command" => request.command(),
1000                "addr" => self.metrics_label.clone(),
1001            )
1002            .increment(1);
1003            self.update_state_metrics(format!("Out::Req::Canceled::{}", request.command()));
1004
1005            return;
1006        }
1007
1008        debug!(state = %self.state, %request, "sending request from Zebra to peer");
1009
1010        // Add a metric for outbound requests.
1011        metrics::counter!(
1012            "zebra.net.out.requests",
1013            "command" => request.command(),
1014            "addr" => self.metrics_label.clone(),
1015        )
1016        .increment(1);
1017        self.update_state_metrics(format!("Out::Req::{}", request.command()));
1018
1019        let new_handler = match (&self.state, request) {
1020            (Failed, request) => panic!(
1021                "failed connection cannot handle new request: {:?}, client_receiver: {:?}",
1022                request,
1023                self.client_rx
1024            ),
1025            (pending @ AwaitingResponse { .. }, request) => panic!(
1026                "tried to process new request: {:?} while awaiting a response: {:?}, client_receiver: {:?}",
1027                request,
1028                pending,
1029                self.client_rx
1030            ),
1031
1032            // Take some cached addresses from the peer connection. This address cache helps
1033            // work-around a `zcashd` addr response rate-limit.
1034            (AwaitingRequest, Peers) if !self.cached_addrs.is_empty() => {
1035                // Security: This method performs security-sensitive operations, see its comments
1036                // for details.
1037                let response_addrs = Handler::update_addr_cache(&mut self.cached_addrs, None, PEER_ADDR_RESPONSE_LIMIT);
1038
1039                debug!(
1040                    response_addrs = response_addrs.len(),
1041                    remaining_addrs = self.cached_addrs.len(),
1042                    PEER_ADDR_RESPONSE_LIMIT,
1043                    "responding to Peers request using some cached addresses",
1044                );
1045
1046                Ok(Handler::Finished(Ok(Response::Peers(response_addrs))))
1047            }
1048            (AwaitingRequest, Peers) => self
1049                .peer_tx
1050                .send(Message::GetAddr)
1051                .await
1052                .map(|()| Handler::Peers),
1053
1054            (AwaitingRequest, Ping(nonce)) => {
1055                let ping_sent_at = Instant::now();
1056
1057                self
1058                    .peer_tx
1059                    .send(Message::Ping(nonce))
1060                    .await
1061                    .map(|()| Handler::Ping { nonce, ping_sent_at })
1062            }
1063
1064            (AwaitingRequest, BlockDownload { hash, .. }) => self
1065                .peer_tx
1066                .send(Message::GetData(vec![hash.into()]))
1067                .await
1068                .map(|()| Handler::BlocksByHash {
1069                    blocks: Vec::with_capacity(1),
1070                    pending_hashes: HashSet::from([hash]),
1071                }),
1072            (AwaitingRequest, BlocksByHash(hashes)) => {
1073                self
1074                    .peer_tx
1075                    .send(Message::GetData(
1076                        hashes.iter().map(|h| (*h).into()).collect(),
1077                    ))
1078                    .await
1079                    .map(|()|
1080                         Handler::BlocksByHash {
1081                             blocks: Vec::with_capacity(hashes.len()),
1082                             pending_hashes: hashes,
1083                         }
1084                    )
1085            }
1086            (AwaitingRequest, TransactionsById(ids)) => {
1087                self
1088                    .peer_tx
1089                    .send(Message::GetData(
1090                        ids.iter().map(Into::into).collect(),
1091                    ))
1092                    .await
1093                    .map(|()|
1094                         Handler::TransactionsById {
1095                             transactions: Vec::with_capacity(ids.len()),
1096                             pending_ids: ids,
1097                         })
1098            }
1099
1100            (AwaitingRequest, FindBlocks { known_blocks, stop }) => {
1101                self
1102                    .peer_tx
1103                    .send(Message::GetBlocks { known_blocks, stop })
1104                    .await
1105                    .map(|()|
1106                         Handler::FindBlocks
1107                    )
1108            }
1109            (AwaitingRequest, FindHeaders { known_blocks, stop }) => {
1110                self
1111                    .peer_tx
1112                    .send(Message::GetHeaders { known_blocks, stop })
1113                    .await
1114                    .map(|()|
1115                         Handler::FindHeaders
1116                    )
1117            }
1118
1119            (AwaitingRequest, MempoolTransactionIds) => {
1120                self
1121                    .peer_tx
1122                    .send(Message::Mempool)
1123                    .await
1124                    .map(|()|
1125                         Handler::MempoolTransactionIds
1126                    )
1127            }
1128
1129            (AwaitingRequest, PushTransaction(transaction, _)) => {
1130                self
1131                    .peer_tx
1132                    .send(Message::Tx(transaction))
1133                    .await
1134                    .map(|()|
1135                         Handler::Finished(Ok(Response::Nil))
1136                    )
1137            }
1138            (AwaitingRequest, AdvertiseTransactionIds(hashes, _)) => {
1139                let max_tx_inv_in_message: usize = MAX_TX_INV_IN_SENT_MESSAGE
1140                    .try_into()
1141                    .expect("constant fits in usize");
1142
1143                // # Security
1144                //
1145                // In most cases, we try to split over-sized requests into multiple network-layer
1146                // messages. But we are unlikely to reach this limit with the default mempool
1147                // config, so a gossip like this could indicate a network amplification attack.
1148                //
1149                // This limit is particularly important here, because advertisements send the same
1150                // message to half our available peers.
1151                //
1152                // If there are thousands of transactions in the mempool, letting peers know the
1153                // exact transactions we have isn't that important, so it's ok to drop arbitrary
1154                // transaction hashes from our response.
1155                if hashes.len() > max_tx_inv_in_message {
1156                    debug!(inv_count = ?hashes.len(), ?MAX_TX_INV_IN_SENT_MESSAGE, "unusually large transaction ID gossip");
1157                }
1158
1159                let hashes = hashes.into_iter().take(max_tx_inv_in_message).map(Into::into).collect();
1160
1161                self
1162                    .peer_tx
1163                    .send(Message::Inv(hashes))
1164                    .await
1165                    .map(|()|
1166                         Handler::Finished(Ok(Response::Nil))
1167                    )
1168            }
1169            (AwaitingRequest, AdvertiseBlock(hash, _) | AdvertiseBlockToAll(hash)) => {
1170                self
1171                    .peer_tx
1172                    .send(Message::Inv(vec![hash.into()]))
1173                    .await
1174                    .map(|()|
1175                         Handler::Finished(Ok(Response::Nil))
1176                    )
1177            }
1178            // The peer set handles this request itself, so there is nothing to send to the peer.
1179            (AwaitingRequest, PollPeerSet) => Ok(Handler::Finished(Ok(Response::Nil))),
1180        };
1181
1182        // Update the connection state with a new handler, or fail with an error.
1183        match new_handler {
1184            Ok(handler) => {
1185                self.state = AwaitingResponse { handler, span, tx };
1186                self.request_timer = Some(Box::pin(sleep(constants::REQUEST_TIMEOUT)));
1187            }
1188            Err(error) => {
1189                let error = SharedPeerError::from(error);
1190                let _ = tx.send(Err(error.clone()));
1191                self.fail_with(error).await;
1192            }
1193        };
1194    }
1195
1196    /// Handle `msg` as a request from a peer to this Zebra instance.
1197    ///
1198    /// If the message is not handled, it is returned.
1199    // This function has its own span, because we're creating a new work
1200    // context (namely, the work of processing the inbound msg as a request)
1201    #[instrument(name = "msg_as_req", skip(self, msg), fields(msg = msg.command()))]
1202    async fn handle_message_as_request(&mut self, msg: Message) -> Option<Message> {
1203        trace!(?msg);
1204        debug!(state = %self.state, %msg, "received inbound peer message");
1205
1206        self.update_state_metrics(format!("In::Msg::{}", msg.command()));
1207
1208        use InboundMessage::*;
1209
1210        let req = match msg {
1211            Message::Ping(nonce) => {
1212                trace!(?nonce, "responding to heartbeat");
1213                if let Err(e) = self.peer_tx.send(Message::Pong(nonce)).await {
1214                    self.fail_with(e).await;
1215                }
1216                Consumed
1217            }
1218            // These messages shouldn't be sent outside of a handshake.
1219            Message::Version { .. } => {
1220                self.fail_with(PeerError::DuplicateHandshake).await;
1221                Consumed
1222            }
1223            Message::Verack => {
1224                self.fail_with(PeerError::DuplicateHandshake).await;
1225                Consumed
1226            }
1227            // These messages should already be handled as a response if they
1228            // could be a response, so if we see them here, they were either
1229            // sent unsolicited, or they were sent in response to a canceled request
1230            // that we've already forgotten about.
1231            Message::Reject { .. } => {
1232                debug!(%msg, "got reject message unsolicited or from canceled request");
1233                Unused
1234            }
1235            Message::NotFound { .. } => {
1236                debug!(%msg, "got notfound message unsolicited or from canceled request");
1237                Unused
1238            }
1239            Message::Pong(_) => {
1240                debug!(%msg, "got pong message unsolicited or from canceled request");
1241                Unused
1242            }
1243            Message::Block(_) => {
1244                debug!(%msg, "got block message unsolicited or from canceled request");
1245                Unused
1246            }
1247            Message::Headers(_) => {
1248                debug!(%msg, "got headers message unsolicited or from canceled request");
1249                Unused
1250            }
1251            // These messages should never be sent by peers.
1252            Message::FilterLoad { .. } | Message::FilterAdd { .. } | Message::FilterClear => {
1253                // # Security
1254                //
1255                // Zcash connections are not authenticated, so malicious nodes can send fake messages,
1256                // with connected peers' IP addresses in the IP header.
1257                //
1258                // Since we can't verify their source, Zebra needs to ignore unexpected messages,
1259                // because closing the connection could cause a denial of service or eclipse attack.
1260                debug!(%msg, "got BIP111 message without advertising NODE_BLOOM");
1261
1262                // Ignored, but consumed because it is technically a protocol error.
1263                Consumed
1264            }
1265
1266            // # Security
1267            //
1268            // Zebra crawls the network proactively, and that's the only way peers get into our
1269            // address book. This prevents peers from filling our address book with malicious peer
1270            // addresses.
1271            Message::Addr(ref new_addrs) => {
1272                // # Peer Set Reliability
1273                //
1274                // We keep a list of the unused peer addresses sent by each connection, to work
1275                // around `zcashd`'s `getaddr` response rate-limit.
1276                let no_response =
1277                    Handler::update_addr_cache(&mut self.cached_addrs, new_addrs, None);
1278                assert_eq!(
1279                    no_response,
1280                    Vec::new(),
1281                    "peers unexpectedly taken from cache"
1282                );
1283
1284                debug!(
1285                    new_addrs = new_addrs.len(),
1286                    cached_addrs = self.cached_addrs.len(),
1287                    "adding unsolicited addresses to cached addresses",
1288                );
1289
1290                Consumed
1291            }
1292            Message::Tx(ref transaction) => Request::PushTransaction(
1293                transaction.clone(),
1294                // Tag the directly pushed transaction with the sending peer so the
1295                // mempool downloader can enforce a per-peer queue cap, just like
1296                // advertised transaction IDs. See `GHSA-m9xx-8rcj-vmgp`.
1297                self.connection_info.connected_addr.get_transient_addr(),
1298            )
1299            .into(),
1300            Message::Inv(ref items) => match &items[..] {
1301                // We don't expect to be advertised multiple blocks at a time,
1302                // so we ignore any advertisements of multiple blocks.
1303                [InventoryHash::Block(hash)] => Request::AdvertiseBlock(
1304                    *hash,
1305                    self.connection_info.connected_addr.get_transient_addr(),
1306                )
1307                .into(),
1308
1309                // Some peers advertise invs with mixed item types.
1310                // But we're just interested in the transaction invs.
1311                //
1312                // TODO: split mixed invs into multiple requests,
1313                //       but skip runs of multiple blocks.
1314                tx_ids if tx_ids.iter().any(|item| item.unmined_tx_id().is_some()) => {
1315                    Request::AdvertiseTransactionIds(
1316                        transaction_ids(items).collect(),
1317                        self.connection_info.connected_addr.get_transient_addr(),
1318                    )
1319                    .into()
1320                }
1321
1322                // Log detailed messages for ignored inv advertisement messages.
1323                [] => {
1324                    debug!(%msg, "ignoring empty inv");
1325
1326                    // This might be a minor protocol error, or it might mean "not found".
1327                    Unused
1328                }
1329                [InventoryHash::Block(_), InventoryHash::Block(_), ..] => {
1330                    debug!(%msg, "ignoring inv with multiple blocks");
1331                    Unused
1332                }
1333                _ => {
1334                    debug!(%msg, "ignoring inv with no transactions");
1335                    Unused
1336                }
1337            },
1338            Message::GetData(ref items) => match &items[..] {
1339                // Some peers advertise invs with mixed item types.
1340                // So we suspect they might do the same with getdata.
1341                //
1342                // Since we can only handle one message at a time,
1343                // we treat it as a block request if there are any blocks,
1344                // or a transaction request if there are any transactions.
1345                //
1346                // TODO: split mixed getdata into multiple requests.
1347                b_hashes
1348                    if b_hashes
1349                        .iter()
1350                        .any(|item| matches!(item, InventoryHash::Block(_))) =>
1351                {
1352                    Request::BlocksByHash(block_hashes(items).collect()).into()
1353                }
1354                tx_ids if tx_ids.iter().any(|item| item.unmined_tx_id().is_some()) => {
1355                    Request::TransactionsById(transaction_ids(items).collect()).into()
1356                }
1357
1358                // Log detailed messages for ignored getdata request messages.
1359                [] => {
1360                    debug!(%msg, "ignoring empty getdata");
1361
1362                    // This might be a minor protocol error, or it might mean "not found".
1363                    Unused
1364                }
1365                _ => {
1366                    debug!(%msg, "ignoring getdata with no blocks or transactions");
1367                    Unused
1368                }
1369            },
1370            Message::GetAddr => Request::Peers.into(),
1371            Message::GetBlocks {
1372                ref known_blocks,
1373                stop,
1374            } => Request::FindBlocks {
1375                known_blocks: known_blocks.clone(),
1376                stop,
1377            }
1378            .into(),
1379            Message::GetHeaders {
1380                ref known_blocks,
1381                stop,
1382            } => Request::FindHeaders {
1383                known_blocks: known_blocks.clone(),
1384                stop,
1385            }
1386            .into(),
1387            Message::Mempool => Request::MempoolTransactionIds.into(),
1388        };
1389
1390        // Handle the request, and return unused messages.
1391        match req {
1392            AsRequest(req) => {
1393                self.drive_peer_request(req).await;
1394                None
1395            }
1396            Consumed => None,
1397            Unused => Some(msg),
1398        }
1399    }
1400
1401    /// Given a `req` originating from the peer, drive it to completion and send
1402    /// any appropriate messages to the remote peer. If an error occurs while
1403    /// processing the request (e.g., the service is shedding load), then we call
1404    /// fail_with to terminate the entire peer connection, shrinking the number
1405    /// of connected peers.
1406    async fn drive_peer_request(&mut self, req: Request) {
1407        trace!(?req);
1408
1409        // Add a metric for inbound requests
1410        metrics::counter!(
1411            "zebra.net.in.requests",
1412            "command" => req.command(),
1413            "addr" => self.metrics_label.clone(),
1414        )
1415        .increment(1);
1416        self.update_state_metrics(format!("In::Req::{}", req.command()));
1417
1418        // Give the inbound service time to clear its queue,
1419        // before sending the next inbound request.
1420        tokio::task::yield_now().await;
1421
1422        // # Security
1423        //
1424        // Holding buffer slots for a long time can cause hangs:
1425        // <https://docs.rs/tower/latest/tower/buffer/struct.Buffer.html#a-note-on-choosing-a-bound>
1426        //
1427        // The inbound service must be called immediately after a buffer slot is reserved.
1428        //
1429        // The inbound service never times out in readiness, because the load shed layer is always
1430        // ready, and returns an error in response to the request instead.
1431        if self.svc.ready().await.is_err() {
1432            self.fail_with(PeerError::ServiceShutdown).await;
1433            return;
1434        }
1435
1436        // Inbound service request timeouts are handled by the timeout layer in `start::start()`.
1437        let rsp = match self.svc.call(req.clone()).await {
1438            Err(e) => {
1439                if e.is::<tower::load_shed::error::Overloaded>() {
1440                    // # Security
1441                    //
1442                    // The peer request queue must have a limited length.
1443                    // The buffer and load shed layers are added in `start::start()`.
1444                    tracing::debug!("inbound service is overloaded, may close connection");
1445
1446                    let now = Instant::now();
1447
1448                    self.handle_inbound_overload(req, now, PeerError::Overloaded)
1449                        .await;
1450                } else if e.is::<tower::timeout::error::Elapsed>() {
1451                    // # Security
1452                    //
1453                    // Peer requests must have a timeout.
1454                    // The timeout layer is added in `start::start()`.
1455                    tracing::info!(%req, "inbound service request timed out, may close connection");
1456
1457                    let now = Instant::now();
1458
1459                    self.handle_inbound_overload(req, now, PeerError::InboundTimeout)
1460                        .await;
1461                } else {
1462                    // We could send a reject to the remote peer, but that might cause
1463                    // them to disconnect, and we might be using them to sync blocks.
1464                    // For similar reasons, we don't want to fail_with() here - we
1465                    // only close the connection if the peer is doing something wrong.
1466                    info!(
1467                        %e,
1468                        connection_state = ?self.state,
1469                        client_receiver = ?self.client_rx,
1470                        "error processing peer request",
1471                    );
1472                    self.update_state_metrics(format!("In::Req::{}/Rsp::Error", req.command()));
1473                }
1474
1475                return;
1476            }
1477            Ok(rsp) => rsp,
1478        };
1479
1480        // Add a metric for outbound responses to inbound requests
1481        metrics::counter!(
1482            "zebra.net.out.responses",
1483            "command" => rsp.command(),
1484            "addr" => self.metrics_label.clone(),
1485        )
1486        .increment(1);
1487        self.update_state_metrics(format!("In::Rsp::{}", rsp.command()));
1488
1489        // TODO: split response handler into its own method
1490        match rsp.clone() {
1491            Response::Nil => { /* generic success, do nothing */ }
1492            Response::Peers(addrs) => {
1493                if let Err(e) = self.peer_tx.send(Message::Addr(addrs)).await {
1494                    self.fail_with(e).await;
1495                }
1496            }
1497            Response::Pong(duration) => {
1498                debug!(?duration, "responding to Ping with Pong RTT");
1499            }
1500            Response::Transactions(transactions) => {
1501                // Generate one tx message per transaction,
1502                // then a notfound message with all the missing transaction ids.
1503                let mut missing_ids = Vec::new();
1504
1505                for transaction in transactions.into_iter() {
1506                    match transaction {
1507                        Available((transaction, _)) => {
1508                            if let Err(e) = self.peer_tx.send(Message::Tx(transaction)).await {
1509                                self.fail_with(e).await;
1510                                return;
1511                            }
1512                        }
1513                        Missing(id) => missing_ids.push(id.into()),
1514                    }
1515                }
1516
1517                if !missing_ids.is_empty() {
1518                    if let Err(e) = self.peer_tx.send(Message::NotFound(missing_ids)).await {
1519                        self.fail_with(e).await;
1520                        return;
1521                    }
1522                }
1523            }
1524            Response::Blocks(blocks) => {
1525                // Generate one tx message per block,
1526                // then a notfound message with all the missing block hashes.
1527                let mut missing_hashes = Vec::new();
1528
1529                for block in blocks.into_iter() {
1530                    match block {
1531                        Available((block, _)) => {
1532                            if let Err(e) = self.peer_tx.send(Message::Block(block)).await {
1533                                self.fail_with(e).await;
1534                                return;
1535                            }
1536                        }
1537                        Missing(hash) => missing_hashes.push(hash.into()),
1538                    }
1539                }
1540
1541                if !missing_hashes.is_empty() {
1542                    if let Err(e) = self.peer_tx.send(Message::NotFound(missing_hashes)).await {
1543                        self.fail_with(e).await;
1544                        return;
1545                    }
1546                }
1547            }
1548            Response::BlockHashes(hashes) => {
1549                if let Err(e) = self
1550                    .peer_tx
1551                    .send(Message::Inv(hashes.into_iter().map(Into::into).collect()))
1552                    .await
1553                {
1554                    self.fail_with(e).await
1555                }
1556            }
1557            Response::BlockHeaders(headers) => {
1558                if let Err(e) = self.peer_tx.send(Message::Headers(headers)).await {
1559                    self.fail_with(e).await
1560                }
1561            }
1562            Response::TransactionIds(hashes) => {
1563                let max_tx_inv_in_message: usize = MAX_TX_INV_IN_SENT_MESSAGE
1564                    .try_into()
1565                    .expect("constant fits in usize");
1566
1567                // # Security
1568                //
1569                // In most cases, we try to split over-sized responses into multiple network-layer
1570                // messages. But we are unlikely to reach this limit with the default mempool
1571                // config, so a response like this could indicate a network amplification attack.
1572                //
1573                // If there are thousands of transactions in the mempool, letting peers know the
1574                // exact transactions we have isn't that important, so it's ok to drop arbitrary
1575                // transaction hashes from our response.
1576                if hashes.len() > max_tx_inv_in_message {
1577                    debug!(inv_count = ?hashes.len(), ?MAX_TX_INV_IN_SENT_MESSAGE, "unusually large transaction ID response");
1578                }
1579
1580                let hashes = hashes
1581                    .into_iter()
1582                    .take(max_tx_inv_in_message)
1583                    .map(Into::into)
1584                    .collect();
1585
1586                if let Err(e) = self.peer_tx.send(Message::Inv(hashes)).await {
1587                    self.fail_with(e).await
1588                }
1589            }
1590        }
1591
1592        debug!(state = %self.state, %req, %rsp, "sent Zebra response to peer");
1593
1594        // Give the inbound service time to clear its queue,
1595        // before checking the connection for the next inbound or outbound request.
1596        tokio::task::yield_now().await;
1597    }
1598
1599    /// Handle inbound service overload and timeout error responses by randomly terminating some
1600    /// connections.
1601    ///
1602    /// # Security
1603    ///
1604    /// When the inbound service is overloaded with requests, Zebra needs to drop some connections,
1605    /// to reduce the load on the application. But dropping every connection that receives an
1606    /// `Overloaded` error from the inbound service could cause Zebra to drop too many peer
1607    /// connections, and stop itself downloading blocks or transactions.
1608    ///
1609    /// Malicious or misbehaving peers can also overload the inbound service, and make Zebra drop
1610    /// its connections to other peers.
1611    ///
1612    /// So instead, Zebra drops some overloaded connections at random. If a connection has recently
1613    /// overloaded the inbound service, it is more likely to be dropped. This makes it harder for a
1614    /// single peer (or multiple peers) to perform a denial of service attack.
1615    ///
1616    /// The inbound connection rate-limit also makes it hard for multiple peers to perform this
1617    /// attack, because each inbound connection can only send one inbound request before its
1618    /// probability of being disconnected increases.
1619    async fn handle_inbound_overload(&mut self, req: Request, now: Instant, error: PeerError) {
1620        let prev = self.last_overload_time.replace(now);
1621        let drop_connection_probability = overload_drop_connection_probability(now, prev);
1622
1623        if rand::rng().random::<f32>() < drop_connection_probability {
1624            if matches!(error, PeerError::Overloaded) {
1625                metrics::counter!("pool.closed.loadshed").increment(1);
1626            } else {
1627                metrics::counter!("pool.closed.inbound.timeout").increment(1);
1628            }
1629
1630            tracing::info!(
1631                drop_connection_probability = format!("{drop_connection_probability:.3}"),
1632                remote_user_agent = ?self.connection_info.remote.user_agent,
1633                negotiated_version = ?self.connection_info.negotiated_version,
1634                peer = ?self.metrics_label,
1635                last_peer_state = ?self.last_metrics_state,
1636                // TODO: remove this detailed debug info once #6506 is fixed
1637                remote_height = ?self.connection_info.remote.start_height,
1638                cached_addrs = ?self.cached_addrs.len(),
1639                connection_state = ?self.state,
1640                "inbound service {error} error, closing connection",
1641            );
1642
1643            self.update_state_metrics(format!("In::Req::{}/Rsp::{error}::Error", req.command()));
1644            self.fail_with(error).await;
1645        } else {
1646            self.update_state_metrics(format!("In::Req::{}/Rsp::{error}::Ignored", req.command()));
1647
1648            if matches!(error, PeerError::Overloaded) {
1649                metrics::counter!("pool.ignored.loadshed").increment(1);
1650            } else {
1651                metrics::counter!("pool.ignored.inbound.timeout").increment(1);
1652            }
1653        }
1654    }
1655}
1656
1657/// Returns the probability of dropping a connection where the last overload was at `prev`,
1658/// and the current overload is `now`.
1659///
1660/// # Security
1661///
1662/// Connections that haven't seen an overload error in the past OVERLOAD_PROTECTION_INTERVAL
1663/// have a small chance of being closed (MIN_OVERLOAD_DROP_PROBABILITY).
1664///
1665/// Connections that have seen a previous overload error in that time
1666/// have a higher chance of being dropped up to MAX_OVERLOAD_DROP_PROBABILITY.
1667/// This probability increases quadratically, so peers that send lots of inbound
1668/// requests are more likely to be dropped.
1669///
1670/// ## Examples
1671///
1672/// If a connection sends multiple overloads close together, it is very likely to be
1673/// disconnected. If a connection has two overloads multiple seconds apart, it is unlikely
1674/// to be disconnected.
1675fn overload_drop_connection_probability(now: Instant, prev: Option<Instant>) -> f32 {
1676    let Some(prev) = prev else {
1677        return MIN_OVERLOAD_DROP_PROBABILITY;
1678    };
1679
1680    let protection_fraction_since_last_overload =
1681        (now - prev).as_secs_f32() / OVERLOAD_PROTECTION_INTERVAL.as_secs_f32();
1682
1683    // Quadratically increase the disconnection probability for very recent overloads.
1684    // Negative values are ignored by clamping to MIN_OVERLOAD_DROP_PROBABILITY.
1685    let overload_fraction = protection_fraction_since_last_overload.powi(2);
1686
1687    let probability_range = MAX_OVERLOAD_DROP_PROBABILITY - MIN_OVERLOAD_DROP_PROBABILITY;
1688    let raw_drop_probability =
1689        MAX_OVERLOAD_DROP_PROBABILITY - (overload_fraction * probability_range);
1690
1691    raw_drop_probability.clamp(MIN_OVERLOAD_DROP_PROBABILITY, MAX_OVERLOAD_DROP_PROBABILITY)
1692}
1693
1694impl<S, Tx> Connection<S, Tx>
1695where
1696    Tx: Sink<Message, Error = SerializationError> + Unpin,
1697{
1698    /// Update the connection state metrics for this connection,
1699    /// using `extra_state_info` as additional state information.
1700    fn update_state_metrics(&mut self, extra_state_info: impl Into<Option<String>>) {
1701        let current_metrics_state = if let Some(extra_state_info) = extra_state_info.into() {
1702            format!("{}::{extra_state_info}", self.state.command()).into()
1703        } else {
1704            self.state.command()
1705        };
1706
1707        if self.last_metrics_state.as_ref() == Some(&current_metrics_state) {
1708            return;
1709        }
1710
1711        self.erase_state_metrics();
1712
1713        // Set the new state
1714        metrics::gauge!(
1715            "zebra.net.connection.state",
1716            "command" => current_metrics_state.clone(),
1717            "addr" => self.metrics_label.clone(),
1718        )
1719        .increment(1.0);
1720
1721        self.last_metrics_state = Some(current_metrics_state);
1722    }
1723
1724    /// Erase the connection state metrics for this connection.
1725    fn erase_state_metrics(&mut self) {
1726        if let Some(last_metrics_state) = self.last_metrics_state.take() {
1727            metrics::gauge!(
1728                "zebra.net.connection.state",
1729                "command" => last_metrics_state,
1730                "addr" => self.metrics_label.clone(),
1731            )
1732            .set(0.0);
1733        }
1734    }
1735
1736    /// Marks the peer as having failed with `error`, and performs connection cleanup,
1737    /// including async channel closes.
1738    ///
1739    /// If the connection has errored already, re-use the original error.
1740    /// Otherwise, fail the connection with `error`.
1741    async fn shutdown_async(&mut self, error: impl Into<SharedPeerError>) {
1742        // Close async channels first, so other tasks can start shutting down.
1743        // There's nothing we can do about errors while shutting down, and some errors are expected.
1744        //
1745        // TODO: close peer_tx and peer_rx in shutdown() and Drop, after:
1746        // - using channels instead of streams/sinks?
1747        // - exposing the underlying implementation rather than using generics and closures?
1748        // - adding peer_rx to the connection struct (optional)
1749        let _ = self.peer_tx.close().await;
1750
1751        self.shutdown(error);
1752    }
1753
1754    /// Marks the peer as having failed with `error`, and performs connection cleanup.
1755    /// See [`Self::shutdown_async()`] for details.
1756    ///
1757    /// Call [`Self::shutdown_async()`] in async code, because it can shut down more channels.
1758    fn shutdown(&mut self, error: impl Into<SharedPeerError>) {
1759        let mut error = error.into();
1760
1761        // Close channels first, so other tasks can start shutting down.
1762        self.client_rx.close();
1763
1764        // Update the shared error slot
1765        //
1766        // # Correctness
1767        //
1768        // Error slots use a threaded `std::sync::Mutex`, so accessing the slot
1769        // can block the async task's current thread. We only perform a single
1770        // slot update per `Client`. We ignore subsequent error slot updates.
1771        let slot_result = self.error_slot.try_update_error(error.clone());
1772
1773        if let Err(AlreadyErrored { original_error }) = slot_result {
1774            debug!(
1775                new_error = %error,
1776                %original_error,
1777                connection_state = ?self.state,
1778                "multiple errors on connection: \
1779                 failed connections should stop processing pending requests and responses, \
1780                 then close the connection"
1781            );
1782
1783            error = original_error;
1784        } else {
1785            debug!(%error,
1786                   connection_state = ?self.state,
1787                   "shutting down peer service with error");
1788        }
1789
1790        // Prepare to flush any pending client requests.
1791        //
1792        // We've already closed the client channel, so setting State::Failed
1793        // will make the main loop flush any pending requests.
1794        //
1795        // However, we may have an outstanding client request in State::AwaitingResponse,
1796        // so we need to deal with it first.
1797        if let State::AwaitingResponse { tx, .. } =
1798            std::mem::replace(&mut self.state, State::Failed)
1799        {
1800            // # Correctness
1801            //
1802            // We know the slot has Some(error), because we just set it above,
1803            // and the error slot is never unset.
1804            //
1805            // Accessing the error slot locks a threaded std::sync::Mutex, which
1806            // can block the current async task thread. We briefly lock the mutex
1807            // to clone the error.
1808            let _ = tx.send(Err(error.clone()));
1809        }
1810
1811        // Make the timer and metrics consistent with the Failed state.
1812        self.request_timer = None;
1813        self.update_state_metrics(None);
1814
1815        // Finally, flush pending client requests.
1816        while let Some(InProgressClientRequest { tx, span, .. }) =
1817            self.client_rx.close_and_flush_next()
1818        {
1819            trace!(
1820                parent: &span,
1821                %error,
1822                "sending an error response to a pending request on a failed connection"
1823            );
1824            let _ = tx.send(Err(error.clone()));
1825        }
1826    }
1827}
1828
1829impl<S, Tx> Drop for Connection<S, Tx>
1830where
1831    Tx: Sink<Message, Error = SerializationError> + Unpin,
1832{
1833    fn drop(&mut self) {
1834        self.shutdown(PeerError::ConnectionDropped);
1835
1836        self.erase_state_metrics();
1837    }
1838}
1839
1840/// Map a list of inventory hashes to the corresponding unmined transaction IDs.
1841/// Non-transaction inventory hashes are skipped.
1842///
1843/// v4 transactions use a legacy transaction ID, and
1844/// v5 transactions use a witnessed transaction ID.
1845fn transaction_ids(items: &'_ [InventoryHash]) -> impl Iterator<Item = UnminedTxId> + '_ {
1846    items.iter().filter_map(InventoryHash::unmined_tx_id)
1847}
1848
1849/// Map a list of inventory hashes to the corresponding block hashes.
1850/// Non-block inventory hashes are skipped.
1851fn block_hashes(items: &'_ [InventoryHash]) -> impl Iterator<Item = block::Hash> + '_ {
1852    items.iter().filter_map(InventoryHash::block_hash)
1853}