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_GOVERNANCE_MAX_ID, MEMORY_MANAGER_LEDGER_ID,
7 MemoryManagerAuthorityRecord, MemoryManagerIdRange, MemoryManagerRangeAuthority,
8 MemoryManagerRangeAuthorityError, MemoryManagerRangeMode, is_ic_memory_stable_key,
9 },
10 text::validate_diagnostic_text,
11};
12use serde::{Deserialize, Serialize};
13use std::{
14 collections::BTreeMap,
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)]
34pub struct StaticMemoryDeclaration {
35 authority: String,
36 declaration: AllocationDeclaration,
37}
38
39impl StaticMemoryDeclaration {
40 pub fn new(
42 authority: impl Into<String>,
43 declaration: AllocationDeclaration,
44 ) -> Result<Self, StaticMemoryDeclarationError> {
45 let authority = authority.into();
46 validate_external_authority(&authority)?;
47 declaration.validate()?;
48 if is_ic_memory_stable_key(declaration.stable_key().as_str()) {
49 return Err(StaticMemoryDeclarationError::ReservedStableKey {
50 stable_key: declaration.stable_key().as_str().to_string(),
51 });
52 }
53 Ok(Self {
54 authority,
55 declaration,
56 })
57 }
58
59 #[must_use]
61 pub fn authority(&self) -> &str {
62 &self.authority
63 }
64
65 #[must_use]
67 pub const fn declaration(&self) -> &AllocationDeclaration {
68 &self.declaration
69 }
70
71 #[must_use]
73 pub fn into_declaration(self) -> AllocationDeclaration {
74 self.declaration
75 }
76}
77
78#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
86pub struct MemoryRequest {
87 authority: String,
88 stable_key: crate::StableKey,
89 schema: SchemaMetadata,
90}
91
92impl MemoryRequest {
93 pub fn new(
95 authority: impl Into<String>,
96 stable_key: &str,
97 schema: SchemaMetadata,
98 ) -> Result<Self, StaticMemoryDeclarationError> {
99 let authority = authority.into();
100 validate_external_authority(&authority)?;
101 let stable_key =
102 crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
103 schema
104 .validate()
105 .map_err(crate::DeclarationSnapshotError::SchemaMetadata)?;
106 if is_ic_memory_stable_key(stable_key.as_str()) {
107 return Err(StaticMemoryDeclarationError::ReservedStableKey {
108 stable_key: stable_key.as_str().to_string(),
109 });
110 }
111 Ok(Self {
112 authority,
113 stable_key,
114 schema,
115 })
116 }
117
118 pub(crate) fn with_schema(
119 mut self,
120 schema: SchemaMetadata,
121 ) -> Result<Self, crate::DeclarationSnapshotError> {
122 schema
123 .validate()
124 .map_err(crate::DeclarationSnapshotError::SchemaMetadata)?;
125 self.schema = schema;
126 Ok(self)
127 }
128
129 #[must_use]
131 pub const fn stable_key(&self) -> &crate::StableKey {
132 &self.stable_key
133 }
134
135 #[must_use]
137 pub const fn schema(&self) -> &SchemaMetadata {
138 &self.schema
139 }
140
141 #[must_use]
143 pub fn authority(&self) -> &str {
144 &self.authority
145 }
146}
147
148pub fn register_memory_request(request: MemoryRequest) -> Result<(), StaticMemoryDeclarationError> {
150 with_unsealed_registry(|registry| registry.requests.push(request))
151}
152
153#[derive(Clone, Debug, Eq, PartialEq)]
162pub struct StaticMemoryRangeDeclaration {
163 record: MemoryManagerAuthorityRecord,
164}
165
166impl StaticMemoryRangeDeclaration {
167 pub fn new(record: MemoryManagerAuthorityRecord) -> Result<Self, StaticMemoryDeclarationError> {
169 validate_external_authority(record.authority())?;
170 record.validate()?;
171 Ok(Self { record })
172 }
173
174 #[must_use]
176 pub fn authority(&self) -> &str {
177 self.record.authority()
178 }
179
180 #[must_use]
182 pub const fn record(&self) -> &MemoryManagerAuthorityRecord {
183 &self.record
184 }
185
186 #[must_use]
188 pub fn into_record(self) -> MemoryManagerAuthorityRecord {
189 self.record
190 }
191}
192
193#[non_exhaustive]
198#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
199pub enum StaticMemoryDeclarationError {
200 #[error("at most 254 external declarations and ranges are supported")]
201 TooManyDeclarations,
202 #[error("duplicate requested stable key {stable_key}")]
203 DuplicateRequest { stable_key: crate::StableKey },
204 #[error("static memory declaration registry lock poisoned")]
206 RegistryPoisoned,
207 #[error("static memory declaration registry is already sealed")]
209 RegistrySealed,
210 #[error("static memory declaration snapshot sealing is already active on this thread")]
212 ReentrantSealing,
213 #[error("static memory declaration registry lifecycle is internally inconsistent")]
215 InconsistentLifecycle,
216 #[error("static memory declaration eager-init hook panicked")]
218 EagerInitPanicked,
219 #[error(transparent)]
221 Declaration(#[from] crate::DeclarationSnapshotError),
222 #[error(transparent)]
224 Range(#[from] MemoryManagerRangeAuthorityError),
225 #[error("failed to encode canonical sealed declaration fingerprint material: {message}")]
227 SnapshotFingerprintEncoding {
228 message: String,
230 },
231 #[error("authority {reason}")]
233 InvalidAuthority {
234 reason: &'static str,
236 },
237 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
239 ReservedAuthority {
240 authority: String,
242 },
243 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
245 ReservedStableKey {
246 stable_key: String,
248 },
249}
250
251#[derive(Clone, Debug, Eq, PartialEq)]
264pub struct SealedDeclarationSnapshot {
265 inner: Arc<SealedDeclarationSnapshotInner>,
266}
267
268#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
281#[serde(deny_unknown_fields)]
282pub struct SealedDeclarationFingerprint {
283 algorithm_version: u8,
284 value: u64,
285}
286
287impl SealedDeclarationFingerprint {
288 #[must_use]
290 pub const fn algorithm_version(&self) -> u8 {
291 self.algorithm_version
292 }
293
294 #[must_use]
296 pub const fn value(&self) -> u64 {
297 self.value
298 }
299}
300
301#[derive(Debug, Eq, PartialEq)]
302struct SealedDeclarationSnapshotInner {
303 allocation_snapshot: DeclarationSnapshot,
304 requests: Vec<MemoryRequest>,
305 registered_declarations: Vec<StaticMemoryDeclaration>,
306 registered_ranges: Vec<StaticMemoryRangeDeclaration>,
307 range_authority: MemoryManagerRangeAuthority,
308 declaration_authority: BTreeMap<String, RuntimeDeclarationAuthority>,
309 fingerprint: SealedDeclarationFingerprint,
310}
311
312#[derive(Clone, Debug, Eq, PartialEq)]
313pub enum RuntimeDeclarationAuthority {
314 Internal,
315 External(String),
316}
317
318impl SealedDeclarationSnapshot {
319 pub fn new(
321 declarations: &[StaticMemoryDeclaration],
322 ranges: &[StaticMemoryRangeDeclaration],
323 requests: &[MemoryRequest],
324 ) -> Result<Self, StaticMemoryDeclarationError> {
325 build_snapshot(declarations, ranges, requests)
326 }
327
328 #[must_use]
330 pub fn requests(&self) -> &[MemoryRequest] {
331 &self.inner.requests
332 }
333
334 pub(crate) fn resolve(
335 &self,
336 ledger: &crate::AllocationLedger,
337 historical: Vec<MemoryRequest>,
338 ) -> Result<Self, crate::MemoryResolutionError> {
339 if self.requests().is_empty() && historical.is_empty() {
340 return Ok(self.clone());
341 }
342 if self.registered_declarations().len() + self.requests().len() + historical.len() > 254 {
343 return Err(StaticMemoryDeclarationError::TooManyDeclarations.into());
344 }
345 let mut declarations = self.registered_declarations().to_vec();
346 let mut occupied = [false; 255];
347 for record in ledger.allocation_history().records() {
348 occupied[usize::from(
349 record
350 .slot()
351 .memory_manager_id()
352 .expect("validated ledger slot"),
353 )] = true;
354 }
355 for fixed in &declarations {
356 occupied[usize::from(
357 fixed
358 .declaration()
359 .slot()
360 .memory_manager_id()
361 .expect("checked slot"),
362 )] = true;
363 }
364 for request in self.requests().iter().chain(&historical) {
369 let historical = ledger
370 .allocation_history()
371 .records()
372 .iter()
373 .find(|record| record.stable_key() == &request.stable_key);
374 let id = if let Some(record) = historical {
375 record
376 .slot()
377 .memory_manager_id()
378 .expect("validated ledger slot")
379 } else {
380 (0..255_u8)
381 .find(|id| {
382 !occupied[usize::from(*id)]
383 && self.range_authority().authorities().iter().any(|range| {
384 range.authority() == request.authority
385 && range.mode() == MemoryManagerRangeMode::Allowed
386 && range.range().contains(*id)
387 })
388 })
389 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
390 stable_key: request.stable_key.clone(),
391 authority: request.authority.clone(),
392 })?
393 };
394 self.range_authority()
396 .validate_slot_authority(
397 &crate::AllocationSlotDescriptor::memory_manager(id).expect("usable id"),
398 &request.authority,
399 )
400 .map_err(crate::MemoryResolutionError::Range)?;
401 occupied[usize::from(id)] = true;
402 declarations.push(StaticMemoryDeclaration::new(
403 request.authority.clone(),
404 AllocationDeclaration::memory_manager_unlabeled_with_schema(
405 request.stable_key.as_str(),
406 id,
407 request.schema.clone(),
408 )?,
409 )?);
410 }
411 Ok(build_snapshot(
412 &declarations,
413 self.registered_ranges(),
414 &[],
415 )?)
416 }
417
418 #[must_use]
422 pub fn allocation_snapshot(&self) -> &DeclarationSnapshot {
423 &self.inner.allocation_snapshot
424 }
425
426 #[must_use]
428 pub fn registered_declarations(&self) -> &[StaticMemoryDeclaration] {
429 &self.inner.registered_declarations
430 }
431
432 #[must_use]
434 pub fn registered_ranges(&self) -> &[StaticMemoryRangeDeclaration] {
435 &self.inner.registered_ranges
436 }
437
438 #[must_use]
440 pub fn range_authority(&self) -> &MemoryManagerRangeAuthority {
441 &self.inner.range_authority
442 }
443
444 #[must_use]
446 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
447 self.inner.fingerprint
448 }
449
450 pub(crate) fn declaration_authority(&self) -> &BTreeMap<String, RuntimeDeclarationAuthority> {
451 &self.inner.declaration_authority
452 }
453
454 pub(crate) fn user_ranges_registered(&self) -> bool {
455 !self.inner.registered_ranges.is_empty()
456 }
457
458 #[cfg(test)]
459 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
460 Arc::ptr_eq(&self.inner, &other.inner)
461 }
462}
463
464type StaticRegistrationHook = fn() -> Result<(), StaticMemoryDeclarationError>;
465
466#[derive(Debug)]
467struct StaticMemoryDeclarationRegistry {
468 declarations: Vec<StaticMemoryDeclaration>,
469 requests: Vec<MemoryRequest>,
470 ranges: Vec<StaticMemoryRangeDeclaration>,
471 registration_hooks: Vec<StaticRegistrationHook>,
472 eager_init_hooks: Vec<fn()>,
473 lifecycle: StaticRegistryLifecycle,
474}
475
476impl StaticMemoryDeclarationRegistry {
477 fn finish_sealing(
478 &mut self,
479 result: Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError>,
480 ) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
481 self.declarations = Vec::new();
483 self.requests = Vec::new();
484 self.ranges = Vec::new();
485 self.registration_hooks = Vec::new();
486 self.eager_init_hooks = Vec::new();
487 self.lifecycle = match &result {
488 Ok(snapshot) => StaticRegistryLifecycle::Sealed(snapshot.clone()),
489 Err(error) => StaticRegistryLifecycle::Failed(error.clone()),
490 };
491 result
492 }
493}
494
495#[derive(Debug)]
496enum StaticRegistryLifecycle {
497 Open,
498 Sealing {
499 owner: ThreadId,
500 deferred_error: Option<StaticMemoryDeclarationError>,
501 },
502 Sealed(SealedDeclarationSnapshot),
503 Failed(StaticMemoryDeclarationError),
504}
505
506static STATIC_MEMORY_DECLARATIONS: Mutex<StaticMemoryDeclarationRegistry> =
507 Mutex::new(StaticMemoryDeclarationRegistry {
508 declarations: Vec::new(),
509 requests: Vec::new(),
510 ranges: Vec::new(),
511 registration_hooks: Vec::new(),
512 eager_init_hooks: Vec::new(),
513 lifecycle: StaticRegistryLifecycle::Open,
514 });
515
516static STATIC_MEMORY_SEAL: Mutex<()> = Mutex::new(());
517
518fn lock_registry()
519-> Result<MutexGuard<'static, StaticMemoryDeclarationRegistry>, StaticMemoryDeclarationError> {
520 STATIC_MEMORY_DECLARATIONS
521 .lock()
522 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
523}
524
525fn ensure_registration_open(
526 registry: &StaticMemoryDeclarationRegistry,
527) -> Result<(), StaticMemoryDeclarationError> {
528 match ®istry.lifecycle {
529 StaticRegistryLifecycle::Open => Ok(()),
530 StaticRegistryLifecycle::Sealing { owner, .. } if *owner == std::thread::current().id() => {
531 Ok(())
532 }
533 StaticRegistryLifecycle::Sealing { .. }
534 | StaticRegistryLifecycle::Sealed(_)
535 | StaticRegistryLifecycle::Failed(_) => Err(StaticMemoryDeclarationError::RegistrySealed),
536 }
537}
538
539fn with_unsealed_registry(
540 op: impl FnOnce(&mut StaticMemoryDeclarationRegistry),
541) -> Result<(), StaticMemoryDeclarationError> {
542 let mut registry = lock_registry()?;
543 ensure_registration_open(®istry)?;
544 op(&mut registry);
545 Ok(())
546}
547
548#[doc(hidden)]
553pub fn defer_static_memory_registration(hook: StaticRegistrationHook) {
554 defer_constructor_registration(|registry| {
555 registry.registration_hooks.push(hook);
556 });
557}
558
559#[doc(hidden)]
564pub fn defer_eager_init(hook: fn()) {
565 defer_constructor_registration(|registry| {
566 registry.eager_init_hooks.push(hook);
567 });
568}
569
570fn defer_constructor_registration(op: impl FnOnce(&mut StaticMemoryDeclarationRegistry)) {
571 let Ok(mut registry) = STATIC_MEMORY_DECLARATIONS.lock() else {
572 return;
575 };
576 if matches!(registry.lifecycle, StaticRegistryLifecycle::Open) {
577 op(&mut registry);
578 return;
579 }
580 match &mut registry.lifecycle {
581 StaticRegistryLifecycle::Sealing { deferred_error, .. } => {
582 if deferred_error.is_none() {
583 *deferred_error = Some(StaticMemoryDeclarationError::RegistrySealed);
584 }
585 }
586 StaticRegistryLifecycle::Sealed(_) => {
587 registry.lifecycle =
588 StaticRegistryLifecycle::Failed(StaticMemoryDeclarationError::RegistrySealed);
589 }
590 StaticRegistryLifecycle::Failed(_) | StaticRegistryLifecycle::Open => {}
591 }
592}
593
594pub fn register_static_memory_declaration(
596 authority: impl Into<String>,
597 declaration: AllocationDeclaration,
598) -> Result<(), StaticMemoryDeclarationError> {
599 let registration = StaticMemoryDeclaration::new(authority, declaration)?;
600 with_unsealed_registry(|registry| {
601 registry.declarations.push(registration);
602 })
603}
604
605pub fn register_static_memory_manager_range(
607 start: u8,
608 end: u8,
609 authority: impl Into<String>,
610 mode: MemoryManagerRangeMode,
611 purpose: Option<String>,
612) -> Result<(), StaticMemoryDeclarationError> {
613 let authority = authority.into();
614 let record = MemoryManagerAuthorityRecord::new(
615 MemoryManagerIdRange::new(start, end).map_err(MemoryManagerRangeAuthorityError::Range)?,
616 authority,
617 mode,
618 purpose,
619 )?;
620 register_static_memory_range_declaration(StaticMemoryRangeDeclaration::new(record)?)
621}
622
623pub fn register_static_memory_range_declaration(
625 declaration: StaticMemoryRangeDeclaration,
626) -> Result<(), StaticMemoryDeclarationError> {
627 validate_external_authority(declaration.authority())?;
628 with_unsealed_registry(|registry| {
629 registry.ranges.push(declaration);
630 })
631}
632
633fn validate_external_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
634 if value == IC_MEMORY_AUTHORITY_OWNER {
635 return Err(StaticMemoryDeclarationError::ReservedAuthority {
636 authority: value.to_string(),
637 });
638 }
639 validate_diagnostic_text(value).map_err(|error| {
640 StaticMemoryDeclarationError::InvalidAuthority {
641 reason: error.reason(),
642 }
643 })
644}
645
646pub fn register_static_memory_manager_declaration(
648 id: u8,
649 authority: impl Into<String>,
650 label: impl Into<String>,
651 stable_key: impl AsRef<str>,
652) -> Result<(), StaticMemoryDeclarationError> {
653 register_static_memory_manager_declaration_with_schema(
654 id,
655 authority,
656 label,
657 stable_key,
658 SchemaMetadata::default(),
659 )
660}
661
662pub fn register_static_memory_manager_declaration_with_schema(
664 id: u8,
665 authority: impl Into<String>,
666 label: impl Into<String>,
667 stable_key: impl AsRef<str>,
668 schema: SchemaMetadata,
669) -> Result<(), StaticMemoryDeclarationError> {
670 let declaration =
671 AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
672 register_static_memory_declaration(authority, declaration)
673}
674
675pub fn sealed_declaration_snapshot()
682-> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
683 {
684 let registry = lock_registry()?;
685 match ®istry.lifecycle {
686 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
687 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
688 StaticRegistryLifecycle::Sealing { owner, .. }
689 if *owner == std::thread::current().id() =>
690 {
691 return Err(StaticMemoryDeclarationError::ReentrantSealing);
692 }
693 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealing { .. } => {}
694 }
695 }
696
697 let _seal = STATIC_MEMORY_SEAL
698 .lock()
699 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
700 let (registration_hooks, eager_init_hooks) = {
701 let mut registry = lock_registry()?;
702 match ®istry.lifecycle {
703 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
704 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
705 StaticRegistryLifecycle::Sealing { .. } => {
706 return Err(StaticMemoryDeclarationError::ReentrantSealing);
707 }
708 StaticRegistryLifecycle::Open => {}
709 }
710 registry.lifecycle = StaticRegistryLifecycle::Sealing {
711 owner: std::thread::current().id(),
712 deferred_error: None,
713 };
714 (
715 std::mem::take(&mut registry.registration_hooks),
716 std::mem::take(&mut registry.eager_init_hooks),
717 )
718 };
719
720 for hook in registration_hooks {
721 let result = catch_unwind(AssertUnwindSafe(hook))
722 .map_err(|_| StaticMemoryDeclarationError::EagerInitPanicked)
723 .and_then(std::convert::identity);
724 if let Err(err) = result {
725 return fail_sealing(err);
726 }
727 }
728 for hook in eager_init_hooks {
729 if catch_unwind(AssertUnwindSafe(hook)).is_err() {
730 return fail_sealing(StaticMemoryDeclarationError::EagerInitPanicked);
731 }
732 }
733
734 let mut registry = lock_registry()?;
735 let deferred_error = match ®istry.lifecycle {
736 StaticRegistryLifecycle::Sealing { deferred_error, .. } => deferred_error.clone(),
737 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
738 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealed(_) => {
739 return Err(StaticMemoryDeclarationError::InconsistentLifecycle);
740 }
741 };
742 let result = match deferred_error {
743 Some(error) => Err(error),
744 None => build_snapshot(®istry.declarations, ®istry.ranges, ®istry.requests),
745 };
746 registry.finish_sealing(result)
747}
748
749fn fail_sealing(
750 err: StaticMemoryDeclarationError,
751) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
752 let mut registry = lock_registry()?;
753 let failure = match ®istry.lifecycle {
754 StaticRegistryLifecycle::Sealing {
755 deferred_error: Some(deferred_error),
756 ..
757 } => deferred_error.clone(),
758 StaticRegistryLifecycle::Open
759 | StaticRegistryLifecycle::Sealing {
760 deferred_error: None,
761 ..
762 }
763 | StaticRegistryLifecycle::Sealed(_) => err,
764 StaticRegistryLifecycle::Failed(failure) => failure.clone(),
765 };
766 registry.finish_sealing(Err(failure))
767}
768
769fn build_snapshot(
770 declarations: &[StaticMemoryDeclaration],
771 ranges: &[StaticMemoryRangeDeclaration],
772 requests: &[MemoryRequest],
773) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
774 if declarations.len().saturating_add(requests.len()) > 254 || ranges.len() > 254 {
775 return Err(StaticMemoryDeclarationError::TooManyDeclarations);
776 }
777 let mut requests = requests.to_vec();
778 requests.sort_unstable_by(|a, b| a.stable_key.cmp(&b.stable_key));
780 let mut keys = std::collections::BTreeSet::new();
781 keys.extend(declarations.iter().map(|d| d.declaration().stable_key()));
782 for key in requests.iter().map(|r| &r.stable_key) {
783 if !keys.insert(key) {
784 return Err(StaticMemoryDeclarationError::DuplicateRequest {
785 stable_key: key.clone(),
786 });
787 }
788 }
789 let mut registered_declarations = declarations.to_vec();
790 registered_declarations.sort_by(|left, right| {
791 left.declaration()
792 .stable_key()
793 .cmp(right.declaration().stable_key())
794 .then_with(|| left.declaration().slot().cmp(right.declaration().slot()))
795 .then_with(|| left.authority().cmp(right.authority()))
796 });
797
798 let mut registered_ranges = ranges.to_vec();
799 registered_ranges.sort_by(|left, right| {
800 let left = left.record();
801 let right = right.record();
802 left.range()
803 .start()
804 .cmp(&right.range().start())
805 .then_with(|| left.range().end().cmp(&right.range().end()))
806 .then_with(|| left.authority().cmp(right.authority()))
807 .then_with(|| range_mode_order(left.mode()).cmp(&range_mode_order(right.mode())))
808 .then_with(|| left.purpose().cmp(&right.purpose()))
809 });
810
811 let mut allocation_declarations = Vec::with_capacity(registered_declarations.len() + 1);
812 allocation_declarations.push(internal_ledger_declaration()?);
813 allocation_declarations.extend(
814 registered_declarations
815 .iter()
816 .map(|registration| registration.declaration().clone()),
817 );
818 let allocation_snapshot = DeclarationSnapshot::new(allocation_declarations)?;
819
820 let mut authority_records = Vec::with_capacity(registered_ranges.len() + 1);
821 authority_records.push(internal_ledger_range()?);
822 authority_records.extend(
823 registered_ranges
824 .iter()
825 .map(|registration| registration.record().clone()),
826 );
827 let range_authority = MemoryManagerRangeAuthority::from_records(authority_records)?;
828 let fingerprint = sealed_declaration_fingerprint(
829 &allocation_snapshot,
830 ®istered_declarations,
831 range_authority.authorities(),
832 &requests,
833 )?;
834
835 let mut declaration_authority = BTreeMap::new();
836 declaration_authority.insert(
837 IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
838 RuntimeDeclarationAuthority::Internal,
839 );
840 for registration in ®istered_declarations {
841 declaration_authority.insert(
842 registration.declaration().stable_key().as_str().to_string(),
843 RuntimeDeclarationAuthority::External(registration.authority().to_string()),
844 );
845 }
846
847 Ok(SealedDeclarationSnapshot {
848 inner: Arc::new(SealedDeclarationSnapshotInner {
849 allocation_snapshot,
850 requests,
851 registered_declarations,
852 registered_ranges,
853 range_authority,
854 declaration_authority,
855 fingerprint,
856 }),
857 })
858}
859
860#[derive(Serialize)]
861struct FingerprintDeclaration<'a> {
862 authority: &'a str,
863 declaration: &'a AllocationDeclaration,
864}
865
866#[derive(Serialize)]
867struct SealedDeclarationFingerprintMaterial<'a> {
868 format: &'static str,
869 allocation_snapshot: &'a DeclarationSnapshot,
870 registered_declarations: Vec<FingerprintDeclaration<'a>>,
871 effective_ranges: &'a [MemoryManagerAuthorityRecord],
872 requests: &'a [MemoryRequest],
873}
874
875fn sealed_declaration_fingerprint(
876 allocation_snapshot: &DeclarationSnapshot,
877 registered_declarations: &[StaticMemoryDeclaration],
878 effective_ranges: &[MemoryManagerAuthorityRecord],
879 requests: &[MemoryRequest],
880) -> Result<SealedDeclarationFingerprint, StaticMemoryDeclarationError> {
881 let material = SealedDeclarationFingerprintMaterial {
882 format: "ic-memory.sealed-declaration-fingerprint.v1",
883 allocation_snapshot,
884 registered_declarations: registered_declarations
885 .iter()
886 .map(|registration| FingerprintDeclaration {
887 authority: registration.authority(),
888 declaration: registration.declaration(),
889 })
890 .collect(),
891 effective_ranges,
892 requests,
893 };
894 let mut bytes = Vec::new();
895 ciborium::into_writer(&material, &mut bytes).map_err(|err| {
896 StaticMemoryDeclarationError::SnapshotFingerprintEncoding {
897 message: err.to_string(),
898 }
899 })?;
900
901 Ok(SealedDeclarationFingerprint {
902 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
903 value: crate::hash::fnv64(crate::hash::FNV_OFFSET, &bytes),
904 })
905}
906
907const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
908const fn range_mode_order(mode: MemoryManagerRangeMode) -> u8 {
909 match mode {
910 MemoryManagerRangeMode::Reserved => 0,
911 MemoryManagerRangeMode::Allowed => 1,
912 }
913}
914
915fn internal_ledger_declaration() -> Result<AllocationDeclaration, crate::DeclarationSnapshotError> {
916 AllocationDeclaration::memory_manager(
917 IC_MEMORY_LEDGER_STABLE_KEY,
918 MEMORY_MANAGER_LEDGER_ID,
919 IC_MEMORY_LEDGER_LABEL,
920 )
921}
922
923fn internal_ledger_range() -> Result<MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError>
924{
925 MemoryManagerAuthorityRecord::new(
926 MemoryManagerIdRange::new(MEMORY_MANAGER_LEDGER_ID, MEMORY_MANAGER_GOVERNANCE_MAX_ID)?,
927 IC_MEMORY_AUTHORITY_OWNER,
928 MemoryManagerRangeMode::Reserved,
929 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
930 )
931}
932
933#[cfg(test)]
934pub fn reset_static_memory_declarations_for_tests() {
935 let mut registry = STATIC_MEMORY_DECLARATIONS
936 .lock()
937 .expect("static memory declaration registry poisoned");
938 registry.declarations.clear();
939 registry.requests.clear();
940 registry.ranges.clear();
941 registry.registration_hooks.clear();
942 registry.eager_init_hooks.clear();
943 registry.lifecycle = StaticRegistryLifecycle::Open;
944}
945
946#[cfg(test)]
947mod tests;