Skip to main content

canic_core/memory/
mod.rs

1//! Canic-managed stable-memory runtime boundary.
2//!
3//! This module is the Canic-owned adapter around current stable-memory
4//! bootstrap mechanics while durable allocation-governance primitives move
5//! into `ic-memory`.
6
7use ic_memory::{
8    AllocationSession, AllocationSessionError, AllocationSlotDescriptor, MemoryManagerSlotError,
9    StableKey, StorageSubstrate,
10    stable_structures::{
11        DefaultMemoryImpl,
12        memory_manager::{MemoryId, VirtualMemory},
13    },
14};
15
16pub(crate) mod ledger;
17mod manager;
18mod policy;
19pub mod registry;
20pub mod runtime;
21
22pub use crate::{eager_init, eager_static, ic_memory_key};
23
24///
25/// open_validated_memory
26///
27
28#[doc(hidden)]
29#[must_use]
30pub fn open_validated_memory(
31    stable_key: &str,
32    label: &str,
33    id: u8,
34) -> VirtualMemory<DefaultMemoryImpl> {
35    runtime::assert_memory_bootstrap_ready(label, id);
36    try_open_validated_memory(stable_key, id).unwrap_or_else(|err| {
37        panic!(
38            "stable memory slot '{label}' (id {id}, key '{stable_key}') failed validated open: {err}"
39        );
40    })
41}
42
43fn try_open_validated_memory(
44    stable_key: &str,
45    id: u8,
46) -> Result<VirtualMemory<DefaultMemoryImpl>, registry::MemoryRegistryError> {
47    let key = StableKey::parse(stable_key).map_err(|err| {
48        registry::MemoryRegistryError::InvalidDeclaration {
49            stable_key: err.stable_key,
50            reason: err.reason,
51        }
52    })?;
53    let validated = runtime::registry::MemoryRegistryRuntime::validated_allocations()?;
54    let slot = validated
55        .slot_for(&key)
56        .ok_or(registry::MemoryRegistryError::RegistrationAfterBootstrap { registrations: 1 })?;
57    let slot_id = memory_manager_id_from_slot(slot)?;
58    if slot_id != id {
59        return Err(registry::MemoryRegistryError::RegistrationAfterBootstrap { registrations: 1 });
60    }
61
62    let session = AllocationSession::new(MemoryManagerSubstrate, validated);
63    session
64        .open(&key)
65        .map_err(memory_registry_error_from_session_error)
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69struct MemoryManagerSubstrate;
70
71impl StorageSubstrate for MemoryManagerSubstrate {
72    type Slot = u8;
73    type LedgerMemory = VirtualMemory<DefaultMemoryImpl>;
74    type MemoryHandle = VirtualMemory<DefaultMemoryImpl>;
75    type Error = registry::MemoryRegistryError;
76
77    fn open_ledger(&self) -> Result<Self::LedgerMemory, Self::Error> {
78        Ok(open_memory(ledger::MEMORY_LAYOUT_LEDGER_ID))
79    }
80
81    fn open_slot(
82        &self,
83        slot: &AllocationSlotDescriptor,
84    ) -> Result<Self::MemoryHandle, Self::Error> {
85        let id = memory_manager_id_from_slot(slot)?;
86        Ok(open_memory(id))
87    }
88
89    fn describe_slot(&self, slot: &Self::Slot) -> AllocationSlotDescriptor {
90        AllocationSlotDescriptor::memory_manager(*slot)
91            .expect("MemoryManagerSubstrate only describes usable MemoryManager IDs")
92    }
93}
94
95fn open_memory(id: u8) -> VirtualMemory<DefaultMemoryImpl> {
96    manager::MEMORY_MANAGER.with_borrow_mut(|mgr| mgr.get(MemoryId::new(id)))
97}
98
99fn memory_registry_error_from_session_error(
100    err: AllocationSessionError<registry::MemoryRegistryError>,
101) -> registry::MemoryRegistryError {
102    match err {
103        AllocationSessionError::UnknownStableKey(_) => {
104            registry::MemoryRegistryError::RegistrationAfterBootstrap { registrations: 1 }
105        }
106        AllocationSessionError::Substrate(err) => err,
107    }
108}
109
110fn memory_manager_id_from_slot(
111    slot: &AllocationSlotDescriptor,
112) -> Result<u8, registry::MemoryRegistryError> {
113    slot.memory_manager_id()
114        .map_err(memory_registry_error_from_slot_error)
115}
116
117fn memory_registry_error_from_slot_error(
118    err: MemoryManagerSlotError,
119) -> registry::MemoryRegistryError {
120    match err {
121        MemoryManagerSlotError::InvalidMemoryManagerId { id } => {
122            registry::MemoryRegistryError::InvalidDeclaration {
123                stable_key: "<slot>".to_string(),
124                reason: if id == ic_memory::MEMORY_MANAGER_INVALID_ID {
125                    "MemoryManager ID 255 is not usable"
126                } else {
127                    "MemoryManager ID is not usable"
128                },
129            }
130        }
131        MemoryManagerSlotError::UnsupportedSlot
132        | MemoryManagerSlotError::UnsupportedSubstrate { .. }
133        | MemoryManagerSlotError::UnsupportedDescriptorVersion { .. } => {
134            registry::MemoryRegistryError::LedgerCorrupt {
135                reason: "unsupported MemoryManager allocation slot descriptor",
136            }
137        }
138    }
139}
140
141///
142/// TESTS
143///
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::memory::registry::{defer_register_with_key, reset_for_tests};
149
150    #[test]
151    fn validated_open_requires_declared_stable_key() {
152        reset_for_tests();
153        defer_register_with_key(101, "crate_a", "slot", "app.crate_a.slot.v1")
154            .expect("defer register");
155        runtime::registry::MemoryRegistryRuntime::init().expect("bootstrap registry");
156
157        let _memory =
158            try_open_validated_memory("app.crate_a.slot.v1", 101).expect("open by stable key");
159    }
160
161    #[test]
162    fn validated_open_rejects_key_id_mismatch() {
163        reset_for_tests();
164        defer_register_with_key(101, "crate_a", "slot", "app.crate_a.slot.v1")
165            .expect("defer register");
166        runtime::registry::MemoryRegistryRuntime::init().expect("bootstrap registry");
167
168        let Err(err) = try_open_validated_memory("app.crate_a.slot.v1", 102) else {
169            panic!("wrong id must not open declared key");
170        };
171        assert!(matches!(
172            err,
173            registry::MemoryRegistryError::RegistrationAfterBootstrap { .. }
174        ));
175    }
176}