use std::borrow::Cow;
use std::io;
use std::net::SocketAddr;
use std::time::Duration;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum P2PError {
#[error("Network error: {0}")]
Network(#[from] NetworkError),
#[error("DHT error: {0}")]
Dht(#[from] DhtError),
#[error("Identity error: {0}")]
Identity(#[from] IdentityError),
#[error("Cryptography error: {0}")]
Crypto(#[from] CryptoError),
#[error("State error: {0}")]
State(#[from] StateError),
#[error("Transport error: {0}")]
Transport(#[from] TransportError),
#[error("Security error: {0}")]
Security(#[from] SecurityError),
#[error("Bootstrap error: {0}")]
Bootstrap(#[from] BootstrapError),
#[error("IO error: {0}")]
Io(#[from] io::Error),
#[error("Serialization error: {0}")]
Serialization(Cow<'static, str>),
#[error("Validation error: {0}")]
Validation(Cow<'static, str>),
#[error("Operation timed out after {0:?}")]
Timeout(Duration),
#[error("Resource exhausted: {0}")]
ResourceExhausted(Cow<'static, str>),
#[error("Internal error: {0}")]
Internal(Cow<'static, str>),
#[error("Encoding error: {0}")]
Encoding(Cow<'static, str>),
#[error("Record too large: {0} bytes (max 512)")]
RecordTooLarge(usize),
#[error("Time error")]
TimeError,
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("WebRTC error: {0}")]
WebRtcError(String),
#[error("Trust error: {0}")]
Trust(Cow<'static, str>),
}
impl From<crate::identity::peer_id::PeerIdParseError> for P2PError {
fn from(err: crate::identity::peer_id::PeerIdParseError) -> Self {
P2PError::Identity(IdentityError::InvalidPeerId(Cow::Owned(err.to_string())))
}
}
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("Connection failed to {addr}: {reason}")]
ConnectionFailed {
addr: SocketAddr,
reason: Cow<'static, str>,
},
#[error("Connection closed unexpectedly for peer: {peer_id}")]
ConnectionClosed { peer_id: Cow<'static, str> },
#[error("Invalid network address: {0}")]
InvalidAddress(Cow<'static, str>),
#[error("Peer not found: {0}")]
PeerNotFound(Cow<'static, str>),
#[error("Peer blocked (trust below threshold): {0}")]
PeerBlocked(crate::PeerId),
#[error("Peer disconnected - peer: {peer}, reason: {reason}")]
PeerDisconnected { peer: crate::PeerId, reason: String },
#[error("Network timeout")]
Timeout,
#[error("Too many connections")]
TooManyConnections,
#[error("Protocol error: {0}")]
ProtocolError(Cow<'static, str>),
#[error("Operation cancelled (peer blocked): {0}")]
OperationCancelled(crate::PeerId),
#[error("Bind error: {0}")]
BindError(Cow<'static, str>),
}
#[derive(Debug, Error)]
pub enum DhtError {
#[error("Key not found: {0}")]
KeyNotFound(Cow<'static, str>),
#[error("Store operation failed: {0}")]
StoreFailed(Cow<'static, str>),
#[error("Invalid key format: {0}")]
InvalidKey(Cow<'static, str>),
#[error("Routing table full")]
RoutingTableFull,
#[error("No suitable peers found")]
NoPeersFound,
#[error("Query timeout")]
QueryTimeout,
#[error("Routing error: {0}")]
RoutingError(Cow<'static, str>),
#[error("Operation failed: {0}")]
OperationFailed(Cow<'static, str>),
#[error("Insufficient peers: {0}")]
InsufficientPeers(Cow<'static, str>),
}
#[derive(Debug, Error)]
pub enum IdentityError {
#[error("Invalid three-word address: {0}")]
InvalidThreeWordAddress(Cow<'static, str>),
#[error("Identity not found: {0}")]
IdentityNotFound(Cow<'static, str>),
#[error("Identity already exists: {0}")]
IdentityExists(Cow<'static, str>),
#[error("Invalid signature")]
InvalidSignature,
#[error("Invalid canonical bytes")]
InvalidCanonicalBytes,
#[error("Membership conflict")]
MembershipConflict,
#[error("Missing group key")]
MissingGroupKey,
#[error("Website root update refused")]
WebsiteRootUpdateRefused,
#[error("Key derivation failed: {0}")]
KeyDerivationFailed(Cow<'static, str>),
#[error("Permission denied")]
PermissionDenied,
#[error("Identity mismatch: expected {expected} but peer authenticated as {actual}")]
IdentityMismatch {
expected: Cow<'static, str>,
actual: Cow<'static, str>,
},
#[error("Invalid peer ID: {0}")]
InvalidPeerId(Cow<'static, str>),
#[error("Invalid format: {0}")]
InvalidFormat(Cow<'static, str>),
#[error("System time error: {0}")]
SystemTime(Cow<'static, str>),
#[error("Not found: {0}")]
NotFound(Cow<'static, str>),
#[error("Verification failed: {0}")]
VerificationFailed(Cow<'static, str>),
#[error("Insufficient entropy")]
InsufficientEntropy,
#[error("Access denied: {0}")]
AccessDenied(Cow<'static, str>),
}
#[derive(Debug, Error)]
pub enum CryptoError {
#[error("Encryption failed: {0}")]
EncryptionFailed(Cow<'static, str>),
#[error("Decryption failed: {0}")]
DecryptionFailed(Cow<'static, str>),
#[error("Invalid key length: expected {expected}, got {actual}")]
InvalidKeyLength { expected: usize, actual: usize },
#[error("Signature verification failed")]
SignatureVerificationFailed,
#[error("Key generation failed: {0}")]
KeyGenerationFailed(Cow<'static, str>),
#[error("Invalid public key")]
InvalidPublicKey,
#[error("Invalid private key")]
InvalidPrivateKey,
#[error("HKDF expansion failed: {0}")]
HkdfError(Cow<'static, str>),
}
#[derive(Debug, Error)]
pub enum StateError {
#[error("Database error: {0}")]
Database(Cow<'static, str>),
#[error("Disk full")]
DiskFull,
#[error("Corrupt data: {0}")]
CorruptData(Cow<'static, str>),
#[error("Storage path not found: {0}")]
PathNotFound(Cow<'static, str>),
#[error("Permission denied: {0}")]
PermissionDenied(Cow<'static, str>),
#[error("Lock acquisition failed")]
LockFailed,
#[error("Lock poisoned: {0}")]
LockPoisoned(Cow<'static, str>),
#[error("File not found: {0}")]
FileNotFound(Cow<'static, str>),
#[error("Corruption detected: {0}")]
CorruptionDetected(Cow<'static, str>),
}
#[derive(Debug, Error)]
pub enum TransportError {
#[error("QUIC error: {0}")]
Quic(Cow<'static, str>),
#[error("TCP error: {0}")]
Tcp(Cow<'static, str>),
#[error("Invalid transport configuration: {0}")]
InvalidConfig(Cow<'static, str>),
#[error("Transport not supported: {0}")]
NotSupported(Cow<'static, str>),
#[error("Stream error: {0}")]
StreamError(Cow<'static, str>),
#[error("Certificate error: {0}")]
CertificateError(Cow<'static, str>),
#[error("Setup failed: {0}")]
SetupFailed(Cow<'static, str>),
#[error("Connection failed to {addr}: {reason}")]
ConnectionFailed {
addr: SocketAddr,
reason: Cow<'static, str>,
},
#[error("Bind error: {0}")]
BindError(Cow<'static, str>),
#[error("Accept failed: {0}")]
AcceptFailed(Cow<'static, str>),
#[error("Not listening")]
NotListening,
#[error("Not initialized")]
NotInitialized,
}
#[derive(Debug, Error)]
pub enum SecurityError {
#[error("Authentication failed")]
AuthenticationFailed,
#[error("Authorization denied")]
AuthorizationDenied,
#[error("Invalid credentials")]
InvalidCredentials,
#[error("Certificate error: {0}")]
CertificateError(Cow<'static, str>),
#[error("Encryption failed: {0}")]
EncryptionFailed(Cow<'static, str>),
#[error("Decryption failed: {0}")]
DecryptionFailed(Cow<'static, str>),
#[error("Invalid key: {0}")]
InvalidKey(Cow<'static, str>),
#[error("Signature verification failed: {0}")]
SignatureVerificationFailed(Cow<'static, str>),
#[error("Key generation failed: {0}")]
KeyGenerationFailed(Cow<'static, str>),
#[error("Authorization failed: {0}")]
AuthorizationFailed(Cow<'static, str>),
}
#[derive(Debug, Error)]
pub enum BootstrapError {
#[error("No bootstrap nodes available")]
NoBootstrapNodes,
#[error("Bootstrap failed: {0}")]
BootstrapFailed(Cow<'static, str>),
#[error("Invalid bootstrap node: {0}")]
InvalidBootstrapNode(Cow<'static, str>),
#[error("Bootstrap timeout")]
BootstrapTimeout,
#[error("Cache error: {0}")]
CacheError(Cow<'static, str>),
#[error("Invalid data: {0}")]
InvalidData(Cow<'static, str>),
#[error("Rate limited: {0}")]
RateLimited(Cow<'static, str>),
}
#[derive(Debug, Error, Clone)]
pub enum GeoRejectionError {
#[error("Peer from blocked region: {0}")]
BlockedRegion(String),
#[error("Geographic diversity violation in region {region} (ratio: {current_ratio:.1}%)")]
DiversityViolation { region: String, current_ratio: f64 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GeoEnforcementMode {
#[default]
Strict,
}
#[derive(Debug, Clone)]
pub struct GeographicConfig {
pub max_single_region_ratio: f64,
pub blocked_regions: Vec<String>,
pub enforcement_mode: GeoEnforcementMode,
}
impl Default for GeographicConfig {
fn default() -> Self {
Self {
max_single_region_ratio: 0.4,
blocked_regions: Vec::new(),
enforcement_mode: GeoEnforcementMode::Strict,
}
}
}
pub type P2pResult<T> = Result<T, P2PError>;
impl P2PError {
pub fn connection_failed(addr: SocketAddr, reason: impl Into<String>) -> Self {
P2PError::Network(NetworkError::ConnectionFailed {
addr,
reason: reason.into().into(),
})
}
pub fn timeout(duration: Duration) -> Self {
P2PError::Timeout(duration)
}
pub fn validation(msg: impl Into<Cow<'static, str>>) -> Self {
P2PError::Validation(msg.into())
}
pub fn internal(msg: impl Into<Cow<'static, str>>) -> Self {
P2PError::Internal(msg.into())
}
}
impl P2PError {
pub fn log(&self) {
use tracing::{error, warn};
match self {
P2PError::Network(NetworkError::Timeout) | P2PError::Timeout(_) => warn!("{}", self),
P2PError::Validation(_) => warn!("{}", self),
_ => error!("{}", self),
}
}
pub fn log_with_context(&self, context: &str) {
use tracing::error;
error!("{}: {}", context, self);
}
}
impl From<serde_json::Error> for P2PError {
fn from(err: serde_json::Error) -> Self {
P2PError::Serialization(err.to_string().into())
}
}
impl From<postcard::Error> for P2PError {
fn from(err: postcard::Error) -> Self {
P2PError::Serialization(err.to_string().into())
}
}
impl From<std::net::AddrParseError> for P2PError {
fn from(err: std::net::AddrParseError) -> Self {
P2PError::Network(NetworkError::InvalidAddress(err.to_string().into()))
}
}
impl From<tokio::time::error::Elapsed> for P2PError {
fn from(_: tokio::time::error::Elapsed) -> Self {
P2PError::Network(NetworkError::Timeout)
}
}
impl From<crate::adaptive::AdaptiveNetworkError> for P2PError {
fn from(err: crate::adaptive::AdaptiveNetworkError) -> Self {
use crate::adaptive::AdaptiveNetworkError;
match err {
AdaptiveNetworkError::Network(io_err) => P2PError::Io(io_err),
AdaptiveNetworkError::Serialization(ser_err) => {
P2PError::Serialization(ser_err.to_string().into())
}
AdaptiveNetworkError::Routing(msg) => {
P2PError::Internal(format!("Routing error: {msg}").into())
}
AdaptiveNetworkError::Trust(msg) => {
P2PError::Internal(format!("Trust error: {msg}").into())
}
AdaptiveNetworkError::Learning(msg) => {
P2PError::Internal(format!("Learning error: {msg}").into())
}
AdaptiveNetworkError::Gossip(msg) => {
P2PError::Internal(format!("Gossip error: {msg}").into())
}
AdaptiveNetworkError::Other(msg) => P2PError::Internal(msg.into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err =
P2PError::connection_failed("127.0.0.1:8080".parse().unwrap(), "Connection refused");
assert_eq!(
err.to_string(),
"Network error: Connection failed to 127.0.0.1:8080: Connection refused"
);
}
#[test]
fn test_timeout_error() {
let err = P2PError::timeout(Duration::from_secs(30));
assert_eq!(err.to_string(), "Operation timed out after 30s");
}
#[test]
fn test_crypto_error() {
let err = P2PError::Crypto(CryptoError::InvalidKeyLength {
expected: 32,
actual: 16,
});
assert_eq!(
err.to_string(),
"Cryptography error: Invalid key length: expected 32, got 16"
);
}
}