1use crate::{
2 declaration::{AllocationDeclaration, DeclarationSnapshot},
3 schema::SchemaMetadata,
4 slot::{
5 IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_LABEL, IC_MEMORY_LEDGER_STABLE_KEY,
6 MEMORY_MANAGER_LEDGER_ID, is_ic_memory_stable_key,
7 },
8 text::validate_diagnostic_text,
9};
10use serde::{Deserialize, Serialize};
11use std::sync::Arc;
12
13#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
21pub struct MemoryRequest {
22 authority: String,
23 stable_key: crate::StableKey,
24 schema: SchemaMetadata,
25}
26
27impl<'de> Deserialize<'de> for MemoryRequest {
28 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
29 use serde::de::Error as _;
30 #[derive(Deserialize)]
31 #[serde(deny_unknown_fields)]
32 struct Request {
33 authority: String,
34 stable_key: crate::StableKey,
35 schema: SchemaMetadata,
36 }
37 let request = Request::deserialize(deserializer)?;
38 validate_external_authority(&request.authority).map_err(D::Error::custom)?;
39 Self::from_checked_key(request.authority, request.stable_key, request.schema)
40 .map_err(D::Error::custom)
41 }
42}
43
44impl MemoryRequest {
45 pub fn new(
47 authority: impl Into<String>,
48 stable_key: &str,
49 schema: SchemaMetadata,
50 ) -> Result<Self, MemoryDeclarationError> {
51 let authority = authority.into();
52 validate_external_authority(&authority)?;
53 let stable_key =
54 crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
55 Self::from_checked_key(authority, stable_key, schema)
56 }
57
58 fn from_checked_key(
61 authority: String,
62 stable_key: crate::StableKey,
63 schema: SchemaMetadata,
64 ) -> Result<Self, MemoryDeclarationError> {
65 if is_ic_memory_stable_key(stable_key.as_str()) {
66 return Err(MemoryDeclarationError::ReservedStableKey {
67 stable_key: stable_key.as_str().to_string(),
68 });
69 }
70 Ok(Self {
71 authority,
72 stable_key,
73 schema,
74 })
75 }
76
77 pub(crate) fn into_declaration(
79 self,
80 slot: crate::MemoryManagerSlot,
81 schema: SchemaMetadata,
82 ) -> AllocationDeclaration {
83 AllocationDeclaration {
84 stable_key: self.stable_key,
85 slot,
86 label: None,
87 schema,
88 }
89 }
90
91 #[must_use]
93 pub const fn stable_key(&self) -> &crate::StableKey {
94 &self.stable_key
95 }
96
97 #[must_use]
99 pub const fn schema(&self) -> &SchemaMetadata {
100 &self.schema
101 }
102
103 #[must_use]
105 pub fn authority(&self) -> &str {
106 &self.authority
107 }
108}
109
110#[non_exhaustive]
117#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
118pub enum MemoryDeclarationError {
119 #[error("at most 254 external requests are supported")]
120 TooManyDeclarations,
121 #[error("duplicate requested stable key {stable_key}")]
122 DuplicateRequest { stable_key: crate::StableKey },
123 #[error(transparent)]
125 Declaration(#[from] crate::DeclarationSnapshotError),
126 #[error("authority {reason}")]
128 InvalidAuthority {
129 reason: &'static str,
131 },
132 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
134 ReservedAuthority {
135 authority: String,
137 },
138 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
140 ReservedStableKey {
141 stable_key: String,
143 },
144}
145
146#[derive(Clone, Debug, Eq, PartialEq)]
158pub struct SealedDeclarationSnapshot {
159 inner: Arc<SealedDeclarationSnapshotInner>,
160}
161
162#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
174#[serde(deny_unknown_fields)]
175pub struct SealedDeclarationFingerprint {
176 algorithm_version: u8,
177 value: u64,
178}
179
180impl SealedDeclarationFingerprint {
181 #[must_use]
183 pub const fn algorithm_version(&self) -> u8 {
184 self.algorithm_version
185 }
186
187 #[must_use]
189 pub const fn value(&self) -> u64 {
190 self.value
191 }
192}
193
194#[derive(Debug, Eq, PartialEq)]
195struct SealedDeclarationSnapshotInner {
196 requests: Vec<MemoryRequest>,
197 fingerprint: SealedDeclarationFingerprint,
198}
199
200impl SealedDeclarationSnapshot {
201 pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, MemoryDeclarationError> {
204 if requests.len() > 254 {
205 return Err(MemoryDeclarationError::TooManyDeclarations);
206 }
207 crate::sort::sort_unstable_by(&mut requests, |a, b| a.stable_key.cmp(&b.stable_key));
208 for pair in requests.windows(2) {
209 if pair[0].stable_key == pair[1].stable_key {
210 return Err(MemoryDeclarationError::DuplicateRequest {
211 stable_key: pair[1].stable_key.clone(),
212 });
213 }
214 }
215 let fingerprint = sealed_declaration_fingerprint(&requests);
216 Ok(Self {
217 inner: Arc::new(SealedDeclarationSnapshotInner {
218 requests,
219 fingerprint,
220 }),
221 })
222 }
223
224 #[must_use]
226 pub fn requests(&self) -> &[MemoryRequest] {
227 &self.inner.requests
228 }
229
230 pub(crate) fn resolve(
231 &self,
232 ledger: &crate::AllocationLedger,
233 occupied: &[bool; crate::constants::MAX_ALLOCATIONS],
234 historical: Vec<AllocationDeclaration>,
235 pool: &crate::MemoryAllocationPool,
236 ) -> Result<DeclarationSnapshot, crate::MemoryResolutionError> {
237 if self.requests().len() + historical.len() > 254 {
238 return Err(MemoryDeclarationError::TooManyDeclarations.into());
239 }
240 let mut declarations = Vec::with_capacity(self.requests().len() + historical.len() + 1);
241 declarations.push(internal_ledger_declaration());
242 let mut available_ids = 0..crate::MEMORY_MANAGER_INVALID_ID;
245 for request in self.requests() {
248 let historical = ledger
249 .records()
250 .iter()
251 .find(|record| record.stable_key() == &request.stable_key);
252 pool.validate_authority(&request.stable_key, &request.authority)?;
253 let id = if let Some(record) = historical {
254 pool.validate_id(record.slot().id())?;
257 record.slot().id()
258 } else {
259 available_ids
260 .find(|id| pool.contains(*id) && !occupied[usize::from(*id)])
261 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
262 stable_key: request.stable_key.clone(),
263 authority: request.authority.clone(),
264 })?
265 };
266 let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
267 declarations.push(AllocationDeclaration {
271 stable_key: request.stable_key.clone(),
272 slot,
273 label: None,
274 schema: request.schema.clone(),
275 });
276 }
277 declarations.extend(historical);
278 Ok(
279 DeclarationSnapshot::canonicalize(declarations)
280 .map_err(MemoryDeclarationError::from)?,
281 )
282 }
283
284 #[must_use]
286 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
287 self.inner.fingerprint
288 }
289
290 #[cfg(test)]
291 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
292 Arc::ptr_eq(&self.inner, &other.inner)
293 }
294}
295
296fn validate_external_authority(value: &str) -> Result<(), MemoryDeclarationError> {
297 reject_internal_authority(value)?;
298 validate_diagnostic_text(value).map_err(|error| MemoryDeclarationError::InvalidAuthority {
299 reason: error.reason(),
300 })
301}
302
303fn reject_internal_authority(value: &str) -> Result<(), MemoryDeclarationError> {
304 if value == IC_MEMORY_AUTHORITY_OWNER {
305 return Err(MemoryDeclarationError::ReservedAuthority {
306 authority: value.to_string(),
307 });
308 }
309 Ok(())
310}
311
312#[derive(Serialize)]
313struct SealedDeclarationFingerprintMaterial<'a> {
314 format: &'static str,
315 requests: &'a [MemoryRequest],
316}
317
318struct FingerprintWriter(u64);
321
322impl std::io::Write for FingerprintWriter {
323 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
324 self.0 = crate::hash::fnv64(self.0, bytes);
325 Ok(bytes.len())
326 }
327
328 fn flush(&mut self) -> std::io::Result<()> {
329 Ok(())
330 }
331}
332
333fn sealed_declaration_fingerprint(requests: &[MemoryRequest]) -> SealedDeclarationFingerprint {
334 let material = SealedDeclarationFingerprintMaterial {
335 format: "ic-memory.sealed-declaration-fingerprint.v1",
336 requests,
337 };
338 let mut writer = FingerprintWriter(crate::hash::FNV_OFFSET);
339 ciborium::into_writer(&material, &mut writer)
341 .unwrap_or_else(|_| panic!("sealed declaration fingerprint encodes into hash"));
342
343 SealedDeclarationFingerprint {
344 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
345 value: writer.0,
346 }
347}
348
349const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
350
351fn internal_ledger_declaration() -> AllocationDeclaration {
352 AllocationDeclaration::memory_manager(
353 IC_MEMORY_LEDGER_STABLE_KEY,
354 MEMORY_MANAGER_LEDGER_ID,
355 IC_MEMORY_LEDGER_LABEL,
356 )
357 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
358}
359
360#[cfg(test)]
361mod tests;