Skip to main content

ic_memory/
capability.rs

1use crate::{declaration::AllocationDeclaration, key::StableKey, slot::AllocationSlotDescriptor};
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<&AllocationSlotDescriptor> {
72        self.declarations()
73            .iter()
74            .find(|declaration| &declaration.stable_key == key)
75            .map(|declaration| &declaration.slot)
76    }
77
78    pub(crate) const fn confirm_persisted(self, generation: u64) -> CommittedAllocations {
79        CommittedAllocations {
80            validated: self,
81            generation,
82        }
83    }
84}
85
86///
87/// CommittedAllocations
88///
89/// Allocation-open capability confirmed after the validated ledger generation
90/// was persisted.
91///
92/// This type is not serializable, default-constructible, or publicly
93/// constructible. Generic persistence owners obtain it only by explicitly
94/// confirming a successful [`crate::PendingBootstrapCommit`]. A
95/// [`crate::MemoryRuntime`] stores it only after that runtime's stable-cell write
96/// succeeds.
97///
98/// Its immutable declarations have validated keys, slots and diagnostic
99/// metadata, with unique keys and slots. Consumers may rely on those invariants
100/// without rebuilding uniqueness sets. The capability does not validate live
101/// store bytes, application schemas, journals or lifecycle readiness.
102///
103
104#[derive(Clone, Debug, Eq, PartialEq)]
105pub struct CommittedAllocations {
106    validated: ValidatedAllocations,
107    generation: u64,
108}
109
110impl CommittedAllocations {
111    /// Return the persisted ledger generation that grants this capability.
112    #[must_use]
113    pub const fn generation(&self) -> u64 {
114        self.generation
115    }
116
117    /// Borrow the committed allocation declarations.
118    #[must_use]
119    pub fn declarations(&self) -> &[AllocationDeclaration] {
120        self.validated.declarations()
121    }
122
123    /// Borrow the optional runtime fingerprint.
124    #[must_use]
125    pub fn runtime_fingerprint(&self) -> Option<&str> {
126        self.validated.runtime_fingerprint()
127    }
128
129    /// Find a committed slot by stable key.
130    #[must_use]
131    pub fn slot_for(&self, key: &StableKey) -> Option<&AllocationSlotDescriptor> {
132        self.validated.slot_for(key)
133    }
134
135    pub(crate) fn without_stable_key_prefix(mut self, prefix: &str) -> Self {
136        let mut state = Arc::unwrap_or_clone(self.validated.inner);
137        state
138            .declarations
139            .retain(|declaration| !declaration.stable_key.as_str().starts_with(prefix));
140        self.validated.inner = Arc::new(state);
141        self
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn filtering_governance_does_not_change_shared_capabilities() {
151        let validated = ValidatedAllocations::new(
152            1,
153            vec![
154                AllocationDeclaration::memory_manager(
155                    crate::IC_MEMORY_LEDGER_STABLE_KEY,
156                    0,
157                    "ledger",
158                )
159                .unwrap(),
160                AllocationDeclaration::memory_manager("app.rows.v1", 100, "rows").unwrap(),
161            ],
162            Some("host".to_string()),
163        );
164        let committed = validated.clone().confirm_persisted(2);
165        let filtered = committed
166            .clone()
167            .without_stable_key_prefix(crate::IC_MEMORY_STABLE_KEY_PREFIX);
168
169        assert_eq!(validated.declarations().len(), 2);
170        assert_eq!(committed.declarations().len(), 2);
171        assert_eq!(filtered.declarations().len(), 1);
172        assert_eq!(
173            filtered.declarations()[0].stable_key().as_str(),
174            "app.rows.v1"
175        );
176        assert_eq!(filtered.generation(), committed.generation());
177        assert_eq!(
178            filtered.runtime_fingerprint(),
179            committed.runtime_fingerprint()
180        );
181    }
182}