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    /// Lease duration must be non-zero.
353    #[error("Lease duration must be non-zero")]
354    InvalidDuration,
355}
356
357/// Represents a lease on a Sovereign resource.
358pub struct Lease<T> {
359    /// The holder's unique identifier.
360    pub holder: u128,
361    /// Duration of the lease.
362    pub duration: core::time::Duration,
363    /// Phantom data for the resource type.
364    _phantom: PhantomData<T>,
365}
366
367impl<T> Lease<T> {
368    /// Creates a new lease.
369    ///
370    /// # Duration Validation
371    ///
372    /// If `duration` is zero, returns `Err(LeaseError::InvalidDuration)`.
373    pub fn new(holder: u128, duration: core::time::Duration) -> Result<Self, LeaseError> {
374        if duration.is_zero() {
375            return Err(LeaseError::InvalidDuration);
376        }
377
378        Ok(Self {
379            holder,
380            duration,
381            _phantom: PhantomData,
382        })
383    }
384
385    /// Returns the duration of the lease.
386    #[inline]
387    pub fn duration(&self) -> core::time::Duration {
388        self.duration
389    }
390
391    /// Returns the holder ID.
392    #[inline]
393    pub fn holder(&self) -> u128 {
394        self.holder
395    }
396}
397
398/// Trait for distributed borrow operations.
399pub trait DistributedBorrow<T> {
400    /// Attempt to acquire a lease on the resource.
401    fn try_hire(
402        &self,
403        candidate_id: u128,
404        term: core::time::Duration,
405    ) -> Result<Lease<T>, LeaseError>;
406}
407
408impl<T> DistributedBorrow<T> for Sovereign<T> {
409    fn try_hire(
410        &self,
411        candidate_id: u128,
412        term: core::time::Duration,
413    ) -> Result<Lease<T>, LeaseError> {
414        let current = self.state.load(Ordering::SeqCst);
415        if current == SovereignState::Exiled as u8 {
416            return Err(LeaseError::AlreadyLeased);
417        }
418
419        // Transition to exiled state (leased)
420        self.state
421            .store(SovereignState::Exiled as u8, Ordering::SeqCst);
422        Lease::<T>::new(candidate_id, term)
423    }
424}
425
426/// Extension trait for Sovereign types whose inner types implement formal verification.
427///
428/// This trait is automatically implemented for `Sovereign<T>` where `T` can be
429/// formally verified via the Garuda Proof System.
430pub trait VerifiedAnnex<T> {
431    /// Annexes the resource after successful formal verification.
432    ///
433    /// Unlike `annex()`, this method requires mathematical proof that all
434    /// invariants are satisfied before the state transition occurs.
435    ///
436    /// # Returns
437    ///
438    /// - `Ok(ProofCarrying<()>)` - Verification passed, resource is now Exiled
439    /// - `Err(AnnexError)` - Verification failed or resource already Exiled
440    ///
441    /// # Example
442    ///
443    /// ```ignore
444    /// use praborrow_core::{Sovereign, VerifiedAnnex};
445    ///
446    /// let resource = Sovereign::new(MyVerifiableStruct { balance: 100 });
447    ///
448    /// // This will run SMT verification before annexing
449    /// match resource.annex_verified() {
450    ///     Ok(proof) => println!("Annexation proven safe!"),
451    ///     Err(e) => println!("Cannot annex: {}", e),
452    /// }
453    /// ```
454    fn annex_verified(&self) -> Result<ProofCarrying<()>, AnnexError>;
455}
456
457// Note: The actual implementation of VerifiedAnnex requires praborrow-prover,
458// which would create a circular dependency. Instead, the implementation is
459// provided via blanket impl in praborrow-prover or via the facade crate.
460//
461// Users should use the `praborrow` facade crate for full functionality.
462
463#[cfg(test)]
464mod tests {
465    use super::*;
466
467    #[test]
468    fn test_sovereign_new() {
469        let s = Sovereign::new(42i32);
470        assert_eq!(s.state(), SovereignState::Domestic);
471        assert!(s.is_domestic());
472        assert!(!s.is_exiled());
473    }
474
475    #[test]
476    fn test_sovereign_new_exiled() {
477        let s = Sovereign::new_exiled(42i32);
478        assert_eq!(s.state(), SovereignState::Exiled);
479        assert!(s.is_exiled());
480        assert!(!s.is_domestic());
481    }
482
483    #[test]
484    fn test_sovereign_deref() {
485        let s = Sovereign::new(42i32);
486        assert_eq!(*s, 42);
487    }
488
489    #[test]
490    fn test_sovereign_deref_mut() {
491        let mut s = Sovereign::new(42i32);
492        *s = 100;
493        assert_eq!(*s, 100);
494    }
495
496    #[test]
497    fn test_sovereign_annex() {
498        let s = Sovereign::new(42i32);
499        assert!(s.annex().is_ok());
500        assert_eq!(s.state(), SovereignState::Exiled);
501        assert!(s.is_exiled());
502    }
503
504    #[test]
505    fn test_sovereign_double_annex() {
506        let s = Sovereign::new(42i32);
507        assert!(s.annex().is_ok());
508        assert!(s.annex().is_err());
509    }
510
511    #[test]
512    fn test_sovereign_repatriate() {
513        let s = Sovereign::new(42i32);
514        s.annex().unwrap();
515        assert!(s.is_exiled());
516
517        // SAFETY: In test context, we control both sides
518        // SAFETY: In test context, we control both sides
519        let token = unsafe { RepatriationToken::new() };
520        s.repatriate(token);
521        assert!(s.is_domestic());
522
523        // Should be able to access again
524        assert_eq!(*s, 42);
525    }
526
527    #[test]
528    #[should_panic(expected = "SOVEREIGNTY VIOLATION")]
529    fn test_sovereignty_violation() {
530        let s = Sovereign::new(42i32);
531        s.annex().unwrap();
532        let _ = *s; // This should panic
533    }
534
535    #[test]
536    fn test_try_get_domestic() {
537        let s = Sovereign::new(42i32);
538        assert_eq!(*s.try_get().unwrap(), 42);
539    }
540
541    #[test]
542    fn test_try_get_exiled() {
543        let s = Sovereign::new(42i32);
544        s.annex().unwrap();
545        assert!(matches!(
546            s.try_get(),
547            Err(SovereigntyError::ForeignJurisdiction)
548        ));
549    }
550
551    #[test]
552    fn test_proof_carrying() {
553        let proof = ProofCarrying::new_unchecked(42i32);
554        assert_eq!(proof.value, 42);
555        assert_eq!(proof.into_inner(), 42);
556    }
557
558    #[test]
559    fn test_annex_error_display() {
560        let e = AnnexError::AlreadyExiled;
561        assert!(e.to_string().contains("foreign jurisdiction"));
562
563        let e = AnnexError::VerificationFailed("test".to_string());
564        assert!(e.to_string().contains("test"));
565    }
566
567    #[test]
568    fn test_lease_zero_duration_fails() {
569        let lease = Lease::<i32>::new(1, core::time::Duration::ZERO);
570        assert!(matches!(lease, Err(LeaseError::InvalidDuration)));
571    }
572
573    #[test]
574    fn test_lease_normal_duration() {
575        let duration = core::time::Duration::from_secs(10);
576        let lease = Lease::<i32>::new(1, duration).unwrap();
577        assert_eq!(lease.duration(), duration);
578        assert_eq!(lease.holder(), 1);
579    }
580}