praborrow_core/
lib.rs

1//! Core primitives for distributed ownership enforcement.
2//!
3//! This crate provides `Sovereign<T>`, a wrapper type that tracks ownership
4//! across network boundaries. When a resource is "annexed" (moved to another node),
5//! local access is prohibited.
6//!
7//! # The Garuda Proof System
8//!
9//! With `praborrow-prover`, this crate now supports **formally verified** state
10//! transitions. Use `annex_verified()` to require SMT proof before annexation.
11//!
12//! # Safety
13//! Uses `UnsafeCell` and `AtomicU8` for interior mutability with thread-safety.
14//! The `Send`/`Sync` implementations are safe when `T` is `Send`/`Sync`.
15
16#![cfg_attr(not(feature = "std"), no_std)]
17
18extern crate alloc;
19
20use alloc::string::String;
21use core::cell::UnsafeCell;
22use core::marker::PhantomData;
23use core::ops::{Deref, DerefMut};
24use core::sync::atomic::{AtomicU8, Ordering};
25
26/// The state of a Sovereign resource.
27/// 0: Domestic (Local jurisdiction)
28/// 1: Exiled (Foreign jurisdiction - moved to another node)
29#[derive(Debug, PartialEq, Eq, Clone, Copy)]
30#[repr(u8)]
31pub enum SovereignState {
32    Domestic = 0,
33    Exiled = 1,
34}
35
36/// A token that proves a resource has been returned to domestic jurisdiction.
37///
38/// This token is required to call `Sovereign::repatriate`. It can only be constructed
39/// by trusted system components (like the lease manager) that can guarantee the
40/// resource is safe to reclaim.
41pub struct RepatriationToken {
42    _private: (),
43}
44
45impl RepatriationToken {
46    /// Creates a new repatriation token.
47    ///
48    /// # Safety
49    ///
50    /// This function is unsafe because creating a token allows the holder to
51    /// repatriate a sovereign resource. The caller must guarantee that the
52    /// resource is indeed back in domestic jurisdiction and no longer accessed
53    /// remotely.
54    pub unsafe fn new() -> Self {
55        Self { _private: () }
56    }
57}
58
59/// A wrapper that enforces ownership semantics across network boundaries.
60///
61/// "Memory safety with sovereign integrity."
62pub struct Sovereign<T> {
63    inner: UnsafeCell<T>,
64    state: AtomicU8,
65}
66
67/// Error enforcing constitutional invariants.
68#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
69pub enum ConstitutionError {
70    #[error("Invariant violated: {0}")]
71    InvariantViolation(String),
72}
73
74/// Error returned when accessing a Sovereign resource fails.
75#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
76pub enum SovereigntyError {
77    /// Resource is under foreign jurisdiction (Exiled).
78    #[error("SOVEREIGNTY VIOLATION: Resource is under foreign jurisdiction.")]
79    ForeignJurisdiction,
80}
81
82impl<T> Sovereign<T> {
83    /// Creates a new Sovereign resource under domestic jurisdiction.
84    ///
85    /// # Atomic Ordering
86    ///
87    /// Uses `SeqCst` ordering for maximum safety in distributed scenarios.
88    /// This ensures all threads see state changes in a consistent order,
89    /// which is critical for ownership semantics across network boundaries.
90    pub fn new(value: T) -> Self {
91        Self {
92            inner: UnsafeCell::new(value),
93            state: AtomicU8::new(SovereignState::Domestic as u8),
94        }
95    }
96
97    /// Creates a new Sovereign resource that is already under foreign jurisdiction.
98    ///
99    /// This is useful for nodes receiving data that has been transferred from
100    /// another node - the resource starts in an Exiled state.
101    ///
102    /// # Example
103    ///
104    /// ```
105    /// use praborrow_core::{Sovereign, SovereignState};
106    ///
107    /// let foreign_data = Sovereign::new_exiled(42i32);
108    /// assert!(foreign_data.is_exiled());
109    /// ```
110    pub fn new_exiled(value: T) -> Self {
111        Self {
112            inner: UnsafeCell::new(value),
113            state: AtomicU8::new(SovereignState::Exiled as u8),
114        }
115    }
116
117    /// Annexes the resource, moving it to foreign jurisdiction.
118    ///
119    /// Once annexed, the resource cannot be accessed locally.
120    /// Access attempts will result in a Sovereignty Violation.
121    pub fn annex(&self) -> Result<(), AnnexError> {
122        let current = self.state.load(Ordering::SeqCst);
123        if current == SovereignState::Exiled as u8 {
124            return Err(AnnexError::AlreadyExiled);
125        }
126
127        // Diplomatically transition state
128        self.state
129            .store(SovereignState::Exiled as u8, Ordering::SeqCst);
130
131        tracing::debug!(
132            from = "Domestic",
133            to = "Exiled",
134            "Resource annexed to foreign jurisdiction"
135        );
136
137        Ok(())
138    }
139
140    /// Returns a reference to the inner value without jurisdiction check.
141    ///
142    /// # Safety
143    ///
144    /// This is safe because we're returning a shared reference and the caller
145    /// is responsible for ensuring the resource is domestic. Use `try_get()`
146    /// for safe access with jurisdiction verification.
147    pub fn inner_ref(&self) -> &T {
148        // SAFETY: We're only reading, and this is safe when called from
149        // contexts that have already verified jurisdiction.
150        unsafe { &*self.inner.get() }
151    }
152
153    /// Returns the current state of the resource.
154    #[inline]
155    pub fn state(&self) -> SovereignState {
156        match self.state.load(Ordering::SeqCst) {
157            0 => SovereignState::Domestic,
158            _ => SovereignState::Exiled,
159        }
160    }
161
162    /// Returns `true` if the resource is under domestic jurisdiction.
163    #[inline]
164    pub fn is_domestic(&self) -> bool {
165        self.state.load(Ordering::SeqCst) == SovereignState::Domestic as u8
166    }
167
168    /// Returns `true` if the resource is under foreign jurisdiction (exiled).
169    #[inline]
170    pub fn is_exiled(&self) -> bool {
171        self.state.load(Ordering::SeqCst) == SovereignState::Exiled as u8
172    }
173
174    /// Attempts to get a reference to the value, returning an error if Exiled.
175    pub fn try_get(&self) -> Result<&T, SovereigntyError> {
176        if self.is_exiled() {
177            return Err(SovereigntyError::ForeignJurisdiction);
178        }
179        // SAFETY: We verified the resource is domestic.
180        unsafe { Ok(&*self.inner.get()) }
181    }
182
183    /// Attempts to get a mutable reference to the value, returning an error if Exiled.
184    pub fn try_get_mut(&mut self) -> Result<&mut T, SovereigntyError> {
185        if self.is_exiled() {
186            return Err(SovereigntyError::ForeignJurisdiction);
187        }
188        // SAFETY: We verified resource is domestic and have &mut self.
189        unsafe { Ok(&mut *self.inner.get()) }
190    }
191
192    /// Repatriates a resource, transitioning it from Exiled back to Domestic.
193    ///
194    /// Requires a `RepatriationToken` as proof that the resource is safe to reclaim.
195    ///
196    /// # Example
197    ///
198    /// ```
199    /// use praborrow_core::{Sovereign, SovereignState, RepatriationToken};
200    ///
201    /// let resource = Sovereign::new(42i32);
202    /// resource.annex().unwrap();
203    /// assert!(resource.is_exiled());
204    ///
205    /// // ... resource is sent to foreign node and returned ...
206    ///
207    /// // SAFETY: construction of token implies safety verification
208    /// let token = unsafe { RepatriationToken::new() };
209    /// resource.repatriate(token);
210    /// assert!(resource.is_domestic());
211    /// ```
212    pub fn repatriate(&self, _token: RepatriationToken) {
213        let previous = self
214            .state
215            .swap(SovereignState::Domestic as u8, Ordering::SeqCst);
216
217        if previous == SovereignState::Exiled as u8 {
218            tracing::debug!(
219                from = "Exiled",
220                to = "Domestic",
221                "Resource repatriated to domestic jurisdiction"
222            );
223        }
224    }
225
226    /// Checks if the resource is currently domestic, panicking if not.
227    ///
228    /// Used internally by Deref/DerefMut. Prefer `try_get()` for non-panicking access.
229    fn verify_jurisdiction(&self) {
230        if self.is_exiled() {
231            panic!("SOVEREIGNTY VIOLATION: Resource is under foreign jurisdiction.");
232        }
233    }
234}
235
236impl<T> Deref for Sovereign<T> {
237    type Target = T;
238
239    fn deref(&self) -> &Self::Target {
240        self.verify_jurisdiction();
241        // SAFETY: We've verified the resource is domestic, so access is valid.
242        unsafe { &*self.inner.get() }
243    }
244}
245
246impl<T> DerefMut for Sovereign<T> {
247    fn deref_mut(&mut self) -> &mut Self::Target {
248        self.verify_jurisdiction();
249        // SAFETY: We've verified the resource is domestic and have &mut self.
250        unsafe { &mut *self.inner.get() }
251    }
252}
253
254// SAFETY: Sovereign<T> is Send/Sync if T is Send/Sync, as we use AtomicU8 for state
255// and check it before access. The UnsafeCell is protected by the atomic state check.
256unsafe impl<T: Send> Send for Sovereign<T> {}
257unsafe impl<T: Sync> Sync for Sovereign<T> {}
258
259/// Protocol for enforcing constitutional invariants (runtime checks).
260///
261/// Types implementing this trait can validate their internal state against
262/// a set of invariants defined via the `#[derive(Constitution)]` macro.
263pub trait CheckProtocol {
264    /// Enforces all invariants, returning an error if any are violated.
265    ///
266    /// # Returns
267    ///
268    /// - `Ok(())` if all invariants are satisfied
269    /// - `Err(ConstitutionError)` containing a description of the violated invariant
270    ///
271    /// # Example
272    ///
273    /// ```ignore
274    /// use praborrow_core::CheckProtocol;
275    ///
276    /// let data = MyStruct { value: -1 };
277    /// match data.enforce_law() {
278    ///     Ok(()) => println!("All invariants satisfied"),
279    ///     Err(e) => println!("Invariant violated: {}", e),
280    /// }
281    /// ```
282    fn enforce_law(&self) -> Result<(), ConstitutionError>;
283}
284
285/// A value carrying cryptographic proof of verification.
286///
287/// This type can only be constructed by successful SMT verification.
288/// Its existence in a type signature proves that formal verification occurred.
289///
290/// # Type Safety Guarantee
291///
292/// `ProofCarrying<T>` cannot be forged - the private `_proof` field
293/// ensures only the prover crate can construct it.
294#[derive(Debug)]
295pub struct ProofCarrying<T> {
296    /// The carried value.
297    pub value: T,
298    /// Private marker ensuring construction only via verification.
299    _proof: PhantomData<()>,
300}
301
302impl<T> ProofCarrying<T> {
303    /// Creates a new proof-carrying value.
304    ///
305    /// This should only be called after successful verification.
306    #[doc(hidden)]
307    pub fn new_unchecked(value: T) -> Self {
308        Self {
309            value,
310            _proof: PhantomData,
311        }
312    }
313
314    /// Extracts the inner value, consuming the proof.
315    pub fn into_inner(self) -> T {
316        self.value
317    }
318}
319
320impl<T: Clone> Clone for ProofCarrying<T> {
321    fn clone(&self) -> Self {
322        Self {
323            value: self.value.clone(),
324            _proof: PhantomData,
325        }
326    }
327}
328
329/// Error type for verified annexation operations.
330#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
331pub enum AnnexError {
332    /// Resource is already under foreign jurisdiction.
333    #[error("Resource is already under foreign jurisdiction")]
334    AlreadyExiled,
335    /// SMT verification failed.
336    #[error("Verification failed: {0}")]
337    VerificationFailed(String),
338    /// Prover encountered an error.
339    #[error("Prover error: {0}")]
340    ProverError(String),
341}
342
343/// Error returned when a lease operation fails.
344#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
345pub enum LeaseError {
346    /// Resource is already leased to another holder.
347    #[error("Resource is already leased to another holder")]
348    AlreadyLeased,
349    /// Resource is under foreign jurisdiction.
350    #[error("Resource is under foreign jurisdiction")]
351    ForeignJurisdiction,
352}
353
354/// Represents a lease on a Sovereign resource.
355pub struct Lease<T> {
356    /// The holder's unique identifier.
357    pub holder: u128,
358    /// Duration of the lease.
359    pub duration: core::time::Duration,
360    /// Phantom data for the resource type.
361    _phantom: PhantomData<T>,
362}
363
364impl<T> Lease<T> {
365    /// Creates a new lease.
366    ///
367    /// # Duration Validation
368    ///
369    /// If `duration` is zero, it will be coerced to a minimum of 1 millisecond
370    /// to prevent immediate expiration edge cases.
371    pub fn new(holder: u128, duration: core::time::Duration) -> Self {
372        // Prevent zero-duration leases which could cause immediate expiration
373        let duration = if duration.is_zero() {
374            tracing::warn!(
375                holder = holder,
376                "Zero-duration lease requested, coercing to 1ms minimum"
377            );
378            core::time::Duration::from_millis(1)
379        } else {
380            duration
381        };
382
383        Self {
384            holder,
385            duration,
386            _phantom: PhantomData,
387        }
388    }
389
390    /// Returns the duration of the lease.
391    #[inline]
392    pub fn duration(&self) -> core::time::Duration {
393        self.duration
394    }
395
396    /// Returns the holder ID.
397    #[inline]
398    pub fn holder(&self) -> u128 {
399        self.holder
400    }
401}
402
403/// Trait for distributed borrow operations.
404pub trait DistributedBorrow<T> {
405    /// Attempt to acquire a lease on the resource.
406    fn try_hire(
407        &self,
408        candidate_id: u128,
409        term: core::time::Duration,
410    ) -> Result<Lease<T>, LeaseError>;
411}
412
413impl<T> DistributedBorrow<T> for Sovereign<T> {
414    fn try_hire(
415        &self,
416        candidate_id: u128,
417        term: core::time::Duration,
418    ) -> Result<Lease<T>, LeaseError> {
419        let current = self.state.load(Ordering::SeqCst);
420        if current == SovereignState::Exiled as u8 {
421            return Err(LeaseError::AlreadyLeased);
422        }
423
424        // Transition to exiled state (leased)
425        self.state
426            .store(SovereignState::Exiled as u8, Ordering::SeqCst);
427        Ok(Lease::<T>::new(candidate_id, term))
428    }
429}
430
431/// Extension trait for Sovereign types whose inner types implement formal verification.
432///
433/// This trait is automatically implemented for `Sovereign<T>` where `T` can be
434/// formally verified via the Garuda Proof System.
435pub trait VerifiedAnnex<T> {
436    /// Annexes the resource after successful formal verification.
437    ///
438    /// Unlike `annex()`, this method requires mathematical proof that all
439    /// invariants are satisfied before the state transition occurs.
440    ///
441    /// # Returns
442    ///
443    /// - `Ok(ProofCarrying<()>)` - Verification passed, resource is now Exiled
444    /// - `Err(AnnexError)` - Verification failed or resource already Exiled
445    ///
446    /// # Example
447    ///
448    /// ```ignore
449    /// use praborrow_core::{Sovereign, VerifiedAnnex};
450    ///
451    /// let resource = Sovereign::new(MyVerifiableStruct { balance: 100 });
452    ///
453    /// // This will run SMT verification before annexing
454    /// match resource.annex_verified() {
455    ///     Ok(proof) => println!("Annexation proven safe!"),
456    ///     Err(e) => println!("Cannot annex: {}", e),
457    /// }
458    /// ```
459    fn annex_verified(&self) -> Result<ProofCarrying<()>, AnnexError>;
460}
461
462// Note: The actual implementation of VerifiedAnnex requires praborrow-prover,
463// which would create a circular dependency. Instead, the implementation is
464// provided via blanket impl in praborrow-prover or via the facade crate.
465//
466// Users should use the `praborrow` facade crate for full functionality.
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471
472    #[test]
473    fn test_sovereign_new() {
474        let s = Sovereign::new(42i32);
475        assert_eq!(s.state(), SovereignState::Domestic);
476        assert!(s.is_domestic());
477        assert!(!s.is_exiled());
478    }
479
480    #[test]
481    fn test_sovereign_new_exiled() {
482        let s = Sovereign::new_exiled(42i32);
483        assert_eq!(s.state(), SovereignState::Exiled);
484        assert!(s.is_exiled());
485        assert!(!s.is_domestic());
486    }
487
488    #[test]
489    fn test_sovereign_deref() {
490        let s = Sovereign::new(42i32);
491        assert_eq!(*s, 42);
492    }
493
494    #[test]
495    fn test_sovereign_deref_mut() {
496        let mut s = Sovereign::new(42i32);
497        *s = 100;
498        assert_eq!(*s, 100);
499    }
500
501    #[test]
502    fn test_sovereign_annex() {
503        let s = Sovereign::new(42i32);
504        assert!(s.annex().is_ok());
505        assert_eq!(s.state(), SovereignState::Exiled);
506        assert!(s.is_exiled());
507    }
508
509    #[test]
510    fn test_sovereign_double_annex() {
511        let s = Sovereign::new(42i32);
512        assert!(s.annex().is_ok());
513        assert!(s.annex().is_err());
514    }
515
516    #[test]
517    fn test_sovereign_repatriate() {
518        let s = Sovereign::new(42i32);
519        s.annex().unwrap();
520        assert!(s.is_exiled());
521
522        // SAFETY: In test context, we control both sides
523        // SAFETY: In test context, we control both sides
524        let token = unsafe { RepatriationToken::new() };
525        s.repatriate(token);
526        assert!(s.is_domestic());
527
528        // Should be able to access again
529        assert_eq!(*s, 42);
530    }
531
532    #[test]
533    #[should_panic(expected = "SOVEREIGNTY VIOLATION")]
534    fn test_sovereignty_violation() {
535        let s = Sovereign::new(42i32);
536        s.annex().unwrap();
537        let _ = *s; // This should panic
538    }
539
540    #[test]
541    fn test_try_get_domestic() {
542        let s = Sovereign::new(42i32);
543        assert_eq!(*s.try_get().unwrap(), 42);
544    }
545
546    #[test]
547    fn test_try_get_exiled() {
548        let s = Sovereign::new(42i32);
549        s.annex().unwrap();
550        assert!(matches!(
551            s.try_get(),
552            Err(SovereigntyError::ForeignJurisdiction)
553        ));
554    }
555
556    #[test]
557    fn test_proof_carrying() {
558        let proof = ProofCarrying::new_unchecked(42i32);
559        assert_eq!(proof.value, 42);
560        assert_eq!(proof.into_inner(), 42);
561    }
562
563    #[test]
564    fn test_annex_error_display() {
565        let e = AnnexError::AlreadyExiled;
566        assert!(e.to_string().contains("foreign jurisdiction"));
567
568        let e = AnnexError::VerificationFailed("test".to_string());
569        assert!(e.to_string().contains("test"));
570    }
571
572    #[test]
573    fn test_lease_zero_duration_coercion() {
574        let lease = Lease::<i32>::new(1, core::time::Duration::ZERO);
575        assert_eq!(lease.duration(), core::time::Duration::from_millis(1));
576    }
577
578    #[test]
579    fn test_lease_normal_duration() {
580        let duration = core::time::Duration::from_secs(10);
581        let lease = Lease::<i32>::new(1, duration);
582        assert_eq!(lease.duration(), duration);
583        assert_eq!(lease.holder(), 1);
584    }
585}