Skip to main content

ic_memory/
requests.rs

1use crate::{
2    declaration::AllocationDeclaration,
3    slot::{
4        IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_LEDGER_ID,
5        is_ic_memory_stable_key,
6    },
7    text::validate_diagnostic_text,
8};
9#[cfg(any(test, feature = "test-support"))]
10use serde::Deserialize;
11use serde::Serialize;
12use std::sync::Arc;
13
14///
15/// MemoryRequest
16///
17/// Key-only request resolved after ledger recovery. All admitted owners share the
18/// host's free application pool; known keys retain their durable slot.
19///
20/// Construct inputs with [`Self::new`]; serialization is for diagnostic output.
21/// Decoding is available only with `test-support` for explicit fixtures.
22///
23
24#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
25pub struct MemoryRequest {
26    authority: String,
27    stable_key: crate::StableKey,
28}
29
30#[cfg(any(test, feature = "test-support"))]
31impl<'de> Deserialize<'de> for MemoryRequest {
32    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
33        use serde::de::Error as _;
34        #[derive(Deserialize)]
35        #[serde(deny_unknown_fields)]
36        struct Request {
37            authority: String,
38            stable_key: crate::StableKey,
39        }
40        let request = Request::deserialize(deserializer)?;
41        validate_external_authority(&request.authority).map_err(D::Error::custom)?;
42        validate_external_key(&request.stable_key).map_err(D::Error::custom)?;
43        Ok(Self {
44            authority: request.authority,
45            stable_key: request.stable_key,
46        })
47    }
48}
49
50impl MemoryRequest {
51    /// Build a checked logical request before sealing.
52    pub fn new(
53        authority: impl Into<String>,
54        stable_key: &str,
55    ) -> Result<Self, MemoryDeclarationError> {
56        let authority = authority.into();
57        let stable_key = checked_request_key(&authority, stable_key)?;
58        Ok(Self {
59            authority,
60            stable_key,
61        })
62    }
63
64    /// Borrow the requested durable key.
65    #[must_use]
66    pub const fn stable_key(&self) -> &crate::StableKey {
67        &self.stable_key
68    }
69
70    /// Borrow the declaring authority.
71    #[must_use]
72    pub fn authority(&self) -> &str {
73        &self.authority
74    }
75}
76
77///
78/// MemoryDeclarationError
79///
80/// Failure to construct a complete allocation declaration snapshot.
81///
82
83#[non_exhaustive]
84#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
85pub enum MemoryDeclarationError {
86    #[error("at most 254 external requests are supported")]
87    TooManyDeclarations,
88    #[error("duplicate requested stable key {stable_key}")]
89    DuplicateRequest { stable_key: crate::StableKey },
90    /// Stable-key grammar validation failed.
91    #[error(transparent)]
92    Key(#[from] crate::StableKeyError),
93    /// A request supplied an invalid authority identifier.
94    #[error("authority {reason}")]
95    InvalidAuthority {
96        /// Validation failure.
97        reason: &'static str,
98    },
99    /// A request attempted to impersonate the internal authority.
100    #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
101    ReservedAuthority {
102        /// Reserved authority identifier.
103        authority: String,
104    },
105    /// A request attempted to claim the internal stable-key namespace.
106    #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
107    ReservedStableKey {
108        /// Reserved stable key.
109        stable_key: String,
110    },
111}
112
113///
114/// SealedDeclarationSnapshot
115///
116/// Immutable, canonical host-composed allocation requests supplied to each concrete [`crate::MemoryRuntime`].
117///
118/// Construction takes ownership of the complete request list and validates its
119/// canonical meaning. Clones share the same immutable snapshot. This value contains
120/// declaration authority only; it contains no memory handles, recovery state,
121/// bootstrap lifecycle, or committed allocation capability.
122///
123
124#[derive(Clone, Debug, Eq, PartialEq)]
125pub struct SealedDeclarationSnapshot {
126    inner: Arc<Vec<MemoryRequest>>,
127}
128
129///
130/// DeclarationSnapshot
131///
132/// Internal canonical output of resolution, without open authority.
133/// Sealing, recovered-ledger admission and the resolver establish its bounds
134/// and unique keys/slots. Only this module constructs production snapshots.
135///
136
137#[derive(Debug, Eq, PartialEq)]
138pub struct DeclarationSnapshot {
139    declarations: Vec<AllocationDeclaration>,
140}
141
142impl DeclarationSnapshot {
143    pub(crate) fn declarations(&self) -> &[AllocationDeclaration] {
144        &self.declarations
145    }
146
147    pub(crate) fn into_declarations(self) -> Vec<AllocationDeclaration> {
148        self.declarations
149    }
150
151    // Explicit synthetic staging inputs remain confined to unit fixtures.
152    #[cfg(test)]
153    pub(crate) fn for_test(mut declarations: Vec<AllocationDeclaration>) -> Self {
154        crate::sort::sort_unstable_by(&mut declarations, |a, b| a.stable_key.cmp(&b.stable_key));
155        Self { declarations }
156    }
157}
158
159impl SealedDeclarationSnapshot {
160    /// Take ownership of the host's complete requests, checking the bound and
161    /// rejecting duplicate keys before sharing their canonical contents.
162    pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, MemoryDeclarationError> {
163        if requests.len() > 254 {
164            return Err(MemoryDeclarationError::TooManyDeclarations);
165        }
166        crate::sort::sort_unstable_by(&mut requests, |a, b| a.stable_key.cmp(&b.stable_key));
167        for pair in requests.windows(2) {
168            if pair[0].stable_key == pair[1].stable_key {
169                return Err(MemoryDeclarationError::DuplicateRequest {
170                    stable_key: pair[1].stable_key.clone(),
171                });
172            }
173        }
174        Ok(Self {
175            inner: Arc::new(requests),
176        })
177    }
178
179    /// Borrow canonical unresolved key-only requests.
180    #[must_use]
181    pub fn requests(&self) -> &[MemoryRequest] {
182        &self.inner
183    }
184
185    pub(crate) fn resolve(
186        &self,
187        recovered: &crate::RecoveredLedger,
188        occupied: &[bool; crate::constants::MAX_ALLOCATIONS],
189        historical: Vec<AllocationDeclaration>,
190        pool: &crate::MemoryAllocationPool,
191    ) -> Result<DeclarationSnapshot, crate::MemoryResolutionError> {
192        let ledger = recovered.ledger();
193        if self.requests().len() + historical.len() > 254 {
194            return Err(MemoryDeclarationError::TooManyDeclarations.into());
195        }
196        let mut declarations = Vec::with_capacity(self.requests().len() + historical.len() + 1);
197        declarations.push(internal_ledger_declaration());
198        // Retained slots are already occupied. The iterator consumes each new
199        // ID once, so no request rescans or updates that recovered occupancy.
200        let mut available_ids = 0..crate::MEMORY_MANAGER_INVALID_ID;
201        // Admission already resolved historical selections against this ledger
202        // and pool. Only the original requests can allocate new slots.
203        for request in self.requests() {
204            let historical = ledger
205                .records()
206                .iter()
207                .find(|record| record.stable_key() == &request.stable_key);
208            pool.validate_authority(&request.stable_key, &request.authority)?;
209            let id = if let Some(record) = historical {
210                // Durable identity selects placement; the host separately grants
211                // current key ownership and retains explicit physical exclusions.
212                pool.validate_id(record.slot().id())?;
213                record.slot().id()
214            } else {
215                available_ids
216                    .find(|id| pool.contains(*id) && !occupied[usize::from(*id)])
217                    .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
218                        stable_key: request.stable_key.clone(),
219                        authority: request.authority.clone(),
220                    })?
221            };
222            let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
223            // Resolution retains checked allocation identity without copying the owner.
224            declarations.push(AllocationDeclaration {
225                stable_key: request.stable_key.clone(),
226                slot,
227            });
228        }
229        declarations.extend(historical);
230        crate::sort::sort_unstable_by(&mut declarations, |a, b| a.stable_key.cmp(&b.stable_key));
231        Ok(DeclarationSnapshot { declarations })
232    }
233
234    #[cfg(test)]
235    pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
236        Arc::ptr_eq(&self.inner, &other.inner)
237    }
238}
239
240// Admission needs a checked key, without allocating a discarded request owner.
241pub fn checked_request_key(
242    authority: &str,
243    stable_key: &str,
244) -> Result<crate::StableKey, MemoryDeclarationError> {
245    validate_external_authority(authority)?;
246    let stable_key = crate::StableKey::parse(stable_key)?;
247    validate_external_key(&stable_key)?;
248    Ok(stable_key)
249}
250
251fn validate_external_key(stable_key: &crate::StableKey) -> Result<(), MemoryDeclarationError> {
252    if is_ic_memory_stable_key(stable_key.as_str()) {
253        return Err(MemoryDeclarationError::ReservedStableKey {
254            stable_key: stable_key.as_str().to_string(),
255        });
256    }
257    Ok(())
258}
259
260fn validate_external_authority(value: &str) -> Result<(), MemoryDeclarationError> {
261    reject_internal_authority(value)?;
262    validate_diagnostic_text(value).map_err(|error| MemoryDeclarationError::InvalidAuthority {
263        reason: error.reason(),
264    })
265}
266
267fn reject_internal_authority(value: &str) -> Result<(), MemoryDeclarationError> {
268    if value == IC_MEMORY_AUTHORITY_OWNER {
269        return Err(MemoryDeclarationError::ReservedAuthority {
270            authority: value.to_string(),
271        });
272    }
273    Ok(())
274}
275
276fn internal_ledger_declaration() -> AllocationDeclaration {
277    AllocationDeclaration {
278        stable_key: crate::StableKey::parse(IC_MEMORY_LEDGER_STABLE_KEY)
279            .expect("built-in ledger key is valid"),
280        slot: crate::MemoryManagerSlot::new(MEMORY_MANAGER_LEDGER_ID)
281            .expect("built-in ledger ID is usable"),
282    }
283}
284
285#[cfg(test)]
286mod tests;