1use crate::crypto::{CryptoError, KeyStore, Result, SoftwareKeyStore};
53use crate::proof::Action;
54use crate::watchdog::{Watchdog, WatchdogError};
55use parking_lot::RwLock;
56use serde::{Deserialize, Serialize};
57use sha2::{Digest, Sha256};
58use std::collections::HashMap;
59use std::sync::Arc;
60use std::time::{SystemTime, UNIX_EPOCH};
61
62#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
68pub struct MemberId(pub String);
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct Vote {
73 pub member_id: MemberId,
75
76 pub decision: VoteDecision,
78
79 pub signature: Vec<u8>,
81
82 pub timestamp: u64,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub enum VoteDecision {
89 Approve,
91 Deny,
93 HardReset,
95 Abstain,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ConsensusResult {
102 pub decision: VoteDecision,
104
105 pub vote_count: usize,
107
108 pub quorum: usize,
110
111 pub votes: Vec<Vote>,
113
114 pub threshold_signature: ThresholdSignature,
116
117 pub action_hash: [u8; 32],
119
120 pub timestamp: u64,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ThresholdSignature {
127 pub combined_signature: Vec<u8>,
129
130 pub signer_pubkeys: Vec<Vec<u8>>,
132
133 pub count: usize,
135}
136
137pub struct CouncilMember {
139 pub id: MemberId,
141
142 watchdog: Watchdog,
144
145 keystore: SoftwareKeyStore,
147
148 pub active: bool,
150}
151
152#[allow(dead_code)]
157pub struct WatchdogCouncil {
158 members: Vec<Arc<RwLock<CouncilMember>>>,
160
161 total_members: usize,
163
164 max_faulty: usize,
166
167 quorum: usize,
169
170 pub council_id: String,
172
173 rules: Vec<String>,
175}
176
177impl CouncilMember {
182 pub fn new(
184 id: impl Into<String>,
185 rules: Vec<String>,
186 capsule_hash: impl Into<String>,
187 ) -> Result<Self> {
188 let keystore = SoftwareKeyStore::generate()?;
189 let watchdog = Watchdog::new(rules, capsule_hash.into(), Box::new(keystore.clone()));
190
191 Ok(CouncilMember {
192 id: MemberId(id.into()),
193 watchdog,
194 keystore,
195 active: true,
196 })
197 }
198
199 pub fn vote(&self, action: &Action) -> Result<Vote> {
201 if !self.active {
202 return Ok(Vote {
203 member_id: self.id.clone(),
204 decision: VoteDecision::Abstain,
205 signature: vec![],
206 timestamp: Self::now(),
207 });
208 }
209
210 let decision = match self.watchdog.verify_action(action) {
213 Ok(None) => VoteDecision::Approve,
214 Ok(Some(_denial_proof)) => VoteDecision::Deny,
215 Err(WatchdogError::HardResetRequired(..)) => VoteDecision::HardReset,
216 Err(_) => VoteDecision::Deny,
217 };
218
219 let timestamp = Self::now();
220
221 let vote_data = self.serialize_vote_data(action, decision, timestamp);
223 let signature = self.keystore.sign(&vote_data)?;
224
225 Ok(Vote {
226 member_id: self.id.clone(),
227 decision,
228 signature,
229 timestamp,
230 })
231 }
232
233 fn serialize_vote_data(
235 &self,
236 action: &Action,
237 decision: VoteDecision,
238 timestamp: u64,
239 ) -> Vec<u8> {
240 let mut data = Vec::new();
241 data.extend_from_slice(self.id.0.as_bytes());
242 data.extend_from_slice(&action.hash());
243 data.push(decision as u8);
244 data.extend_from_slice(×tamp.to_le_bytes());
245 data
246 }
247
248 pub fn public_key(&self) -> Vec<u8> {
250 self.keystore.public_key_bytes()
251 }
252
253 fn now() -> u64 {
254 SystemTime::now()
255 .duration_since(UNIX_EPOCH)
256 .unwrap()
257 .as_secs()
258 }
259}
260
261impl WatchdogCouncil {
262 pub fn new(
284 num_members: usize,
285 rules: Vec<String>,
286 capsule_hash: impl Into<String> + Clone,
287 ) -> Result<Self> {
288 if num_members < 4 {
289 return Err(CryptoError::InvalidState(
290 "BFT requires at least 4 members (3f+1 where f=1)".into(),
291 ));
292 }
293
294 let max_faulty = (num_members - 1) / 3;
297 let quorum = 2 * max_faulty + 1;
298
299 let mut members = Vec::new();
300 for i in 0..num_members {
301 let member =
302 CouncilMember::new(format!("member-{}", i), rules.clone(), capsule_hash.clone())?;
303 members.push(Arc::new(RwLock::new(member)));
304 }
305
306 let council_id = format!(
307 "council-{:x}",
308 Sha256::digest(format!("{:?}-{}", rules, num_members).as_bytes())[..8]
309 .iter()
310 .fold(0u64, |acc, &b| acc << 8 | b as u64)
311 );
312
313 Ok(WatchdogCouncil {
314 members,
315 total_members: num_members,
316 max_faulty,
317 quorum,
318 council_id,
319 rules,
320 })
321 }
322
323 pub fn verify_action(&self, action: &Action) -> Result<ConsensusResult> {
331 let mut votes = Vec::new();
332
333 for member_arc in &self.members {
335 let member = member_arc.read();
336 match member.vote(action) {
337 Ok(vote) => votes.push(vote),
338 Err(_) => {
339 votes.push(Vote {
341 member_id: member.id.clone(),
342 decision: VoteDecision::Abstain,
343 signature: vec![],
344 timestamp: CouncilMember::now(),
345 });
346 }
347 }
348 }
349
350 let mut vote_counts: HashMap<VoteDecision, usize> = HashMap::new();
352 for vote in &votes {
353 if vote.decision != VoteDecision::Abstain {
354 *vote_counts.entry(vote.decision).or_insert(0) += 1;
355 }
356 }
357
358 let (decision, count) = vote_counts
360 .iter()
361 .max_by_key(|(_, &count)| count)
362 .map(|(&d, &c)| (d, c))
363 .unwrap_or((VoteDecision::Abstain, 0));
364
365 if count < self.quorum {
366 return Err(CryptoError::VerificationFailed(format!(
367 "No quorum reached: {} votes, {} required",
368 count, self.quorum
369 )));
370 }
371
372 let threshold_sig = self.create_threshold_signature(&votes, action)?;
374
375 Ok(ConsensusResult {
376 decision,
377 vote_count: count,
378 quorum: self.quorum,
379 votes,
380 threshold_signature: threshold_sig,
381 action_hash: action.hash(),
382 timestamp: CouncilMember::now(),
383 })
384 }
385
386 fn create_threshold_signature(
388 &self,
389 votes: &[Vote],
390 _action: &Action,
391 ) -> Result<ThresholdSignature> {
392 let mut combined = Vec::new();
393 let mut pubkeys = Vec::new();
394 let mut count = 0;
395
396 for vote in votes {
397 if vote.decision != VoteDecision::Abstain && !vote.signature.is_empty() {
398 combined.extend_from_slice(&vote.signature);
399 count += 1;
400
401 for member_arc in &self.members {
403 let member = member_arc.read();
404 if member.id == vote.member_id {
405 pubkeys.push(member.public_key());
406 break;
407 }
408 }
409 }
410 }
411
412 let combined_hash = Sha256::digest(&combined);
414
415 Ok(ThresholdSignature {
416 combined_signature: combined_hash.to_vec(),
417 signer_pubkeys: pubkeys,
418 count,
419 })
420 }
421
422 pub fn disable_member(&self, member_idx: usize) -> Result<()> {
424 if member_idx >= self.total_members {
425 return Err(CryptoError::InvalidState("Invalid member index".into()));
426 }
427
428 let mut member = self.members[member_idx].write();
429 member.active = false;
430 Ok(())
431 }
432
433 pub fn enable_member(&self, member_idx: usize) -> Result<()> {
435 if member_idx >= self.total_members {
436 return Err(CryptoError::InvalidState("Invalid member index".into()));
437 }
438
439 let mut member = self.members[member_idx].write();
440 member.active = true;
441 Ok(())
442 }
443
444 pub fn status(&self) -> CouncilStatus {
446 let active_count = self.members.iter().filter(|m| m.read().active).count();
447
448 CouncilStatus {
449 council_id: self.council_id.clone(),
450 total_members: self.total_members,
451 active_members: active_count,
452 max_faulty: self.max_faulty,
453 quorum: self.quorum,
454 healthy: active_count >= self.quorum,
455 }
456 }
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
461pub struct CouncilStatus {
462 pub council_id: String,
463 pub total_members: usize,
464 pub active_members: usize,
465 pub max_faulty: usize,
466 pub quorum: usize,
467 pub healthy: bool,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct RemoteMemberConfig {
477 pub id: String,
479 pub address: String,
481 pub public_key: Vec<u8>,
483 pub attestation: Option<Vec<u8>>,
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct DistributedCouncilConfig {
490 pub members: Vec<RemoteMemberConfig>,
492 pub vote_timeout_ms: u64,
494 pub retry_count: u32,
496}
497
498#[cfg(test)]
503mod tests {
504 use super::*;
505
506 fn create_test_action() -> Action {
507 Action::delete("test.txt")
508 }
509
510 #[test]
511 fn test_council_creation() {
512 let council =
513 WatchdogCouncil::new(4, vec!["Do no harm".to_string()], "test_capsule").unwrap();
514
515 assert_eq!(council.total_members, 4);
516 assert_eq!(council.max_faulty, 1); assert_eq!(council.quorum, 3); }
519
520 #[test]
521 fn test_council_consensus() {
522 let council = WatchdogCouncil::new(4, vec!["Allow all".to_string()], "test").unwrap();
523
524 let action = create_test_action();
525 let result = council.verify_action(&action).unwrap();
526
527 assert_eq!(result.decision, VoteDecision::Approve);
528 assert!(result.vote_count >= 3); }
530
531 #[test]
532 fn test_council_survives_one_failure() {
533 let council = WatchdogCouncil::new(4, vec!["Allow all".to_string()], "test").unwrap();
534
535 council.disable_member(0).unwrap();
537
538 let status = council.status();
539 assert_eq!(status.active_members, 3);
540 assert!(status.healthy); let action = create_test_action();
544 let result = council.verify_action(&action).unwrap();
545 assert_eq!(result.decision, VoteDecision::Approve);
546 }
547
548 #[test]
549 fn test_council_fails_with_too_many_failures() {
550 let council = WatchdogCouncil::new(4, vec!["Allow all".to_string()], "test").unwrap();
551
552 council.disable_member(0).unwrap();
554 council.disable_member(1).unwrap();
555
556 let status = council.status();
557 assert_eq!(status.active_members, 2);
558 assert!(!status.healthy); let action = create_test_action();
562 assert!(council.verify_action(&action).is_err());
563 }
564
565 #[test]
566 fn test_threshold_signature() {
567 let council = WatchdogCouncil::new(4, vec!["Allow all".to_string()], "test").unwrap();
568
569 let action = create_test_action();
570 let result = council.verify_action(&action).unwrap();
571
572 assert!(result.threshold_signature.count >= council.quorum);
574 assert!(!result.threshold_signature.combined_signature.is_empty());
575 }
576
577 #[test]
578 fn test_minimum_council_size() {
579 let result = WatchdogCouncil::new(3, vec!["test".to_string()], "test");
581
582 assert!(result.is_err());
583 }
584
585 #[test]
586 fn test_larger_council() {
587 let council = WatchdogCouncil::new(7, vec!["Allow all".to_string()], "test").unwrap();
589
590 assert_eq!(council.max_faulty, 2); assert_eq!(council.quorum, 5); council.disable_member(0).unwrap();
595 council.disable_member(1).unwrap();
596
597 let action = create_test_action();
598 let result = council.verify_action(&action).unwrap();
599 assert_eq!(result.decision, VoteDecision::Approve);
600 }
601}