arcium-primitives 0.4.2

Arcium primitives
Documentation
use serde::{Deserialize, Serialize};
use wincode::{SchemaRead, SchemaWrite};

use crate::types::{identifiers::FaultyPeer, PeerId, PeerIndex};

#[derive(
    thiserror::Error, Debug, Clone, Serialize, Deserialize, SchemaRead, SchemaWrite, PartialEq, Eq,
)]
#[repr(C)]
pub enum PrimitiveError {
    // --- Serialization Errors ---
    #[error("Serialization Failed: {0}.")]
    SerializationFailed(String),
    #[error("Deserialization Failed: {0}.")]
    DeserializationFailed(String),

    // --- Verification Errors (Wrong...) ---
    #[error("Wrong Opening: Expected {0}, got {1}.")]
    WrongOpening(String, String),
    #[error("Wrong Opening for {0}: Expected {1}, got {2}.")]
    WrongOpeningFor(FaultyPeer, String, String),
    #[error("Wrong Echo from {0}.")]
    WrongEcho(FaultyPeer),
    #[error("Wrong signature: {0}.")]
    WrongSignature(String),
    #[error("Wrong MAC for {0}: got {1:?}.")]
    WrongMACFor(FaultyPeer, String),
    #[error("Wrong MAC: got {0:?}.")]
    WrongMAC(String),
    #[error("Wrong correlation. {0}")]
    WrongCorrelation(String),

    // --- Validation Errors ---
    #[error("Invalid Size: expected {0} elements, got {1}")]
    InvalidSize(usize, usize),
    #[error("Minimum Length Error: expected at least {0} elements, got {1}")]
    MinimumLength(usize, usize),
    #[error("Invalid Parameters: {0}")]
    InvalidParameters(String),
    #[error("PeerIndex out of bounds: {0} ∉ [0, {1}].")]
    InvalidPeerIndex(PeerIndex, usize),
    #[error("Invalid peer ID: {0}")]
    InvalidPeerId(PeerId),

    // --- Other Errors ---
    #[error("Zero value sampled for type {0}")]
    ZeroValueSampled(String),
}

impl From<quinn::rustls::Error> for PrimitiveError {
    fn from(err: quinn::rustls::Error) -> Self {
        PrimitiveError::InvalidParameters(err.to_string())
    }
}

impl From<Vec<PrimitiveError>> for PrimitiveError {
    /// Aggregate multiple PrimitiveErrors into one. Only WrongMACFor and WrongEcho can be
    /// aggregated, and all errors must be of the same type.
    fn from(errors: Vec<PrimitiveError>) -> Self {
        if errors.is_empty() {
            return PrimitiveError::InvalidParameters(
                "Cannot aggregate an empty list of errors.".to_owned(),
            );
        };
        if errors.len() == 1 {
            return errors.into_iter().next().unwrap();
        }

        let (peers, receiver, invalid_errs) = errors.into_iter().fold(
            (vec![], vec![], vec![]),
            |(mut peers, mut receiver, mut invalid_errors), e| {
                match e {
                    PrimitiveError::WrongMACFor(p, r) => {
                        peers.push(p);
                        receiver.push(r);
                    }
                    PrimitiveError::WrongEcho(p) => {
                        peers.push(p);
                    }
                    _ => invalid_errors.push(e.to_string()),
                }
                (peers, receiver, invalid_errors)
            },
        );
        if !invalid_errs.is_empty() {
            return PrimitiveError::InvalidParameters(
                "Only WrongMACFor|WrongEcho can be aggregated. Got: ".to_owned()
                    + &invalid_errs.join(", "),
            );
        }
        match (peers, receiver.as_slice()) {
            (peers, []) => PrimitiveError::WrongEcho(FaultyPeer::Multiple(peers)),
            (peers, receiver) if peers.len() == receiver.len() => {
                PrimitiveError::WrongMACFor(FaultyPeer::Multiple(peers), receiver.join(", "))
            }
            _ => PrimitiveError::InvalidParameters(
                "All errors must be of the same type to be aggregated".to_owned(),
            ),
        }
    }
}
impl PrimitiveError {
    /// Blames another peer for this error.
    pub fn blame(self, peer_to_blame: PeerIndex) -> PrimitiveError {
        match self {
            PrimitiveError::WrongMAC(receiver) => {
                PrimitiveError::WrongMACFor(FaultyPeer::Foreign(peer_to_blame), receiver)
            }
            PrimitiveError::WrongMACFor(peers, receiver) => {
                PrimitiveError::WrongMACFor(peers.push(peer_to_blame), receiver)
            }
            PrimitiveError::WrongOpening(expected, got) => {
                PrimitiveError::WrongOpeningFor(FaultyPeer::Foreign(peer_to_blame), expected, got)
            }
            PrimitiveError::WrongOpeningFor(peers, expected, got) => {
                PrimitiveError::WrongOpeningFor(peers.push(peer_to_blame), expected, got)
            }
            _ => self,
        }
    }
}