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///
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct ValidatedAllocations {
20    inner: Arc<ValidatedState>,
21}
22
23#[derive(Clone, Debug, Eq, PartialEq)]
24struct ValidatedState {
25    /// Recovered generation against which these declarations were validated.
26    base_generation: u64,
27    /// Validated declarations.
28    declarations: Vec<AllocationDeclaration>,
29    /// Optional binary/runtime identity for generation diagnostics.
30    runtime_fingerprint: Option<String>,
31}
32
33impl ValidatedAllocations {
34    pub(crate) fn new(
35        base_generation: u64,
36        declarations: Vec<AllocationDeclaration>,
37        runtime_fingerprint: Option<String>,
38    ) -> Self {
39        Self {
40            inner: Arc::new(ValidatedState {
41                base_generation,
42                declarations,
43                runtime_fingerprint,
44            }),
45        }
46    }
47
48    /// Return the recovered generation used as the validation base.
49    #[must_use]
50    pub fn base_generation(&self) -> u64 {
51        self.inner.base_generation
52    }
53
54    /// Borrow the validated declarations.
55    #[must_use]
56    pub fn declarations(&self) -> &[AllocationDeclaration] {
57        &self.inner.declarations
58    }
59
60    /// Borrow the optional runtime fingerprint.
61    #[must_use]
62    pub fn runtime_fingerprint(&self) -> Option<&str> {
63        self.inner.runtime_fingerprint.as_deref()
64    }
65
66    /// Find a validated slot by stable key.
67    #[must_use]
68    pub fn slot_for(&self, key: &StableKey) -> Option<&AllocationSlotDescriptor> {
69        self.declarations()
70            .iter()
71            .find(|declaration| &declaration.stable_key == key)
72            .map(|declaration| &declaration.slot)
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///
84/// CommittedAllocations
85///
86/// Allocation-open capability confirmed after the validated ledger generation
87/// was persisted.
88///
89/// This type is not serializable, default-constructible, or publicly
90/// constructible. Generic persistence owners obtain it only by explicitly
91/// confirming a successful [`crate::PendingBootstrapCommit`]. A
92/// [`crate::MemoryRuntime`] stores it only after that runtime's stable-cell write
93/// succeeds.
94///
95/// Its immutable declarations have validated keys, slots and diagnostic
96/// metadata, with unique keys and slots. Consumers may rely on those invariants
97/// without rebuilding uniqueness sets. The capability does not validate live
98/// store bytes, application schemas, journals or lifecycle readiness.
99///
100
101#[derive(Clone, Debug, Eq, PartialEq)]
102pub struct CommittedAllocations {
103    validated: ValidatedAllocations,
104    generation: u64,
105}
106
107impl CommittedAllocations {
108    /// Return the persisted ledger generation that grants this capability.
109    #[must_use]
110    pub const fn generation(&self) -> u64 {
111        self.generation
112    }
113
114    /// Borrow the committed allocation declarations.
115    #[must_use]
116    pub fn declarations(&self) -> &[AllocationDeclaration] {
117        self.validated.declarations()
118    }
119
120    /// Borrow the optional runtime fingerprint.
121    #[must_use]
122    pub fn runtime_fingerprint(&self) -> Option<&str> {
123        self.validated.runtime_fingerprint()
124    }
125
126    /// Find a committed slot by stable key.
127    #[must_use]
128    pub fn slot_for(&self, key: &StableKey) -> Option<&AllocationSlotDescriptor> {
129        self.validated.slot_for(key)
130    }
131
132    pub(crate) fn without_stable_key_prefix(mut self, prefix: &str) -> Self {
133        let mut state = Arc::unwrap_or_clone(self.validated.inner);
134        state
135            .declarations
136            .retain(|declaration| !declaration.stable_key.as_str().starts_with(prefix));
137        self.validated.inner = Arc::new(state);
138        self
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn filtering_governance_does_not_change_shared_capabilities() {
148        let validated = ValidatedAllocations::new(
149            1,
150            vec![
151                AllocationDeclaration::memory_manager(
152                    crate::IC_MEMORY_LEDGER_STABLE_KEY,
153                    0,
154                    "ledger",
155                )
156                .unwrap(),
157                AllocationDeclaration::memory_manager("app.rows.v1", 100, "rows").unwrap(),
158            ],
159            Some("host".to_string()),
160        );
161        let committed = validated.clone().confirm_persisted(2);
162        let filtered = committed
163            .clone()
164            .without_stable_key_prefix(crate::IC_MEMORY_STABLE_KEY_PREFIX);
165
166        assert_eq!(validated.declarations().len(), 2);
167        assert_eq!(committed.declarations().len(), 2);
168        assert_eq!(filtered.declarations().len(), 1);
169        assert_eq!(
170            filtered.declarations()[0].stable_key().as_str(),
171            "app.rows.v1"
172        );
173        assert_eq!(filtered.generation(), committed.generation());
174        assert_eq!(
175            filtered.runtime_fingerprint(),
176            committed.runtime_fingerprint()
177        );
178    }
179}