1#![allow(deprecated)]
3
4use crate::crypto::KeyPair;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8#[derive(Debug, Error)]
9pub enum ConsensusError {
10 #[error("No consensus achieved: required {required} sources, got {achieved}")]
11 NoConsensus { required: usize, achieved: usize },
12
13 #[error("Invalid signature from source: {0}")]
14 InvalidSignature(String),
15
16 #[error("Insufficient readings: required at least {required}, got {actual}")]
17 InsufficientReadings { required: usize, actual: usize },
18
19 #[error("Crypto error: {0}")]
20 CryptoError(#[from] crate::crypto::CryptoError),
21}
22
23pub type Result<T> = std::result::Result<T, ConsensusError>;
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct SensorReading {
30 pub value: f64,
32
33 pub timestamp: u64,
35
36 pub source_id: String,
38
39 pub signature: Vec<u8>,
41}
42
43impl SensorReading {
44 pub fn new(value: f64, source_id: String) -> Self {
46 let timestamp = chrono::Utc::now().timestamp() as u64;
47
48 SensorReading {
49 value,
50 timestamp,
51 source_id,
52 signature: Vec::new(), }
54 }
55
56 pub fn signing_data(&self) -> Vec<u8> {
58 let mut data = Vec::new();
59 data.extend_from_slice(&self.value.to_le_bytes());
60 data.extend_from_slice(&self.timestamp.to_le_bytes());
61 data.extend_from_slice(self.source_id.as_bytes());
62 data
63 }
64
65 pub fn sign(&mut self, keypair: &KeyPair) -> Result<()> {
67 let data = self.signing_data();
68 self.signature = keypair.sign(&data)?;
69 Ok(())
70 }
71
72 pub fn verify(&self, keypair: &KeyPair) -> Result<()> {
74 let data = self.signing_data();
75 keypair
76 .verify(&data, &self.signature)
77 .map_err(|_| ConsensusError::InvalidSignature(self.source_id.clone()))?;
78 Ok(())
79 }
80}
81
82pub struct ConsensusVerifier {
87 required_sources: usize,
89
90 tolerance: f64,
92}
93
94impl ConsensusVerifier {
95 pub fn new(required_sources: usize, tolerance: f64) -> Self {
101 ConsensusVerifier {
102 required_sources,
103 tolerance,
104 }
105 }
106
107 pub fn verify_readings(&self, readings: &[SensorReading], keypairs: &[KeyPair]) -> Result<f64> {
115 if readings.len() < self.required_sources {
117 return Err(ConsensusError::InsufficientReadings {
118 required: self.required_sources,
119 actual: readings.len(),
120 });
121 }
122
123 for (reading, keypair) in readings.iter().zip(keypairs.iter()) {
125 reading.verify(keypair)?;
126 }
127
128 let median = self.calculate_median(readings);
130
131 let consensus_count = readings
133 .iter()
134 .filter(|r| (r.value - median).abs() < self.tolerance)
135 .count();
136
137 if consensus_count < self.required_sources {
139 return Err(ConsensusError::NoConsensus {
140 required: self.required_sources,
141 achieved: consensus_count,
142 });
143 }
144
145 Ok(median)
146 }
147
148 fn calculate_median(&self, readings: &[SensorReading]) -> f64 {
150 let mut values: Vec<f64> = readings.iter().map(|r| r.value).collect();
151 values.sort_by(|a, b| a.partial_cmp(b).unwrap());
152
153 let len = values.len();
154 if len.is_multiple_of(2) {
155 (values[len / 2 - 1] + values[len / 2]) / 2.0
156 } else {
157 values[len / 2]
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 fn create_signed_reading(value: f64, source_id: &str, keypair: &KeyPair) -> SensorReading {
167 let mut reading = SensorReading::new(value, source_id.to_string());
168 reading.sign(keypair).unwrap();
169 reading
170 }
171
172 #[test]
173 fn test_sensor_reading_creation() {
174 let reading = SensorReading::new(42.0, "sensor1".to_string());
175 assert_eq!(reading.value, 42.0);
176 assert_eq!(reading.source_id, "sensor1");
177 }
178
179 #[test]
180 fn test_sensor_reading_signature() {
181 let keypair = KeyPair::generate().unwrap();
182 let mut reading = SensorReading::new(42.0, "sensor1".to_string());
183
184 reading.sign(&keypair).unwrap();
185 assert!(!reading.signature.is_empty());
186
187 assert!(reading.verify(&keypair).is_ok());
189 }
190
191 #[test]
192 fn test_consensus_with_agreement() {
193 let verifier = ConsensusVerifier::new(3, 0.1);
194
195 let keypairs: Vec<KeyPair> = (0..4).map(|_| KeyPair::generate().unwrap()).collect();
196
197 let readings = vec![
198 create_signed_reading(10.0, "A", &keypairs[0]),
199 create_signed_reading(10.05, "B", &keypairs[1]),
200 create_signed_reading(10.02, "C", &keypairs[2]),
201 create_signed_reading(10.01, "D", &keypairs[3]),
202 ];
203
204 let result = verifier.verify_readings(&readings, &keypairs).unwrap();
205 assert!((result - 10.015).abs() < 0.01); }
207
208 #[test]
209 fn test_consensus_fails_with_disagreement() {
210 let verifier = ConsensusVerifier::new(3, 0.1);
211
212 let keypairs: Vec<KeyPair> = (0..3).map(|_| KeyPair::generate().unwrap()).collect();
213
214 let readings = vec![
215 create_signed_reading(10.0, "A", &keypairs[0]),
216 create_signed_reading(10.05, "B", &keypairs[1]),
217 create_signed_reading(50.0, "C", &keypairs[2]), ];
219
220 let result = verifier.verify_readings(&readings, &keypairs);
221 assert!(matches!(result, Err(ConsensusError::NoConsensus { .. })));
222 }
223
224 #[test]
225 fn test_insufficient_readings() {
226 let verifier = ConsensusVerifier::new(5, 0.1);
227
228 let keypairs: Vec<KeyPair> = (0..2).map(|_| KeyPair::generate().unwrap()).collect();
229
230 let readings = vec![
231 create_signed_reading(10.0, "A", &keypairs[0]),
232 create_signed_reading(10.0, "B", &keypairs[1]),
233 ];
234
235 let result = verifier.verify_readings(&readings, &keypairs);
236 assert!(matches!(
237 result,
238 Err(ConsensusError::InsufficientReadings { .. })
239 ));
240 }
241
242 #[test]
243 fn test_invalid_signature_detected() {
244 let verifier = ConsensusVerifier::new(2, 0.1);
245
246 let keypairs: Vec<KeyPair> = (0..2).map(|_| KeyPair::generate().unwrap()).collect();
247
248 let mut readings = vec![
249 create_signed_reading(10.0, "A", &keypairs[0]),
250 create_signed_reading(10.0, "B", &keypairs[1]),
251 ];
252
253 readings[0].signature[0] ^= 0xFF;
255
256 let result = verifier.verify_readings(&readings, &keypairs);
257 assert!(matches!(
258 result,
259 Err(ConsensusError::InvalidSignature { .. })
260 ));
261 }
262
263 #[test]
264 fn test_median_calculation_odd() {
265 let verifier = ConsensusVerifier::new(3, 0.1);
266
267 let keypairs: Vec<KeyPair> = (0..5).map(|_| KeyPair::generate().unwrap()).collect();
268
269 let readings = vec![
270 create_signed_reading(1.0, "A", &keypairs[0]),
271 create_signed_reading(2.0, "B", &keypairs[1]),
272 create_signed_reading(3.0, "C", &keypairs[2]),
273 create_signed_reading(4.0, "D", &keypairs[3]),
274 create_signed_reading(5.0, "E", &keypairs[4]),
275 ];
276
277 let median = verifier.calculate_median(&readings);
278 assert_eq!(median, 3.0);
279 }
280
281 #[test]
282 fn test_median_calculation_even() {
283 let verifier = ConsensusVerifier::new(2, 0.1);
284
285 let keypairs: Vec<KeyPair> = (0..4).map(|_| KeyPair::generate().unwrap()).collect();
286
287 let readings = vec![
288 create_signed_reading(1.0, "A", &keypairs[0]),
289 create_signed_reading(2.0, "B", &keypairs[1]),
290 create_signed_reading(3.0, "C", &keypairs[2]),
291 create_signed_reading(4.0, "D", &keypairs[3]),
292 ];
293
294 let median = verifier.calculate_median(&readings);
295 assert_eq!(median, 2.5);
296 }
297}