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