Skip to main content

ic_memory/
session.rs

1use crate::{
2    declaration::AllocationDeclaration, key::StableKey, slot::AllocationSlotDescriptor,
3    substrate::StorageSubstrate,
4};
5use std::sync::Arc;
6
7///
8/// ValidatedAllocations
9///
10/// Pre-commit allocation declarations accepted by policy and historical ledger
11/// validation.
12///
13/// This value is produced by [`crate::validate_allocations`] and may be staged
14/// into the next ledger generation. It cannot construct an
15/// [`AllocationSession`] or open storage. Only a [`CommittedAllocations`]
16/// capability confirmed after persistence can do that.
17///
18/// This is an in-memory capability, not a serde DTO. It has no public
19/// constructor and should only be produced by validation or bootstrap paths.
20///
21
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub struct ValidatedAllocations {
24    inner: Arc<ValidatedState>,
25    _private: (),
26}
27
28#[derive(Clone, Debug, Eq, PartialEq)]
29struct ValidatedState {
30    /// Recovered generation against which these declarations were validated.
31    base_generation: u64,
32    /// Validated declarations.
33    declarations: Vec<AllocationDeclaration>,
34    /// Optional binary/runtime identity for generation diagnostics.
35    runtime_fingerprint: Option<String>,
36}
37
38impl ValidatedAllocations {
39    pub(crate) fn new(
40        base_generation: u64,
41        declarations: Vec<AllocationDeclaration>,
42        runtime_fingerprint: Option<String>,
43    ) -> Self {
44        Self {
45            inner: Arc::new(ValidatedState {
46                base_generation,
47                declarations,
48                runtime_fingerprint,
49            }),
50            _private: (),
51        }
52    }
53
54    /// Return the recovered generation used as the validation base.
55    #[must_use]
56    pub fn base_generation(&self) -> u64 {
57        self.inner.base_generation
58    }
59
60    /// Borrow the validated declarations.
61    #[must_use]
62    pub fn declarations(&self) -> &[AllocationDeclaration] {
63        &self.inner.declarations
64    }
65
66    /// Borrow the optional runtime fingerprint.
67    #[must_use]
68    pub fn runtime_fingerprint(&self) -> Option<&str> {
69        self.inner.runtime_fingerprint.as_deref()
70    }
71
72    /// Find a validated slot by stable key.
73    #[must_use]
74    pub fn slot_for(&self, key: &StableKey) -> Option<&AllocationSlotDescriptor> {
75        self.declarations()
76            .iter()
77            .find(|declaration| &declaration.stable_key == key)
78            .map(|declaration| &declaration.slot)
79    }
80
81    pub(crate) const fn confirm_persisted(self, generation: u64) -> CommittedAllocations {
82        CommittedAllocations {
83            validated: self,
84            generation,
85            _private: (),
86        }
87    }
88}
89
90///
91/// CommittedAllocations
92///
93/// Allocation-open capability confirmed after the validated ledger generation
94/// was persisted.
95///
96/// This type is not serializable, default-constructible, or publicly
97/// constructible. Generic persistence owners obtain it only by explicitly
98/// confirming a successful [`crate::PendingBootstrapCommit`]. The default runtime
99/// publishes it only after its stable cell write succeeds.
100///
101
102#[derive(Clone, Debug, Eq, PartialEq)]
103pub struct CommittedAllocations {
104    validated: ValidatedAllocations,
105    generation: u64,
106    _private: (),
107}
108
109impl CommittedAllocations {
110    /// Return the persisted ledger generation that grants this capability.
111    #[must_use]
112    pub const fn generation(&self) -> u64 {
113        self.generation
114    }
115
116    /// Borrow the committed allocation declarations.
117    #[must_use]
118    pub fn declarations(&self) -> &[AllocationDeclaration] {
119        self.validated.declarations()
120    }
121
122    /// Borrow the optional runtime fingerprint.
123    #[must_use]
124    pub fn runtime_fingerprint(&self) -> Option<&str> {
125        self.validated.runtime_fingerprint()
126    }
127
128    /// Find a committed slot by stable key.
129    #[must_use]
130    pub fn slot_for(&self, key: &StableKey) -> Option<&AllocationSlotDescriptor> {
131        self.validated.slot_for(key)
132    }
133
134    pub(crate) fn without_stable_key_prefix(mut self, prefix: &str) -> Self {
135        let mut state = (*self.validated.inner).clone();
136        state
137            .declarations
138            .retain(|declaration| !declaration.stable_key.as_str().starts_with(prefix));
139        self.validated.inner = Arc::new(state);
140        self
141    }
142}
143
144///
145/// AllocationSession
146///
147/// Persisted allocation capability required before opening allocation slots.
148///
149/// Integrations should construct sessions only after recovering the ledger,
150/// validating declarations, and committing the next generation. Opening storage
151/// through this type keeps handle creation tied to the validated stable-key
152/// snapshot.
153pub struct AllocationSession<S: StorageSubstrate> {
154    substrate: S,
155    committed: CommittedAllocations,
156}
157
158impl<S: StorageSubstrate> AllocationSession<S> {
159    /// Construct a session from a substrate and committed allocation set.
160    #[must_use]
161    pub const fn new(substrate: S, committed: CommittedAllocations) -> Self {
162        Self {
163            substrate,
164            committed,
165        }
166    }
167
168    /// Borrow the committed allocation set.
169    #[must_use]
170    pub const fn committed(&self) -> &CommittedAllocations {
171        &self.committed
172    }
173
174    /// Open an allocation by stable key.
175    pub fn open(
176        &self,
177        key: &StableKey,
178    ) -> Result<S::MemoryHandle, AllocationSessionError<S::Error>> {
179        let slot = self
180            .committed
181            .slot_for(key)
182            .ok_or_else(|| AllocationSessionError::UnknownStableKey(key.clone()))?;
183        self.substrate
184            .open_slot(slot)
185            .map_err(AllocationSessionError::Substrate)
186    }
187}
188
189///
190/// AllocationSessionError
191///
192/// Failure to open through a committed allocation session.
193#[non_exhaustive]
194#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
195pub enum AllocationSessionError<E> {
196    /// Stable key was not part of the committed allocation snapshot.
197    #[error("stable key '{0}' was not committed for this allocation session")]
198    UnknownStableKey(StableKey),
199    /// Storage substrate failed to open the committed slot.
200    #[error("storage substrate failed to open allocation slot")]
201    Substrate(E),
202}