Skip to main content

ic_memory/
capability.rs

1use crate::{declaration::AllocationDeclaration, key::StableKey, slot::MemoryManagerSlot};
2use std::sync::Arc;
3
4///
5/// ValidatedAllocations
6///
7/// Pre-commit allocation declarations accepted by policy and historical ledger
8/// validation.
9///
10/// This value is produced by [`crate::validate_allocations`] and may be staged
11/// into the next ledger generation. It cannot open storage. Only a
12/// [`CommittedAllocations`] capability confirmed after persistence can do that.
13///
14/// This is an in-memory capability, not a serde DTO. It has no public
15/// constructor and should only be produced by validation or bootstrap paths.
16/// Its declarations have valid schema metadata and at most 255 unique keys and
17/// slots. These facts are established before the proof is constructed.
18/// The base generation has passed bounded committed-history validation.
19///
20
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct ValidatedAllocations {
23    inner: Arc<ValidatedState>,
24}
25
26#[derive(Clone, Debug, Eq, PartialEq)]
27struct ValidatedState {
28    /// Recovered generation against which these declarations were validated.
29    base_generation: u64,
30    /// Validated declarations.
31    declarations: Vec<AllocationDeclaration>,
32    /// Optional binary/runtime identity for generation diagnostics.
33    runtime_fingerprint: Option<String>,
34}
35
36impl ValidatedAllocations {
37    pub(crate) fn new(
38        base_generation: u64,
39        declarations: Vec<AllocationDeclaration>,
40        runtime_fingerprint: Option<String>,
41    ) -> Self {
42        Self {
43            inner: Arc::new(ValidatedState {
44                base_generation,
45                declarations,
46                runtime_fingerprint,
47            }),
48        }
49    }
50
51    /// Return the recovered generation used as the validation base.
52    #[must_use]
53    pub fn base_generation(&self) -> u64 {
54        self.inner.base_generation
55    }
56
57    /// Borrow the validated declarations.
58    #[must_use]
59    pub fn declarations(&self) -> &[AllocationDeclaration] {
60        &self.inner.declarations
61    }
62
63    /// Borrow the optional runtime fingerprint.
64    #[must_use]
65    pub fn runtime_fingerprint(&self) -> Option<&str> {
66        self.inner.runtime_fingerprint.as_deref()
67    }
68
69    /// Find a validated slot by stable key.
70    #[must_use]
71    pub fn slot_for(&self, key: &StableKey) -> Option<&MemoryManagerSlot> {
72        slot_for_key(self.declarations(), key.as_str())
73    }
74
75    pub(crate) const fn confirm_persisted(self, generation: u64) -> CommittedAllocations {
76        CommittedAllocations {
77            validated: self,
78            generation,
79        }
80    }
81}
82
83// Typed capability callers and the runtime's validated borrowed input share
84// one lookup. Comparing text needs no temporary owned StableKey or second index.
85pub fn slot_for_key<'a>(
86    declarations: &'a [AllocationDeclaration],
87    key: &str,
88) -> Option<&'a MemoryManagerSlot> {
89    declarations
90        .iter()
91        .find(|declaration| declaration.stable_key.as_str() == key)
92        .map(|declaration| &declaration.slot)
93}
94
95///
96/// CommittedAllocations
97///
98/// Allocation-open capability confirmed after the validated ledger generation
99/// was persisted.
100///
101/// This type is not serializable, default-constructible, or publicly
102/// constructible. Generic persistence owners obtain it only by explicitly
103/// confirming a successful [`crate::PendingBootstrapCommit`]. A
104/// [`crate::MemoryRuntime`] stores it only after that runtime's stable-cell write
105/// succeeds.
106///
107/// Its immutable declarations have validated keys, slots and diagnostic
108/// metadata, with unique keys and slots. Consumers may rely on those invariants
109/// without rebuilding uniqueness sets. The capability does not validate live
110/// store bytes, application schemas, journals or lifecycle readiness.
111///
112
113#[derive(Clone, Debug, Eq, PartialEq)]
114pub struct CommittedAllocations {
115    validated: ValidatedAllocations,
116    generation: u64,
117}
118
119impl CommittedAllocations {
120    /// Return the persisted ledger generation that grants this capability.
121    #[must_use]
122    pub const fn generation(&self) -> u64 {
123        self.generation
124    }
125
126    /// Borrow the committed allocation declarations.
127    #[must_use]
128    pub fn declarations(&self) -> &[AllocationDeclaration] {
129        self.validated.declarations()
130    }
131
132    /// Borrow the optional runtime fingerprint.
133    #[must_use]
134    pub fn runtime_fingerprint(&self) -> Option<&str> {
135        self.validated.runtime_fingerprint()
136    }
137
138    /// Find a committed slot by stable key.
139    #[must_use]
140    pub fn slot_for(&self, key: &StableKey) -> Option<&MemoryManagerSlot> {
141        self.validated.slot_for(key)
142    }
143
144    // Runtime publication exposes application allocations only. Manual commit
145    // owners retain the complete capability returned by persistence confirmation.
146    pub(crate) fn into_application_allocations(mut self) -> Self {
147        Arc::make_mut(&mut self.validated.inner)
148            .declarations
149            .retain(|declaration| !crate::is_ic_memory_stable_key(declaration.stable_key.as_str()));
150        self
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    #[test]
159    fn filtering_governance_does_not_change_shared_capabilities() {
160        let validated = ValidatedAllocations::new(
161            1,
162            vec![
163                AllocationDeclaration::memory_manager(
164                    crate::IC_MEMORY_LEDGER_STABLE_KEY,
165                    0,
166                    "ledger",
167                )
168                .unwrap(),
169                AllocationDeclaration::memory_manager("app.rows.v1", 100, "rows").unwrap(),
170            ],
171            Some("host".to_string()),
172        );
173        let committed = validated.clone().confirm_persisted(2);
174        let filtered = committed.clone().into_application_allocations();
175
176        assert_eq!(validated.declarations().len(), 2);
177        assert_eq!(committed.declarations().len(), 2);
178        assert_eq!(filtered.declarations().len(), 1);
179        assert_eq!(
180            filtered.declarations()[0].stable_key().as_str(),
181            "app.rows.v1"
182        );
183        assert_eq!(filtered.generation(), committed.generation());
184        assert_eq!(
185            filtered.runtime_fingerprint(),
186            committed.runtime_fingerprint()
187        );
188    }
189}