sodot_mpc/
error.rs

1use std::convert::Infallible;
2
3use crate::ffi;
4
5/// Represents all possible errors that can occur when using the Sodot SDK.
6///
7/// `SodotError` provides comprehensive error handling for all operations in the SDK,
8/// including validation failures, operation failures, and encoding errors.
9/// Each error includes a descriptive message explaining what went wrong.
10///
11/// # Examples
12///
13/// ```rust
14/// use sodot_mpc::{SodotError, Ecdsa};
15///
16/// // Example of handling an error from MPC operations
17/// let ecdsa = Ecdsa::new("invalid_host".to_string());
18/// // This would typically result in an error during actual operations
19/// // The error handling would look like this:
20/// // match some_operation() {
21/// //     Ok(result) => println!("Success: {:?}", result),
22/// //     Err(error) => {
23/// //         println!("Error occurred: {}", error);
24/// //         // Error handling logic here
25/// //     }
26/// // }
27/// println!("Error handling example - see actual usage in examples/");
28/// ```
29#[derive(Debug)]
30#[must_use]
31pub struct SodotError(Repr);
32
33/// Internal representation of different error types.
34///
35/// This enum contains all the specific error variants that can occur in the SDK.
36/// Each variant includes a descriptive message providing context about the failure.
37#[derive(Debug)]
38pub(crate) enum Repr {
39    /// The provided UUID format is invalid
40    InvalidUuid(String),
41    /// The host URL format is invalid or unreachable
42    InvalidHostUrl(String),
43    /// The secret share format is invalid or corrupted
44    InvalidSecretShare(String),
45    /// The message format is invalid for the operation
46    InvalidMessage(String),
47    /// The number of parties is invalid for the protocol
48    InvalidNumberOfParties(String),
49    /// General error from the MPC protocol execution
50    ExecutorError(String),
51    /// The keygen keypair format is invalid
52    InvalidKeygenKeypair(String),
53    /// The keygen ID format is invalid
54    InvalidKeygenId(String),
55    /// The derivation path element is invalid
56    InvalidDerivationElement(String),
57    /// The extended private key format is invalid
58    InvalidXpriv(String),
59    /// The secret private key format is invalid
60    InvalidSpriv(String),
61    /// The API key is invalid or unauthorized
62    InvalidApiKey(String),
63    /// The private key format is invalid
64    InvalidPrivateKey(String),
65    /// The cryptographic tweak value is invalid
66    InvalidTweak(String),
67    /// The extended public key format is invalid
68    InvalidXpub(String),
69    /// The secret public key format is invalid
70    InvalidSpub(String),
71    /// The public key format is invalid
72    InvalidPublicKey(String),
73    /// An internal pointer error occurred
74    NullPointer(String),
75    /// An unexpected error occurred in the cryptographic operations
76    PanicError(String),
77    /// A platform-specific error occurred
78    PlatformError(String),
79    /// Internal SDK error
80    Internal(String),
81    /// Unknown executor code returned
82    UnknownExecutorError { code: i32, msg: String },
83}
84
85impl std::fmt::Display for SodotError {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match &self.0 {
88            Repr::InvalidUuid(msg) => write!(f, "Invalid UUID: {msg}"),
89            Repr::InvalidHostUrl(msg) => write!(f, "Invalid host URL: {msg}"),
90            Repr::InvalidSecretShare(msg) => write!(f, "Invalid secret share: {msg}"),
91            Repr::InvalidMessage(msg) => write!(f, "Invalid message: {msg}"),
92            Repr::InvalidNumberOfParties(msg) => write!(f, "Invalid number of parties: {msg}"),
93            Repr::ExecutorError(msg) => write!(f, "Executor error: {msg}"),
94            Repr::InvalidKeygenKeypair(msg) => write!(f, "Invalid keygen keypair: {msg}"),
95            Repr::InvalidKeygenId(msg) => write!(f, "Invalid keygen ID: {msg}"),
96            Repr::InvalidDerivationElement(msg) => write!(f, "Invalid derivation element: {msg}"),
97            Repr::InvalidXpriv(msg) => write!(f, "Invalid extended private key: {msg}"),
98            Repr::InvalidSpriv(msg) => write!(f, "Invalid secret private key: {msg}"),
99            Repr::InvalidApiKey(msg) => write!(f, "Invalid API key: {msg}"),
100            Repr::InvalidPrivateKey(msg) => write!(f, "Invalid private key: {msg}"),
101            Repr::InvalidTweak(msg) => write!(f, "Invalid tweak: {msg}"),
102            Repr::InvalidXpub(msg) => write!(f, "Invalid extended public key: {msg}"),
103            Repr::InvalidSpub(msg) => write!(f, "Invalid secret public key: {msg}"),
104            Repr::InvalidPublicKey(msg) => write!(f, "Invalid public key: {msg}"),
105            Repr::NullPointer(msg) => write!(f, "Null pointer error: {msg}"),
106            Repr::PanicError(msg) => write!(f, "Panic error: {msg}"),
107            Repr::PlatformError(msg) => write!(f, "Platform error: {msg}"),
108            Repr::Internal(msg) => write!(f, "Internal error: {msg}"),
109            Repr::UnknownExecutorError { code, msg } => write!(f, "Unknown executor error (code: {code}): {msg}"),
110        }
111    }
112}
113
114impl std::error::Error for SodotError {}
115
116impl SodotError {
117    /// Creates an internal SDK error.
118    ///
119    /// Used for unexpected errors that occur during SDK operations.
120    pub(crate) fn internal(message: String) -> Self {
121        SodotError(Repr::Internal(message))
122    }
123
124    /// Returns a reference to the error representation.
125    #[cfg(test)]
126    pub(crate) fn repr(&self) -> &Repr {
127        &self.0
128    }
129}
130
131impl From<Infallible> for SodotError {
132    fn from(inf: Infallible) -> Self {
133        match inf {}
134    }
135}
136
137impl From<ffi::ResultFFI> for Result<(), SodotError> {
138    /// Maps error codes to structured SodotError variants.
139    fn from(value: ffi::ResultFFI) -> Self {
140        if value.error_code == ffi::SODOT_OK {
141            return Ok(());
142        }
143        let message = value.err_str.as_str();
144        let repr = match value.error_code {
145            ffi::SODOT_INVALID_UUID => Repr::InvalidUuid(message.to_string()),
146            ffi::SODOT_INVALID_HOST_URL => Repr::InvalidHostUrl(message.to_string()),
147            ffi::SODOT_INVALID_SECRET_SHARE_ERR => Repr::InvalidSecretShare(message.to_string()),
148            ffi::SODOT_INVALID_MESSAGE_ERR => Repr::InvalidMessage(message.to_string()),
149            ffi::SODOT_INVALID_NUMBER_OF_PARTIES_ERR => Repr::InvalidNumberOfParties(message.to_string()),
150            ffi::SODOT_EXECUTOR_ERROR => Repr::ExecutorError(message.to_string()),
151            ffi::SODOT_INVALID_KEYGEN_INIT_KEYPAIR_ERR => Repr::InvalidKeygenKeypair(message.to_string()),
152            ffi::SODOT_INVALID_KEYGEN_ID_ERR => Repr::InvalidKeygenId(message.to_string()),
153            ffi::SODOT_INVALID_DERIVATION_ELEM_ERR => Repr::InvalidDerivationElement(message.to_string()),
154            ffi::SODOT_INVALID_XPRIV_ERR => Repr::InvalidXpriv(message.to_string()),
155            ffi::SODOT_INVALID_SPRIV_ERR => Repr::InvalidSpriv(message.to_string()),
156            ffi::SODOT_INVALID_API_KEY => Repr::InvalidApiKey(message.to_string()),
157            ffi::SODOT_INVALID_PRIVATE_KEY => Repr::InvalidPrivateKey(message.to_string()),
158            ffi::SODOT_INVALID_TWEAK => Repr::InvalidTweak(message.to_string()),
159            ffi::SODOT_INVALID_XPUB_ERR => Repr::InvalidXpub(message.to_string()),
160            ffi::SODOT_INVALID_SPUB_ERR => Repr::InvalidSpub(message.to_string()),
161            ffi::SODOT_INVALID_PUBLIC_KEY => Repr::InvalidPublicKey(message.to_string()),
162            ffi::SODOT_NULL_POINTER => Repr::NullPointer(message.to_string()),
163            ffi::SODOT_PANIC_ERR => Repr::PanicError(message.to_string()),
164            ffi::SODOT_PLATFORM_ERR => Repr::PlatformError(message.to_string()),
165            _ => Repr::UnknownExecutorError { code: value.error_code, msg: message.to_string() },
166        };
167        Err(SodotError(repr))
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174    use crate::ffi::{HeapString, ResultFFI};
175    use rstest::rstest;
176
177    #[rstest]
178    #[case(-1, "uuid")]
179    #[case(-8, "executor")]
180    #[case(-253, "null")]
181    #[case(-254, "panic")]
182    fn test_ffi_error_mapping(#[case] code: i32, #[case] expected: &str) {
183        let result = ResultFFI { error_code: code, err_str: HeapString::NULL };
184        let error = Result::from(result).unwrap_err();
185        let msg = format!("{error}").to_lowercase();
186        assert!(msg.contains(expected), "Error code {code} should contain '{expected}': {msg}");
187    }
188
189    #[rstest]
190    fn test_handle_success() {
191        let result = ResultFFI { error_code: 0, err_str: HeapString::NULL };
192        Result::from(result).unwrap();
193    }
194
195    #[rstest]
196    fn test_error_constructors() {
197        let internal_err = SodotError::internal("internal".to_string());
198        assert!(format!("{internal_err}").to_lowercase().contains("internal"));
199    }
200}