#![allow(clippy::enum_variant_names)]
use crate::{
protocol::Direction,
transport::manager::limits::ConnectionLimitsError,
types::{protocol::ProtocolName, ConnectionId, SubstreamId},
PeerId,
};
use multiaddr::Multiaddr;
use multihash::{Multihash, MultihashGeneric};
use std::io::{self, ErrorKind};
#[allow(clippy::large_enum_variant)]
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Peer `{0}` does not exist")]
PeerDoesntExist(PeerId),
#[error("Peer `{0}` already exists")]
PeerAlreadyExists(PeerId),
#[error("Protocol `{0}` not supported")]
ProtocolNotSupported(String),
#[error("Address error: `{0}`")]
AddressError(#[from] AddressError),
#[error("Parse error: `{0}`")]
ParseError(ParseError),
#[error("I/O error: `{0}`")]
IoError(ErrorKind),
#[error("Negotiation error: `{0}`")]
NegotiationError(#[from] NegotiationError),
#[error("Substream error: `{0}`")]
SubstreamError(#[from] SubstreamError),
#[error("Substream error: `{0}`")]
NotificationError(NotificationError),
#[error("Essential task closed")]
EssentialTaskClosed,
#[error("Unknown error occurred")]
Unknown,
#[error("Cannot dial self: `{0}`")]
CannotDialSelf(Multiaddr),
#[error("Transport not supported")]
TransportNotSupported(Multiaddr),
#[error("Yamux error for substream `{0:?}`: `{1}`")]
YamuxError(Direction, crate::yamux::ConnectionError),
#[error("Operation not supported: `{0}`")]
NotSupported(String),
#[error("Other error occurred: `{0}`")]
Other(String),
#[error("Protocol already exists: `{0:?}`")]
ProtocolAlreadyExists(ProtocolName),
#[error("Operation timed out")]
Timeout,
#[error("Invalid state transition")]
InvalidState,
#[error("DNS address resolution failed")]
DnsAddressResolutionFailed,
#[error("Transport error: `{0}`")]
TransportError(String),
#[cfg(feature = "quic")]
#[error("Failed to generate certificate: `{0}`")]
CertificateGeneration(#[from] crate::crypto::tls::certificate::GenError),
#[error("Invalid data")]
InvalidData,
#[error("Input rejected")]
InputRejected,
#[cfg(feature = "websocket")]
#[error("WebSocket error: `{0}`")]
WebSocket(#[from] tokio_tungstenite::tungstenite::error::Error),
#[error("Insufficient peers")]
InsufficientPeers,
#[error("Substream doens't exist")]
SubstreamDoesntExist,
#[cfg(feature = "webrtc")]
#[error("`str0m` error: `{0}`")]
WebRtc(#[from] str0m::RtcError),
#[error("Remote peer disconnected")]
Disconnected,
#[error("Channel does not exist")]
ChannelDoesntExist,
#[error("Tried to dial self")]
TriedToDialSelf,
#[error("Litep2p is already connected to the peer")]
AlreadyConnected,
#[error("No addres available for `{0}`")]
NoAddressAvailable(PeerId),
#[error("Connection closed")]
ConnectionClosed,
#[cfg(feature = "quic")]
#[error("Quinn error: `{0}`")]
Quinn(quinn::ConnectionError),
#[error("Invalid certificate")]
InvalidCertificate,
#[error("Peer ID mismatch: expected `{0}`, got `{1}`")]
PeerIdMismatch(PeerId, PeerId),
#[error("Channel is clogged")]
ChannelClogged,
#[error("Connection doesn't exist: `{0:?}`")]
ConnectionDoesntExist(ConnectionId),
#[error("Exceeded connection limits `{0:?}`")]
ConnectionLimit(ConnectionLimitsError),
#[error("Failed to dial peer immediately")]
ImmediateDialError(#[from] ImmediateDialError),
}
#[derive(Debug, thiserror::Error)]
pub enum AddressError {
#[error("Invalid address for protocol")]
InvalidProtocol,
#[error("Invalid URL")]
InvalidUrl,
#[error("`PeerId` missing from the address")]
PeerIdMissing,
#[error("Address not available")]
AddressNotAvailable,
#[error("Multihash does not contain a valid peer ID : `{0:?}`")]
InvalidPeerId(Multihash),
}
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum ParseError {
#[error("Failed to decode protobuf message: `{0:?}`")]
ProstDecodeError(#[from] prost::DecodeError),
#[error("Failed to encode protobuf message: `{0:?}`")]
ProstEncodeError(#[from] prost::EncodeError),
#[error("Unknown key type from protobuf message: `{0}`")]
UnknownKeyType(i32),
#[error("Invalid public key")]
InvalidPublicKey,
#[error("Invalid data")]
InvalidData,
}
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum SubstreamError {
#[error("Connection closed")]
ConnectionClosed,
#[error("Connection channel clogged")]
ChannelClogged,
#[error("Connection to peer does not exist: `{0}`")]
PeerDoesNotExist(PeerId),
#[error("I/O error: `{0}`")]
IoError(ErrorKind),
#[error("yamux error: `{0}`")]
YamuxError(crate::yamux::ConnectionError, Direction),
#[error("Failed to read from substream, substream id `{0:?}`")]
ReadFailure(Option<SubstreamId>),
#[error("Failed to write to substream, substream id `{0:?}`")]
WriteFailure(Option<SubstreamId>),
#[error("Negotiation error: `{0:?}`")]
NegotiationError(#[from] NegotiationError),
}
#[derive(Debug, thiserror::Error)]
pub enum NegotiationError {
#[error("multistream-select error: `{0:?}`")]
MultistreamSelectError(#[from] crate::multistream_select::NegotiationError),
#[error("multistream-select error: `{0:?}`")]
SnowError(#[from] snow::Error),
#[error("`PeerId` missing from Noise handshake")]
PeerIdMissing,
#[error("Operation timed out")]
Timeout,
#[error("Parse error: `{0}`")]
ParseError(#[from] ParseError),
#[error("I/O error: `{0}`")]
IoError(ErrorKind),
#[error("Expected a different state")]
StateMismatch,
#[error("Peer ID mismatch: expected `{0}`, got `{1}`")]
PeerIdMismatch(PeerId, PeerId),
#[cfg(feature = "quic")]
#[error("QUIC error: `{0}`")]
Quic(#[from] QuicError),
#[cfg(feature = "websocket")]
#[error("WebSocket error: `{0}`")]
WebSocket(#[from] tokio_tungstenite::tungstenite::error::Error),
}
impl PartialEq for NegotiationError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::MultistreamSelectError(lhs), Self::MultistreamSelectError(rhs)) => lhs == rhs,
(Self::SnowError(lhs), Self::SnowError(rhs)) => lhs == rhs,
(Self::ParseError(lhs), Self::ParseError(rhs)) => lhs == rhs,
(Self::IoError(lhs), Self::IoError(rhs)) => lhs == rhs,
(Self::PeerIdMismatch(lhs, lhs_1), Self::PeerIdMismatch(rhs, rhs_1)) =>
lhs == rhs && lhs_1 == rhs_1,
#[cfg(feature = "quic")]
(Self::Quic(lhs), Self::Quic(rhs)) => lhs == rhs,
#[cfg(feature = "websocket")]
(Self::WebSocket(lhs), Self::WebSocket(rhs)) =>
core::mem::discriminant(lhs) == core::mem::discriminant(rhs),
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum NotificationError {
#[error("Peer already exists")]
PeerAlreadyExists,
#[error("Peer is in invalid state")]
InvalidState,
#[error("Notifications clogged")]
NotificationsClogged,
#[error("Notification stream closed")]
NotificationStreamClosed(PeerId),
}
#[derive(Debug, thiserror::Error)]
pub enum DialError {
#[error("Dial timed out")]
Timeout,
#[error("Address error: `{0}`")]
AddressError(#[from] AddressError),
#[error("DNS lookup error for `{0}`")]
DnsError(#[from] DnsError),
#[error("Negotiation error: `{0}`")]
NegotiationError(#[from] NegotiationError),
}
#[derive(Debug, thiserror::Error, Copy, Clone, Eq, PartialEq)]
pub enum ImmediateDialError {
#[error("`PeerId` missing from the address")]
PeerIdMissing,
#[error("Tried to dial self")]
TriedToDialSelf,
#[error("Already connected to peer")]
AlreadyConnected,
#[error("No address available for peer")]
NoAddressAvailable,
#[error("TaskClosed")]
TaskClosed,
#[error("Connection channel clogged")]
ChannelClogged,
}
#[cfg(feature = "quic")]
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum QuicError {
#[error("Invalid certificate")]
InvalidCertificate,
#[error("Failed to negotiate QUIC: `{0}`")]
ConnectionError(#[from] quinn::ConnectionError),
#[error("Failed to connect to peer: `{0}`")]
ConnectError(#[from] quinn::ConnectError),
}
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum DnsError {
#[error("DNS failed to resolve url `{0}`")]
ResolveError(String),
#[error("DNS type is different from the provided IP address")]
IpVersionMismatch,
}
impl From<MultihashGeneric<64>> for Error {
fn from(hash: MultihashGeneric<64>) -> Self {
Error::AddressError(AddressError::InvalidPeerId(hash))
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Error {
Error::IoError(error.kind())
}
}
impl From<io::Error> for SubstreamError {
fn from(error: io::Error) -> SubstreamError {
SubstreamError::IoError(error.kind())
}
}
impl From<io::Error> for DialError {
fn from(error: io::Error) -> Self {
DialError::NegotiationError(NegotiationError::IoError(error.kind()))
}
}
impl From<crate::multistream_select::NegotiationError> for Error {
fn from(error: crate::multistream_select::NegotiationError) -> Error {
Error::NegotiationError(NegotiationError::MultistreamSelectError(error))
}
}
impl From<snow::Error> for Error {
fn from(error: snow::Error) -> Self {
Error::NegotiationError(NegotiationError::SnowError(error))
}
}
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for Error {
fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self {
Error::EssentialTaskClosed
}
}
impl From<tokio::sync::oneshot::error::RecvError> for Error {
fn from(_: tokio::sync::oneshot::error::RecvError) -> Self {
Error::EssentialTaskClosed
}
}
impl From<prost::DecodeError> for Error {
fn from(error: prost::DecodeError) -> Self {
Error::ParseError(ParseError::ProstDecodeError(error))
}
}
impl From<prost::EncodeError> for Error {
fn from(error: prost::EncodeError) -> Self {
Error::ParseError(ParseError::ProstEncodeError(error))
}
}
impl From<io::Error> for NegotiationError {
fn from(error: io::Error) -> Self {
NegotiationError::IoError(error.kind())
}
}
impl From<ParseError> for Error {
fn from(error: ParseError) -> Self {
Error::ParseError(error)
}
}
impl From<MultihashGeneric<64>> for AddressError {
fn from(hash: MultihashGeneric<64>) -> Self {
AddressError::InvalidPeerId(hash)
}
}
#[cfg(feature = "quic")]
impl From<quinn::ConnectionError> for Error {
fn from(error: quinn::ConnectionError) -> Self {
match error {
quinn::ConnectionError::TimedOut => Error::Timeout,
error => Error::Quinn(error),
}
}
}
#[cfg(feature = "quic")]
impl From<quinn::ConnectionError> for DialError {
fn from(error: quinn::ConnectionError) -> Self {
match error {
quinn::ConnectionError::TimedOut => DialError::Timeout,
error => DialError::NegotiationError(NegotiationError::Quic(error.into())),
}
}
}
#[cfg(feature = "quic")]
impl From<quinn::ConnectError> for DialError {
fn from(error: quinn::ConnectError) -> Self {
DialError::NegotiationError(NegotiationError::Quic(error.into()))
}
}
impl From<ConnectionLimitsError> for Error {
fn from(error: ConnectionLimitsError) -> Self {
Error::ConnectionLimit(error)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio::sync::mpsc::{channel, Sender};
#[tokio::test]
async fn try_from_errors() {
let (tx, rx) = channel(1);
drop(rx);
async fn test(tx: Sender<()>) -> crate::Result<()> {
tx.send(()).await.map_err(From::from)
}
match test(tx).await.unwrap_err() {
Error::EssentialTaskClosed => {}
_ => panic!("invalid error"),
}
}
}