Skip to main content

ic_memory/
declaration.rs

1use crate::{
2    key::{StableKey, StableKeyError},
3    schema::SchemaMetadata,
4    slot::{MemoryManagerSlot, MemoryManagerSlotError},
5};
6use serde::{Deserialize, Serialize};
7
8///
9/// AllocationDeclaration
10///
11/// Checked runtime claim that a stable key should own an allocation slot.
12///
13/// Declarations are supplied by the current binary before opening storage.
14/// Construction and decoding check the stable key, slot and schema
15/// metadata. A declaration becomes authoritative only after validation against
16/// the recovered ledger and commitment in a generation.
17///
18
19#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
20#[serde(deny_unknown_fields)]
21pub struct AllocationDeclaration {
22    /// Durable stable key.
23    pub(crate) stable_key: StableKey,
24    /// Claimed allocation slot.
25    pub(crate) slot: MemoryManagerSlot,
26    /// Optional diagnostic schema metadata.
27    pub(crate) schema: SchemaMetadata,
28}
29
30impl AllocationDeclaration {
31    /// Build a declaration from raw parts after validating diagnostic metadata.
32    pub fn new(
33        stable_key: impl AsRef<str>,
34        slot: MemoryManagerSlot,
35        schema: SchemaMetadata,
36    ) -> Result<Self, DeclarationSnapshotError> {
37        let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
38        Ok(Self {
39            stable_key,
40            slot,
41            schema,
42        })
43    }
44
45    /// Build a checked `MemoryManager` declaration with default schema metadata.
46    pub fn memory_manager(
47        stable_key: impl AsRef<str>,
48        id: u8,
49    ) -> Result<Self, DeclarationSnapshotError> {
50        Self::memory_manager_with_schema(stable_key, id, SchemaMetadata::default())
51    }
52
53    /// Build a checked `MemoryManager` declaration with schema metadata.
54    pub fn memory_manager_with_schema(
55        stable_key: impl AsRef<str>,
56        id: u8,
57        schema: SchemaMetadata,
58    ) -> Result<Self, DeclarationSnapshotError> {
59        let slot =
60            MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
61        Self::new(stable_key, slot, schema)
62    }
63
64    /// Return the durable stable key claimed by this declaration.
65    #[must_use]
66    pub const fn stable_key(&self) -> &StableKey {
67        &self.stable_key
68    }
69
70    /// Return the allocation slot claimed by this declaration.
71    #[must_use]
72    pub const fn slot(&self) -> &MemoryManagerSlot {
73        &self.slot
74    }
75
76    /// Return the optional schema metadata.
77    #[must_use]
78    pub const fn schema(&self) -> &SchemaMetadata {
79        &self.schema
80    }
81}
82
83///
84/// DeclarationSnapshot
85///
86/// Internal resolved declarations with bounded count and unique keys and slots.
87/// Resolution owns canonical order. A snapshot is not permission to open storage.
88/// [`crate::MemoryRuntime::bootstrap`] owns validation and persistence before
89/// publishing committed allocation authority.
90///
91
92#[derive(Debug, Eq, PartialEq)]
93pub struct DeclarationSnapshot {
94    /// Runtime declarations.
95    declarations: Vec<AllocationDeclaration>,
96}
97
98impl DeclarationSnapshot {
99    pub(crate) fn canonicalize(
100        mut declarations: Vec<AllocationDeclaration>,
101    ) -> Result<Self, DeclarationSnapshotError> {
102        if declarations.len() > crate::constants::MAX_ALLOCATIONS {
103            return Err(DeclarationSnapshotError::TooManyDeclarations);
104        }
105        crate::sort::sort_unstable_by(&mut declarations, |a, b| a.stable_key.cmp(&b.stable_key));
106        let mut slots = [false; crate::constants::MAX_ALLOCATIONS];
107        for (index, declaration) in declarations.iter().enumerate() {
108            let occupied = &mut slots[usize::from(declaration.slot.id())];
109            if *occupied {
110                return Err(DeclarationSnapshotError::DuplicateSlot(
111                    declaration.slot.clone(),
112                ));
113            }
114            *occupied = true;
115            if index > 0 && declarations[index - 1].stable_key == declaration.stable_key {
116                return Err(DeclarationSnapshotError::DuplicateStableKey(
117                    declaration.stable_key.clone(),
118                ));
119            }
120        }
121        Ok(Self { declarations })
122    }
123
124    /// Borrow the sealed declarations.
125    #[must_use]
126    pub fn declarations(&self) -> &[AllocationDeclaration] {
127        &self.declarations
128    }
129
130    pub(crate) fn into_declarations(self) -> Vec<AllocationDeclaration> {
131        self.declarations
132    }
133}
134
135///
136/// DeclarationSnapshotError
137///
138/// Declaration snapshot validation failure.
139#[non_exhaustive]
140#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
141pub enum DeclarationSnapshotError {
142    #[error("at most 255 allocation declarations are supported")]
143    TooManyDeclarations,
144    /// Stable-key grammar failure.
145    #[error(transparent)]
146    Key(StableKeyError),
147    /// `MemoryManager` slot validation failure.
148    #[error(transparent)]
149    MemoryManagerSlot(MemoryManagerSlotError),
150    /// A stable key appeared more than once in one snapshot.
151    #[error("stable key '{0}' is declared more than once")]
152    DuplicateStableKey(StableKey),
153    /// An allocation slot appeared more than once in one snapshot.
154    #[error("allocation slot '{0:?}' is declared more than once")]
155    DuplicateSlot(MemoryManagerSlot),
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::slot::MemoryManagerSlot;
162
163    fn declaration(key: &str, id: u8) -> AllocationDeclaration {
164        AllocationDeclaration::new(
165            key,
166            MemoryManagerSlot::new(id).expect("usable slot"),
167            SchemaMetadata::default(),
168        )
169        .expect("declaration")
170    }
171
172    #[test]
173    fn memory_manager_declaration_constructor_builds_common_declaration() {
174        let declaration =
175            AllocationDeclaration::memory_manager("app.orders.v1", 100).expect("declaration");
176
177        assert_eq!(declaration.stable_key.as_str(), "app.orders.v1");
178        assert_eq!(
179            declaration.slot,
180            MemoryManagerSlot::new(100).expect("usable slot")
181        );
182        assert_eq!(declaration.schema, SchemaMetadata::default());
183    }
184
185    #[test]
186    fn memory_manager_declaration_constructor_rejects_invalid_slot() {
187        let err = AllocationDeclaration::memory_manager("app.orders.v1", u8::MAX)
188            .expect_err("sentinel must fail");
189
190        assert!(matches!(
191            err,
192            DeclarationSnapshotError::MemoryManagerSlot(_)
193        ));
194    }
195
196    #[test]
197    fn declaration_decode_rejects_unusable_memory_manager_slot() {
198        let mut value = serde_json::to_value(declaration("app.orders.v1", 100)).unwrap();
199        value["slot"] = serde_json::json!(255);
200        assert!(serde_json::from_value::<AllocationDeclaration>(value.clone()).is_err());
201        let bytes = crate::test_cbor::to_vec(&value).unwrap();
202        assert!(crate::test_cbor::from_slice::<AllocationDeclaration>(&bytes).is_err());
203    }
204
205    #[test]
206    fn rejects_duplicate_keys() {
207        let err = DeclarationSnapshot::canonicalize(vec![
208            declaration("app.users.v1", 100),
209            declaration("app.users.v1", 101),
210        ])
211        .expect_err("duplicate key");
212
213        assert_eq!(
214            err,
215            DeclarationSnapshotError::DuplicateStableKey(StableKey::parse("app.users.v1").unwrap())
216        );
217    }
218
219    #[test]
220    fn rejects_duplicate_slots() {
221        for second_key in ["app.orders.v1", "app.users.v1"] {
222            let err = DeclarationSnapshot::canonicalize(vec![
223                declaration("app.users.v1", 100),
224                declaration(second_key, 100),
225            ])
226            .expect_err("duplicate slot precedes duplicate key");
227
228            assert_eq!(
229                err,
230                DeclarationSnapshotError::DuplicateSlot(MemoryManagerSlot::new(100).unwrap())
231            );
232        }
233    }
234
235    #[test]
236    fn canonical_resolution_preserves_identity_failures_and_bounds() {
237        // Independently inspect prior rows to check competing slot/key failures
238        // in the runtime's canonical traversal order.
239        let choices = [
240            ("app.a.v1", 10),
241            ("app.a.v1", 11),
242            ("app.b.v1", 10),
243            ("app.b.v1", 11),
244        ];
245        for code in 0..256 {
246            let mut code = code;
247            let input: Vec<_> = (0..4)
248                .map(|_| {
249                    let (key, id) = choices[code % choices.len()];
250                    code /= choices.len();
251                    declaration(key, id)
252                })
253                .collect();
254            let mut ordered = input.clone();
255            crate::sort::sort_unstable_by(&mut ordered, |a, b| a.stable_key.cmp(&b.stable_key));
256            let expected = ordered.iter().enumerate().find_map(|(index, declaration)| {
257                let prior = &ordered[..index];
258                if prior.iter().any(|row| row.slot == declaration.slot) {
259                    Some(DeclarationSnapshotError::DuplicateSlot(
260                        declaration.slot.clone(),
261                    ))
262                } else if prior
263                    .iter()
264                    .any(|row| row.stable_key == declaration.stable_key)
265                {
266                    Some(DeclarationSnapshotError::DuplicateStableKey(
267                        declaration.stable_key.clone(),
268                    ))
269                } else {
270                    None
271                }
272            });
273            assert_eq!(
274                DeclarationSnapshot::canonicalize(input).unwrap_err(),
275                expected.unwrap()
276            );
277        }
278        for count in [0, 1, 254, 255, 256] {
279            let input: Vec<_> = (0..count)
280                .rev()
281                .map(|index| {
282                    declaration(
283                        &format!("app.store{index:03}.v1"),
284                        u8::try_from(index % 255).unwrap(),
285                    )
286                })
287                .collect();
288            let result = DeclarationSnapshot::canonicalize(input);
289            if count <= 255 {
290                let snapshot = result.unwrap();
291                assert_eq!(snapshot.declarations().len(), count);
292                assert!(
293                    snapshot
294                        .declarations()
295                        .windows(2)
296                        .all(|pair| { pair[0].stable_key < pair[1].stable_key })
297                );
298            } else {
299                assert_eq!(result, Err(DeclarationSnapshotError::TooManyDeclarations));
300            }
301        }
302    }
303}