Skip to main content

reth_eth_wire/errors/
p2p.rs

1//! Error handling for [`P2PStream`](crate::P2PStream).
2
3use std::io;
4
5use reth_eth_wire_types::{DisconnectReason, UnknownDisconnectReason};
6use reth_primitives_traits::GotExpected;
7
8use crate::{capability::SharedCapabilityError, Capability, ProtocolVersion};
9
10/// Errors when sending/receiving p2p messages. These should result in kicking the peer.
11#[derive(thiserror::Error, Debug)]
12pub enum P2PStreamError {
13    /// I/O error.
14    #[error(transparent)]
15    Io(#[from] io::Error),
16
17    /// RLP encoding/decoding error.
18    #[error(transparent)]
19    Rlp(#[from] alloy_rlp::Error),
20
21    /// Error in compression/decompression using Snappy.
22    #[error(transparent)]
23    Snap(#[from] snap::Error),
24
25    /// Error during the P2P handshake.
26    #[error(transparent)]
27    HandshakeError(#[from] P2PHandshakeError),
28
29    /// Message size exceeds maximum length error.
30    #[error("message size ({message_size}) exceeds max length ({max_size})")]
31    MessageTooBig {
32        /// The actual size of the message received.
33        message_size: usize,
34        /// The maximum allowed size for the message.
35        max_size: usize,
36    },
37
38    /// A subprotocol message exceeds the limit declared by its local handler.
39    #[error(
40        "message for subprotocol {capability} has size {message_size}, exceeding max length {max_size}"
41    )]
42    SubprotocolMessageTooBig {
43        /// The negotiated capability whose limit was exceeded.
44        capability: Capability,
45        /// The frame size, including the capability-local message ID.
46        message_size: usize,
47        /// The maximum frame size declared by the protocol handler.
48        max_size: usize,
49    },
50
51    /// A subprotocol's bounded inbound queue has no remaining capacity.
52    #[error("inbound buffer for subprotocol {capability} is full")]
53    SubprotocolInboundBufferFull {
54        /// The negotiated capability whose queue is full.
55        capability: Capability,
56    },
57
58    /// An incoming message ID is outside all negotiated subprotocol ranges.
59    #[error("unknown subprotocol message id: {0:#04x}")]
60    UnknownSubprotocolMessageId(u8),
61
62    /// Unknown reserved P2P message ID error.
63    #[error("unknown reserved p2p message id: {0}")]
64    UnknownReservedMessageId(u8),
65
66    /// Empty protocol message received error.
67    #[error("empty protocol message received")]
68    EmptyProtocolMessage,
69
70    /// Ping or Pong payload is not an RLP empty list.
71    #[error("invalid ping/pong payload for p2p message id: {0:#x}")]
72    InvalidPingPongPayload(u8),
73
74    /// Incoming ping rate limit exceeded.
75    #[error("too many pings received")]
76    TooManyPings,
77
78    /// Error related to the Pinger.
79    #[error(transparent)]
80    PingerError(#[from] PingerError),
81
82    /// Ping timeout error.
83    #[error("ping timed out with")]
84    PingTimeout,
85
86    /// Error parsing shared capabilities.
87    #[error(transparent)]
88    ParseSharedCapability(#[from] SharedCapabilityError),
89
90    /// Capability not supported on the stream to this peer.
91    #[error("capability not supported on stream to this peer")]
92    CapabilityNotShared,
93
94    /// Mismatched protocol version error.
95    #[error("mismatched protocol version in Hello message: {0}")]
96    MismatchedProtocolVersion(GotExpected<ProtocolVersion>),
97
98    /// Too many messages buffered before sending.
99    #[error("too many messages buffered before sending")]
100    SendBufferFull,
101
102    /// Disconnected error.
103    #[error("disconnected: {0}")]
104    Disconnected(DisconnectReason),
105
106    /// Unknown disconnect reason error.
107    #[error("unknown disconnect reason: {0}")]
108    UnknownDisconnectReason(#[from] UnknownDisconnectReason),
109}
110
111// === impl P2PStreamError ===
112
113impl P2PStreamError {
114    /// Returns the [`DisconnectReason`] if it is the `Disconnected` variant.
115    pub const fn as_disconnected(&self) -> Option<DisconnectReason> {
116        let reason = match self {
117            Self::HandshakeError(P2PHandshakeError::Disconnected(reason)) |
118            Self::Disconnected(reason) => reason,
119            _ => return None,
120        };
121
122        Some(*reason)
123    }
124}
125
126/// Errors when conducting a p2p handshake.
127#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
128pub enum P2PHandshakeError {
129    /// Hello message received/sent outside of handshake error.
130    #[error("hello message can only be recv/sent in handshake")]
131    HelloNotInHandshake,
132
133    /// Received a non-hello message when trying to handshake.
134    #[error("received non-hello message when trying to handshake")]
135    NonHelloMessageInHandshake,
136
137    /// No capabilities shared with the peer.
138    #[error("no capabilities shared with peer")]
139    NoSharedCapabilities,
140
141    /// No response received when sending out handshake.
142    #[error("no response received when sending out handshake")]
143    NoResponse,
144
145    /// Handshake timed out.
146    #[error("handshake timed out")]
147    Timeout,
148
149    /// Disconnected by peer with a specific reason.
150    #[error("disconnected by peer: {0}")]
151    Disconnected(DisconnectReason),
152
153    /// Error decoding a message during handshake.
154    #[error("error decoding a message during handshake: {0}")]
155    DecodeError(#[from] alloy_rlp::Error),
156}
157
158/// An error that can occur when interacting with a pinger.
159#[derive(Debug, thiserror::Error)]
160pub enum PingerError {
161    /// An unexpected pong was received while the pinger was in the `Ready` state.
162    #[error("pong received while ready")]
163    UnexpectedPong,
164}