Skip to main content

ic_memory/
declaration.rs

1use crate::{
2    key::{StableKey, StableKeyError},
3    slot::{MemoryManagerSlot, MemoryManagerSlotError},
4};
5use serde::{Deserialize, Serialize};
6
7///
8/// AllocationDeclaration
9///
10/// Checked runtime claim that a stable key should own an allocation slot.
11///
12/// Declarations are supplied by the current binary before opening storage.
13/// Construction and decoding check the stable key and slot. A declaration
14/// becomes authoritative only after validation against the recovered ledger
15/// and commitment in a generation.
16///
17
18#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19#[serde(deny_unknown_fields)]
20pub struct AllocationDeclaration {
21    /// Durable stable key.
22    pub(crate) stable_key: StableKey,
23    /// Claimed allocation slot.
24    pub(crate) slot: MemoryManagerSlot,
25}
26
27impl AllocationDeclaration {
28    /// Build a declaration from raw parts after validating allocation identity.
29    pub fn new(
30        stable_key: impl AsRef<str>,
31        slot: MemoryManagerSlot,
32    ) -> Result<Self, DeclarationSnapshotError> {
33        let stable_key = StableKey::parse(stable_key).map_err(DeclarationSnapshotError::Key)?;
34        Ok(Self { stable_key, slot })
35    }
36
37    /// Build a checked `MemoryManager` declaration with a usable slot.
38    pub fn memory_manager(
39        stable_key: impl AsRef<str>,
40        id: u8,
41    ) -> Result<Self, DeclarationSnapshotError> {
42        let slot =
43            MemoryManagerSlot::new(id).map_err(DeclarationSnapshotError::MemoryManagerSlot)?;
44        Self::new(stable_key, slot)
45    }
46
47    /// Return the durable stable key claimed by this declaration.
48    #[must_use]
49    pub const fn stable_key(&self) -> &StableKey {
50        &self.stable_key
51    }
52
53    /// Return the allocation slot claimed by this declaration.
54    #[must_use]
55    pub const fn slot(&self) -> &MemoryManagerSlot {
56        &self.slot
57    }
58}
59
60///
61/// DeclarationSnapshotError
62///
63/// Checked allocation identity construction failure.
64///
65
66#[non_exhaustive]
67#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
68pub enum DeclarationSnapshotError {
69    /// Stable-key grammar failure.
70    #[error(transparent)]
71    Key(StableKeyError),
72    /// `MemoryManager` slot validation failure.
73    #[error(transparent)]
74    MemoryManagerSlot(MemoryManagerSlotError),
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use crate::slot::MemoryManagerSlot;
81
82    fn declaration(key: &str, id: u8) -> AllocationDeclaration {
83        AllocationDeclaration::new(key, MemoryManagerSlot::new(id).expect("usable slot"))
84            .expect("declaration")
85    }
86
87    #[test]
88    fn memory_manager_declaration_constructor_builds_common_declaration() {
89        let declaration =
90            AllocationDeclaration::memory_manager("app.orders.v1", 100).expect("declaration");
91
92        assert_eq!(declaration.stable_key.as_str(), "app.orders.v1");
93        assert_eq!(
94            declaration.slot,
95            MemoryManagerSlot::new(100).expect("usable slot")
96        );
97    }
98
99    #[test]
100    fn memory_manager_declaration_constructor_rejects_invalid_slot() {
101        let err = AllocationDeclaration::memory_manager("app.orders.v1", u8::MAX)
102            .expect_err("sentinel must fail");
103
104        assert!(matches!(
105            err,
106            DeclarationSnapshotError::MemoryManagerSlot(_)
107        ));
108    }
109
110    #[test]
111    fn declaration_decode_rejects_unusable_memory_manager_slot() {
112        let mut value = serde_json::to_value(declaration("app.orders.v1", 100)).unwrap();
113        value["slot"] = serde_json::json!(255);
114        assert!(serde_json::from_value::<AllocationDeclaration>(value.clone()).is_err());
115        let bytes = crate::test_cbor::to_vec(&value).unwrap();
116        assert!(crate::test_cbor::from_slice::<AllocationDeclaration>(&bytes).is_err());
117    }
118}