Skip to main content

primitives/errors/
mod.rs

1use serde::{de::DeserializeOwned, Deserialize, Serialize};
2use wincode::{SchemaRead, SchemaReadOwned, SchemaWrite};
3
4use crate::types::{identifiers::FaultyPeer, PeerId, PeerIndex};
5
6mod builder_error;
7
8pub use builder_error::BuilderError;
9
10/// General trait
11pub trait Error:
12    std::error::Error
13    + Clone
14    + PartialEq
15    + Eq
16    + Serialize
17    + DeserializeOwned
18    + SchemaReadOwned
19    + SchemaWrite
20    + Send
21    + Sync
22{
23}
24impl<T> Error for T where
25    T: std::error::Error
26        + Clone
27        + PartialEq
28        + Eq
29        + Serialize
30        + DeserializeOwned
31        + SchemaReadOwned
32        + SchemaWrite
33        + Send
34        + Sync
35{
36}
37
38#[derive(
39    thiserror::Error, Debug, Clone, Serialize, Deserialize, SchemaRead, SchemaWrite, PartialEq, Eq,
40)]
41#[repr(C)]
42pub enum PrimitiveError {
43    // --- Serialization Errors ---
44    #[error("Serialization Failed: {0}.")]
45    SerializationFailed(String),
46    #[error("Deserialization Failed: {0}.")]
47    DeserializationFailed(String),
48
49    // --- Verification Errors (Wrong...) ---
50    #[error("Wrong Opening: Expected {0}, got {1}.")]
51    WrongOpening(String, String),
52    #[error("Wrong Opening for {0}: Expected {1}, got {2}.")]
53    WrongOpeningFor(FaultyPeer, String, String),
54    #[error("Wrong Echo from {0}.")]
55    WrongEcho(FaultyPeer),
56    #[error("Wrong signature: {0}.")]
57    WrongSignature(String),
58    #[error("Wrong MAC for {0}: got {1:?}.")]
59    WrongMACFor(FaultyPeer, String),
60    #[error("Wrong MAC: got {0:?}.")]
61    WrongMAC(String),
62    #[error("Wrong correlation. {0}")]
63    WrongCorrelation(String),
64
65    // --- Validation Errors ---
66    #[error("Invalid Size: expected {0} elements, got {1}")]
67    InvalidSize(usize, usize),
68    #[error("Minimum Length Error: expected at least {0} elements, got {1}")]
69    MinimumLength(usize, usize),
70    #[error("Invalid Parameters: {0}")]
71    InvalidParameters(String),
72    #[error("PeerIndex out of bounds: {0} ∉ [0, {1}].")]
73    InvalidPeerIndex(PeerIndex, usize),
74    #[error("Invalid peer ID: {0}")]
75    InvalidPeerId(PeerId),
76
77    // --- Other Errors ---
78    #[error("Zero value sampled for type {0}")]
79    ZeroValueSampled(String),
80}
81
82impl From<quinn::rustls::Error> for PrimitiveError {
83    fn from(err: quinn::rustls::Error) -> Self {
84        PrimitiveError::InvalidParameters(err.to_string())
85    }
86}
87
88impl From<Vec<PrimitiveError>> for PrimitiveError {
89    /// Aggregate multiple PrimitiveErrors into one. Only WrongMACFor and WrongEcho can be
90    /// aggregated, and all errors must be of the same type.
91    fn from(errors: Vec<PrimitiveError>) -> Self {
92        if errors.is_empty() {
93            return PrimitiveError::InvalidParameters(
94                "Cannot aggregate an empty list of errors.".to_owned(),
95            );
96        };
97        if errors.len() == 1 {
98            return errors.into_iter().next().unwrap();
99        }
100
101        let (peers, receiver, invalid_errs) = errors.into_iter().fold(
102            (vec![], vec![], vec![]),
103            |(mut peers, mut receiver, mut invalid_errors), e| {
104                match e {
105                    PrimitiveError::WrongMACFor(p, r) => {
106                        peers.push(p);
107                        receiver.push(r);
108                    }
109                    PrimitiveError::WrongEcho(p) => {
110                        peers.push(p);
111                    }
112                    _ => invalid_errors.push(e.to_string()),
113                }
114                (peers, receiver, invalid_errors)
115            },
116        );
117        if !invalid_errs.is_empty() {
118            return PrimitiveError::InvalidParameters(
119                "Only WrongMACFor|WrongEcho can be aggregated. Got: ".to_owned()
120                    + &invalid_errs.join(", "),
121            );
122        }
123        match (peers, receiver.as_slice()) {
124            (peers, []) => PrimitiveError::WrongEcho(FaultyPeer::Multiple(peers)),
125            (peers, receiver) if peers.len() == receiver.len() => {
126                PrimitiveError::WrongMACFor(FaultyPeer::Multiple(peers), receiver.join(", "))
127            }
128            _ => PrimitiveError::InvalidParameters(
129                "All errors must be of the same type to be aggregated".to_owned(),
130            ),
131        }
132    }
133}
134impl PrimitiveError {
135    /// Blames another peer for this error.
136    pub fn blame(self, peer_to_blame: PeerIndex) -> PrimitiveError {
137        match self {
138            PrimitiveError::WrongMAC(receiver) => {
139                PrimitiveError::WrongMACFor(FaultyPeer::Foreign(peer_to_blame), receiver)
140            }
141            PrimitiveError::WrongMACFor(peers, receiver) => {
142                PrimitiveError::WrongMACFor(peers.push(peer_to_blame), receiver)
143            }
144            PrimitiveError::WrongOpening(expected, got) => {
145                PrimitiveError::WrongOpeningFor(FaultyPeer::Foreign(peer_to_blame), expected, got)
146            }
147            PrimitiveError::WrongOpeningFor(peers, expected, got) => {
148                PrimitiveError::WrongOpeningFor(peers.push(peer_to_blame), expected, got)
149            }
150            _ => self,
151        }
152    }
153}