Skip to main content

fcdb_concur/
lib.rs

1//! # Enishi Concurrency (Own+CFA)
2//!
3//! Phase D: Own+CFA Final - Ownership types and capability functor composition
4//!
5//! Merkle DAG: enishi_concur -> ownership_types, cap_functor, txn_safety
6
7use fcdb_core::{Cap, Cid};
8use std::sync::Arc;
9use tokio::sync::{RwLock, Mutex};
10use async_trait::async_trait;
11use thiserror::Error;
12use serde::{Serialize, Deserialize};
13
14/// Capability-CID pair
15#[derive(Clone, PartialEq, Eq)]
16pub struct CapCid {
17    pub cap: Cap,
18    pub cid: Cid,
19}
20
21impl CapCid {
22    pub fn new(cid: Cid, cap: Cap) -> Self {
23        Self { cap, cid }
24    }
25}
26
27/// Errors for concurrency operations
28#[derive(Error, Debug)]
29pub enum ConcurError {
30    #[error("Capability check failed")]
31    CapCheckFailed,
32    #[error("Ownership violation")]
33    OwnershipViolation,
34    #[error("Transaction conflict")]
35    TransactionConflict,
36    #[error("Lease expired")]
37    LeaseExpired,
38    #[error("Permission denied")]
39    PermissionDenied,
40}
41
42/// Permission flags for capabilities
43pub mod perms {
44    pub const READ: u32 = 1 << 0;
45    pub const WRITE: u32 = 1 << 1;
46    pub const EXECUTE: u32 = 1 << 2;
47    pub const DERIVE: u32 = 1 << 3;
48    pub const DELEGATE: u32 = 1 << 4;
49}
50
51/// Phase D: Owned Capability Content Identifier
52/// Rust ownership ensures exclusive access and prevents data races
53pub struct OwnedCapCid<T> {
54    cap_cid: CapCid,
55    data: T,
56}
57
58impl<T> OwnedCapCid<T> {
59    /// Create owned capability-CID pair (consumes data)
60    pub fn new(data: T, cap: Cap, cid: Cid) -> Self {
61        Self {
62            cap_cid: CapCid::new(cid, cap),
63            data,
64        }
65    }
66
67    /// Get immutable reference (shared borrow)
68    pub fn as_ref(&self) -> (&Cap, &T) {
69        (&self.cap_cid.cap, &self.data)
70    }
71
72    /// Get mutable reference (exclusive borrow)
73    pub fn as_mut(&mut self) -> (&mut Cap, &mut T) {
74        (&mut self.cap_cid.cap, &mut self.data)
75    }
76
77    /// Consume self and return components
78    pub fn into_parts(self) -> (CapCid, T) {
79        (self.cap_cid, self.data)
80    }
81}
82
83/// Phase D: Borrowed Capability Content Identifier
84/// Compile-time borrow checking prevents use-after-free and data races
85pub struct BorrowCapCid<'a, T> {
86    cap_cid: &'a CapCid,
87    data: &'a T,
88}
89
90impl<'a, T> BorrowCapCid<'a, T> {
91    pub fn new(cap_cid: &'a CapCid, data: &'a T) -> Self {
92        Self { cap_cid, data }
93    }
94
95    pub fn cap(&self) -> &Cap {
96        &self.cap_cid.cap
97    }
98
99    pub fn cid(&self) -> &Cid {
100        &self.cap_cid.cid
101    }
102
103    pub fn data(&self) -> &T {
104        self.data
105    }
106}
107
108/// Phase D: Mutable Borrowed Capability Content Identifier
109/// Exclusive access for mutation with capability checking
110pub struct BorrowMutCapCid<'a, T> {
111    cap_cid: &'a mut CapCid,
112    data: &'a mut T,
113}
114
115impl<'a, T> BorrowMutCapCid<'a, T> {
116    pub fn new(cap_cid: &'a mut CapCid, data: &'a mut T) -> Self {
117        Self { cap_cid, data }
118    }
119
120    pub fn cap(&self) -> &Cap {
121        &self.cap_cid.cap
122    }
123
124    pub fn cap_mut(&mut self) -> &mut Cap {
125        &mut self.cap_cid.cap
126    }
127
128    pub fn cid(&self) -> &Cid {
129        &self.cap_cid.cid
130    }
131
132    pub fn data(&self) -> &T {
133        self.data
134    }
135
136    pub fn data_mut(&mut self) -> &mut T {
137        self.data
138    }
139}
140
141/// Phase D: Capability Functor
142/// F(Cap ▷ X) = Cap ▷ F(X) - functor composition for security
143pub trait CapFunctor {
144    type Target<U>;
145    type Data;
146
147    /// Map function while preserving capability
148    fn cap_map<U, F>(self, f: F) -> Self::Target<U>
149    where
150        F: FnOnce(Self::Data) -> U;
151
152    /// FlatMap with capability composition
153    fn cap_flat_map<U, F>(self, f: F) -> Self::Target<U>
154    where
155        F: FnOnce(Self::Data) -> Self::Target<U>;
156}
157
158impl<T> CapFunctor for OwnedCapCid<T> {
159    type Target<U> = OwnedCapCid<U>;
160    type Data = T;
161
162    fn cap_map<U, F>(self, f: F) -> Self::Target<U>
163    where
164        F: FnOnce(Self::Data) -> U,
165    {
166        let (cap_cid, data) = self.into_parts();
167        OwnedCapCid::new(f(data), cap_cid.cap, cap_cid.cid)
168    }
169
170    fn cap_flat_map<U, F>(self, f: F) -> Self::Target<U>
171    where
172        F: FnOnce(Self::Data) -> Self::Target<U>,
173    {
174        let (cap_cid, data) = self.into_parts();
175        let OwnedCapCid { cap_cid: new_cap_cid, data: new_data } = f(data);
176
177        // Compose capabilities: new_cap ∩ original_cap
178        let composed_cap = Cap {
179            base: new_cap_cid.cap.base.max(cap_cid.cap.base),
180            len: new_cap_cid.cap.len.min(cap_cid.cap.len),
181            perms: new_cap_cid.cap.perms & cap_cid.cap.perms,
182            proof: new_cap_cid.cap.proof, // Keep new proof
183        };
184
185        OwnedCapCid::new(new_data, composed_cap, new_cap_cid.cid)
186    }
187}
188
189/// Phase D: Transaction with ownership tracking
190pub struct Transaction {
191    id: u64,
192    owned_resources: Vec<OwnedCapCid<Box<dyn std::any::Any + Send + Sync>>>,
193    borrowed_resources: Vec<Arc<RwLock<CapCid>>>,
194    start_time: std::time::Instant,
195    timeout_ms: u64,
196}
197
198impl Transaction {
199    pub fn new(id: u64) -> Self {
200        Self {
201            id,
202            owned_resources: Vec::new(),
203            borrowed_resources: Vec::new(),
204            start_time: std::time::Instant::now(),
205            timeout_ms: 5000, // 5 second default timeout
206        }
207    }
208
209    /// Check if transaction has timed out
210    pub fn is_expired(&self) -> bool {
211        self.start_time.elapsed().as_millis() as u64 > self.timeout_ms
212    }
213
214    /// Add owned resource to transaction
215    pub fn add_owned<T: Send + Sync + 'static>(&mut self, owned: OwnedCapCid<T>) {
216        let boxed = OwnedCapCid::new(
217            Box::new(owned.data) as Box<dyn std::any::Any + Send + Sync>,
218            owned.cap_cid.cap,
219            owned.cap_cid.cid
220        );
221        self.owned_resources.push(boxed);
222    }
223
224    /// Add borrowed resource to transaction
225    pub fn add_borrowed(&mut self, borrowed: Arc<RwLock<CapCid>>) {
226        self.borrowed_resources.push(borrowed);
227    }
228
229    /// Check if transaction has write permission for resource
230    pub async fn check_write_perm(&self, target_cid: &Cid) -> Result<(), ConcurError> {
231        // Check owned resources first
232        for owned in &self.owned_resources {
233            if owned.cap_cid.cid == *target_cid {
234                if owned.cap_cid.cap.has_perm(perms::WRITE) {
235                    return Ok(());
236                } else {
237                    return Err(ConcurError::PermissionDenied);
238                }
239            }
240        }
241
242        // Check borrowed resources
243        for borrowed in &self.borrowed_resources {
244            let cap_cid = borrowed.read().await;
245            if cap_cid.cid == *target_cid {
246                if cap_cid.cap.has_perm(perms::WRITE) {
247                    return Ok(());
248                } else {
249                    return Err(ConcurError::PermissionDenied);
250                }
251            }
252        }
253
254        Err(ConcurError::PermissionDenied)
255    }
256}
257
258/// Phase D: Lease management for capability expiration
259pub struct LeaseManager {
260    active_leases: Arc<RwLock<std::collections::HashMap<u64, LeaseInfo>>>,
261}
262
263#[derive(Clone)]
264pub struct LeaseInfo {
265    pub resource_id: u64,
266    pub holder: String,
267    pub permissions: u32,
268    pub expires_at: u64,
269    pub auto_renew: bool,
270}
271
272impl LeaseManager {
273    pub fn new() -> Self {
274        Self {
275            active_leases: Arc::new(RwLock::new(std::collections::HashMap::new())),
276        }
277    }
278
279    /// Grant lease for resource
280    pub async fn grant_lease(&self, lease_id: u64, info: LeaseInfo) -> Result<(), ConcurError> {
281        let mut leases = self.active_leases.write().await;
282        leases.insert(lease_id, info);
283        Ok(())
284    }
285
286    /// Check if lease is valid
287    pub async fn check_lease(&self, lease_id: u64) -> Result<LeaseInfo, ConcurError> {
288        let leases = self.active_leases.read().await;
289        match leases.get(&lease_id) {
290            Some(info) => {
291                let now = std::time::SystemTime::now()
292                    .duration_since(std::time::UNIX_EPOCH)
293                    .unwrap()
294                    .as_secs();
295
296                if now > info.expires_at {
297                    return Err(ConcurError::LeaseExpired);
298                }
299
300                Ok(info.clone())
301            }
302            None => Err(ConcurError::LeaseExpired),
303        }
304    }
305
306    /// Revoke lease
307    pub async fn revoke_lease(&self, lease_id: u64) -> Result<(), ConcurError> {
308        let mut leases = self.active_leases.write().await;
309        leases.remove(&lease_id);
310        Ok(())
311    }
312
313    /// Renew lease if auto-renew is enabled
314    pub async fn renew_lease(&self, lease_id: u64, new_expiry: u64) -> Result<(), ConcurError> {
315        let mut leases = self.active_leases.write().await;
316        if let Some(info) = leases.get_mut(&lease_id) {
317            if info.auto_renew {
318                info.expires_at = new_expiry;
319                Ok(())
320            } else {
321                Err(ConcurError::PermissionDenied)
322            }
323        } else {
324            Err(ConcurError::LeaseExpired)
325        }
326    }
327}
328
329/// Phase D: Resource Manager with ownership tracking
330pub struct ResourceManager {
331    resources: Arc<RwLock<std::collections::HashMap<Cid, Arc<RwLock<CapCid>>>>>,
332    lease_manager: LeaseManager,
333    next_txn_id: Arc<Mutex<u64>>,
334}
335
336impl ResourceManager {
337    pub fn new() -> Self {
338        Self {
339            resources: Arc::new(RwLock::new(std::collections::HashMap::new())),
340            lease_manager: LeaseManager::new(),
341            next_txn_id: Arc::new(Mutex::new(1)),
342        }
343    }
344
345    /// Create new transaction
346    pub async fn begin_transaction(&self) -> Result<Transaction, ConcurError> {
347        let mut next_id = self.next_txn_id.lock().await;
348        let txn_id = *next_id;
349        *next_id += 1;
350
351        Ok(Transaction::new(txn_id))
352    }
353
354    /// Register resource with capability
355    pub async fn register_resource(&self, cid: Cid, cap: Cap) -> Result<(), ConcurError> {
356        let cap_cid = CapCid::new(cid, cap);
357        let mut resources = self.resources.write().await;
358        resources.insert(cid, Arc::new(RwLock::new(cap_cid)));
359        Ok(())
360    }
361
362    /// Acquire exclusive ownership (mutable borrow)
363    pub async fn acquire_exclusive(&self, cid: &Cid, txn: &mut Transaction) -> Result<(), ConcurError> {
364        let resources = self.resources.read().await;
365        if let Some(resource) = resources.get(cid) {
366            txn.check_write_perm(cid).await?;
367            txn.add_borrowed(resource.clone());
368            Ok(())
369        } else {
370            Err(ConcurError::OwnershipViolation)
371        }
372    }
373
374    /// Acquire shared ownership (immutable borrow)
375    pub async fn acquire_shared(&self, cid: &Cid, txn: &mut Transaction) -> Result<(), ConcurError> {
376        let resources = self.resources.read().await;
377        if let Some(resource) = resources.get(cid) {
378            txn.add_borrowed(resource.clone());
379            Ok(())
380        } else {
381            Err(ConcurError::OwnershipViolation)
382        }
383    }
384
385    /// Commit transaction with ownership transfer
386    pub async fn commit_transaction(&self, txn: Transaction) -> Result<(), ConcurError> {
387        if txn.is_expired() {
388            return Err(ConcurError::TransactionConflict);
389        }
390
391        // Validate all capability checks
392        for borrowed in &txn.borrowed_resources {
393            let cap_cid = borrowed.read().await;
394            // Additional validation could be added here
395        }
396
397        // Transaction committed successfully
398        Ok(())
399    }
400
401    /// Abort transaction and release resources
402    pub async fn abort_transaction(&self, txn: Transaction) -> Result<(), ConcurError> {
403        // Resources are automatically released when transaction is dropped
404        // due to Rust's ownership system
405        Ok(())
406    }
407}
408
409/// Phase D: Capability Tracer for audit trail
410pub struct CapTracer {
411    trace_log: Arc<RwLock<Vec<CapTraceEntry>>>,
412}
413
414#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
415pub struct CapTraceEntry {
416    pub timestamp: u64,
417    pub operation: String,
418    pub actor: String,
419    pub resource: Cid,
420    pub capability: Cap,
421    pub success: bool,
422    pub details: String,
423}
424
425impl CapTracer {
426    pub fn new() -> Self {
427        Self {
428            trace_log: Arc::new(RwLock::new(Vec::new())),
429        }
430    }
431
432    /// Record capability operation
433    pub async fn record_operation(
434        &self,
435        operation: &str,
436        actor: &str,
437        resource: &Cid,
438        capability: &Cap,
439        success: bool,
440        details: &str,
441    ) {
442        let entry = CapTraceEntry {
443            timestamp: std::time::SystemTime::now()
444                .duration_since(std::time::UNIX_EPOCH)
445                .unwrap()
446                .as_secs(),
447            operation: operation.to_string(),
448            actor: actor.to_string(),
449            resource: *resource,
450            capability: *capability,
451            success,
452            details: details.to_string(),
453        };
454
455        let mut log = self.trace_log.write().await;
456        log.push(entry);
457
458        // Keep only recent entries (last 1000)
459        if log.len() > 1000 {
460            log.remove(0);
461        }
462    }
463
464    /// Get audit trail for resource
465    pub async fn get_audit_trail(&self, resource: &Cid) -> Vec<CapTraceEntry> {
466        let log = self.trace_log.read().await;
467        log.iter()
468            .filter(|entry| entry.resource == *resource)
469            .cloned()
470            .collect()
471    }
472
473    /// Get operations by actor
474    pub async fn get_actor_operations(&self, actor: &str) -> Vec<CapTraceEntry> {
475        let log = self.trace_log.read().await;
476        log.iter()
477            .filter(|entry| entry.actor == actor)
478            .cloned()
479            .collect()
480    }
481}
482
483/// Phase D: Safe wrapper for concurrent operations
484pub struct SafeExecutor {
485    resource_manager: ResourceManager,
486    tracer: CapTracer,
487}
488
489impl SafeExecutor {
490    pub fn new() -> Self {
491        Self {
492            resource_manager: ResourceManager::new(),
493            tracer: CapTracer::new(),
494        }
495    }
496
497    /// Execute operation with full Own+CFA safety
498    pub async fn execute_safe<F, Fut, T>(
499        &self,
500        actor: &str,
501        operation: &str,
502        resource: &Cid,
503        cap_check: F,
504    ) -> Result<T, ConcurError>
505    where
506        F: FnOnce() -> Fut,
507        Fut: std::future::Future<Output = Result<T, ConcurError>>,
508    {
509        // Pre-operation capability check
510        let mut txn = self.resource_manager.begin_transaction().await?;
511        self.resource_manager.acquire_shared(resource, &mut txn).await?;
512
513        let cap_cid = {
514            let resources = self.resource_manager.resources.read().await;
515            let temp = resources.get(resource)
516                .ok_or(ConcurError::OwnershipViolation)?
517                .read().await
518                .clone();
519            temp
520        };
521
522        // Execute operation
523        let result = cap_check().await;
524
525        // Record result in audit trail
526        let success = result.is_ok();
527        let details = if success { "success" } else { "failed" };
528        self.tracer.record_operation(
529            operation,
530            actor,
531            resource,
532            &cap_cid.cap,
533            success,
534            details,
535        ).await;
536
537        // Commit or abort transaction
538        match result {
539            Ok(value) => {
540                self.resource_manager.commit_transaction(txn).await?;
541                Ok(value)
542            }
543            Err(e) => {
544                self.resource_manager.abort_transaction(txn).await?;
545                Err(e)
546            }
547        }
548    }
549}
550
551#[cfg(test)]
552mod tests {
553    use super::*;
554
555    #[tokio::test]
556    async fn test_owned_cap_cid() {
557        let data = "test data".to_string();
558        let cap = Cap::new(0, 100, perms::READ | perms::WRITE);
559        let cid = Cid::hash(data.as_bytes());
560
561        let owned = OwnedCapCid::new(data, cap, cid);
562
563        // Test borrowing
564        {
565            let (cap_ref, data_ref) = owned.as_ref();
566            assert!(cap_ref.has_perm(perms::READ));
567            assert_eq!(data_ref, "test data");
568        }
569
570        // Test mutable borrowing
571        let mut owned = owned;
572        {
573            let (cap_mut, data_mut) = owned.as_mut();
574            *data_mut = "modified".to_string();
575            cap_mut.perms &= !perms::WRITE; // Remove write permission
576        }
577
578        // Verify changes
579        let (final_cap, final_data) = owned.as_ref();
580        assert_eq!(final_data, "modified");
581        assert!(!final_cap.has_perm(perms::WRITE));
582    }
583
584    #[tokio::test]
585    async fn test_capability_functor() {
586        let data = 42;
587        let cap = Cap::new(0, 100, perms::READ | perms::WRITE);
588        let cid = Cid::hash(&data.to_le_bytes());
589
590        let owned = OwnedCapCid::new(data, cap, cid);
591
592        // Test map operation
593        let mapped = owned.cap_map(|x| x * 2);
594        let (_, result) = mapped.as_ref();
595        assert_eq!(*result, 84);
596    }
597
598    #[tokio::test]
599    async fn test_transaction_lifecycle() {
600        let rm = ResourceManager::new();
601        let cid = Cid::hash(b"test resource");
602        let cap = Cap::new(0, 100, perms::READ | perms::WRITE);
603
604        // Register resource
605        rm.register_resource(cid, cap).await.unwrap();
606
607        // Begin transaction
608        let mut txn = rm.begin_transaction().await.unwrap();
609
610        // Acquire resource
611        rm.acquire_exclusive(&cid, &mut txn).await.unwrap();
612
613        // Check permissions
614        assert!(txn.check_write_perm(&cid).await.is_ok());
615
616        // Commit transaction
617        rm.commit_transaction(txn).await.unwrap();
618    }
619
620    #[tokio::test]
621    async fn test_lease_management() {
622        let lm = LeaseManager::new();
623        let lease_id = 12345;
624
625        let info = LeaseInfo {
626            resource_id: 1,
627            holder: "test_user".to_string(),
628            permissions: perms::READ | perms::WRITE,
629            expires_at: std::time::SystemTime::now()
630                .duration_since(std::time::UNIX_EPOCH)
631                .unwrap()
632                .as_secs() + 3600, // 1 hour from now
633            auto_renew: true,
634        };
635
636        // Grant lease
637        lm.grant_lease(lease_id, info.clone()).await.unwrap();
638
639        // Check valid lease
640        let checked = lm.check_lease(lease_id).await.unwrap();
641        assert_eq!(checked.holder, "test_user");
642
643        // Revoke lease
644        lm.revoke_lease(lease_id).await.unwrap();
645
646        // Check should fail
647        assert!(lm.check_lease(lease_id).await.is_err());
648    }
649
650    #[tokio::test]
651    async fn test_capability_tracing() {
652        let tracer = CapTracer::new();
653        let cid = Cid::hash(b"test resource");
654        let cap = Cap::new(0, 100, perms::READ);
655
656        // Record operations
657        tracer.record_operation(
658            "read",
659            "alice",
660            &cid,
661            &cap,
662            true,
663            "successful read"
664        ).await;
665
666        tracer.record_operation(
667            "write",
668            "bob",
669            &cid,
670            &cap,
671            false,
672            "permission denied"
673        ).await;
674
675        // Check audit trail
676        let alice_ops = tracer.get_actor_operations("alice").await;
677        assert_eq!(alice_ops.len(), 1);
678        assert_eq!(alice_ops[0].operation, "read");
679        assert!(alice_ops[0].success);
680
681        let resource_trail = tracer.get_audit_trail(&cid).await;
682        assert_eq!(resource_trail.len(), 2);
683    }
684}