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.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 .records()
346 .iter()
347 .find(|record| record.stable_key() == &request.stable_key);
348 let id = if let Some(record) = historical {
349 let id = record.slot().id();
350 self.range_authority()
354 .validate_id_authority(id, &request.authority)
355 .map_err(crate::MemoryResolutionError::Range)?;
356 id
357 } else {
358 self.range_authority()
361 .authorities()
362 .iter()
363 .filter(|range| {
364 range.authority() == request.authority
365 && range.mode() == MemoryManagerRangeMode::Allowed
366 })
367 .flat_map(|range| range.range().start()..=range.range().end())
368 .find(|id| !occupied[usize::from(*id)])
369 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
370 stable_key: request.stable_key.clone(),
371 authority: request.authority.clone(),
372 })?
373 };
374 let slot = crate::MemoryManagerSlot::new(id).expect("usable id");
375 occupied[usize::from(id)] = true;
376 let request = request.into_owned();
381 declarations.push(StaticMemoryDeclaration {
382 authority: request.authority,
383 declaration: AllocationDeclaration {
384 stable_key: request.stable_key,
385 slot,
386 label: None,
387 schema: request.schema,
388 },
389 });
390 }
391 Ok(build_snapshot(
392 Cow::Owned(declarations),
393 Cow::Borrowed(self.registered_ranges()),
394 Cow::Owned(Vec::new()),
395 )?)
396 }
397
398 #[must_use]
402 pub fn allocation_snapshot(&self) -> &DeclarationSnapshot {
403 &self.inner.allocation_snapshot
404 }
405
406 #[must_use]
408 pub fn registered_declarations(&self) -> &[StaticMemoryDeclaration] {
409 &self.inner.registered_declarations
410 }
411
412 #[must_use]
414 pub fn registered_ranges(&self) -> &[StaticMemoryRangeDeclaration] {
415 &self.inner.registered_ranges
416 }
417
418 #[must_use]
420 pub fn range_authority(&self) -> &MemoryManagerRangeAuthority {
421 &self.inner.range_authority
422 }
423
424 #[must_use]
426 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
427 self.inner.fingerprint
428 }
429
430 pub(crate) fn registered_declaration(
431 &self,
432 key: &crate::StableKey,
433 ) -> Option<&StaticMemoryDeclaration> {
434 let declarations = self.registered_declarations();
435 declarations
437 .binary_search_by(|registration| registration.declaration().stable_key().cmp(key))
438 .ok()
439 .map(|index| &declarations[index])
440 }
441
442 pub(crate) fn user_ranges_registered(&self) -> bool {
443 !self.inner.registered_ranges.is_empty()
444 }
445
446 #[cfg(test)]
447 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
448 Arc::ptr_eq(&self.inner, &other.inner)
449 }
450}
451
452type StaticRegistrationHook = fn() -> Result<(), StaticMemoryDeclarationError>;
453
454#[derive(Debug)]
455struct StaticMemoryDeclarationRegistry {
456 declarations: Vec<StaticMemoryDeclaration>,
457 requests: Vec<MemoryRequest>,
458 ranges: Vec<StaticMemoryRangeDeclaration>,
459 registration_hooks: Vec<StaticRegistrationHook>,
460 eager_init_hooks: Vec<fn()>,
461 lifecycle: StaticRegistryLifecycle,
462}
463
464impl StaticMemoryDeclarationRegistry {
465 fn finish_sealing(
466 &mut self,
467 result: Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError>,
468 ) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
469 self.declarations = Vec::new();
471 self.requests = Vec::new();
472 self.ranges = Vec::new();
473 self.registration_hooks = Vec::new();
474 self.eager_init_hooks = Vec::new();
475 self.lifecycle = match &result {
476 Ok(snapshot) => StaticRegistryLifecycle::Sealed(snapshot.clone()),
477 Err(error) => StaticRegistryLifecycle::Failed(error.clone()),
478 };
479 result
480 }
481}
482
483#[derive(Debug)]
484enum StaticRegistryLifecycle {
485 Open,
486 Sealing {
487 owner: ThreadId,
488 deferred_error: Option<StaticMemoryDeclarationError>,
489 },
490 Sealed(SealedDeclarationSnapshot),
491 Failed(StaticMemoryDeclarationError),
492}
493
494static STATIC_MEMORY_DECLARATIONS: Mutex<StaticMemoryDeclarationRegistry> =
495 Mutex::new(StaticMemoryDeclarationRegistry {
496 declarations: Vec::new(),
497 requests: Vec::new(),
498 ranges: Vec::new(),
499 registration_hooks: Vec::new(),
500 eager_init_hooks: Vec::new(),
501 lifecycle: StaticRegistryLifecycle::Open,
502 });
503
504static STATIC_MEMORY_SEAL: Mutex<()> = Mutex::new(());
505
506fn lock_registry()
507-> Result<MutexGuard<'static, StaticMemoryDeclarationRegistry>, StaticMemoryDeclarationError> {
508 STATIC_MEMORY_DECLARATIONS
509 .lock()
510 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
511}
512
513fn ensure_registration_open(
514 registry: &StaticMemoryDeclarationRegistry,
515) -> Result<(), StaticMemoryDeclarationError> {
516 match ®istry.lifecycle {
517 StaticRegistryLifecycle::Open => Ok(()),
518 StaticRegistryLifecycle::Sealing { owner, .. } if *owner == std::thread::current().id() => {
519 Ok(())
520 }
521 StaticRegistryLifecycle::Sealing { .. }
522 | StaticRegistryLifecycle::Sealed(_)
523 | StaticRegistryLifecycle::Failed(_) => Err(StaticMemoryDeclarationError::RegistrySealed),
524 }
525}
526
527fn with_unsealed_registry(
528 op: impl FnOnce(&mut StaticMemoryDeclarationRegistry),
529) -> Result<(), StaticMemoryDeclarationError> {
530 let mut registry = lock_registry()?;
531 ensure_registration_open(®istry)?;
532 op(&mut registry);
533 Ok(())
534}
535
536#[doc(hidden)]
541pub fn defer_static_memory_registration(hook: StaticRegistrationHook) {
542 defer_constructor_registration(|registry| {
543 registry.registration_hooks.push(hook);
544 });
545}
546
547#[doc(hidden)]
552pub fn defer_eager_init(hook: fn()) {
553 defer_constructor_registration(|registry| {
554 registry.eager_init_hooks.push(hook);
555 });
556}
557
558fn defer_constructor_registration(op: impl FnOnce(&mut StaticMemoryDeclarationRegistry)) {
559 let Ok(mut registry) = STATIC_MEMORY_DECLARATIONS.lock() else {
560 return;
563 };
564 if matches!(registry.lifecycle, StaticRegistryLifecycle::Open) {
565 op(&mut registry);
566 return;
567 }
568 match &mut registry.lifecycle {
569 StaticRegistryLifecycle::Sealing { deferred_error, .. } => {
570 if deferred_error.is_none() {
571 *deferred_error = Some(StaticMemoryDeclarationError::RegistrySealed);
572 }
573 }
574 StaticRegistryLifecycle::Sealed(_) => {
575 registry.lifecycle =
576 StaticRegistryLifecycle::Failed(StaticMemoryDeclarationError::RegistrySealed);
577 }
578 StaticRegistryLifecycle::Failed(_) | StaticRegistryLifecycle::Open => {}
579 }
580}
581
582pub fn register_static_memory_declaration(
584 authority: impl Into<String>,
585 declaration: AllocationDeclaration,
586) -> Result<(), StaticMemoryDeclarationError> {
587 let registration = StaticMemoryDeclaration::new(authority, declaration)?;
588 with_unsealed_registry(|registry| {
589 registry.declarations.push(registration);
590 })
591}
592
593pub fn register_static_memory_manager_range(
595 start: u8,
596 end: u8,
597 authority: impl Into<String>,
598 mode: MemoryManagerRangeMode,
599 purpose: Option<String>,
600) -> Result<(), StaticMemoryDeclarationError> {
601 let authority = authority.into();
602 let record = MemoryManagerAuthorityRecord::new(
603 MemoryManagerIdRange::new(start, end).map_err(MemoryManagerRangeAuthorityError::Range)?,
604 authority,
605 mode,
606 purpose,
607 )?;
608 register_static_memory_range_declaration(StaticMemoryRangeDeclaration::new(record)?)
609}
610
611pub fn register_static_memory_range_declaration(
613 declaration: StaticMemoryRangeDeclaration,
614) -> Result<(), StaticMemoryDeclarationError> {
615 with_unsealed_registry(|registry| {
616 registry.ranges.push(declaration);
617 })
618}
619
620fn validate_external_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
621 reject_internal_authority(value)?;
622 validate_diagnostic_text(value).map_err(|error| {
623 StaticMemoryDeclarationError::InvalidAuthority {
624 reason: error.reason(),
625 }
626 })
627}
628
629fn reject_internal_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
630 if value == IC_MEMORY_AUTHORITY_OWNER {
631 return Err(StaticMemoryDeclarationError::ReservedAuthority {
632 authority: value.to_string(),
633 });
634 }
635 Ok(())
636}
637
638pub fn register_static_memory_manager_declaration(
640 id: u8,
641 authority: impl Into<String>,
642 label: impl Into<String>,
643 stable_key: impl AsRef<str>,
644) -> Result<(), StaticMemoryDeclarationError> {
645 register_static_memory_manager_declaration_with_schema(
646 id,
647 authority,
648 label,
649 stable_key,
650 SchemaMetadata::default(),
651 )
652}
653
654pub fn register_static_memory_manager_declaration_with_schema(
656 id: u8,
657 authority: impl Into<String>,
658 label: impl Into<String>,
659 stable_key: impl AsRef<str>,
660 schema: SchemaMetadata,
661) -> Result<(), StaticMemoryDeclarationError> {
662 let declaration =
663 AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
664 register_static_memory_declaration(authority, declaration)
665}
666
667pub fn sealed_declaration_snapshot()
679-> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
680 {
681 let registry = lock_registry()?;
682 match ®istry.lifecycle {
683 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
684 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
685 StaticRegistryLifecycle::Sealing { owner, .. }
686 if *owner == std::thread::current().id() =>
687 {
688 return Err(StaticMemoryDeclarationError::ReentrantSealing);
689 }
690 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealing { .. } => {}
691 }
692 }
693
694 let _seal = STATIC_MEMORY_SEAL
695 .lock()
696 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
697 let (registration_hooks, eager_init_hooks) = {
698 let mut registry = lock_registry()?;
699 match ®istry.lifecycle {
700 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
701 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
702 StaticRegistryLifecycle::Sealing { .. } => {
703 return Err(StaticMemoryDeclarationError::ReentrantSealing);
704 }
705 StaticRegistryLifecycle::Open => {}
706 }
707 registry.lifecycle = StaticRegistryLifecycle::Sealing {
708 owner: std::thread::current().id(),
709 deferred_error: None,
710 };
711 (
712 std::mem::take(&mut registry.registration_hooks),
713 std::mem::take(&mut registry.eager_init_hooks),
714 )
715 };
716
717 for hook in registration_hooks {
718 let result = catch_unwind(AssertUnwindSafe(hook))
719 .map_err(|_| StaticMemoryDeclarationError::EagerInitPanicked)
720 .and_then(std::convert::identity);
721 if let Err(err) = result {
722 return fail_sealing(err);
723 }
724 }
725 for hook in eager_init_hooks {
726 if catch_unwind(AssertUnwindSafe(hook)).is_err() {
727 return fail_sealing(StaticMemoryDeclarationError::EagerInitPanicked);
728 }
729 }
730
731 let mut registry = lock_registry()?;
732 let deferred_error = match ®istry.lifecycle {
733 StaticRegistryLifecycle::Sealing { deferred_error, .. } => deferred_error.clone(),
734 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
735 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealed(_) => {
736 unreachable!("seal lock preserves the in-progress registry lifecycle");
737 }
738 };
739 let result = match deferred_error {
740 Some(error) => Err(error),
741 None => build_snapshot(
742 Cow::Owned(std::mem::take(&mut registry.declarations)),
743 Cow::Owned(std::mem::take(&mut registry.ranges)),
744 Cow::Owned(std::mem::take(&mut registry.requests)),
745 ),
746 };
747 registry.finish_sealing(result)
748}
749
750fn fail_sealing(
751 err: StaticMemoryDeclarationError,
752) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
753 let mut registry = lock_registry()?;
754 let failure = match ®istry.lifecycle {
755 StaticRegistryLifecycle::Sealing {
756 deferred_error: Some(deferred_error),
757 ..
758 } => deferred_error.clone(),
759 StaticRegistryLifecycle::Open
760 | StaticRegistryLifecycle::Sealing {
761 deferred_error: None,
762 ..
763 }
764 | StaticRegistryLifecycle::Sealed(_) => err,
765 StaticRegistryLifecycle::Failed(failure) => failure.clone(),
766 };
767 registry.finish_sealing(Err(failure))
768}
769
770fn build_snapshot(
771 declarations: Cow<'_, [StaticMemoryDeclaration]>,
772 ranges: Cow<'_, [StaticMemoryRangeDeclaration]>,
773 requests: Cow<'_, [MemoryRequest]>,
774) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
775 if declarations.len().saturating_add(requests.len()) > 254 || ranges.len() > 254 {
776 return Err(StaticMemoryDeclarationError::TooManyDeclarations);
777 }
778 let mut requests = requests.into_owned();
781 requests.sort_unstable_by(|a, b| a.stable_key.cmp(&b.stable_key));
783 let mut registered_declarations = declarations.into_owned();
784 registered_declarations.sort_unstable_by(|left, right| {
786 left.declaration()
787 .stable_key()
788 .cmp(right.declaration().stable_key())
789 .then_with(|| left.declaration().slot().cmp(right.declaration().slot()))
790 .then_with(|| left.authority().cmp(right.authority()))
791 });
792
793 for (index, request) in requests.iter().enumerate() {
797 if (index > 0 && requests[index - 1].stable_key == request.stable_key)
798 || registered_declarations
799 .binary_search_by(|d| d.declaration().stable_key().cmp(&request.stable_key))
800 .is_ok()
801 {
802 return Err(StaticMemoryDeclarationError::DuplicateRequest {
803 stable_key: request.stable_key.clone(),
804 });
805 }
806 }
807
808 let mut registered_ranges = ranges.into_owned();
809 registered_ranges.sort_unstable_by(|left, right| {
812 let left = left.record();
813 let right = right.record();
814 left.range()
815 .start()
816 .cmp(&right.range().start())
817 .then_with(|| left.range().end().cmp(&right.range().end()))
818 });
819
820 let mut allocation_declarations = Vec::with_capacity(registered_declarations.len() + 1);
821 allocation_declarations.push(internal_ledger_declaration());
822 allocation_declarations.extend(
823 registered_declarations
824 .iter()
825 .map(|registration| registration.declaration().clone()),
826 );
827 let allocation_snapshot = DeclarationSnapshot::new(allocation_declarations)?;
828
829 let mut authority_records = Vec::with_capacity(registered_ranges.len() + 1);
830 authority_records.push(internal_ledger_range());
831 authority_records.extend(
832 registered_ranges
833 .iter()
834 .map(|registration| registration.record().clone()),
835 );
836 let range_authority = MemoryManagerRangeAuthority::from_records(authority_records)?;
837 let fingerprint = sealed_declaration_fingerprint(
838 &allocation_snapshot,
839 ®istered_declarations,
840 range_authority.authorities(),
841 &requests,
842 );
843
844 Ok(SealedDeclarationSnapshot {
845 inner: Arc::new(SealedDeclarationSnapshotInner {
846 allocation_snapshot,
847 requests,
848 registered_declarations,
849 registered_ranges,
850 range_authority,
851 fingerprint,
852 }),
853 })
854}
855
856#[derive(Serialize)]
857struct SealedDeclarationFingerprintMaterial<'a> {
858 format: &'static str,
859 allocation_snapshot: &'a DeclarationSnapshot,
860 registered_declarations: &'a [StaticMemoryDeclaration],
861 effective_ranges: &'a [MemoryManagerAuthorityRecord],
862 requests: &'a [MemoryRequest],
863}
864
865struct FingerprintWriter(u64);
868
869impl std::io::Write for FingerprintWriter {
870 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
871 self.0 = crate::hash::fnv64(self.0, bytes);
872 Ok(bytes.len())
873 }
874
875 fn flush(&mut self) -> std::io::Result<()> {
876 Ok(())
877 }
878}
879
880fn sealed_declaration_fingerprint(
881 allocation_snapshot: &DeclarationSnapshot,
882 registered_declarations: &[StaticMemoryDeclaration],
883 effective_ranges: &[MemoryManagerAuthorityRecord],
884 requests: &[MemoryRequest],
885) -> SealedDeclarationFingerprint {
886 let material = SealedDeclarationFingerprintMaterial {
887 format: "ic-memory.sealed-declaration-fingerprint.v1",
888 allocation_snapshot,
889 registered_declarations,
890 effective_ranges,
891 requests,
892 };
893 let mut writer = FingerprintWriter(crate::hash::FNV_OFFSET);
894 ciborium::into_writer(&material, &mut writer)
896 .expect("sealed declaration fingerprint encodes into hash");
897
898 SealedDeclarationFingerprint {
899 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
900 value: writer.0,
901 }
902}
903
904const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
905
906fn internal_ledger_declaration() -> AllocationDeclaration {
907 AllocationDeclaration::memory_manager(
908 IC_MEMORY_LEDGER_STABLE_KEY,
909 MEMORY_MANAGER_LEDGER_ID,
910 IC_MEMORY_LEDGER_LABEL,
911 )
912 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
913}
914
915fn internal_ledger_range() -> MemoryManagerAuthorityRecord {
916 MemoryManagerAuthorityRecord::new(
917 memory_manager_governance_range(),
918 IC_MEMORY_AUTHORITY_OWNER,
919 MemoryManagerRangeMode::Reserved,
920 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
921 )
922 .unwrap_or_else(|_| unreachable!("built-in governance range metadata constants are valid"))
923}
924
925#[cfg(test)]
926pub fn reset_static_memory_declarations_for_tests() {
927 let mut registry = STATIC_MEMORY_DECLARATIONS
928 .lock()
929 .expect("static memory declaration registry poisoned");
930 registry.declarations.clear();
931 registry.requests.clear();
932 registry.ranges.clear();
933 registry.registration_hooks.clear();
934 registry.eager_init_hooks.clear();
935 registry.lifecycle = StaticRegistryLifecycle::Open;
936}
937
938#[cfg(test)]
939mod tests;