Skip to main content

reth_eth_wire/errors/
eth.rs

1//! Error handling for (`EthStream`)[`crate::EthStream`]
2
3use crate::{
4    errors::P2PStreamError, message::MessageError, version::ParseVersionError, DisconnectReason,
5};
6use alloy_chains::Chain;
7use alloy_primitives::B256;
8use reth_eth_wire_types::{snap::SnapProtocolError, EthVersion};
9use reth_ethereum_forks::ValidationError;
10use reth_primitives_traits::{GotExpected, GotExpectedBoxed};
11use std::io;
12
13/// Errors when sending/receiving messages
14#[derive(thiserror::Error, Debug)]
15pub enum EthStreamError {
16    #[error(transparent)]
17    /// Error of the underlying P2P connection.
18    P2PStreamError(#[from] P2PStreamError),
19    #[error(transparent)]
20    /// Failed to parse peer's version.
21    ParseVersionError(#[from] ParseVersionError),
22    #[error(transparent)]
23    /// Failed Ethereum handshake.
24    EthHandshakeError(#[from] EthHandshakeError),
25    /// Thrown when decoding a message failed.
26    #[error(transparent)]
27    InvalidMessage(#[from] MessageError),
28    /// Thrown when decoding an inbound `snap` protocol message failed.
29    #[error(transparent)]
30    InvalidSnapMessage(#[from] SnapProtocolError),
31    #[error("message size ({0}) exceeds max length (10MB)")]
32    /// Received a message whose size exceeds the standard limit.
33    MessageTooBig(usize),
34    #[error(
35        "TransactionHashes invalid len of fields: hashes_len={hashes_len} types_len={types_len} sizes_len={sizes_len}"
36    )]
37    /// Received malformed transaction hashes message with discrepancies in field lengths.
38    TransactionHashesInvalidLenOfFields {
39        /// The number of transaction hashes.
40        hashes_len: usize,
41        /// The number of transaction types.
42        types_len: usize,
43        /// The number of transaction sizes.
44        sizes_len: usize,
45    },
46    /// Error when data is not received from peer for a prolonged period.
47    #[error("never received data from remote peer")]
48    StreamTimeout,
49    /// Error triggered when an unknown or unsupported Ethereum message ID is received.
50    #[error("Received unknown ETH message ID: 0x{message_id:X}")]
51    UnsupportedMessage {
52        /// The identifier of the unknown Ethereum message.
53        message_id: u8,
54    },
55}
56
57// === impl EthStreamError ===
58
59impl EthStreamError {
60    /// Returns the [`DisconnectReason`] if the error is a disconnect message
61    pub const fn as_disconnected(&self) -> Option<DisconnectReason> {
62        if let Self::P2PStreamError(err) = self {
63            err.as_disconnected()
64        } else {
65            None
66        }
67    }
68
69    /// Returns whether this error indicates a protocol breach on the receive side.
70    ///
71    /// These are errors caused by the remote peer sending invalid or malformed data
72    /// that warrant disconnecting with [`DisconnectReason::ProtocolBreach`].
73    pub const fn is_protocol_breach(&self) -> bool {
74        matches!(
75            self,
76            Self::InvalidMessage(_) |
77                Self::InvalidSnapMessage(_) |
78                Self::MessageTooBig(_) |
79                Self::TransactionHashesInvalidLenOfFields { .. } |
80                Self::UnsupportedMessage { .. } |
81                Self::P2PStreamError(
82                    P2PStreamError::Rlp(_) |
83                        P2PStreamError::Snap(_) |
84                        P2PStreamError::MessageTooBig { .. } |
85                        P2PStreamError::UnknownReservedMessageId(_) |
86                        P2PStreamError::EmptyProtocolMessage |
87                        P2PStreamError::UnknownDisconnectReason(_)
88                )
89        )
90    }
91
92    /// Returns the [`io::Error`] if it was caused by IO
93    pub const fn as_io(&self) -> Option<&io::Error> {
94        if let Self::P2PStreamError(P2PStreamError::Io(io)) = self {
95            return Some(io)
96        }
97        None
98    }
99}
100
101impl From<io::Error> for EthStreamError {
102    fn from(err: io::Error) -> Self {
103        P2PStreamError::from(err).into()
104    }
105}
106
107/// Error  that can occur during the `eth` sub-protocol handshake.
108#[derive(thiserror::Error, Debug)]
109pub enum EthHandshakeError {
110    /// Status message received or sent outside of the handshake process.
111    #[error("status message can only be recv/sent in handshake")]
112    StatusNotInHandshake,
113    /// Receiving a non-status message during the handshake phase.
114    #[error("received non-status message when trying to handshake")]
115    NonStatusMessageInHandshake,
116    #[error("no response received when sending out handshake")]
117    /// No response received during the handshake process.
118    NoResponse,
119    #[error(transparent)]
120    /// Invalid fork data.
121    InvalidFork(#[from] ValidationError),
122    #[error("mismatched genesis in status message: {0}")]
123    /// Mismatch in the genesis block during status exchange.
124    MismatchedGenesis(GotExpectedBoxed<B256>),
125    #[error("mismatched protocol version in status message: {0}")]
126    /// Mismatched protocol versions in status messages.
127    MismatchedProtocolVersion(GotExpected<EthVersion>),
128    #[error("mismatched chain in status message: {0}")]
129    /// Mismatch in chain details in status messages.
130    MismatchedChain(GotExpected<Chain>),
131    #[error("total difficulty bitlen is too large: got {got}, maximum {maximum}")]
132    /// Excessively large total difficulty bit lengths.
133    TotalDifficultyBitLenTooLarge {
134        /// The actual bit length of the total difficulty.
135        got: usize,
136        /// The maximum allowed bit length for the total difficulty.
137        maximum: usize,
138    },
139    #[error("earliest block > latest block: got {got}, latest {latest}")]
140    /// Earliest block > latest block.
141    EarliestBlockGreaterThanLatestBlock {
142        /// The earliest block.
143        got: u64,
144        /// The latest block.
145        latest: u64,
146    },
147    #[error("blockhash is zero")]
148    /// Blockhash is zero.
149    BlockhashZero,
150}