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//! [`default_memory_manager_memory_allocations`]. 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//! capability, 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 dual-slot 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//! [`ic_stable_structures`] re-exports the exact substrate version used by this
74//! crate. Use its collections and traits with [`RuntimeMemory`] handles;
75//! `ic-memory` owns allocation governance without wrapping typed collections.
76
77mod allocation_pool;
78mod bootstrap;
79mod capability;
80mod cbor;
81mod constants;
82mod declaration;
83mod diagnostics;
84mod hash;
85mod key;
86mod ledger;
87mod physical;
88mod policy;
89mod requests;
90mod runtime;
91mod schema;
92mod slot;
93mod stable_cell;
94mod text;
95mod validation;
96
97#[cfg(test)]
98mod test_cbor {
99    use serde::{Serialize, de::DeserializeOwned};
100
101    pub use ciborium::Value;
102
103    pub fn to_vec<T: Serialize>(
104        value: &T,
105    ) -> Result<Vec<u8>, ciborium::ser::Error<std::io::Error>> {
106        let mut bytes = Vec::new();
107        ciborium::into_writer(value, &mut bytes)?;
108        Ok(bytes)
109    }
110
111    pub fn from_slice<T: DeserializeOwned>(
112        bytes: &[u8],
113    ) -> Result<T, ciborium::de::Error<std::io::Error>> {
114        crate::cbor::from_slice_exact(bytes)
115    }
116
117    pub fn to_value<T: Serialize>(value: T) -> Result<Value, ciborium::value::Error> {
118        Value::serialized(&value)
119    }
120
121    pub fn hex_fixture(contents: &str) -> Vec<u8> {
122        let hex = contents
123            .chars()
124            .filter(|char| !char.is_whitespace())
125            .collect::<String>();
126        assert_eq!(hex.len() % 2, 0, "fixture hex must have byte pairs");
127        hex.as_bytes()
128            .as_chunks::<2>()
129            .0
130            .iter()
131            .map(|pair| {
132                let pair = std::str::from_utf8(pair).expect("fixture hex is utf8");
133                u8::from_str_radix(pair, 16).expect("fixture hex byte")
134            })
135            .collect()
136    }
137}
138
139/// Stable collections and traits from this crate's exact substrate dependency.
140///
141/// Use the upstream collections with [`RuntimeMemory`] handles obtained through
142/// the owned runtime. This re-export preserves upstream type identity.
143pub use ic_stable_structures;
144
145pub use allocation_pool::{MemoryAllocationPool, MemoryAllocationPoolError, MemoryAuthority};
146pub(crate) use bootstrap::AllocationBootstrap;
147pub use bootstrap::BootstrapError;
148pub use capability::CommittedAllocations;
149pub use constants::{
150    MAX_LEDGER_BYTES, MAX_LEDGER_NESTING, MAX_LEDGER_RECORD_BYTES, WASM_PAGE_SIZE_BYTES,
151};
152pub use declaration::{AllocationDeclaration, DeclarationSnapshot, DeclarationSnapshotError};
153pub use diagnostics::{
154    DiagnosticCheck, DiagnosticCode, DiagnosticExport, DiagnosticFailure, DiagnosticMemorySize,
155    DiagnosticRecord, DiagnosticRuntimeBinding, DiagnosticStableCell, DiagnosticStableCellStatus,
156    MemoryRuntimeDoctorReport,
157};
158pub use key::{StableKey, StableKeyError};
159pub(crate) use ledger::LedgerCommitStore;
160#[cfg(test)]
161pub(crate) use ledger::LedgerPayloadEnvelope;
162pub use ledger::{
163    AllocationLedger, AllocationRecord, AllocationReservationError, AllocationRetirement,
164    AllocationRetirementError, AllocationStageError, AllocationState,
165    LEDGER_PAYLOAD_FORMAT_VERSION, LedgerCommitError, LedgerIntegrityError,
166    LedgerPayloadEnvelopeError, RecoveredLedger,
167};
168pub use physical::{CommitRecoveryError, CommitSlotDiagnostic, CommitStoreDiagnostic};
169#[cfg(test)]
170pub(crate) use physical::{CommittedGenerationBytes, DualCommitStore};
171pub use policy::{AllocationPolicy, PolicyIdentity, PolicyIdentityError, RuntimeBootstrapPolicy};
172pub use requests::{
173    MemoryDeclarationError, MemoryRequest, SealedDeclarationFingerprint, SealedDeclarationSnapshot,
174};
175pub use runtime::{
176    AllocationBinding, BootstrapAdmission, BootstrapAdmissionError, GenericAllocationPolicy,
177    MemoryAllocation, MemoryAllocationSummary, MemoryAllocations, MemoryBindingSummary,
178    MemoryManagerConfig, MemoryManagerLayoutError, MemoryResolutionError, MemoryRuntime,
179    RecoveredAllocationMetadata, RuntimeAdoptionError, RuntimeBootstrapError,
180    RuntimeConstructionError, RuntimeDiagnosticError, RuntimeGrowError, RuntimeMemory,
181    RuntimeOpenError, RuntimeStateError, bootstrap_default_memory_manager,
182    bootstrap_default_memory_manager_with_config, bootstrap_default_memory_manager_with_policy,
183    committed_allocations, default_memory_manager_commit_recovery_diagnostic,
184    default_memory_manager_diagnostic_export, default_memory_manager_doctor_report,
185    default_memory_manager_doctor_report_with_policy,
186    default_memory_manager_memory_allocation_summary, default_memory_manager_memory_allocations,
187    default_memory_manager_memory_id, is_default_memory_manager_bootstrapped,
188    open_default_memory_manager_memory, verify_default_memory_manager_authority,
189};
190pub use schema::{SchemaMetadata, SchemaMetadataError};
191pub use slot::{
192    IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_LABEL, IC_MEMORY_LEDGER_STABLE_KEY,
193    IC_MEMORY_STABLE_KEY_PREFIX, MEMORY_MANAGER_GOVERNANCE_MAX_ID, MEMORY_MANAGER_INVALID_ID,
194    MEMORY_MANAGER_LEDGER_ID, MEMORY_MANAGER_MAX_ID, MEMORY_MANAGER_MIN_ID, MemoryManagerIdRange,
195    MemoryManagerRangeError, MemoryManagerSlot, MemoryManagerSlotError, is_ic_memory_stable_key,
196    memory_manager_governance_range, validate_memory_manager_id,
197};
198pub use stable_cell::{
199    STABLE_CELL_HEADER_SIZE, STABLE_CELL_LAYOUT_VERSION, STABLE_CELL_MAGIC,
200    STABLE_CELL_VALUE_OFFSET, StableCellLedgerError, StableCellLedgerRecord,
201    StableCellPayloadError, decode_stable_cell_ledger_record,
202    decode_stable_cell_ledger_record_from_memory, decode_stable_cell_payload,
203};
204pub use validation::AllocationValidationError;
205#[cfg(test)]
206pub(crate) use validation::validate_allocations;
207
208#[cfg(test)]
209mod allocation_measurements_tests;