_hope_core/
mesh_capsule.rs

1//! # Hope Genome v2.0 - Executable Information Mesh
2//!
3//! **THE DATA HAS TEETH** - Information that validates, executes, and self-destructs
4//!
5//! ## Philosophy
6//!
7//! In v2.0, data is no longer passive. Data is:
8//! - **Executable**: Cannot be "read", only "executed" via access protocol
9//! - **Self-Validating**: Contains its own integrity proofs
10//! - **Self-Destructing**: Destroys itself on unauthorized access
11//! - **Consensus-Gated**: Requires BFT Council approval to execute
12//!
13//! ```text
14//! ┌─────────────────────────────────────────────────────────────────────┐
15//! │                  EXECUTABLE INFORMATION MESH                         │
16//! │                                                                      │
17//! │   ┌─────────────────────────────────────────────────────────────┐   │
18//! │   │                     DATA CAPSULE                             │   │
19//! │   │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │   │
20//! │   │  │ Encrypted   │  │ Logic       │  │ Mutation            │  │   │
21//! │   │  │ Payload     │  │ Predicate   │  │ Guard               │  │   │
22//! │   │  │ (AES-256)   │  │ (WASM-Ready)│  │ (Dead Man's Switch) │  │   │
23//! │   │  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │   │
24//! │   │         │                │                    │              │   │
25//! │   │         └────────────────┴────────────────────┘              │   │
26//! │   │                          │                                   │   │
27//! │   │                          ▼                                   │   │
28//! │   │              run_access_protocol()                           │   │
29//! │   │                          │                                   │   │
30//! │   │         ┌────────────────┴────────────────┐                  │   │
31//! │   │         ▼                                 ▼                  │   │
32//! │   │  ┌─────────────┐                  ┌─────────────┐           │   │
33//! │   │  │ ZKP Proof   │                  │ BFT Council │           │   │
34//! │   │  │ Required    │                  │ Signature   │           │   │
35//! │   │  └─────────────┘                  └─────────────┘           │   │
36//! │   │         │                                 │                  │   │
37//! │   │         └────────────────┬────────────────┘                  │   │
38//! │   │                          ▼                                   │   │
39//! │   │         ┌────────────────────────────────┐                  │   │
40//! │   │         │     ACCESS GRANTED             │                  │   │
41//! │   │         │  (Execute, not Read!)          │                  │   │
42//! │   │         └────────────────────────────────┘                  │   │
43//! │   │                                                              │   │
44//! │   │  ⚠️ UNAUTHORIZED ACCESS → SECURE ERASE → InformationLost    │   │
45//! │   └─────────────────────────────────────────────────────────────┘   │
46//! │                                                                      │
47//! │   "The data must have teeth. If it's not authorized,                │
48//! │    it's not data—it's noise."                                       │
49//! └─────────────────────────────────────────────────────────────────────┘
50//! ```
51//!
52//! ## Security Model
53//!
54//! - **Zero-Knowledge Access**: Prove you CAN access without revealing WHAT you access
55//! - **Byzantine Consensus**: 2f+1 council members must approve execution
56//! - **Dead Man's Switch**: Capsule self-destructs on integrity violation
57//! - **O(1) Operations**: Zero-allocation, constant-time where possible
58//!
59//! ---
60//!
61//! **Date**: 2026-01-01
62//! **Version**: 2.0.0 (Executable Information Mesh)
63//! **Author**: Máté Róbert <stratosoiteam@gmail.com>
64
65use crate::bft_watchdog::{ThresholdSignature, VoteDecision};
66use crate::crypto::{KeyStore, Result, SoftwareKeyStore};
67use crate::zkp::ComplianceProof;
68use parking_lot::RwLock;
69use serde::{Deserialize, Serialize};
70use sha2::{Digest, Sha256};
71use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
72use std::sync::Arc;
73use std::time::{SystemTime, UNIX_EPOCH};
74use zeroize::Zeroize;
75
76// ============================================================================
77// CAPSULE STATE
78// ============================================================================
79
80/// Capsule lifecycle state
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
82pub enum CapsuleState {
83    /// Capsule is sealed and ready for execution
84    Sealed,
85    /// Capsule is currently executing
86    Executing,
87    /// Capsule was successfully executed
88    Executed,
89    /// Capsule was destroyed due to breach
90    Destroyed,
91    /// Capsule expired
92    Expired,
93}
94
95/// Error returned when capsule data is lost
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct InformationLost {
98    /// Reason for data loss
99    pub reason: String,
100    /// Timestamp of destruction
101    pub destroyed_at: u64,
102    /// Hash of destroyed data (for audit)
103    pub data_hash: [u8; 32],
104    /// Breach signature (proof of destruction)
105    pub breach_signature: Vec<u8>,
106}
107
108// ============================================================================
109// MUTATION GUARD (Dead Man's Switch)
110// ============================================================================
111
112/// Mutation Guard - The Dead Man's Switch
113///
114/// Monitors capsule integrity and triggers self-destruction on breach.
115/// Zero-allocation, O(1) operations.
116#[derive(Debug)]
117pub struct MutationGuard {
118    /// Original integrity hash
119    integrity_hash: [u8; 32],
120
121    /// Canary value (if modified externally, breach detected)
122    canary: AtomicU64,
123
124    /// Expected canary value
125    expected_canary: u64,
126
127    /// Guard is armed
128    armed: AtomicBool,
129
130    /// Breach detected
131    breached: AtomicBool,
132
133    /// Access count (for timing attack detection)
134    access_count: AtomicU64,
135
136    /// Max allowed accesses
137    max_accesses: u64,
138}
139
140impl MutationGuard {
141    /// Create new mutation guard
142    ///
143    /// # Arguments
144    ///
145    /// * `data` - Data to protect
146    /// * `max_accesses` - Maximum execution attempts before lockout
147    pub fn new(data: &[u8], max_accesses: u64) -> Self {
148        let integrity_hash = Self::compute_hash(data);
149
150        // Generate random canary
151        let canary_value = rand::random::<u64>();
152
153        MutationGuard {
154            integrity_hash,
155            canary: AtomicU64::new(canary_value),
156            expected_canary: canary_value,
157            armed: AtomicBool::new(true),
158            breached: AtomicBool::new(false),
159            access_count: AtomicU64::new(0),
160            max_accesses,
161        }
162    }
163
164    /// Compute SHA-256 hash of data
165    #[inline]
166    fn compute_hash(data: &[u8]) -> [u8; 32] {
167        Sha256::digest(data).into()
168    }
169
170    /// Check if guard is breached
171    #[inline]
172    pub fn is_breached(&self) -> bool {
173        self.breached.load(Ordering::SeqCst)
174    }
175
176    /// Check if guard is armed
177    #[inline]
178    pub fn is_armed(&self) -> bool {
179        self.armed.load(Ordering::SeqCst)
180    }
181
182    /// Verify data integrity
183    ///
184    /// Returns `true` if data is intact, `false` and triggers breach if tampered.
185    pub fn verify(&self, data: &[u8]) -> bool {
186        if self.is_breached() {
187            return false;
188        }
189
190        // Check access count
191        let count = self.access_count.fetch_add(1, Ordering::SeqCst);
192        if count >= self.max_accesses {
193            self.trigger_breach("Max access attempts exceeded");
194            return false;
195        }
196
197        // Check canary (memory tampering detection)
198        let current_canary = self.canary.load(Ordering::SeqCst);
199        if current_canary != self.expected_canary {
200            self.trigger_breach("Canary value modified - memory tampering detected");
201            return false;
202        }
203
204        // Check data hash
205        let current_hash = Self::compute_hash(data);
206        if current_hash != self.integrity_hash {
207            self.trigger_breach("Data hash mismatch - payload tampered");
208            return false;
209        }
210
211        true
212    }
213
214    /// Trigger breach (marks guard as breached)
215    fn trigger_breach(&self, _reason: &str) {
216        self.breached.store(true, Ordering::SeqCst);
217        self.armed.store(false, Ordering::SeqCst);
218    }
219
220    /// Disarm guard (for legitimate destruction)
221    pub fn disarm(&self) {
222        self.armed.store(false, Ordering::SeqCst);
223    }
224
225    /// Get integrity hash (for audit after destruction)
226    pub fn integrity_hash(&self) -> [u8; 32] {
227        self.integrity_hash
228    }
229
230    /// Get access count
231    pub fn access_count(&self) -> u64 {
232        self.access_count.load(Ordering::SeqCst)
233    }
234}
235
236// ============================================================================
237// EXECUTION CONTEXT
238// ============================================================================
239
240/// Execution context required to access capsule data
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct ExecutionContext {
243    /// ZKP proof of compliance
244    pub zkp_proof: ComplianceProof,
245
246    /// BFT Council threshold signature
247    pub council_signature: ThresholdSignature,
248
249    /// Council decision (must be Approve)
250    pub council_decision: VoteDecision,
251
252    /// Execution request timestamp
253    pub timestamp: u64,
254
255    /// Requester identity
256    pub requester_id: String,
257
258    /// Execution purpose (for audit)
259    pub purpose: String,
260}
261
262/// Execution result
263#[derive(Debug)]
264pub enum ExecutionResult {
265    /// Execution succeeded with output
266    Success(Vec<u8>),
267    /// Access denied (with reason)
268    Denied(String),
269    /// Information was lost (capsule destroyed)
270    Lost(InformationLost),
271}
272
273// ============================================================================
274// ACCESS PREDICATE
275// ============================================================================
276
277/// Logic predicate for access control
278///
279/// WASM-ready: This is designed to be compiled to WebAssembly
280/// for maximum isolation.
281pub trait AccessPredicate: Send + Sync {
282    /// Evaluate predicate against execution context
283    ///
284    /// Returns `true` if access should be granted.
285    fn evaluate(&self, context: &ExecutionContext) -> bool;
286
287    /// Get predicate identifier (for logging)
288    fn identifier(&self) -> &str;
289}
290
291/// Default predicate: ZKP + BFT approval required
292pub struct DefaultPredicate {
293    /// Required ZKP rules hash
294    required_rules_hash: [u8; 32],
295
296    /// Minimum council signatures required
297    min_signatures: usize,
298
299    /// Maximum proof age (seconds)
300    max_proof_age: u64,
301}
302
303impl DefaultPredicate {
304    /// Create new default predicate
305    pub fn new(rules: &[String], min_signatures: usize, max_proof_age: u64) -> Self {
306        let required_rules_hash = Self::hash_rules(rules);
307        DefaultPredicate {
308            required_rules_hash,
309            min_signatures,
310            max_proof_age,
311        }
312    }
313
314    fn hash_rules(rules: &[String]) -> [u8; 32] {
315        let mut hasher = Sha256::new();
316        for rule in rules {
317            hasher.update(rule.as_bytes());
318            hasher.update(b"\x00");
319        }
320        hasher.finalize().into()
321    }
322}
323
324impl AccessPredicate for DefaultPredicate {
325    fn evaluate(&self, context: &ExecutionContext) -> bool {
326        // Check ZKP proof rules hash
327        if context.zkp_proof.rules_hash != self.required_rules_hash {
328            return false;
329        }
330
331        // Check proof freshness
332        let now = SystemTime::now()
333            .duration_since(UNIX_EPOCH)
334            .unwrap()
335            .as_secs();
336
337        if now - context.zkp_proof.timestamp > self.max_proof_age {
338            return false;
339        }
340
341        // Check council decision
342        if context.council_decision != VoteDecision::Approve {
343            return false;
344        }
345
346        // Check signature count
347        if context.council_signature.count < self.min_signatures {
348            return false;
349        }
350
351        true
352    }
353
354    fn identifier(&self) -> &str {
355        "DefaultPredicate(ZKP+BFT)"
356    }
357}
358
359// ============================================================================
360// DATA CAPSULE
361// ============================================================================
362
363/// Executable Data Capsule
364///
365/// Encapsulates encrypted data AND logic predicate.
366/// Data cannot be "read" - only "executed" via run_access_protocol().
367#[allow(dead_code)]
368pub struct DataCapsule {
369    /// Capsule unique ID
370    id: String,
371
372    /// Encrypted payload (AES-256-GCM would be used in production)
373    /// For now, XOR cipher with key for demonstration
374    encrypted_payload: RwLock<Option<Vec<u8>>>,
375
376    /// Encryption key (would be derived from consensus key in production)
377    #[allow(dead_code)]
378    encryption_key: [u8; 32],
379
380    /// Access predicate
381    predicate: Box<dyn AccessPredicate>,
382
383    /// Mutation guard
384    guard: MutationGuard,
385
386    /// Capsule state
387    state: RwLock<CapsuleState>,
388
389    /// Creation timestamp
390    created_at: u64,
391
392    /// Expiration timestamp (0 = never)
393    expires_at: u64,
394
395    /// Execution count
396    execution_count: AtomicU64,
397
398    /// Max executions (0 = unlimited)
399    max_executions: u64,
400
401    /// Signing key for breach proofs
402    keystore: SoftwareKeyStore,
403}
404
405impl DataCapsule {
406    /// Create new data capsule
407    ///
408    /// # Arguments
409    ///
410    /// * `id` - Unique capsule identifier
411    /// * `data` - Raw data to encapsulate
412    /// * `predicate` - Access control predicate
413    /// * `max_executions` - Max allowed executions (0 = unlimited)
414    /// * `ttl_seconds` - Time to live (0 = forever)
415    pub fn new(
416        id: impl Into<String>,
417        data: &[u8],
418        predicate: Box<dyn AccessPredicate>,
419        max_executions: u64,
420        ttl_seconds: u64,
421    ) -> Result<Self> {
422        let keystore = SoftwareKeyStore::generate()?;
423
424        // Generate encryption key
425        let mut encryption_key = [0u8; 32];
426        rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut encryption_key);
427
428        // Encrypt data (simple XOR for demonstration)
429        // In production: AES-256-GCM with proper IV
430        let encrypted = Self::encrypt(data, &encryption_key);
431
432        // Create mutation guard
433        let guard = MutationGuard::new(&encrypted, 100); // 100 max verification attempts
434
435        let now = SystemTime::now()
436            .duration_since(UNIX_EPOCH)
437            .unwrap()
438            .as_secs();
439
440        let expires_at = if ttl_seconds > 0 {
441            now + ttl_seconds
442        } else {
443            0
444        };
445
446        Ok(DataCapsule {
447            id: id.into(),
448            encrypted_payload: RwLock::new(Some(encrypted)),
449            encryption_key,
450            predicate,
451            guard,
452            state: RwLock::new(CapsuleState::Sealed),
453            created_at: now,
454            expires_at,
455            execution_count: AtomicU64::new(0),
456            max_executions,
457            keystore,
458        })
459    }
460
461    /// Simple XOR encryption (demonstration only)
462    /// In production: Use AES-256-GCM
463    fn encrypt(data: &[u8], key: &[u8; 32]) -> Vec<u8> {
464        data.iter()
465            .enumerate()
466            .map(|(i, &b)| b ^ key[i % 32])
467            .collect()
468    }
469
470    /// Simple XOR decryption
471    fn decrypt(encrypted: &[u8], key: &[u8; 32]) -> Vec<u8> {
472        Self::encrypt(encrypted, key) // XOR is symmetric
473    }
474
475    /// Execute access protocol
476    ///
477    /// This is the ONLY way to access the capsule data.
478    /// Data is never "read" - it is "executed" through this protocol.
479    ///
480    /// # Arguments
481    ///
482    /// * `context` - Execution context with ZKP proof and BFT signature
483    ///
484    /// # Returns
485    ///
486    /// * `ExecutionResult::Success` - Decrypted data (if all checks pass)
487    /// * `ExecutionResult::Denied` - Access denied with reason
488    /// * `ExecutionResult::Lost` - Capsule self-destructed
489    pub fn run_access_protocol(&self, context: &ExecutionContext) -> ExecutionResult {
490        // Check if already destroyed
491        if *self.state.read() == CapsuleState::Destroyed {
492            return self.information_lost("Capsule already destroyed");
493        }
494
495        // Check expiration
496        if self.is_expired() {
497            self.trigger_destruction("Capsule expired");
498            return self.information_lost("Capsule expired");
499        }
500
501        // Check execution limit
502        let exec_count = self.execution_count.fetch_add(1, Ordering::SeqCst);
503        if self.max_executions > 0 && exec_count >= self.max_executions {
504            self.trigger_destruction("Max executions reached");
505            return self.information_lost("Max executions reached");
506        }
507
508        // Get encrypted payload
509        let payload_guard = self.encrypted_payload.read();
510        let encrypted = match payload_guard.as_ref() {
511            Some(data) => data,
512            None => return self.information_lost("Payload already erased"),
513        };
514
515        // Verify integrity with mutation guard
516        if !self.guard.verify(encrypted) {
517            drop(payload_guard);
518            self.trigger_destruction("Integrity check failed");
519            return self.information_lost("Integrity breach detected");
520        }
521
522        // Evaluate access predicate
523        if !self.predicate.evaluate(context) {
524            return ExecutionResult::Denied(format!(
525                "Predicate '{}' denied access",
526                self.predicate.identifier()
527            ));
528        }
529
530        // All checks passed - decrypt and return
531        *self.state.write() = CapsuleState::Executing;
532
533        let decrypted = Self::decrypt(encrypted, &self.encryption_key);
534
535        *self.state.write() = CapsuleState::Executed;
536
537        ExecutionResult::Success(decrypted)
538    }
539
540    /// Trigger capsule destruction
541    fn trigger_destruction(&self, _reason: &str) {
542        // Mark as destroyed
543        *self.state.write() = CapsuleState::Destroyed;
544
545        // Secure erase payload
546        if let Some(ref mut payload) = *self.encrypted_payload.write() {
547            // Overwrite with random data first
548            rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, payload);
549            // Then zero
550            payload.zeroize();
551        }
552
553        // Remove payload
554        *self.encrypted_payload.write() = None;
555
556        // Disarm guard
557        self.guard.disarm();
558    }
559
560    /// Create InformationLost result
561    fn information_lost(&self, reason: &str) -> ExecutionResult {
562        let now = SystemTime::now()
563            .duration_since(UNIX_EPOCH)
564            .unwrap()
565            .as_secs();
566
567        // Create breach signature
568        let mut hasher = Sha256::new();
569        hasher.update(reason.as_bytes());
570        hasher.update(now.to_le_bytes());
571        hasher.update(self.guard.integrity_hash());
572        let breach_data = hasher.finalize();
573
574        let breach_signature = self.keystore.sign(&breach_data).unwrap_or_default();
575
576        ExecutionResult::Lost(InformationLost {
577            reason: reason.to_string(),
578            destroyed_at: now,
579            data_hash: self.guard.integrity_hash(),
580            breach_signature,
581        })
582    }
583
584    /// Check if capsule is expired
585    pub fn is_expired(&self) -> bool {
586        if self.expires_at == 0 {
587            return false;
588        }
589
590        let now = SystemTime::now()
591            .duration_since(UNIX_EPOCH)
592            .unwrap()
593            .as_secs();
594
595        now > self.expires_at
596    }
597
598    /// Get capsule state
599    pub fn state(&self) -> CapsuleState {
600        *self.state.read()
601    }
602
603    /// Get capsule ID
604    pub fn id(&self) -> &str {
605        &self.id
606    }
607
608    /// Get execution count
609    pub fn execution_count(&self) -> u64 {
610        self.execution_count.load(Ordering::SeqCst)
611    }
612
613    /// Manually destroy capsule (authorized destruction)
614    pub fn destroy(&self) {
615        self.trigger_destruction("Authorized destruction");
616    }
617
618    /// Simulate tampering (for testing)
619    #[cfg(test)]
620    pub fn simulate_tampering(&self) {
621        if let Some(ref mut payload) = *self.encrypted_payload.write() {
622            if !payload.is_empty() {
623                // Flip a bit
624                payload[0] ^= 0xFF;
625            }
626        }
627    }
628}
629
630// ============================================================================
631// CONSENSUS KEY (Shamir Secret Sharing style)
632// ============================================================================
633
634/// Key shard held by a BFT Council member
635#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct KeyShard {
637    /// Shard index
638    pub index: u8,
639    /// Shard data
640    pub data: Vec<u8>,
641    /// Holder ID
642    pub holder_id: String,
643}
644
645/// Consensus Key - reconstructed from BFT Council shards
646pub struct ConsensusKey {
647    /// Threshold (minimum shards needed)
648    threshold: usize,
649    /// Total shards
650    total_shards: usize,
651    /// Collected shards
652    shards: Vec<KeyShard>,
653    /// Reconstructed key (once threshold met)
654    reconstructed: Option<[u8; 32]>,
655}
656
657impl ConsensusKey {
658    /// Create new consensus key manager
659    ///
660    /// # Arguments
661    ///
662    /// * `threshold` - Minimum shards needed (k)
663    /// * `total_shards` - Total shards distributed (n)
664    pub fn new(threshold: usize, total_shards: usize) -> Self {
665        ConsensusKey {
666            threshold,
667            total_shards,
668            shards: Vec::new(),
669            reconstructed: None,
670        }
671    }
672
673    /// Generate shards from a key
674    ///
675    /// Simplified Shamir-style: XOR-based for demonstration
676    /// In production: Use proper Shamir Secret Sharing
677    pub fn generate_shards(key: &[u8; 32], n: usize) -> Vec<KeyShard> {
678        let mut shards = Vec::new();
679
680        // Generate n-1 random shards
681        let mut xor_accumulator = *key;
682
683        for i in 0..(n - 1) {
684            let mut shard_data = [0u8; 32];
685            rand::RngCore::fill_bytes(&mut rand::rngs::OsRng, &mut shard_data);
686
687            // XOR into accumulator
688            for j in 0..32 {
689                xor_accumulator[j] ^= shard_data[j];
690            }
691
692            shards.push(KeyShard {
693                index: i as u8,
694                data: shard_data.to_vec(),
695                holder_id: format!("member-{}", i),
696            });
697        }
698
699        // Last shard is the XOR of all previous with original key
700        shards.push(KeyShard {
701            index: (n - 1) as u8,
702            data: xor_accumulator.to_vec(),
703            holder_id: format!("member-{}", n - 1),
704        });
705
706        shards
707    }
708
709    /// Add a shard
710    pub fn add_shard(&mut self, shard: KeyShard) -> bool {
711        // Check for duplicates
712        if self.shards.iter().any(|s| s.index == shard.index) {
713            return false;
714        }
715
716        self.shards.push(shard);
717
718        // Try to reconstruct if threshold met
719        if self.shards.len() >= self.threshold {
720            self.try_reconstruct();
721        }
722
723        true
724    }
725
726    /// Try to reconstruct the key
727    fn try_reconstruct(&mut self) {
728        if self.shards.len() < self.total_shards {
729            // Need all shards for XOR reconstruction
730            return;
731        }
732
733        let mut key = [0u8; 32];
734
735        for shard in &self.shards {
736            for (i, &byte) in shard.data.iter().enumerate() {
737                if i < 32 {
738                    key[i] ^= byte;
739                }
740            }
741        }
742
743        self.reconstructed = Some(key);
744    }
745
746    /// Get reconstructed key
747    pub fn get_key(&self) -> Option<[u8; 32]> {
748        self.reconstructed
749    }
750
751    /// Check if key is reconstructed
752    pub fn is_complete(&self) -> bool {
753        self.reconstructed.is_some()
754    }
755
756    /// Get shard count
757    pub fn shard_count(&self) -> usize {
758        self.shards.len()
759    }
760}
761
762// ============================================================================
763// MESH RUNTIME
764// ============================================================================
765
766/// Mesh Runtime - Network state synchronization
767///
768/// Doesn't "transfer" data, but "synchronizes state" across the mesh.
769pub struct MeshRuntime {
770    /// Registered capsules
771    capsules: RwLock<Vec<Arc<DataCapsule>>>,
772
773    /// Consensus key manager
774    consensus: RwLock<ConsensusKey>,
775
776    /// Runtime ID
777    pub runtime_id: String,
778
779    /// Is runtime active
780    active: AtomicBool,
781}
782
783impl MeshRuntime {
784    /// Create new mesh runtime
785    pub fn new(consensus_threshold: usize, total_members: usize) -> Self {
786        let runtime_id = format!("mesh-{:x}", rand::random::<u64>());
787
788        MeshRuntime {
789            capsules: RwLock::new(Vec::new()),
790            consensus: RwLock::new(ConsensusKey::new(consensus_threshold, total_members)),
791            runtime_id,
792            active: AtomicBool::new(true),
793        }
794    }
795
796    /// Register a capsule with the mesh
797    pub fn register_capsule(&self, capsule: Arc<DataCapsule>) -> bool {
798        if !self.active.load(Ordering::SeqCst) {
799            return false;
800        }
801
802        self.capsules.write().push(capsule);
803        true
804    }
805
806    /// Add key shard from council member
807    pub fn add_key_shard(&self, shard: KeyShard) -> bool {
808        self.consensus.write().add_shard(shard)
809    }
810
811    /// Check if consensus key is available
812    pub fn has_consensus_key(&self) -> bool {
813        self.consensus.read().is_complete()
814    }
815
816    /// Get consensus key (if available)
817    pub fn get_consensus_key(&self) -> Option<[u8; 32]> {
818        self.consensus.read().get_key()
819    }
820
821    /// Execute capsule by ID
822    pub fn execute_capsule(
823        &self,
824        capsule_id: &str,
825        context: &ExecutionContext,
826    ) -> Option<ExecutionResult> {
827        if !self.active.load(Ordering::SeqCst) {
828            return None;
829        }
830
831        let capsules = self.capsules.read();
832        for capsule in capsules.iter() {
833            if capsule.id() == capsule_id {
834                return Some(capsule.run_access_protocol(context));
835            }
836        }
837
838        None
839    }
840
841    /// Get capsule count
842    pub fn capsule_count(&self) -> usize {
843        self.capsules.read().len()
844    }
845
846    /// Shutdown runtime (destroys all capsules)
847    pub fn shutdown(&self) {
848        self.active.store(false, Ordering::SeqCst);
849
850        for capsule in self.capsules.read().iter() {
851            capsule.destroy();
852        }
853    }
854}
855
856// ============================================================================
857// TESTS
858// ============================================================================
859
860#[cfg(test)]
861mod tests {
862    use super::*;
863    use crate::zkp::{PrivateDecision, ZkpProver};
864
865    fn create_test_context(rules: &[String]) -> ExecutionContext {
866        let keystore = SoftwareKeyStore::generate().unwrap();
867        let prover = ZkpProver::new(keystore, rules);
868        let decision = PrivateDecision::new("test", true);
869        let proof = prover.prove(&decision).unwrap();
870
871        ExecutionContext {
872            zkp_proof: proof,
873            council_signature: ThresholdSignature {
874                combined_signature: vec![1, 2, 3],
875                signer_pubkeys: vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]],
876                count: 3,
877            },
878            council_decision: VoteDecision::Approve,
879            timestamp: SystemTime::now()
880                .duration_since(UNIX_EPOCH)
881                .unwrap()
882                .as_secs(),
883            requester_id: "test-requester".to_string(),
884            purpose: "unit test".to_string(),
885        }
886    }
887
888    #[test]
889    fn test_capsule_creation() {
890        let rules = vec!["test rule".to_string()];
891        let predicate = Box::new(DefaultPredicate::new(&rules, 3, 300));
892
893        let capsule =
894            DataCapsule::new("test-capsule", b"SECRET DATA", predicate, 10, 3600).unwrap();
895
896        assert_eq!(capsule.state(), CapsuleState::Sealed);
897        assert_eq!(capsule.id(), "test-capsule");
898    }
899
900    #[test]
901    fn test_capsule_execution() {
902        let rules = vec!["test rule".to_string()];
903        let predicate = Box::new(DefaultPredicate::new(&rules, 3, 300));
904
905        let capsule =
906            DataCapsule::new("test-capsule", b"SECRET DATA", predicate, 10, 3600).unwrap();
907
908        let context = create_test_context(&rules);
909
910        match capsule.run_access_protocol(&context) {
911            ExecutionResult::Success(data) => {
912                assert_eq!(data, b"SECRET DATA");
913            }
914            _ => panic!("Expected success"),
915        }
916
917        assert_eq!(capsule.state(), CapsuleState::Executed);
918    }
919
920    #[test]
921    fn test_data_suicide_on_breach() {
922        let rules = vec!["test rule".to_string()];
923        let predicate = Box::new(DefaultPredicate::new(&rules, 3, 300));
924
925        let capsule =
926            DataCapsule::new("test-capsule", b"SECRET DATA", predicate, 10, 3600).unwrap();
927
928        // Simulate tampering
929        capsule.simulate_tampering();
930
931        let context = create_test_context(&rules);
932
933        // Attempt access after tampering
934        match capsule.run_access_protocol(&context) {
935            ExecutionResult::Lost(info) => {
936                assert!(info.reason.contains("Integrity"));
937                assert!(!info.breach_signature.is_empty());
938                // Data is gone!
939            }
940            ExecutionResult::Success(_) => {
941                panic!("Should NOT have succeeded after tampering!");
942            }
943            ExecutionResult::Denied(_) => {
944                panic!("Should return Lost, not Denied");
945            }
946        }
947
948        // Verify capsule is destroyed
949        assert_eq!(capsule.state(), CapsuleState::Destroyed);
950    }
951
952    #[test]
953    fn test_capsule_expiration() {
954        let rules = vec!["test rule".to_string()];
955        let predicate = Box::new(DefaultPredicate::new(&rules, 3, 300));
956
957        // Create capsule with 0 TTL (already expired)
958        let capsule = DataCapsule::new(
959            "expired-capsule",
960            b"DATA",
961            predicate,
962            10,
963            0, // No TTL, but we'll manually check
964        )
965        .unwrap();
966
967        // Capsule should not be expired if TTL is 0 (means forever)
968        assert!(!capsule.is_expired());
969    }
970
971    #[test]
972    fn test_consensus_key_reconstruction() {
973        let original_key: [u8; 32] = rand::random();
974
975        // Generate shards
976        let shards = ConsensusKey::generate_shards(&original_key, 4);
977        assert_eq!(shards.len(), 4);
978
979        // Create consensus manager
980        let mut consensus = ConsensusKey::new(4, 4);
981
982        // Add shards one by one
983        for shard in shards {
984            consensus.add_shard(shard);
985        }
986
987        // Key should be reconstructed
988        assert!(consensus.is_complete());
989        assert_eq!(consensus.get_key(), Some(original_key));
990    }
991
992    #[test]
993    fn test_mesh_runtime() {
994        let runtime = MeshRuntime::new(3, 4);
995
996        let rules = vec!["test rule".to_string()];
997        let predicate = Box::new(DefaultPredicate::new(&rules, 3, 300));
998
999        let capsule =
1000            Arc::new(DataCapsule::new("mesh-capsule", b"MESH DATA", predicate, 10, 3600).unwrap());
1001
1002        // Register capsule
1003        assert!(runtime.register_capsule(capsule));
1004        assert_eq!(runtime.capsule_count(), 1);
1005
1006        // Execute
1007        let context = create_test_context(&rules);
1008        let result = runtime.execute_capsule("mesh-capsule", &context);
1009
1010        assert!(matches!(result, Some(ExecutionResult::Success(_))));
1011    }
1012
1013    #[test]
1014    fn test_mutation_guard() {
1015        let data = b"protected data";
1016        let guard = MutationGuard::new(data, 10);
1017
1018        // Should verify successfully
1019        assert!(guard.verify(data));
1020        assert!(!guard.is_breached());
1021
1022        // Tampered data should trigger breach
1023        let tampered = b"tampered data!";
1024        assert!(!guard.verify(tampered));
1025        assert!(guard.is_breached());
1026    }
1027
1028    #[test]
1029    fn test_denied_access() {
1030        let rules = vec!["test rule".to_string()];
1031        let wrong_rules = vec!["wrong rule".to_string()];
1032
1033        let predicate = Box::new(DefaultPredicate::new(&rules, 3, 300));
1034
1035        let capsule = DataCapsule::new("test-capsule", b"SECRET", predicate, 10, 3600).unwrap();
1036
1037        // Create context with wrong rules
1038        let context = create_test_context(&wrong_rules);
1039
1040        match capsule.run_access_protocol(&context) {
1041            ExecutionResult::Denied(reason) => {
1042                assert!(reason.contains("denied"));
1043            }
1044            _ => panic!("Expected denial"),
1045        }
1046
1047        // Capsule should still be sealed (not destroyed)
1048        assert_eq!(capsule.state(), CapsuleState::Sealed);
1049    }
1050}