litep2p/
error.rs

1// Copyright 2019 Parity Technologies (UK) Ltd.
2// Copyright 2023 litep2p developers
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22#![allow(clippy::enum_variant_names)]
23
24//! [`Litep2p`](`crate::Litep2p`) error types.
25
26// TODO: clean up all these errors into something coherent
27// TODO: move `NegotiationError` under `SubstreamError`
28
29use crate::{
30    protocol::Direction,
31    transport::manager::limits::ConnectionLimitsError,
32    types::{protocol::ProtocolName, ConnectionId, SubstreamId},
33    PeerId,
34};
35
36use multiaddr::Multiaddr;
37use multihash::{Multihash, MultihashGeneric};
38
39use std::io::{self, ErrorKind};
40
41#[allow(clippy::large_enum_variant)]
42#[derive(Debug, thiserror::Error)]
43pub enum Error {
44    #[error("Peer `{0}` does not exist")]
45    PeerDoesntExist(PeerId),
46    #[error("Peer `{0}` already exists")]
47    PeerAlreadyExists(PeerId),
48    #[error("Protocol `{0}` not supported")]
49    ProtocolNotSupported(String),
50    #[error("Address error: `{0}`")]
51    AddressError(#[from] AddressError),
52    #[error("Parse error: `{0}`")]
53    ParseError(ParseError),
54    #[error("I/O error: `{0}`")]
55    IoError(ErrorKind),
56    #[error("Negotiation error: `{0}`")]
57    NegotiationError(#[from] NegotiationError),
58    #[error("Substream error: `{0}`")]
59    SubstreamError(#[from] SubstreamError),
60    #[error("Substream error: `{0}`")]
61    NotificationError(NotificationError),
62    #[error("Essential task closed")]
63    EssentialTaskClosed,
64    #[error("Unknown error occurred")]
65    Unknown,
66    #[error("Cannot dial self: `{0}`")]
67    CannotDialSelf(Multiaddr),
68    #[error("Transport not supported")]
69    TransportNotSupported(Multiaddr),
70    #[error("Yamux error for substream `{0:?}`: `{1}`")]
71    YamuxError(Direction, crate::yamux::ConnectionError),
72    #[error("Operation not supported: `{0}`")]
73    NotSupported(String),
74    #[error("Other error occurred: `{0}`")]
75    Other(String),
76    #[error("Protocol already exists: `{0:?}`")]
77    ProtocolAlreadyExists(ProtocolName),
78    #[error("Operation timed out")]
79    Timeout,
80    #[error("Invalid state transition")]
81    InvalidState,
82    #[error("DNS address resolution failed")]
83    DnsAddressResolutionFailed,
84    #[error("Transport error: `{0}`")]
85    TransportError(String),
86    #[cfg(feature = "quic")]
87    #[error("Failed to generate certificate: `{0}`")]
88    CertificateGeneration(#[from] crate::crypto::tls::certificate::GenError),
89    #[error("Invalid data")]
90    InvalidData,
91    #[error("Input rejected")]
92    InputRejected,
93    #[cfg(feature = "websocket")]
94    #[error("WebSocket error: `{0}`")]
95    WebSocket(#[from] tokio_tungstenite::tungstenite::error::Error),
96    #[error("Insufficient peers")]
97    InsufficientPeers,
98    #[error("Substream doens't exist")]
99    SubstreamDoesntExist,
100    #[cfg(feature = "webrtc")]
101    #[error("`str0m` error: `{0}`")]
102    WebRtc(#[from] str0m::RtcError),
103    #[error("Remote peer disconnected")]
104    Disconnected,
105    #[error("Channel does not exist")]
106    ChannelDoesntExist,
107    #[error("Tried to dial self")]
108    TriedToDialSelf,
109    #[error("Litep2p is already connected to the peer")]
110    AlreadyConnected,
111    #[error("No addres available for `{0}`")]
112    NoAddressAvailable(PeerId),
113    #[error("Connection closed")]
114    ConnectionClosed,
115    #[cfg(feature = "quic")]
116    #[error("Quinn error: `{0}`")]
117    Quinn(quinn::ConnectionError),
118    #[error("Invalid certificate")]
119    InvalidCertificate,
120    #[error("Peer ID mismatch: expected `{0}`, got `{1}`")]
121    PeerIdMismatch(PeerId, PeerId),
122    #[error("Channel is clogged")]
123    ChannelClogged,
124    #[error("Connection doesn't exist: `{0:?}`")]
125    ConnectionDoesntExist(ConnectionId),
126    #[error("Exceeded connection limits `{0:?}`")]
127    ConnectionLimit(ConnectionLimitsError),
128    #[error("Failed to dial peer immediately")]
129    ImmediateDialError(#[from] ImmediateDialError),
130}
131
132/// Error type for address parsing.
133#[derive(Debug, thiserror::Error)]
134pub enum AddressError {
135    /// The provided address does not correspond to the transport protocol.
136    ///
137    /// For example, this can happen when the address used the UDP protocol but
138    /// the handling transport only allows TCP connections.
139    #[error("Invalid address for protocol")]
140    InvalidProtocol,
141    /// The provided address is not a valid URL.
142    #[error("Invalid URL")]
143    InvalidUrl,
144    /// The provided address does not include a peer ID.
145    #[error("`PeerId` missing from the address")]
146    PeerIdMissing,
147    /// No address is available for the provided peer ID.
148    #[error("Address not available")]
149    AddressNotAvailable,
150    /// The provided address contains an invalid multihash.
151    #[error("Multihash does not contain a valid peer ID : `{0:?}`")]
152    InvalidPeerId(Multihash),
153}
154
155#[derive(Debug, thiserror::Error, PartialEq)]
156pub enum ParseError {
157    /// The provided probuf message cannot be decoded.
158    #[error("Failed to decode protobuf message: `{0:?}`")]
159    ProstDecodeError(#[from] prost::DecodeError),
160    /// The provided protobuf message cannot be encoded.
161    #[error("Failed to encode protobuf message: `{0:?}`")]
162    ProstEncodeError(#[from] prost::EncodeError),
163    /// The protobuf message contains an unexpected key type.
164    ///
165    /// This error can happen when:
166    ///  - The provided key type is not recognized.
167    ///  - The provided key type is recognized but not supported.
168    #[error("Unknown key type from protobuf message: `{0}`")]
169    UnknownKeyType(i32),
170    /// The public key bytes are invalid and cannot be parsed.
171    ///
172    /// This error can happen when:
173    ///  - The received number of bytes is not equal to the expected number of bytes (32 bytes).
174    ///  - The bytes are not a valid Ed25519 public key.
175    ///  - Length of the public key is not represented by 2 bytes (WebRTC specific).
176    #[error("Invalid public key")]
177    InvalidPublicKey,
178    /// The provided date has an invalid format.
179    ///
180    /// This error is protocol specific.
181    #[error("Invalid data")]
182    InvalidData,
183}
184
185#[derive(Debug, thiserror::Error, PartialEq)]
186pub enum SubstreamError {
187    #[error("Connection closed")]
188    ConnectionClosed,
189    #[error("Connection channel clogged")]
190    ChannelClogged,
191    #[error("Connection to peer does not exist: `{0}`")]
192    PeerDoesNotExist(PeerId),
193    #[error("I/O error: `{0}`")]
194    IoError(ErrorKind),
195    #[error("yamux error: `{0}`")]
196    YamuxError(crate::yamux::ConnectionError, Direction),
197    #[error("Failed to read from substream, substream id `{0:?}`")]
198    ReadFailure(Option<SubstreamId>),
199    #[error("Failed to write to substream, substream id `{0:?}`")]
200    WriteFailure(Option<SubstreamId>),
201    #[error("Negotiation error: `{0:?}`")]
202    NegotiationError(#[from] NegotiationError),
203}
204
205/// Error during the negotiation phase.
206#[derive(Debug, thiserror::Error)]
207pub enum NegotiationError {
208    /// Error occurred during the multistream-select phase of the negotiation.
209    #[error("multistream-select error: `{0:?}`")]
210    MultistreamSelectError(#[from] crate::multistream_select::NegotiationError),
211    /// Error occurred during the Noise handshake negotiation.
212    #[error("multistream-select error: `{0:?}`")]
213    SnowError(#[from] snow::Error),
214    /// The peer ID was not provided by the noise handshake.
215    #[error("`PeerId` missing from Noise handshake")]
216    PeerIdMissing,
217    /// The negotiation operation timed out.
218    #[error("Operation timed out")]
219    Timeout,
220    /// The message provided over the wire has an invalid format or is unsupported.
221    #[error("Parse error: `{0}`")]
222    ParseError(#[from] ParseError),
223    /// An I/O error occurred during the negotiation process.
224    #[error("I/O error: `{0}`")]
225    IoError(ErrorKind),
226    /// Expected a different state during the negotiation process.
227    #[error("Expected a different state")]
228    StateMismatch,
229    /// The noise handshake provided a different peer ID than the one expected in the dialing
230    /// address.
231    #[error("Peer ID mismatch: expected `{0}`, got `{1}`")]
232    PeerIdMismatch(PeerId, PeerId),
233    /// Error specific to the QUIC transport.
234    #[cfg(feature = "quic")]
235    #[error("QUIC error: `{0}`")]
236    Quic(#[from] QuicError),
237    /// Error specific to the WebSocket transport.
238    #[cfg(feature = "websocket")]
239    #[error("WebSocket error: `{0}`")]
240    WebSocket(#[from] tokio_tungstenite::tungstenite::error::Error),
241}
242
243impl PartialEq for NegotiationError {
244    fn eq(&self, other: &Self) -> bool {
245        match (self, other) {
246            (Self::MultistreamSelectError(lhs), Self::MultistreamSelectError(rhs)) => lhs == rhs,
247            (Self::SnowError(lhs), Self::SnowError(rhs)) => lhs == rhs,
248            (Self::ParseError(lhs), Self::ParseError(rhs)) => lhs == rhs,
249            (Self::IoError(lhs), Self::IoError(rhs)) => lhs == rhs,
250            (Self::PeerIdMismatch(lhs, lhs_1), Self::PeerIdMismatch(rhs, rhs_1)) =>
251                lhs == rhs && lhs_1 == rhs_1,
252            #[cfg(feature = "quic")]
253            (Self::Quic(lhs), Self::Quic(rhs)) => lhs == rhs,
254            #[cfg(feature = "websocket")]
255            (Self::WebSocket(lhs), Self::WebSocket(rhs)) =>
256                core::mem::discriminant(lhs) == core::mem::discriminant(rhs),
257            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
258        }
259    }
260}
261
262#[derive(Debug, thiserror::Error)]
263pub enum NotificationError {
264    #[error("Peer already exists")]
265    PeerAlreadyExists,
266    #[error("Peer is in invalid state")]
267    InvalidState,
268    #[error("Notifications clogged")]
269    NotificationsClogged,
270    #[error("Notification stream closed")]
271    NotificationStreamClosed(PeerId),
272}
273
274/// The error type for dialing a peer.
275///
276/// This error is reported via the litep2p events after performing
277/// a network dialing operation.
278#[derive(Debug, thiserror::Error)]
279pub enum DialError {
280    /// The dialing operation timed out.
281    ///
282    /// This error indicates that the `connection_open_timeout` from the protocol configuration
283    /// was exceeded.
284    #[error("Dial timed out")]
285    Timeout,
286    /// The provided address for dialing is invalid.
287    #[error("Address error: `{0}`")]
288    AddressError(#[from] AddressError),
289    /// An error occurred during DNS lookup operation.
290    ///
291    /// The address provided may be valid, however it failed to resolve to a concrete IP address.
292    /// This error may be recoverable.
293    #[error("DNS lookup error for `{0}`")]
294    DnsError(#[from] DnsError),
295    /// An error occurred during the negotiation process.
296    #[error("Negotiation error: `{0}`")]
297    NegotiationError(#[from] NegotiationError),
298}
299
300/// Dialing resulted in an immediate error before performing any network operations.
301#[derive(Debug, thiserror::Error, Copy, Clone, Eq, PartialEq)]
302pub enum ImmediateDialError {
303    /// The provided address does not include a peer ID.
304    #[error("`PeerId` missing from the address")]
305    PeerIdMissing,
306    /// The peer ID provided in the address is the same as the local peer ID.
307    #[error("Tried to dial self")]
308    TriedToDialSelf,
309    /// Cannot dial an already connected peer.
310    #[error("Already connected to peer")]
311    AlreadyConnected,
312    /// Cannot dial a peer that does not have any address available.
313    #[error("No address available for peer")]
314    NoAddressAvailable,
315    /// The essential task was closed.
316    #[error("TaskClosed")]
317    TaskClosed,
318    /// The channel is clogged.
319    #[error("Connection channel clogged")]
320    ChannelClogged,
321}
322
323/// Error during the QUIC transport negotiation.
324#[cfg(feature = "quic")]
325#[derive(Debug, thiserror::Error, PartialEq)]
326pub enum QuicError {
327    /// The provided certificate is invalid.
328    #[error("Invalid certificate")]
329    InvalidCertificate,
330    /// The connection was lost.
331    #[error("Failed to negotiate QUIC: `{0}`")]
332    ConnectionError(#[from] quinn::ConnectionError),
333    /// The connection could not be established.
334    #[error("Failed to connect to peer: `{0}`")]
335    ConnectError(#[from] quinn::ConnectError),
336}
337
338/// Error during DNS resolution.
339#[derive(Debug, thiserror::Error, PartialEq)]
340pub enum DnsError {
341    /// The DNS resolution failed to resolve the provided URL.
342    #[error("DNS failed to resolve url `{0}`")]
343    ResolveError(String),
344    /// The DNS expected a different IP address version.
345    ///
346    /// For example, DNSv4 was expected but DNSv6 was provided.
347    #[error("DNS type is different from the provided IP address")]
348    IpVersionMismatch,
349}
350
351impl From<MultihashGeneric<64>> for Error {
352    fn from(hash: MultihashGeneric<64>) -> Self {
353        Error::AddressError(AddressError::InvalidPeerId(hash))
354    }
355}
356
357impl From<io::Error> for Error {
358    fn from(error: io::Error) -> Error {
359        Error::IoError(error.kind())
360    }
361}
362
363impl From<io::Error> for SubstreamError {
364    fn from(error: io::Error) -> SubstreamError {
365        SubstreamError::IoError(error.kind())
366    }
367}
368
369impl From<io::Error> for DialError {
370    fn from(error: io::Error) -> Self {
371        DialError::NegotiationError(NegotiationError::IoError(error.kind()))
372    }
373}
374
375impl From<crate::multistream_select::NegotiationError> for Error {
376    fn from(error: crate::multistream_select::NegotiationError) -> Error {
377        Error::NegotiationError(NegotiationError::MultistreamSelectError(error))
378    }
379}
380
381impl From<snow::Error> for Error {
382    fn from(error: snow::Error) -> Self {
383        Error::NegotiationError(NegotiationError::SnowError(error))
384    }
385}
386
387impl<T> From<tokio::sync::mpsc::error::SendError<T>> for Error {
388    fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
389        Error::EssentialTaskClosed
390    }
391}
392
393impl From<tokio::sync::oneshot::error::RecvError> for Error {
394    fn from(_: tokio::sync::oneshot::error::RecvError) -> Self {
395        Error::EssentialTaskClosed
396    }
397}
398
399impl From<prost::DecodeError> for Error {
400    fn from(error: prost::DecodeError) -> Self {
401        Error::ParseError(ParseError::ProstDecodeError(error))
402    }
403}
404
405impl From<prost::EncodeError> for Error {
406    fn from(error: prost::EncodeError) -> Self {
407        Error::ParseError(ParseError::ProstEncodeError(error))
408    }
409}
410
411impl From<io::Error> for NegotiationError {
412    fn from(error: io::Error) -> Self {
413        NegotiationError::IoError(error.kind())
414    }
415}
416
417impl From<ParseError> for Error {
418    fn from(error: ParseError) -> Self {
419        Error::ParseError(error)
420    }
421}
422
423impl From<MultihashGeneric<64>> for AddressError {
424    fn from(hash: MultihashGeneric<64>) -> Self {
425        AddressError::InvalidPeerId(hash)
426    }
427}
428
429#[cfg(feature = "quic")]
430impl From<quinn::ConnectionError> for Error {
431    fn from(error: quinn::ConnectionError) -> Self {
432        match error {
433            quinn::ConnectionError::TimedOut => Error::Timeout,
434            error => Error::Quinn(error),
435        }
436    }
437}
438
439#[cfg(feature = "quic")]
440impl From<quinn::ConnectionError> for DialError {
441    fn from(error: quinn::ConnectionError) -> Self {
442        match error {
443            quinn::ConnectionError::TimedOut => DialError::Timeout,
444            error => DialError::NegotiationError(NegotiationError::Quic(error.into())),
445        }
446    }
447}
448
449#[cfg(feature = "quic")]
450impl From<quinn::ConnectError> for DialError {
451    fn from(error: quinn::ConnectError) -> Self {
452        DialError::NegotiationError(NegotiationError::Quic(error.into()))
453    }
454}
455
456impl From<ConnectionLimitsError> for Error {
457    fn from(error: ConnectionLimitsError) -> Self {
458        Error::ConnectionLimit(error)
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465    use tokio::sync::mpsc::{channel, Sender};
466
467    #[tokio::test]
468    async fn try_from_errors() {
469        let (tx, rx) = channel(1);
470        drop(rx);
471
472        async fn test(tx: Sender<()>) -> crate::Result<()> {
473            tx.send(()).await.map_err(From::from)
474        }
475
476        match test(tx).await.unwrap_err() {
477            Error::EssentialTaskClosed => {}
478            _ => panic!("invalid error"),
479        }
480    }
481}