1use thiserror::Error;
8
9#[derive(Error, Debug)]
11pub enum QflgError {
12 #[error("Aggregation error: {0}")]
14 Aggregation(#[from] AggregationError),
15
16 #[error("Byzantine detection error: {0}")]
18 Byzantine(#[from] ByzantineError),
19
20 #[error("Privacy error: {0}")]
22 Privacy(#[from] PrivacyError),
23
24 #[error("Protocol error: {0}")]
26 Protocol(#[from] ProtocolError),
27
28 #[error("Quantum error: {0}")]
30 Quantum(#[from] QuantumError),
31
32 #[error("Serialization error: {0}")]
34 Serialization(String),
35
36 #[error("Invalid configuration: {0}")]
38 InvalidConfig(String),
39}
40
41#[derive(Error, Debug, Clone)]
43pub enum AggregationError {
44 #[error("Dimension mismatch: expected {expected}, got {actual}")]
46 DimensionMismatch { expected: usize, actual: usize },
47
48 #[error("No gradients provided for aggregation")]
50 EmptyGradients,
51
52 #[error("Invalid weight: {0} (must be non-negative)")]
54 InvalidWeight(f64),
55
56 #[error("Weights do not sum to 1.0: sum = {0}")]
58 WeightNormalization(f64),
59
60 #[error("Insufficient gradients: need at least {required}, got {actual}")]
62 InsufficientGradients { required: usize, actual: usize },
63
64 #[error("Numeric overflow during aggregation")]
66 NumericOverflow,
67}
68
69#[derive(Error, Debug, Clone)]
71pub enum ByzantineError {
72 #[error("Too many Byzantine clients: {detected} > {threshold}")]
74 TooManyByzantine { detected: usize, threshold: usize },
75
76 #[error("Invalid Byzantine tolerance: {0} (must be in (0, 0.5))")]
78 InvalidTolerance(f64),
79
80 #[error("Insufficient clients for Byzantine tolerance: need {required}, have {actual}")]
82 InsufficientClients { required: usize, actual: usize },
83
84 #[error("Detection algorithm failed: {0}")]
86 DetectionFailed(String),
87
88 #[error("Score computation error: {0}")]
90 ScoreError(String),
91}
92
93#[derive(Error, Debug, Clone)]
95pub enum PrivacyError {
96 #[error("Privacy budget exceeded: epsilon {current} > {max}")]
98 BudgetExceeded { current: f64, max: f64 },
99
100 #[error("Invalid epsilon: {0} (must be positive)")]
102 InvalidEpsilon(f64),
103
104 #[error("Invalid delta: {0} (must be in (0, 1))")]
106 InvalidDelta(f64),
107
108 #[error("Invalid sensitivity: {0} (must be positive)")]
110 InvalidSensitivity(f64),
111
112 #[error("Noise generation failed: {0}")]
114 NoiseGenerationFailed(String),
115
116 #[error("Invalid clipping threshold: {0} (must be positive)")]
118 InvalidClippingThreshold(f64),
119}
120
121#[derive(Error, Debug, Clone)]
123pub enum ProtocolError {
124 #[error("Client not registered: {0}")]
126 ClientNotRegistered(String),
127
128 #[error("Client already registered: {0}")]
130 DuplicateClient(String),
131
132 #[error("No active round")]
134 NoActiveRound,
135
136 #[error("Round {0} already in progress")]
138 RoundInProgress(u64),
139
140 #[error("Invalid state transition from {from} to {to}")]
142 InvalidStateTransition { from: String, to: String },
143
144 #[error("Round {round} timed out after {duration_ms}ms")]
146 RoundTimeout { round: u64, duration_ms: u64 },
147
148 #[error("Signature verification failed for client {0}")]
150 SignatureVerificationFailed(String),
151
152 #[error("Key exchange failed: {0}")]
154 KeyExchangeFailed(String),
155
156 #[error("Model synchronization failed: {0}")]
158 SyncFailed(String),
159}
160
161#[derive(Error, Debug, Clone)]
163pub enum QuantumError {
164 #[error("Invalid quantum state: {0}")]
166 InvalidState(String),
167
168 #[error("Entanglement verification failed: fidelity {fidelity} < threshold {threshold}")]
170 EntanglementFailed { fidelity: f64, threshold: f64 },
171
172 #[error("QRNG failed: {0}")]
174 QrngFailed(String),
175
176 #[error("Post-quantum signature error: {0}")]
178 PqSignatureError(String),
179
180 #[error("Coherence lost: {0}")]
182 CoherenceLost(String),
183}
184
185pub type Result<T> = std::result::Result<T, QflgError>;
187
188impl From<serde_json::Error> for QflgError {
189 fn from(err: serde_json::Error) -> Self {
190 QflgError::Serialization(err.to_string())
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_error_display() {
200 let err = QflgError::Aggregation(AggregationError::DimensionMismatch {
201 expected: 100,
202 actual: 50,
203 });
204 assert!(err.to_string().contains("Dimension mismatch"));
205
206 let err = QflgError::Byzantine(ByzantineError::TooManyByzantine {
207 detected: 5,
208 threshold: 3,
209 });
210 assert!(err.to_string().contains("Too many Byzantine"));
211
212 let err = QflgError::Privacy(PrivacyError::BudgetExceeded {
213 current: 2.0,
214 max: 1.0,
215 });
216 assert!(err.to_string().contains("Privacy budget exceeded"));
217 }
218
219 #[test]
220 fn test_error_from_serde() {
221 let json_err = serde_json::from_str::<i32>("invalid").unwrap_err();
222 let qflg_err: QflgError = json_err.into();
223 assert!(matches!(qflg_err, QflgError::Serialization(_)));
224 }
225
226 #[test]
227 fn test_aggregation_errors() {
228 let err = AggregationError::EmptyGradients;
229 assert_eq!(err.to_string(), "No gradients provided for aggregation");
230
231 let err = AggregationError::InvalidWeight(-0.5);
232 assert!(err.to_string().contains("-0.5"));
233 }
234
235 #[test]
236 fn test_byzantine_errors() {
237 let err = ByzantineError::InvalidTolerance(0.6);
238 assert!(err.to_string().contains("0.6"));
239
240 let err = ByzantineError::InsufficientClients {
241 required: 10,
242 actual: 5,
243 };
244 assert!(err.to_string().contains("need 10"));
245 }
246
247 #[test]
248 fn test_privacy_errors() {
249 let err = PrivacyError::InvalidEpsilon(-1.0);
250 assert!(err.to_string().contains("positive"));
251
252 let err = PrivacyError::InvalidDelta(1.5);
253 assert!(err.to_string().contains("(0, 1)"));
254 }
255
256 #[test]
257 fn test_protocol_errors() {
258 let err = ProtocolError::ClientNotRegistered("client_123".to_string());
259 assert!(err.to_string().contains("client_123"));
260
261 let err = ProtocolError::RoundTimeout {
262 round: 5,
263 duration_ms: 30000,
264 };
265 assert!(err.to_string().contains("Round 5"));
266 }
267
268 #[test]
269 fn test_quantum_errors() {
270 let err = QuantumError::EntanglementFailed {
271 fidelity: 0.85,
272 threshold: 0.95,
273 };
274 assert!(err.to_string().contains("fidelity 0.85"));
275 }
276}