auths_core/witness/
error.rs1use auths_verifier::keri::{Prefix, Said};
7use serde::{Deserialize, Serialize};
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct DuplicityEvidence {
24 pub prefix: Prefix,
26 pub sequence: u64,
28 pub event_a_said: Said,
30 pub event_b_said: Said,
32 pub witness_reports: Vec<WitnessReport>,
34}
35
36impl fmt::Display for DuplicityEvidence {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(
39 f,
40 "Duplicity detected for {} at seq {}: {} vs {}",
41 self.prefix, self.sequence, self.event_a_said, self.event_b_said
42 )
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48pub struct WitnessReport {
49 pub witness_id: String,
51 pub observed_said: Said,
53 pub observed_at: Option<String>,
55}
56
57#[derive(Debug, thiserror::Error)]
62pub enum WitnessError {
63 #[error("network error: {0}")]
65 Network(String),
66
67 #[error("duplicity detected: {0}")]
71 Duplicity(DuplicityEvidence),
72
73 #[error("event rejected: {reason}")]
78 Rejected {
79 reason: String,
81 },
82
83 #[error("timeout after {0}ms")]
85 Timeout(u64),
86
87 #[error("invalid receipt signature from witness {witness_id}")]
89 InvalidSignature {
90 witness_id: String,
92 },
93
94 #[error("insufficient receipts: got {got}, need {required}")]
96 InsufficientReceipts {
97 got: usize,
99 required: usize,
101 },
102
103 #[error("receipt SAID mismatch: expected {expected}, got {got}")]
105 SaidMismatch {
106 expected: Said,
108 got: Said,
110 },
111
112 #[error("storage error: {0}")]
114 Storage(String),
115
116 #[error("serialization error: {0}")]
118 Serialization(String),
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn duplicity_evidence_display() {
127 let evidence = DuplicityEvidence {
128 prefix: Prefix::new_unchecked("EPrefix123".into()),
129 sequence: 5,
130 event_a_said: Said::new_unchecked("ESAID_A".into()),
131 event_b_said: Said::new_unchecked("ESAID_B".into()),
132 witness_reports: vec![],
133 };
134 let display = format!("{}", evidence);
135 assert!(display.contains("EPrefix123"));
136 assert!(display.contains("5"));
137 assert!(display.contains("ESAID_A"));
138 assert!(display.contains("ESAID_B"));
139 }
140
141 #[test]
142 fn witness_error_variants() {
143 let network_err = WitnessError::Network("connection refused".into());
144 assert!(format!("{}", network_err).contains("network error"));
145
146 let timeout_err = WitnessError::Timeout(5000);
147 assert!(format!("{}", timeout_err).contains("5000ms"));
148
149 let rejected_err = WitnessError::Rejected {
150 reason: "invalid format".into(),
151 };
152 assert!(format!("{}", rejected_err).contains("invalid format"));
153 }
154
155 #[test]
156 fn duplicity_evidence_serialization() {
157 let evidence = DuplicityEvidence {
158 prefix: Prefix::new_unchecked("EPrefix123".into()),
159 sequence: 5,
160 event_a_said: Said::new_unchecked("ESAID_A".into()),
161 event_b_said: Said::new_unchecked("ESAID_B".into()),
162 witness_reports: vec![WitnessReport {
163 witness_id: "did:key:witness1".into(),
164 observed_said: Said::new_unchecked("ESAID_A".into()),
165 observed_at: Some("2024-01-01T00:00:00Z".into()),
166 }],
167 };
168
169 let json = serde_json::to_string(&evidence).unwrap();
170 let parsed: DuplicityEvidence = serde_json::from_str(&json).unwrap();
171 assert_eq!(evidence, parsed);
172 }
173}