1use crate::{
2 declaration::{AllocationDeclaration, DeclarationSnapshot},
3 schema::SchemaMetadata,
4 slot::{
5 IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_LEDGER_ID,
6 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 schema,
87 }
88 }
89
90 #[must_use]
92 pub const fn stable_key(&self) -> &crate::StableKey {
93 &self.stable_key
94 }
95
96 #[must_use]
98 pub const fn schema(&self) -> &SchemaMetadata {
99 &self.schema
100 }
101
102 #[must_use]
104 pub fn authority(&self) -> &str {
105 &self.authority
106 }
107}
108
109#[non_exhaustive]
116#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
117pub enum MemoryDeclarationError {
118 #[error("at most 254 external requests are supported")]
119 TooManyDeclarations,
120 #[error("duplicate requested stable key {stable_key}")]
121 DuplicateRequest { stable_key: crate::StableKey },
122 #[error(transparent)]
124 Declaration(#[from] crate::DeclarationSnapshotError),
125 #[error("authority {reason}")]
127 InvalidAuthority {
128 reason: &'static str,
130 },
131 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
133 ReservedAuthority {
134 authority: String,
136 },
137 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
139 ReservedStableKey {
140 stable_key: String,
142 },
143}
144
145#[derive(Clone, Debug, Eq, PartialEq)]
157pub struct SealedDeclarationSnapshot {
158 inner: Arc<Vec<MemoryRequest>>,
159}
160
161#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
173#[serde(deny_unknown_fields)]
174pub struct SealedDeclarationFingerprint {
175 algorithm_version: u8,
176 value: u64,
177}
178
179impl SealedDeclarationFingerprint {
180 #[must_use]
182 pub const fn algorithm_version(&self) -> u8 {
183 self.algorithm_version
184 }
185
186 #[must_use]
188 pub const fn value(&self) -> u64 {
189 self.value
190 }
191}
192
193impl SealedDeclarationSnapshot {
194 pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, MemoryDeclarationError> {
197 if requests.len() > 254 {
198 return Err(MemoryDeclarationError::TooManyDeclarations);
199 }
200 crate::sort::sort_unstable_by(&mut requests, |a, b| a.stable_key.cmp(&b.stable_key));
201 for pair in requests.windows(2) {
202 if pair[0].stable_key == pair[1].stable_key {
203 return Err(MemoryDeclarationError::DuplicateRequest {
204 stable_key: pair[1].stable_key.clone(),
205 });
206 }
207 }
208 Ok(Self {
209 inner: Arc::new(requests),
210 })
211 }
212
213 #[must_use]
215 pub fn requests(&self) -> &[MemoryRequest] {
216 &self.inner
217 }
218
219 pub(crate) fn resolve(
220 &self,
221 ledger: &crate::AllocationLedger,
222 occupied: &[bool; crate::constants::MAX_ALLOCATIONS],
223 historical: Vec<AllocationDeclaration>,
224 pool: &crate::MemoryAllocationPool,
225 ) -> Result<DeclarationSnapshot, crate::MemoryResolutionError> {
226 if self.requests().len() + historical.len() > 254 {
227 return Err(MemoryDeclarationError::TooManyDeclarations.into());
228 }
229 let mut declarations = Vec::with_capacity(self.requests().len() + historical.len() + 1);
230 declarations.push(internal_ledger_declaration());
231 let mut available_ids = 0..crate::MEMORY_MANAGER_INVALID_ID;
234 for request in self.requests() {
237 let historical = ledger
238 .records()
239 .iter()
240 .find(|record| record.stable_key() == &request.stable_key);
241 pool.validate_authority(&request.stable_key, &request.authority)?;
242 let id = if let Some(record) = historical {
243 pool.validate_id(record.slot().id())?;
246 record.slot().id()
247 } else {
248 available_ids
249 .find(|id| pool.contains(*id) && !occupied[usize::from(*id)])
250 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
251 stable_key: request.stable_key.clone(),
252 authority: request.authority.clone(),
253 })?
254 };
255 let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
256 declarations.push(AllocationDeclaration {
260 stable_key: request.stable_key.clone(),
261 slot,
262 schema: request.schema.clone(),
263 });
264 }
265 declarations.extend(historical);
266 Ok(
267 DeclarationSnapshot::canonicalize(declarations)
268 .map_err(MemoryDeclarationError::from)?,
269 )
270 }
271
272 #[must_use]
275 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
276 sealed_declaration_fingerprint(self.requests())
277 }
278
279 #[cfg(test)]
280 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
281 Arc::ptr_eq(&self.inner, &other.inner)
282 }
283}
284
285fn validate_external_authority(value: &str) -> Result<(), MemoryDeclarationError> {
286 reject_internal_authority(value)?;
287 validate_diagnostic_text(value).map_err(|error| MemoryDeclarationError::InvalidAuthority {
288 reason: error.reason(),
289 })
290}
291
292fn reject_internal_authority(value: &str) -> Result<(), MemoryDeclarationError> {
293 if value == IC_MEMORY_AUTHORITY_OWNER {
294 return Err(MemoryDeclarationError::ReservedAuthority {
295 authority: value.to_string(),
296 });
297 }
298 Ok(())
299}
300
301#[derive(Serialize)]
302struct SealedDeclarationFingerprintMaterial<'a> {
303 format: &'static str,
304 requests: &'a [MemoryRequest],
305}
306
307struct FingerprintWriter(u64);
310
311impl std::io::Write for FingerprintWriter {
312 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
313 self.0 = crate::hash::fnv64(self.0, bytes);
314 Ok(bytes.len())
315 }
316
317 fn flush(&mut self) -> std::io::Result<()> {
318 Ok(())
319 }
320}
321
322fn sealed_declaration_fingerprint(requests: &[MemoryRequest]) -> SealedDeclarationFingerprint {
323 let material = SealedDeclarationFingerprintMaterial {
324 format: "ic-memory.sealed-declaration-fingerprint.v1",
325 requests,
326 };
327 let mut writer = FingerprintWriter(crate::hash::FNV_OFFSET);
328 ciborium::into_writer(&material, &mut writer)
330 .unwrap_or_else(|_| panic!("sealed declaration fingerprint encodes into hash"));
331
332 SealedDeclarationFingerprint {
333 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
334 value: writer.0,
335 }
336}
337
338const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
339
340fn internal_ledger_declaration() -> AllocationDeclaration {
341 AllocationDeclaration::memory_manager(IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_LEDGER_ID)
342 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
343}
344
345#[cfg(test)]
346mod tests;