Skip to main content

ic_memory/
lib.rs

1#![deny(unsafe_code, unsafe_op_in_unsafe_fn)]
2#![deny(rustdoc::broken_intra_doc_links)]
3#![doc = include_str!("../README.md")]
4
5//! Stable-memory allocation-governance primitives for Internet Computer
6//! canister upgrades.
7//!
8//! `ic-memory` prevents stable-memory slot drift.
9//!
10//! Once a stable key is committed to a physical allocation slot, future binaries
11//! must either reopen that same stable key on that same slot or declare a new
12//! stable key.
13//!
14//! The crate records and validates durable ownership in both directions: an
15//! active stable key cannot move to a different physical slot, and an active
16//! physical slot cannot be reused by a different stable key.
17//!
18//! The intended runtime integration flow is:
19//!
20//! 1. Recover the persisted allocation ledger.
21//! 2. Admit consumer identity and authorized historical selections from bounded
22//!    metadata under the host's `RuntimeBootstrapPolicy`.
23//! 3. Resolve key requests in the host-wide pool under current namespace grants,
24//!    then validate retained key-to-ID bindings and application policy.
25//! 4. Stage and durably persist the next generation.
26//! 5. Only then open stable-memory handles through committed allocation
27//!    authority.
28//!
29//! This crate owns allocation invariants, not framework policy. Namespace
30//! rules, controller authorization, endpoint lifecycle, schema migrations, and
31//! application validation belong to the framework or application.
32//!
33//! The host supplies one [`MemoryAllocationPool`] with explicit namespace grants
34//! and physical exclusions. Linked components request permanent keys and name
35//! their owner; they do not select numeric IDs or reserve component subranges.
36//! Namespace grants are current host policy, not caller authentication or
37//! persisted ownership labels. Application admission remains host-owned.
38//!
39//! Use these primitives before opening stable-memory handles. Integrations
40//! should recover the historical ledger, declare the stores expected by the
41//! current binary, admit recovered identity, resolve requests, validate against
42//! history and policy, persist a new generation, and only then publish authority
43//! before opening slots through the storage owner.
44//!
45//! Bounded physical attribution is available through
46//! [`MemoryRuntime::memory_allocations`] and
47//! [`MemoryRuntime::memory_allocation_summary`]. It reports actual persisted
48//! buckets and explicit residuals without decoding retained ownership. Virtual
49//! extent is not payload occupancy. Opens return [`RuntimeMemory`]; explicit
50//! [`MemoryManagerConfig`] selects fresh-state buckets or checks a persisted
51//! setting without migration. The default remains 128 pages.
52//!
53//! [`MemoryRuntime`] is the canonical owner for one backing memory instance. It
54//! owns that memory's manager, ledger persistence, bootstrap lifecycle, committed
55//! bindings, opens, and diagnostics. Each bootstrap attempt fallibly decodes
56//! its ledger record and uses a temporary cell for capacity-checked writes.
57//! Linked code contributes declarations to
58//! one immutable [`SealedDeclarationSnapshot`], which is supplied to each
59//! runtime independently.
60//!
61//! The host supplies the complete checked request snapshot, allocation pool and
62//! admission policy directly to the runtime. Component factories and generators
63//! contribute request lists; opening a store never registers a declaration.
64//! Exactly one runtime owns bootstrap and persistence for each backing memory.
65//!
66//! `ic-stable-structures` `MemoryManager` IDs are the first-class supported
67//! physical slot substrate. That ID domain is `u8`: IDs `0..=254` are usable,
68//! and ID `255` is always the `ic-stable-structures` unallocated sentinel.
69//! Protected current-snapshot commits and stable-cell encoding remain internal to the
70//! runtime. Public diagnostics describe their state without granting a manual
71//! commit or persistence-confirmation route.
72//!
73//! Hosts use [`DefaultMemoryImpl`], [`VectorMemory`] and [`Memory`] for backing
74//! setup. [`RuntimeMemory`] is an exclusive allocation lease; production typed
75//! collections consume these leases through IC Storage’s governed wrappers.
76
77mod allocation_pool;
78mod constants;
79mod declaration;
80mod diagnostics;
81mod encoding;
82mod hash;
83mod key;
84mod ledger;
85mod physical;
86mod policy;
87mod requests;
88mod runtime;
89mod slot;
90mod sort;
91mod stable_cell;
92mod text;
93mod validation;
94
95#[cfg(test)]
96mod test_wire {
97    use serde::{Serialize, de::DeserializeOwned};
98
99    pub fn to_vec<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, ic_wire::Error> {
100        ic_wire::native::encode(value, crate::encoding::RECORD_LIMITS)
101    }
102
103    pub fn from_slice<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, ic_wire::Error> {
104        ic_wire::native::decode(bytes, crate::encoding::RECORD_LIMITS)
105    }
106
107    pub fn hex_fixture(contents: &str) -> Vec<u8> {
108        let hex = contents
109            .chars()
110            .filter(|char| !char.is_whitespace())
111            .collect::<String>();
112        assert_eq!(hex.len() % 2, 0, "fixture hex must have byte pairs");
113        hex.as_bytes()
114            .as_chunks::<2>()
115            .0
116            .iter()
117            .map(|pair| {
118                let pair = std::str::from_utf8(pair).expect("fixture hex is utf8");
119                u8::from_str_radix(pair, 16).expect("fixture hex byte")
120            })
121            .collect()
122    }
123}
124
125/// Backing setup and IO interfaces from the exact substrate dependency.
126/// Hosts own backing custody; production collections belong behind IC Storage's
127/// governed wrappers and consume [`RuntimeMemory`] leases. Import visibility is
128/// not admission: implementing `Memory` does not construct a checked handle.
129/// Collection codec traits belong to Storage; metric records and restoration
130/// belong to IC Metrics Stable.
131pub use ic_platform::stable_structures::{DefaultMemoryImpl, Memory, VectorMemory};
132
133pub use allocation_pool::{MemoryAllocationPool, MemoryAllocationPoolError, MemoryAuthority};
134pub use constants::{
135    MAX_LEDGER_BYTES, MAX_LEDGER_NESTING, MAX_LEDGER_RECORD_BYTES, WASM_PAGE_SIZE_BYTES,
136};
137pub use declaration::AllocationDeclaration;
138pub use diagnostics::{DiagnosticExport, DiagnosticMemorySize, DiagnosticRecord};
139pub use key::{StableKey, StableKeyError};
140use ledger::{AllocationLedger, LedgerCommitStore, RecoveredLedger};
141pub use ledger::{AllocationRecord, AllocationStageError, LedgerCommitError, LedgerIntegrityError};
142pub use physical::{CommitRecoveryError, CommitSnapshotDiagnostic};
143pub use policy::{PolicyIdentity, RuntimeBootstrapPolicy};
144pub use requests::{MemoryDeclarationError, MemoryRequest, SealedDeclarationSnapshot};
145pub use runtime::{
146    BackingIdentity, BootstrapAdmission, BootstrapAdmissionError, GenericAllocationPolicy,
147    MemoryAllocation, MemoryAllocationSummary, MemoryAllocations, MemoryBacking,
148    MemoryBindingSummary, MemoryManagerConfig, MemoryManagerLayoutError, MemoryResolutionError,
149    MemoryRuntime, RuntimeAdoptionError, RuntimeBootstrapError, RuntimeConstructionError,
150    RuntimeDiagnosticError, RuntimeGrowError, RuntimeMemory, RuntimeOpenError,
151};
152pub use slot::{
153    IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_STABLE_KEY, IC_MEMORY_STABLE_KEY_PREFIX,
154    MEMORY_MANAGER_GOVERNANCE_MAX_ID, MEMORY_MANAGER_INVALID_ID, MEMORY_MANAGER_LEDGER_ID,
155    MEMORY_MANAGER_MAX_ID, MEMORY_MANAGER_MIN_ID, MemoryManagerSlot, MemoryManagerSlotError,
156    is_ic_memory_stable_key, validate_memory_manager_id,
157};
158pub use stable_cell::{
159    STABLE_CELL_HEADER_SIZE, STABLE_CELL_LAYOUT_VERSION, STABLE_CELL_MAGIC,
160    STABLE_CELL_VALUE_OFFSET, StableCellLedgerError, StableCellPayloadError,
161    decode_stable_cell_payload, stable_cell_payload_length,
162};
163#[cfg(test)]
164use stable_cell::{decode_stable_cell_commit_store, decode_stable_cell_commit_store_from_memory};
165pub use validation::AllocationValidationError;
166
167#[cfg(all(test, not(target_arch = "wasm32")))]
168mod allocation_measurements_tests;
169
170#[cfg(any(test, feature = "test-support"))]
171pub mod testing;
172
173#[cfg(test)]
174mod recovery_fuzz_tests;