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(¤t_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}