1use crate::{
2 declaration::{AllocationDeclaration, DeclarationSnapshot},
3 schema::SchemaMetadata,
4 slot::{
5 IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_AUTHORITY_PURPOSE, IC_MEMORY_LEDGER_LABEL,
6 IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_LEDGER_ID, MemoryManagerAuthorityRecord,
7 MemoryManagerIdRange, MemoryManagerRangeAuthority, MemoryManagerRangeAuthorityError,
8 MemoryManagerRangeMode, is_ic_memory_stable_key, memory_manager_governance_range,
9 },
10 text::validate_diagnostic_text,
11};
12use serde::{Deserialize, Serialize};
13use std::{
14 borrow::Cow,
15 panic::{AssertUnwindSafe, catch_unwind},
16 sync::{Arc, Mutex, MutexGuard},
17 thread::ThreadId,
18};
19
20#[cfg(test)]
21pub static TEST_REGISTRY_LOCK: Mutex<()> = Mutex::new(());
22
23#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
36pub struct StaticMemoryDeclaration {
37 authority: String,
38 declaration: AllocationDeclaration,
39}
40
41impl StaticMemoryDeclaration {
42 pub fn new(
44 authority: impl Into<String>,
45 declaration: AllocationDeclaration,
46 ) -> Result<Self, StaticMemoryDeclarationError> {
47 let authority = authority.into();
48 validate_external_authority(&authority)?;
49 if is_ic_memory_stable_key(declaration.stable_key().as_str()) {
50 return Err(StaticMemoryDeclarationError::ReservedStableKey {
51 stable_key: declaration.stable_key().as_str().to_string(),
52 });
53 }
54 Ok(Self {
55 authority,
56 declaration,
57 })
58 }
59
60 #[must_use]
62 pub fn authority(&self) -> &str {
63 &self.authority
64 }
65
66 #[must_use]
68 pub const fn declaration(&self) -> &AllocationDeclaration {
69 &self.declaration
70 }
71
72 #[must_use]
74 pub fn into_declaration(self) -> AllocationDeclaration {
75 self.declaration
76 }
77}
78
79#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
87pub struct MemoryRequest {
88 authority: String,
89 stable_key: crate::StableKey,
90 schema: SchemaMetadata,
91}
92
93impl MemoryRequest {
94 pub fn new(
96 authority: impl Into<String>,
97 stable_key: &str,
98 schema: SchemaMetadata,
99 ) -> Result<Self, StaticMemoryDeclarationError> {
100 let authority = authority.into();
101 validate_external_authority(&authority)?;
102 let stable_key =
103 crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
104 if is_ic_memory_stable_key(stable_key.as_str()) {
105 return Err(StaticMemoryDeclarationError::ReservedStableKey {
106 stable_key: stable_key.as_str().to_string(),
107 });
108 }
109 Ok(Self {
110 authority,
111 stable_key,
112 schema,
113 })
114 }
115
116 pub(crate) const fn with_schema(mut self, schema: SchemaMetadata) -> Self {
118 self.schema = schema;
119 self
120 }
121
122 #[must_use]
124 pub const fn stable_key(&self) -> &crate::StableKey {
125 &self.stable_key
126 }
127
128 #[must_use]
130 pub const fn schema(&self) -> &SchemaMetadata {
131 &self.schema
132 }
133
134 #[must_use]
136 pub fn authority(&self) -> &str {
137 &self.authority
138 }
139}
140
141pub fn register_memory_request(request: MemoryRequest) -> Result<(), StaticMemoryDeclarationError> {
143 with_unsealed_registry(|registry| registry.requests.push(request))
144}
145
146#[derive(Clone, Debug, Eq, PartialEq)]
155pub struct StaticMemoryRangeDeclaration {
156 record: MemoryManagerAuthorityRecord,
157}
158
159impl StaticMemoryRangeDeclaration {
160 pub fn new(record: MemoryManagerAuthorityRecord) -> Result<Self, StaticMemoryDeclarationError> {
162 reject_internal_authority(record.authority())?;
164 Ok(Self { record })
165 }
166
167 #[must_use]
169 pub fn authority(&self) -> &str {
170 self.record.authority()
171 }
172
173 #[must_use]
175 pub const fn record(&self) -> &MemoryManagerAuthorityRecord {
176 &self.record
177 }
178
179 #[must_use]
181 pub fn into_record(self) -> MemoryManagerAuthorityRecord {
182 self.record
183 }
184}
185
186#[non_exhaustive]
191#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
192pub enum StaticMemoryDeclarationError {
193 #[error("at most 254 external declarations and ranges are supported")]
194 TooManyDeclarations,
195 #[error("duplicate requested stable key {stable_key}")]
196 DuplicateRequest { stable_key: crate::StableKey },
197 #[error("static memory declaration registry lock poisoned")]
199 RegistryPoisoned,
200 #[error("static memory declaration registry is already sealed")]
202 RegistrySealed,
203 #[error("static memory declaration snapshot sealing is already active on this thread")]
205 ReentrantSealing,
206 #[error("static memory declaration eager-init hook panicked")]
208 EagerInitPanicked,
209 #[error(transparent)]
211 Declaration(#[from] crate::DeclarationSnapshotError),
212 #[error(transparent)]
214 Range(#[from] MemoryManagerRangeAuthorityError),
215 #[error("authority {reason}")]
217 InvalidAuthority {
218 reason: &'static str,
220 },
221 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
223 ReservedAuthority {
224 authority: String,
226 },
227 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
229 ReservedStableKey {
230 stable_key: String,
232 },
233}
234
235#[derive(Clone, Debug, Eq, PartialEq)]
248pub struct SealedDeclarationSnapshot {
249 inner: Arc<SealedDeclarationSnapshotInner>,
250}
251
252#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
265#[serde(deny_unknown_fields)]
266pub struct SealedDeclarationFingerprint {
267 algorithm_version: u8,
268 value: u64,
269}
270
271impl SealedDeclarationFingerprint {
272 #[must_use]
274 pub const fn algorithm_version(&self) -> u8 {
275 self.algorithm_version
276 }
277
278 #[must_use]
280 pub const fn value(&self) -> u64 {
281 self.value
282 }
283}
284
285#[derive(Debug, Eq, PartialEq)]
286struct SealedDeclarationSnapshotInner {
287 allocation_snapshot: DeclarationSnapshot,
288 requests: Vec<MemoryRequest>,
289 registered_declarations: Vec<StaticMemoryDeclaration>,
290 registered_ranges: Vec<StaticMemoryRangeDeclaration>,
291 range_authority: MemoryManagerRangeAuthority,
292 fingerprint: SealedDeclarationFingerprint,
293}
294
295impl SealedDeclarationSnapshot {
296 pub fn new(
298 declarations: &[StaticMemoryDeclaration],
299 ranges: &[StaticMemoryRangeDeclaration],
300 requests: &[MemoryRequest],
301 ) -> Result<Self, StaticMemoryDeclarationError> {
302 build_snapshot(
303 Cow::Borrowed(declarations),
304 Cow::Borrowed(ranges),
305 Cow::Borrowed(requests),
306 )
307 }
308
309 #[must_use]
311 pub fn requests(&self) -> &[MemoryRequest] {
312 &self.inner.requests
313 }
314
315 pub(crate) fn resolve(
316 &self,
317 ledger: &crate::AllocationLedger,
318 historical: Vec<MemoryRequest>,
319 ) -> Result<Self, crate::MemoryResolutionError> {
320 if self.requests().is_empty() && historical.is_empty() {
321 return Ok(self.clone());
322 }
323 if self.registered_declarations().len() + self.requests().len() + historical.len() > 254 {
324 return Err(StaticMemoryDeclarationError::TooManyDeclarations.into());
325 }
326 let mut declarations = self.registered_declarations().to_vec();
327 let mut occupied = [false; 255];
328 for record in ledger.allocation_history().records() {
329 occupied[usize::from(record.slot().id())] = true;
330 }
331 for fixed in &declarations {
332 occupied[usize::from(fixed.declaration().slot().id())] = true;
333 }
334 for request in self
339 .requests()
340 .iter()
341 .map(Cow::Borrowed)
342 .chain(historical.into_iter().map(Cow::Owned))
343 {
344 let historical = ledger
345 .allocation_history()
346 .records()
347 .iter()
348 .find(|record| record.stable_key() == &request.stable_key);
349 let id = if let Some(record) = historical {
350 let id = record.slot().id();
351 self.range_authority()
355 .validate_id_authority(id, &request.authority)
356 .map_err(crate::MemoryResolutionError::Range)?;
357 id
358 } else {
359 self.range_authority()
362 .authorities()
363 .iter()
364 .filter(|range| {
365 range.authority() == request.authority
366 && range.mode() == MemoryManagerRangeMode::Allowed
367 })
368 .flat_map(|range| range.range().start()..=range.range().end())
369 .find(|id| !occupied[usize::from(*id)])
370 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
371 stable_key: request.stable_key.clone(),
372 authority: request.authority.clone(),
373 })?
374 };
375 let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
376 occupied[usize::from(id)] = true;
377 let request = request.into_owned();
382 declarations.push(StaticMemoryDeclaration {
383 authority: request.authority,
384 declaration: AllocationDeclaration {
385 stable_key: request.stable_key,
386 slot,
387 label: None,
388 schema: request.schema,
389 },
390 });
391 }
392 Ok(build_snapshot(
393 Cow::Owned(declarations),
394 Cow::Borrowed(self.registered_ranges()),
395 Cow::Owned(Vec::new()),
396 )?)
397 }
398
399 #[must_use]
403 pub fn allocation_snapshot(&self) -> &DeclarationSnapshot {
404 &self.inner.allocation_snapshot
405 }
406
407 #[must_use]
409 pub fn registered_declarations(&self) -> &[StaticMemoryDeclaration] {
410 &self.inner.registered_declarations
411 }
412
413 #[must_use]
415 pub fn registered_ranges(&self) -> &[StaticMemoryRangeDeclaration] {
416 &self.inner.registered_ranges
417 }
418
419 #[must_use]
421 pub fn range_authority(&self) -> &MemoryManagerRangeAuthority {
422 &self.inner.range_authority
423 }
424
425 #[must_use]
427 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
428 self.inner.fingerprint
429 }
430
431 pub(crate) fn registered_declaration(
432 &self,
433 key: &crate::StableKey,
434 ) -> Option<&StaticMemoryDeclaration> {
435 let declarations = self.registered_declarations();
436 declarations
438 .binary_search_by(|registration| registration.declaration().stable_key().cmp(key))
439 .ok()
440 .map(|index| &declarations[index])
441 }
442
443 pub(crate) fn user_ranges_registered(&self) -> bool {
444 !self.inner.registered_ranges.is_empty()
445 }
446
447 #[cfg(test)]
448 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
449 Arc::ptr_eq(&self.inner, &other.inner)
450 }
451}
452
453type StaticRegistrationHook = fn() -> Result<(), StaticMemoryDeclarationError>;
454
455#[derive(Debug)]
456struct StaticMemoryDeclarationRegistry {
457 declarations: Vec<StaticMemoryDeclaration>,
458 requests: Vec<MemoryRequest>,
459 ranges: Vec<StaticMemoryRangeDeclaration>,
460 registration_hooks: Vec<StaticRegistrationHook>,
461 eager_init_hooks: Vec<fn()>,
462 lifecycle: StaticRegistryLifecycle,
463}
464
465impl StaticMemoryDeclarationRegistry {
466 fn finish_sealing(
467 &mut self,
468 result: Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError>,
469 ) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
470 self.declarations = Vec::new();
472 self.requests = Vec::new();
473 self.ranges = Vec::new();
474 self.registration_hooks = Vec::new();
475 self.eager_init_hooks = Vec::new();
476 self.lifecycle = match &result {
477 Ok(snapshot) => StaticRegistryLifecycle::Sealed(snapshot.clone()),
478 Err(error) => StaticRegistryLifecycle::Failed(error.clone()),
479 };
480 result
481 }
482}
483
484#[derive(Debug)]
485enum StaticRegistryLifecycle {
486 Open,
487 Sealing {
488 owner: ThreadId,
489 deferred_error: Option<StaticMemoryDeclarationError>,
490 },
491 Sealed(SealedDeclarationSnapshot),
492 Failed(StaticMemoryDeclarationError),
493}
494
495static STATIC_MEMORY_DECLARATIONS: Mutex<StaticMemoryDeclarationRegistry> =
496 Mutex::new(StaticMemoryDeclarationRegistry {
497 declarations: Vec::new(),
498 requests: Vec::new(),
499 ranges: Vec::new(),
500 registration_hooks: Vec::new(),
501 eager_init_hooks: Vec::new(),
502 lifecycle: StaticRegistryLifecycle::Open,
503 });
504
505static STATIC_MEMORY_SEAL: Mutex<()> = Mutex::new(());
506
507fn lock_registry()
508-> Result<MutexGuard<'static, StaticMemoryDeclarationRegistry>, StaticMemoryDeclarationError> {
509 STATIC_MEMORY_DECLARATIONS
510 .lock()
511 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
512}
513
514fn ensure_registration_open(
515 registry: &StaticMemoryDeclarationRegistry,
516) -> Result<(), StaticMemoryDeclarationError> {
517 match ®istry.lifecycle {
518 StaticRegistryLifecycle::Open => Ok(()),
519 StaticRegistryLifecycle::Sealing { owner, .. } if *owner == std::thread::current().id() => {
520 Ok(())
521 }
522 StaticRegistryLifecycle::Sealing { .. }
523 | StaticRegistryLifecycle::Sealed(_)
524 | StaticRegistryLifecycle::Failed(_) => Err(StaticMemoryDeclarationError::RegistrySealed),
525 }
526}
527
528fn with_unsealed_registry(
529 op: impl FnOnce(&mut StaticMemoryDeclarationRegistry),
530) -> Result<(), StaticMemoryDeclarationError> {
531 let mut registry = lock_registry()?;
532 ensure_registration_open(®istry)?;
533 op(&mut registry);
534 Ok(())
535}
536
537#[doc(hidden)]
542pub fn defer_static_memory_registration(hook: StaticRegistrationHook) {
543 defer_constructor_registration(|registry| {
544 registry.registration_hooks.push(hook);
545 });
546}
547
548#[doc(hidden)]
553pub fn defer_eager_init(hook: fn()) {
554 defer_constructor_registration(|registry| {
555 registry.eager_init_hooks.push(hook);
556 });
557}
558
559fn defer_constructor_registration(op: impl FnOnce(&mut StaticMemoryDeclarationRegistry)) {
560 let Ok(mut registry) = STATIC_MEMORY_DECLARATIONS.lock() else {
561 return;
564 };
565 if matches!(registry.lifecycle, StaticRegistryLifecycle::Open) {
566 op(&mut registry);
567 return;
568 }
569 match &mut registry.lifecycle {
570 StaticRegistryLifecycle::Sealing { deferred_error, .. } => {
571 if deferred_error.is_none() {
572 *deferred_error = Some(StaticMemoryDeclarationError::RegistrySealed);
573 }
574 }
575 StaticRegistryLifecycle::Sealed(_) => {
576 registry.lifecycle =
577 StaticRegistryLifecycle::Failed(StaticMemoryDeclarationError::RegistrySealed);
578 }
579 StaticRegistryLifecycle::Failed(_) | StaticRegistryLifecycle::Open => {}
580 }
581}
582
583pub fn register_static_memory_declaration(
585 authority: impl Into<String>,
586 declaration: AllocationDeclaration,
587) -> Result<(), StaticMemoryDeclarationError> {
588 let registration = StaticMemoryDeclaration::new(authority, declaration)?;
589 with_unsealed_registry(|registry| {
590 registry.declarations.push(registration);
591 })
592}
593
594pub fn register_static_memory_manager_range(
596 start: u8,
597 end: u8,
598 authority: impl Into<String>,
599 mode: MemoryManagerRangeMode,
600 purpose: Option<String>,
601) -> Result<(), StaticMemoryDeclarationError> {
602 let authority = authority.into();
603 let record = MemoryManagerAuthorityRecord::new(
604 MemoryManagerIdRange::new(start, end).map_err(MemoryManagerRangeAuthorityError::Range)?,
605 authority,
606 mode,
607 purpose,
608 )?;
609 register_static_memory_range_declaration(StaticMemoryRangeDeclaration::new(record)?)
610}
611
612pub fn register_static_memory_range_declaration(
614 declaration: StaticMemoryRangeDeclaration,
615) -> Result<(), StaticMemoryDeclarationError> {
616 with_unsealed_registry(|registry| {
617 registry.ranges.push(declaration);
618 })
619}
620
621fn validate_external_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
622 reject_internal_authority(value)?;
623 validate_diagnostic_text(value).map_err(|error| {
624 StaticMemoryDeclarationError::InvalidAuthority {
625 reason: error.reason(),
626 }
627 })
628}
629
630fn reject_internal_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
631 if value == IC_MEMORY_AUTHORITY_OWNER {
632 return Err(StaticMemoryDeclarationError::ReservedAuthority {
633 authority: value.to_string(),
634 });
635 }
636 Ok(())
637}
638
639pub fn register_static_memory_manager_declaration(
641 id: u8,
642 authority: impl Into<String>,
643 label: impl Into<String>,
644 stable_key: impl AsRef<str>,
645) -> Result<(), StaticMemoryDeclarationError> {
646 register_static_memory_manager_declaration_with_schema(
647 id,
648 authority,
649 label,
650 stable_key,
651 SchemaMetadata::default(),
652 )
653}
654
655pub fn register_static_memory_manager_declaration_with_schema(
657 id: u8,
658 authority: impl Into<String>,
659 label: impl Into<String>,
660 stable_key: impl AsRef<str>,
661 schema: SchemaMetadata,
662) -> Result<(), StaticMemoryDeclarationError> {
663 let declaration =
664 AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
665 register_static_memory_declaration(authority, declaration)
666}
667
668pub fn sealed_declaration_snapshot()
680-> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
681 {
682 let registry = lock_registry()?;
683 match ®istry.lifecycle {
684 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
685 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
686 StaticRegistryLifecycle::Sealing { owner, .. }
687 if *owner == std::thread::current().id() =>
688 {
689 return Err(StaticMemoryDeclarationError::ReentrantSealing);
690 }
691 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealing { .. } => {}
692 }
693 }
694
695 let _seal = STATIC_MEMORY_SEAL
696 .lock()
697 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
698 let (registration_hooks, eager_init_hooks) = {
699 let mut registry = lock_registry()?;
700 match ®istry.lifecycle {
701 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
702 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
703 StaticRegistryLifecycle::Sealing { .. } => {
704 return Err(StaticMemoryDeclarationError::ReentrantSealing);
705 }
706 StaticRegistryLifecycle::Open => {}
707 }
708 registry.lifecycle = StaticRegistryLifecycle::Sealing {
709 owner: std::thread::current().id(),
710 deferred_error: None,
711 };
712 (
713 std::mem::take(&mut registry.registration_hooks),
714 std::mem::take(&mut registry.eager_init_hooks),
715 )
716 };
717
718 for hook in registration_hooks {
719 let result = catch_unwind(AssertUnwindSafe(hook))
720 .map_err(|_| StaticMemoryDeclarationError::EagerInitPanicked)
721 .and_then(std::convert::identity);
722 if let Err(err) = result {
723 return fail_sealing(err);
724 }
725 }
726 for hook in eager_init_hooks {
727 if catch_unwind(AssertUnwindSafe(hook)).is_err() {
728 return fail_sealing(StaticMemoryDeclarationError::EagerInitPanicked);
729 }
730 }
731
732 let mut registry = lock_registry()?;
733 let deferred_error = match ®istry.lifecycle {
734 StaticRegistryLifecycle::Sealing { deferred_error, .. } => deferred_error.clone(),
735 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
736 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealed(_) => {
737 unreachable!("seal lock preserves the in-progress registry lifecycle");
738 }
739 };
740 let result = match deferred_error {
741 Some(error) => Err(error),
742 None => build_snapshot(
743 Cow::Owned(std::mem::take(&mut registry.declarations)),
744 Cow::Owned(std::mem::take(&mut registry.ranges)),
745 Cow::Owned(std::mem::take(&mut registry.requests)),
746 ),
747 };
748 registry.finish_sealing(result)
749}
750
751fn fail_sealing(
752 err: StaticMemoryDeclarationError,
753) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
754 let mut registry = lock_registry()?;
755 let failure = match ®istry.lifecycle {
756 StaticRegistryLifecycle::Sealing {
757 deferred_error: Some(deferred_error),
758 ..
759 } => deferred_error.clone(),
760 StaticRegistryLifecycle::Open
761 | StaticRegistryLifecycle::Sealing {
762 deferred_error: None,
763 ..
764 }
765 | StaticRegistryLifecycle::Sealed(_) => err,
766 StaticRegistryLifecycle::Failed(failure) => failure.clone(),
767 };
768 registry.finish_sealing(Err(failure))
769}
770
771fn build_snapshot(
772 declarations: Cow<'_, [StaticMemoryDeclaration]>,
773 ranges: Cow<'_, [StaticMemoryRangeDeclaration]>,
774 requests: Cow<'_, [MemoryRequest]>,
775) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
776 if declarations.len().saturating_add(requests.len()) > 254 || ranges.len() > 254 {
777 return Err(StaticMemoryDeclarationError::TooManyDeclarations);
778 }
779 let mut requests = requests.into_owned();
782 requests.sort_unstable_by(|a, b| a.stable_key.cmp(&b.stable_key));
784 let mut registered_declarations = declarations.into_owned();
785 registered_declarations.sort_unstable_by(|left, right| {
787 left.declaration()
788 .stable_key()
789 .cmp(right.declaration().stable_key())
790 .then_with(|| left.declaration().slot().cmp(right.declaration().slot()))
791 .then_with(|| left.authority().cmp(right.authority()))
792 });
793
794 for (index, request) in requests.iter().enumerate() {
798 if (index > 0 && requests[index - 1].stable_key == request.stable_key)
799 || registered_declarations
800 .binary_search_by(|d| d.declaration().stable_key().cmp(&request.stable_key))
801 .is_ok()
802 {
803 return Err(StaticMemoryDeclarationError::DuplicateRequest {
804 stable_key: request.stable_key.clone(),
805 });
806 }
807 }
808
809 let mut registered_ranges = ranges.into_owned();
810 registered_ranges.sort_unstable_by(|left, right| {
813 let left = left.record();
814 let right = right.record();
815 left.range()
816 .start()
817 .cmp(&right.range().start())
818 .then_with(|| left.range().end().cmp(&right.range().end()))
819 });
820
821 let mut allocation_declarations = Vec::with_capacity(registered_declarations.len() + 1);
822 allocation_declarations.push(internal_ledger_declaration());
823 allocation_declarations.extend(
824 registered_declarations
825 .iter()
826 .map(|registration| registration.declaration().clone()),
827 );
828 let allocation_snapshot = DeclarationSnapshot::new(allocation_declarations)?;
829
830 let mut authority_records = Vec::with_capacity(registered_ranges.len() + 1);
831 authority_records.push(internal_ledger_range());
832 authority_records.extend(
833 registered_ranges
834 .iter()
835 .map(|registration| registration.record().clone()),
836 );
837 let range_authority = MemoryManagerRangeAuthority::from_records(authority_records)?;
838 let fingerprint = sealed_declaration_fingerprint(
839 &allocation_snapshot,
840 ®istered_declarations,
841 range_authority.authorities(),
842 &requests,
843 );
844
845 Ok(SealedDeclarationSnapshot {
846 inner: Arc::new(SealedDeclarationSnapshotInner {
847 allocation_snapshot,
848 requests,
849 registered_declarations,
850 registered_ranges,
851 range_authority,
852 fingerprint,
853 }),
854 })
855}
856
857#[derive(Serialize)]
858struct SealedDeclarationFingerprintMaterial<'a> {
859 format: &'static str,
860 allocation_snapshot: &'a DeclarationSnapshot,
861 registered_declarations: &'a [StaticMemoryDeclaration],
862 effective_ranges: &'a [MemoryManagerAuthorityRecord],
863 requests: &'a [MemoryRequest],
864}
865
866struct FingerprintWriter(u64);
869
870impl std::io::Write for FingerprintWriter {
871 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
872 self.0 = crate::hash::fnv64(self.0, bytes);
873 Ok(bytes.len())
874 }
875
876 fn flush(&mut self) -> std::io::Result<()> {
877 Ok(())
878 }
879}
880
881fn sealed_declaration_fingerprint(
882 allocation_snapshot: &DeclarationSnapshot,
883 registered_declarations: &[StaticMemoryDeclaration],
884 effective_ranges: &[MemoryManagerAuthorityRecord],
885 requests: &[MemoryRequest],
886) -> SealedDeclarationFingerprint {
887 let material = SealedDeclarationFingerprintMaterial {
888 format: "ic-memory.sealed-declaration-fingerprint.v1",
889 allocation_snapshot,
890 registered_declarations,
891 effective_ranges,
892 requests,
893 };
894 let mut writer = FingerprintWriter(crate::hash::FNV_OFFSET);
895 ciborium::into_writer(&material, &mut writer)
897 .expect("sealed declaration fingerprint encodes into hash");
898
899 SealedDeclarationFingerprint {
900 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
901 value: writer.0,
902 }
903}
904
905const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
906
907fn internal_ledger_declaration() -> AllocationDeclaration {
908 AllocationDeclaration::memory_manager(
909 IC_MEMORY_LEDGER_STABLE_KEY,
910 MEMORY_MANAGER_LEDGER_ID,
911 IC_MEMORY_LEDGER_LABEL,
912 )
913 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
914}
915
916fn internal_ledger_range() -> MemoryManagerAuthorityRecord {
917 MemoryManagerAuthorityRecord::new(
918 memory_manager_governance_range(),
919 IC_MEMORY_AUTHORITY_OWNER,
920 MemoryManagerRangeMode::Reserved,
921 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
922 )
923 .unwrap_or_else(|_| unreachable!("built-in governance range metadata constants are valid"))
924}
925
926#[cfg(test)]
927pub fn reset_static_memory_declarations_for_tests() {
928 let mut registry = STATIC_MEMORY_DECLARATIONS
929 .lock()
930 .expect("static memory declaration registry poisoned");
931 registry.declarations.clear();
932 registry.requests.clear();
933 registry.ranges.clear();
934 registry.registration_hooks.clear();
935 registry.eager_init_hooks.clear();
936 registry.lifecycle = StaticRegistryLifecycle::Open;
937}
938
939#[cfg(test)]
940mod tests;