Skip to main content

confium_tc_core/
error.rs

1//! Error type for the threshold-cryptography crate.
2//!
3//! Mirrors the snafu-based pattern used by `confium-core`. FFI entry
4//! points in [`crate::ffi`] map these into the numeric `u32` return
5//! codes the C ABI expects.
6
7use snafu::Backtrace;
8use snafu::Snafu;
9
10use crate::registry::TcSchemeKind;
11
12#[derive(Snafu, Debug)]
13#[snafu(visibility(pub))]
14pub enum Error {
15    #[snafu(display("NULL pointer on parameter '{}'", param))]
16    NullPointer {
17        param: &'static str,
18        backtrace: Backtrace,
19    },
20    #[snafu(display("Invalid UTF-8"))]
21    InvalidUTF8 {
22        backtrace: Backtrace,
23        source: std::str::Utf8Error,
24    },
25    #[snafu(display("Interior NUL byte in C string"))]
26    NulByte {
27        backtrace: Backtrace,
28        source: std::ffi::NulError,
29    },
30
31    #[snafu(display("Party index {} out of range (party count {})", idx, count))]
32    PartyIndexOutOfRange {
33        idx: usize,
34        count: usize,
35        backtrace: Backtrace,
36    },
37    #[snafu(display("Party list is empty"))]
38    EmptyPartyList { backtrace: Backtrace },
39    #[snafu(display("Threshold {} is below the minimum of 1", threshold))]
40    ThresholdTooSmall {
41        threshold: u32,
42        backtrace: Backtrace,
43    },
44    #[snafu(display("Threshold {} exceeds party count {}", threshold, party_count))]
45    ThresholdTooLarge {
46        threshold: u32,
47        party_count: usize,
48        backtrace: Backtrace,
49    },
50    #[snafu(display("Duplicate party id '{}'", id))]
51    DuplicatePartyId { id: String, backtrace: Backtrace },
52    #[snafu(display("this_party_idx {} out of range (party count {})", idx, party_count))]
53    ThisPartyIdxOutOfRange {
54        idx: usize,
55        party_count: usize,
56        backtrace: Backtrace,
57    },
58
59    #[snafu(display("Unknown threshold scheme '{}'", name))]
60    SchemeNotFound { name: String, backtrace: Backtrace },
61    #[snafu(display("Session is not complete"))]
62    SessionNotComplete { backtrace: Backtrace },
63    #[snafu(display("Session is already complete"))]
64    SessionAlreadyComplete { backtrace: Backtrace },
65    #[snafu(display("Round counter overflowed at round {}", round))]
66    RoundOverflow { round: u8, backtrace: Backtrace },
67    #[snafu(display("Session is not a DKG session (kind {:?})", kind))]
68    NotADkgSession {
69        kind: TcSchemeKind,
70        backtrace: Backtrace,
71    },
72
73    #[snafu(display("Share scheme mismatch: expected '{}', got '{}'", expected, actual))]
74    ShareSchemeMismatch {
75        expected: String,
76        actual: String,
77        backtrace: Backtrace,
78    },
79    #[snafu(display("Share bytes truncated: need {} bytes, have {}", needed, have))]
80    ShareTruncated {
81        needed: usize,
82        have: usize,
83        backtrace: Backtrace,
84    },
85    #[snafu(display("Share scheme name is not valid UTF-8"))]
86    ShareInvalidScheme { backtrace: Backtrace },
87
88    #[snafu(display("Insufficient buffer"))]
89    InsufficientBuffer { backtrace: Backtrace },
90    #[snafu(display("Scheme plugin returned error code {}", code))]
91    SchemeInternalError { code: u32, backtrace: Backtrace },
92}
93
94impl Error {
95    /// Stable numeric code for this error, returned through the FFI.
96    pub fn code(&self) -> u32 {
97        error_code(self)
98    }
99}
100
101/// Numeric error codes for the `cfm_tc_*` ABI. These are deliberately
102/// disjoint from the core engine's codes (which start at 1) so a
103/// caller can disambiguate the source — TC codes begin at 0x1000.
104#[allow(non_camel_case_types)]
105#[repr(u32)]
106pub enum ErrorCode {
107    UNKNOWN = 0x1000,
108    NULL_POINTER = 0x1001,
109    INVALID_UTF8 = 0x1002,
110    NUL_BYTE = 0x1003,
111
112    PARTY_INDEX_OUT_OF_RANGE = 0x1010,
113    EMPTY_PARTY_LIST = 0x1011,
114    THRESHOLD_TOO_SMALL = 0x1012,
115    THRESHOLD_TOO_LARGE = 0x1013,
116    DUPLICATE_PARTY_ID = 0x1014,
117    THIS_PARTY_IDX_OUT_OF_RANGE = 0x1015,
118
119    SCHEME_NOT_FOUND = 0x1020,
120    SESSION_NOT_COMPLETE = 0x1021,
121    SESSION_ALREADY_COMPLETE = 0x1022,
122    ROUND_OVERFLOW = 0x1023,
123    NOT_A_DKG_SESSION = 0x1024,
124
125    SHARE_SCHEME_MISMATCH = 0x1030,
126    SHARE_TRUNCATED = 0x1031,
127    SHARE_INVALID_SCHEME = 0x1032,
128
129    INSUFFICIENT_BUFFER = 0x1040,
130    SCHEME_INTERNAL_ERROR = 0x1041,
131}
132
133fn error_code(error: &Error) -> u32 {
134    match error {
135        Error::NullPointer { .. } => ErrorCode::NULL_POINTER.into(),
136        Error::InvalidUTF8 { .. } => ErrorCode::INVALID_UTF8.into(),
137        Error::NulByte { .. } => ErrorCode::NUL_BYTE.into(),
138
139        Error::PartyIndexOutOfRange { .. } => ErrorCode::PARTY_INDEX_OUT_OF_RANGE.into(),
140        Error::EmptyPartyList { .. } => ErrorCode::EMPTY_PARTY_LIST.into(),
141        Error::ThresholdTooSmall { .. } => ErrorCode::THRESHOLD_TOO_SMALL.into(),
142        Error::ThresholdTooLarge { .. } => ErrorCode::THRESHOLD_TOO_LARGE.into(),
143        Error::DuplicatePartyId { .. } => ErrorCode::DUPLICATE_PARTY_ID.into(),
144        Error::ThisPartyIdxOutOfRange { .. } => ErrorCode::THIS_PARTY_IDX_OUT_OF_RANGE.into(),
145
146        Error::SchemeNotFound { .. } => ErrorCode::SCHEME_NOT_FOUND.into(),
147        Error::SessionNotComplete { .. } => ErrorCode::SESSION_NOT_COMPLETE.into(),
148        Error::SessionAlreadyComplete { .. } => ErrorCode::SESSION_ALREADY_COMPLETE.into(),
149        Error::RoundOverflow { .. } => ErrorCode::ROUND_OVERFLOW.into(),
150        Error::NotADkgSession { .. } => ErrorCode::NOT_A_DKG_SESSION.into(),
151
152        Error::ShareSchemeMismatch { .. } => ErrorCode::SHARE_SCHEME_MISMATCH.into(),
153        Error::ShareTruncated { .. } => ErrorCode::SHARE_TRUNCATED.into(),
154        Error::ShareInvalidScheme { .. } => ErrorCode::SHARE_INVALID_SCHEME.into(),
155
156        Error::InsufficientBuffer { .. } => ErrorCode::INSUFFICIENT_BUFFER.into(),
157        Error::SchemeInternalError { .. } => ErrorCode::SCHEME_INTERNAL_ERROR.into(),
158    }
159}
160
161impl From<ErrorCode> for u32 {
162    #[inline]
163    fn from(code: ErrorCode) -> u32 {
164        code as u32
165    }
166}
167
168impl From<Error> for u32 {
169    #[inline]
170    fn from(err: Error) -> u32 {
171        error_code(&err)
172    }
173}
174
175pub type Result<T> = std::result::Result<T, Error>;
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180
181    #[test]
182    fn error_code_is_disjoint_from_core() {
183        // Core codes start at 1; TC codes start at 0x1000.
184        let err = SchemeNotFoundSnafu {
185            name: "x".to_string(),
186        }
187        .build();
188        assert!(err.code() >= 0x1000);
189    }
190
191    #[test]
192    fn null_pointer_code() {
193        let err = NullPointerSnafu { param: "x" }.build();
194        assert_eq!(err.code(), ErrorCode::NULL_POINTER as u32);
195    }
196}