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 ownership 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}
33
34impl ValidatedAllocations {
35    pub(crate) fn new(base_generation: u64, declarations: Vec<AllocationDeclaration>) -> Self {
36        Self {
37            inner: Arc::new(ValidatedState {
38                base_generation,
39                declarations,
40            }),
41        }
42    }
43
44    /// Return the recovered generation used as the validation base.
45    #[must_use]
46    pub fn base_generation(&self) -> u64 {
47        self.inner.base_generation
48    }
49
50    /// Borrow the validated declarations.
51    #[must_use]
52    pub fn declarations(&self) -> &[AllocationDeclaration] {
53        &self.inner.declarations
54    }
55
56    /// Find a validated slot by stable key.
57    #[must_use]
58    pub fn slot_for(&self, key: &StableKey) -> Option<&MemoryManagerSlot> {
59        slot_for_key(self.declarations(), key.as_str())
60    }
61
62    pub(crate) const fn confirm_persisted(self, generation: u64) -> CommittedAllocations {
63        CommittedAllocations {
64            validated: self,
65            generation,
66        }
67    }
68}
69
70// Typed capability callers and the runtime's validated borrowed input share
71// one lookup. Comparing text needs no temporary owned StableKey or second index.
72pub fn slot_for_key<'a>(
73    declarations: &'a [AllocationDeclaration],
74    key: &str,
75) -> Option<&'a MemoryManagerSlot> {
76    declarations
77        .iter()
78        .find(|declaration| declaration.stable_key.as_str() == key)
79        .map(|declaration| &declaration.slot)
80}
81
82///
83/// CommittedAllocations
84///
85/// Allocation-open capability confirmed after the validated ledger generation
86/// was persisted.
87///
88/// This type is not serializable, default-constructible, or publicly
89/// constructible. Generic persistence owners obtain it only by explicitly
90/// confirming a successful [`crate::PendingBootstrapCommit`]. A
91/// [`crate::MemoryRuntime`] stores it only after that runtime's stable-cell write
92/// succeeds.
93///
94/// Its immutable declarations have validated keys, slots and diagnostic
95/// metadata, with unique keys and slots. Consumers may rely on those invariants
96/// without rebuilding uniqueness sets. The capability does not validate live
97/// store bytes, application schemas, journals or lifecycle readiness.
98///
99
100#[derive(Clone, Debug, Eq, PartialEq)]
101pub struct CommittedAllocations {
102    validated: ValidatedAllocations,
103    generation: u64,
104}
105
106impl CommittedAllocations {
107    /// Return the persisted ledger generation that grants this capability.
108    #[must_use]
109    pub const fn generation(&self) -> u64 {
110        self.generation
111    }
112
113    /// Borrow the committed allocation declarations.
114    #[must_use]
115    pub fn declarations(&self) -> &[AllocationDeclaration] {
116        self.validated.declarations()
117    }
118
119    /// Find a committed slot by stable key.
120    #[must_use]
121    pub fn slot_for(&self, key: &StableKey) -> Option<&MemoryManagerSlot> {
122        self.validated.slot_for(key)
123    }
124
125    // Runtime publication exposes application allocations only. Manual commit
126    // owners retain the complete capability returned by persistence confirmation.
127    pub(crate) fn into_application_allocations(mut self) -> Self {
128        Arc::make_mut(&mut self.validated.inner)
129            .declarations
130            .retain(|declaration| !crate::is_ic_memory_stable_key(declaration.stable_key.as_str()));
131        self
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn filtering_governance_does_not_change_shared_capabilities() {
141        let validated = ValidatedAllocations::new(
142            1,
143            vec![
144                AllocationDeclaration::memory_manager(
145                    crate::IC_MEMORY_LEDGER_STABLE_KEY,
146                    0,
147                    "ledger",
148                )
149                .unwrap(),
150                AllocationDeclaration::memory_manager("app.rows.v1", 100, "rows").unwrap(),
151            ],
152        );
153        let committed = validated.clone().confirm_persisted(2);
154        let filtered = committed.clone().into_application_allocations();
155
156        assert_eq!(validated.declarations().len(), 2);
157        assert_eq!(committed.declarations().len(), 2);
158        assert_eq!(filtered.declarations().len(), 1);
159        assert_eq!(
160            filtered.declarations()[0].stable_key().as_str(),
161            "app.rows.v1"
162        );
163        assert_eq!(filtered.generation(), committed.generation());
164    }
165}