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 panic::{AssertUnwindSafe, catch_unwind},
15 sync::{Arc, Mutex, MutexGuard},
16 thread::ThreadId,
17};
18
19#[cfg(test)]
20pub static TEST_REGISTRY_LOCK: Mutex<()> = Mutex::new(());
21
22#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
35pub struct StaticMemoryDeclaration {
36 authority: String,
37 declaration: AllocationDeclaration,
38}
39
40impl StaticMemoryDeclaration {
41 pub fn new(
43 authority: impl Into<String>,
44 declaration: AllocationDeclaration,
45 ) -> Result<Self, StaticMemoryDeclarationError> {
46 let authority = authority.into();
47 validate_external_authority(&authority)?;
48 declaration.validate()?;
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 schema
105 .validate()
106 .map_err(crate::DeclarationSnapshotError::SchemaMetadata)?;
107 if is_ic_memory_stable_key(stable_key.as_str()) {
108 return Err(StaticMemoryDeclarationError::ReservedStableKey {
109 stable_key: stable_key.as_str().to_string(),
110 });
111 }
112 Ok(Self {
113 authority,
114 stable_key,
115 schema,
116 })
117 }
118
119 pub(crate) const fn with_schema(mut self, schema: SchemaMetadata) -> Self {
121 self.schema = schema;
122 self
123 }
124
125 #[must_use]
127 pub const fn stable_key(&self) -> &crate::StableKey {
128 &self.stable_key
129 }
130
131 #[must_use]
133 pub const fn schema(&self) -> &SchemaMetadata {
134 &self.schema
135 }
136
137 #[must_use]
139 pub fn authority(&self) -> &str {
140 &self.authority
141 }
142}
143
144pub fn register_memory_request(request: MemoryRequest) -> Result<(), StaticMemoryDeclarationError> {
146 with_unsealed_registry(|registry| registry.requests.push(request))
147}
148
149#[derive(Clone, Debug, Eq, PartialEq)]
158pub struct StaticMemoryRangeDeclaration {
159 record: MemoryManagerAuthorityRecord,
160}
161
162impl StaticMemoryRangeDeclaration {
163 pub fn new(record: MemoryManagerAuthorityRecord) -> Result<Self, StaticMemoryDeclarationError> {
165 validate_external_authority(record.authority())?;
166 record.validate()?;
167 Ok(Self { record })
168 }
169
170 #[must_use]
172 pub fn authority(&self) -> &str {
173 self.record.authority()
174 }
175
176 #[must_use]
178 pub const fn record(&self) -> &MemoryManagerAuthorityRecord {
179 &self.record
180 }
181
182 #[must_use]
184 pub fn into_record(self) -> MemoryManagerAuthorityRecord {
185 self.record
186 }
187}
188
189#[non_exhaustive]
194#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
195pub enum StaticMemoryDeclarationError {
196 #[error("at most 254 external declarations and ranges are supported")]
197 TooManyDeclarations,
198 #[error("duplicate requested stable key {stable_key}")]
199 DuplicateRequest { stable_key: crate::StableKey },
200 #[error("static memory declaration registry lock poisoned")]
202 RegistryPoisoned,
203 #[error("static memory declaration registry is already sealed")]
205 RegistrySealed,
206 #[error("static memory declaration snapshot sealing is already active on this thread")]
208 ReentrantSealing,
209 #[error("static memory declaration eager-init hook panicked")]
211 EagerInitPanicked,
212 #[error(transparent)]
214 Declaration(#[from] crate::DeclarationSnapshotError),
215 #[error(transparent)]
217 Range(#[from] MemoryManagerRangeAuthorityError),
218 #[error("authority {reason}")]
220 InvalidAuthority {
221 reason: &'static str,
223 },
224 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
226 ReservedAuthority {
227 authority: String,
229 },
230 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
232 ReservedStableKey {
233 stable_key: String,
235 },
236}
237
238#[derive(Clone, Debug, Eq, PartialEq)]
251pub struct SealedDeclarationSnapshot {
252 inner: Arc<SealedDeclarationSnapshotInner>,
253}
254
255#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
268#[serde(deny_unknown_fields)]
269pub struct SealedDeclarationFingerprint {
270 algorithm_version: u8,
271 value: u64,
272}
273
274impl SealedDeclarationFingerprint {
275 #[must_use]
277 pub const fn algorithm_version(&self) -> u8 {
278 self.algorithm_version
279 }
280
281 #[must_use]
283 pub const fn value(&self) -> u64 {
284 self.value
285 }
286}
287
288#[derive(Debug, Eq, PartialEq)]
289struct SealedDeclarationSnapshotInner {
290 allocation_snapshot: DeclarationSnapshot,
291 requests: Vec<MemoryRequest>,
292 registered_declarations: Vec<StaticMemoryDeclaration>,
293 registered_ranges: Vec<StaticMemoryRangeDeclaration>,
294 range_authority: MemoryManagerRangeAuthority,
295 fingerprint: SealedDeclarationFingerprint,
296}
297
298impl SealedDeclarationSnapshot {
299 pub fn new(
301 declarations: &[StaticMemoryDeclaration],
302 ranges: &[StaticMemoryRangeDeclaration],
303 requests: &[MemoryRequest],
304 ) -> Result<Self, StaticMemoryDeclarationError> {
305 build_snapshot(declarations, ranges, requests)
306 }
307
308 #[must_use]
310 pub fn requests(&self) -> &[MemoryRequest] {
311 &self.inner.requests
312 }
313
314 pub(crate) fn resolve(
315 &self,
316 ledger: &crate::AllocationLedger,
317 historical: Vec<MemoryRequest>,
318 ) -> Result<Self, crate::MemoryResolutionError> {
319 if self.requests().is_empty() && historical.is_empty() {
320 return Ok(self.clone());
321 }
322 if self.registered_declarations().len() + self.requests().len() + historical.len() > 254 {
323 return Err(StaticMemoryDeclarationError::TooManyDeclarations.into());
324 }
325 let mut declarations = self.registered_declarations().to_vec();
326 let mut occupied = [false; 255];
327 for record in ledger.allocation_history().records() {
328 occupied[usize::from(
329 record
330 .slot()
331 .memory_manager_id()
332 .expect("validated ledger slot"),
333 )] = true;
334 }
335 for fixed in &declarations {
336 occupied[usize::from(
337 fixed
338 .declaration()
339 .slot()
340 .memory_manager_id()
341 .expect("checked slot"),
342 )] = true;
343 }
344 for request in self.requests().iter().chain(&historical) {
349 let historical = ledger
350 .allocation_history()
351 .records()
352 .iter()
353 .find(|record| record.stable_key() == &request.stable_key);
354 let id = if let Some(record) = historical {
355 record
356 .slot()
357 .memory_manager_id()
358 .expect("validated ledger slot")
359 } else {
360 (0..255_u8)
361 .find(|id| {
362 !occupied[usize::from(*id)]
363 && self.range_authority().authorities().iter().any(|range| {
364 range.authority() == request.authority
365 && range.mode() == MemoryManagerRangeMode::Allowed
366 && range.range().contains(*id)
367 })
368 })
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::AllocationSlotDescriptor::memory_manager(id).expect("usable id");
375 self.range_authority()
377 .validate_slot_authority(&slot, &request.authority)
378 .map_err(crate::MemoryResolutionError::Range)?;
379 occupied[usize::from(id)] = true;
380 declarations.push(StaticMemoryDeclaration {
384 authority: request.authority.clone(),
385 declaration: AllocationDeclaration {
386 stable_key: request.stable_key.clone(),
387 slot,
388 label: None,
389 schema: request.schema.clone(),
390 },
391 });
392 }
393 Ok(build_snapshot(
394 &declarations,
395 self.registered_ranges(),
396 &[],
397 )?)
398 }
399
400 #[must_use]
404 pub fn allocation_snapshot(&self) -> &DeclarationSnapshot {
405 &self.inner.allocation_snapshot
406 }
407
408 #[must_use]
410 pub fn registered_declarations(&self) -> &[StaticMemoryDeclaration] {
411 &self.inner.registered_declarations
412 }
413
414 #[must_use]
416 pub fn registered_ranges(&self) -> &[StaticMemoryRangeDeclaration] {
417 &self.inner.registered_ranges
418 }
419
420 #[must_use]
422 pub fn range_authority(&self) -> &MemoryManagerRangeAuthority {
423 &self.inner.range_authority
424 }
425
426 #[must_use]
428 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
429 self.inner.fingerprint
430 }
431
432 pub(crate) fn registered_declaration(
433 &self,
434 key: &crate::StableKey,
435 ) -> Option<&StaticMemoryDeclaration> {
436 let declarations = self.registered_declarations();
437 declarations
439 .binary_search_by(|registration| registration.declaration().stable_key().cmp(key))
440 .ok()
441 .map(|index| &declarations[index])
442 }
443
444 pub(crate) fn user_ranges_registered(&self) -> bool {
445 !self.inner.registered_ranges.is_empty()
446 }
447
448 #[cfg(test)]
449 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
450 Arc::ptr_eq(&self.inner, &other.inner)
451 }
452}
453
454type StaticRegistrationHook = fn() -> Result<(), StaticMemoryDeclarationError>;
455
456#[derive(Debug)]
457struct StaticMemoryDeclarationRegistry {
458 declarations: Vec<StaticMemoryDeclaration>,
459 requests: Vec<MemoryRequest>,
460 ranges: Vec<StaticMemoryRangeDeclaration>,
461 registration_hooks: Vec<StaticRegistrationHook>,
462 eager_init_hooks: Vec<fn()>,
463 lifecycle: StaticRegistryLifecycle,
464}
465
466impl StaticMemoryDeclarationRegistry {
467 fn finish_sealing(
468 &mut self,
469 result: Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError>,
470 ) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
471 self.declarations = Vec::new();
473 self.requests = Vec::new();
474 self.ranges = Vec::new();
475 self.registration_hooks = Vec::new();
476 self.eager_init_hooks = Vec::new();
477 self.lifecycle = match &result {
478 Ok(snapshot) => StaticRegistryLifecycle::Sealed(snapshot.clone()),
479 Err(error) => StaticRegistryLifecycle::Failed(error.clone()),
480 };
481 result
482 }
483}
484
485#[derive(Debug)]
486enum StaticRegistryLifecycle {
487 Open,
488 Sealing {
489 owner: ThreadId,
490 deferred_error: Option<StaticMemoryDeclarationError>,
491 },
492 Sealed(SealedDeclarationSnapshot),
493 Failed(StaticMemoryDeclarationError),
494}
495
496static STATIC_MEMORY_DECLARATIONS: Mutex<StaticMemoryDeclarationRegistry> =
497 Mutex::new(StaticMemoryDeclarationRegistry {
498 declarations: Vec::new(),
499 requests: Vec::new(),
500 ranges: Vec::new(),
501 registration_hooks: Vec::new(),
502 eager_init_hooks: Vec::new(),
503 lifecycle: StaticRegistryLifecycle::Open,
504 });
505
506static STATIC_MEMORY_SEAL: Mutex<()> = Mutex::new(());
507
508fn lock_registry()
509-> Result<MutexGuard<'static, StaticMemoryDeclarationRegistry>, StaticMemoryDeclarationError> {
510 STATIC_MEMORY_DECLARATIONS
511 .lock()
512 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
513}
514
515fn ensure_registration_open(
516 registry: &StaticMemoryDeclarationRegistry,
517) -> Result<(), StaticMemoryDeclarationError> {
518 match ®istry.lifecycle {
519 StaticRegistryLifecycle::Open => Ok(()),
520 StaticRegistryLifecycle::Sealing { owner, .. } if *owner == std::thread::current().id() => {
521 Ok(())
522 }
523 StaticRegistryLifecycle::Sealing { .. }
524 | StaticRegistryLifecycle::Sealed(_)
525 | StaticRegistryLifecycle::Failed(_) => Err(StaticMemoryDeclarationError::RegistrySealed),
526 }
527}
528
529fn with_unsealed_registry(
530 op: impl FnOnce(&mut StaticMemoryDeclarationRegistry),
531) -> Result<(), StaticMemoryDeclarationError> {
532 let mut registry = lock_registry()?;
533 ensure_registration_open(®istry)?;
534 op(&mut registry);
535 Ok(())
536}
537
538#[doc(hidden)]
543pub fn defer_static_memory_registration(hook: StaticRegistrationHook) {
544 defer_constructor_registration(|registry| {
545 registry.registration_hooks.push(hook);
546 });
547}
548
549#[doc(hidden)]
554pub fn defer_eager_init(hook: fn()) {
555 defer_constructor_registration(|registry| {
556 registry.eager_init_hooks.push(hook);
557 });
558}
559
560fn defer_constructor_registration(op: impl FnOnce(&mut StaticMemoryDeclarationRegistry)) {
561 let Ok(mut registry) = STATIC_MEMORY_DECLARATIONS.lock() else {
562 return;
565 };
566 if matches!(registry.lifecycle, StaticRegistryLifecycle::Open) {
567 op(&mut registry);
568 return;
569 }
570 match &mut registry.lifecycle {
571 StaticRegistryLifecycle::Sealing { deferred_error, .. } => {
572 if deferred_error.is_none() {
573 *deferred_error = Some(StaticMemoryDeclarationError::RegistrySealed);
574 }
575 }
576 StaticRegistryLifecycle::Sealed(_) => {
577 registry.lifecycle =
578 StaticRegistryLifecycle::Failed(StaticMemoryDeclarationError::RegistrySealed);
579 }
580 StaticRegistryLifecycle::Failed(_) | StaticRegistryLifecycle::Open => {}
581 }
582}
583
584pub fn register_static_memory_declaration(
586 authority: impl Into<String>,
587 declaration: AllocationDeclaration,
588) -> Result<(), StaticMemoryDeclarationError> {
589 let registration = StaticMemoryDeclaration::new(authority, declaration)?;
590 with_unsealed_registry(|registry| {
591 registry.declarations.push(registration);
592 })
593}
594
595pub fn register_static_memory_manager_range(
597 start: u8,
598 end: u8,
599 authority: impl Into<String>,
600 mode: MemoryManagerRangeMode,
601 purpose: Option<String>,
602) -> Result<(), StaticMemoryDeclarationError> {
603 let authority = authority.into();
604 let record = MemoryManagerAuthorityRecord::new(
605 MemoryManagerIdRange::new(start, end).map_err(MemoryManagerRangeAuthorityError::Range)?,
606 authority,
607 mode,
608 purpose,
609 )?;
610 register_static_memory_range_declaration(StaticMemoryRangeDeclaration::new(record)?)
611}
612
613pub fn register_static_memory_range_declaration(
615 declaration: StaticMemoryRangeDeclaration,
616) -> Result<(), StaticMemoryDeclarationError> {
617 validate_external_authority(declaration.authority())?;
618 with_unsealed_registry(|registry| {
619 registry.ranges.push(declaration);
620 })
621}
622
623fn validate_external_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
624 if value == IC_MEMORY_AUTHORITY_OWNER {
625 return Err(StaticMemoryDeclarationError::ReservedAuthority {
626 authority: value.to_string(),
627 });
628 }
629 validate_diagnostic_text(value).map_err(|error| {
630 StaticMemoryDeclarationError::InvalidAuthority {
631 reason: error.reason(),
632 }
633 })
634}
635
636pub fn register_static_memory_manager_declaration(
638 id: u8,
639 authority: impl Into<String>,
640 label: impl Into<String>,
641 stable_key: impl AsRef<str>,
642) -> Result<(), StaticMemoryDeclarationError> {
643 register_static_memory_manager_declaration_with_schema(
644 id,
645 authority,
646 label,
647 stable_key,
648 SchemaMetadata::default(),
649 )
650}
651
652pub fn register_static_memory_manager_declaration_with_schema(
654 id: u8,
655 authority: impl Into<String>,
656 label: impl Into<String>,
657 stable_key: impl AsRef<str>,
658 schema: SchemaMetadata,
659) -> Result<(), StaticMemoryDeclarationError> {
660 let declaration =
661 AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
662 register_static_memory_declaration(authority, declaration)
663}
664
665pub fn sealed_declaration_snapshot()
677-> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
678 {
679 let registry = lock_registry()?;
680 match ®istry.lifecycle {
681 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
682 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
683 StaticRegistryLifecycle::Sealing { owner, .. }
684 if *owner == std::thread::current().id() =>
685 {
686 return Err(StaticMemoryDeclarationError::ReentrantSealing);
687 }
688 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealing { .. } => {}
689 }
690 }
691
692 let _seal = STATIC_MEMORY_SEAL
693 .lock()
694 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
695 let (registration_hooks, eager_init_hooks) = {
696 let mut registry = lock_registry()?;
697 match ®istry.lifecycle {
698 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
699 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
700 StaticRegistryLifecycle::Sealing { .. } => {
701 return Err(StaticMemoryDeclarationError::ReentrantSealing);
702 }
703 StaticRegistryLifecycle::Open => {}
704 }
705 registry.lifecycle = StaticRegistryLifecycle::Sealing {
706 owner: std::thread::current().id(),
707 deferred_error: None,
708 };
709 (
710 std::mem::take(&mut registry.registration_hooks),
711 std::mem::take(&mut registry.eager_init_hooks),
712 )
713 };
714
715 for hook in registration_hooks {
716 let result = catch_unwind(AssertUnwindSafe(hook))
717 .map_err(|_| StaticMemoryDeclarationError::EagerInitPanicked)
718 .and_then(std::convert::identity);
719 if let Err(err) = result {
720 return fail_sealing(err);
721 }
722 }
723 for hook in eager_init_hooks {
724 if catch_unwind(AssertUnwindSafe(hook)).is_err() {
725 return fail_sealing(StaticMemoryDeclarationError::EagerInitPanicked);
726 }
727 }
728
729 let mut registry = lock_registry()?;
730 let deferred_error = match ®istry.lifecycle {
731 StaticRegistryLifecycle::Sealing { deferred_error, .. } => deferred_error.clone(),
732 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
733 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealed(_) => {
734 unreachable!("seal lock preserves the in-progress registry lifecycle");
735 }
736 };
737 let result = match deferred_error {
738 Some(error) => Err(error),
739 None => build_snapshot(®istry.declarations, ®istry.ranges, ®istry.requests),
740 };
741 registry.finish_sealing(result)
742}
743
744fn fail_sealing(
745 err: StaticMemoryDeclarationError,
746) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
747 let mut registry = lock_registry()?;
748 let failure = match ®istry.lifecycle {
749 StaticRegistryLifecycle::Sealing {
750 deferred_error: Some(deferred_error),
751 ..
752 } => deferred_error.clone(),
753 StaticRegistryLifecycle::Open
754 | StaticRegistryLifecycle::Sealing {
755 deferred_error: None,
756 ..
757 }
758 | StaticRegistryLifecycle::Sealed(_) => err,
759 StaticRegistryLifecycle::Failed(failure) => failure.clone(),
760 };
761 registry.finish_sealing(Err(failure))
762}
763
764fn build_snapshot(
765 declarations: &[StaticMemoryDeclaration],
766 ranges: &[StaticMemoryRangeDeclaration],
767 requests: &[MemoryRequest],
768) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
769 if declarations.len().saturating_add(requests.len()) > 254 || ranges.len() > 254 {
770 return Err(StaticMemoryDeclarationError::TooManyDeclarations);
771 }
772 let mut requests = requests.to_vec();
773 requests.sort_unstable_by(|a, b| a.stable_key.cmp(&b.stable_key));
775 let mut keys = std::collections::BTreeSet::new();
776 keys.extend(declarations.iter().map(|d| d.declaration().stable_key()));
777 for key in requests.iter().map(|r| &r.stable_key) {
778 if !keys.insert(key) {
779 return Err(StaticMemoryDeclarationError::DuplicateRequest {
780 stable_key: key.clone(),
781 });
782 }
783 }
784 let mut registered_declarations = declarations.to_vec();
785 registered_declarations.sort_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 let mut registered_ranges = ranges.to_vec();
794 registered_ranges.sort_by(|left, right| {
795 let left = left.record();
796 let right = right.record();
797 left.range()
798 .start()
799 .cmp(&right.range().start())
800 .then_with(|| left.range().end().cmp(&right.range().end()))
801 .then_with(|| left.authority().cmp(right.authority()))
802 .then_with(|| range_mode_order(left.mode()).cmp(&range_mode_order(right.mode())))
803 .then_with(|| left.purpose().cmp(&right.purpose()))
804 });
805
806 let mut allocation_declarations = Vec::with_capacity(registered_declarations.len() + 1);
807 allocation_declarations.push(internal_ledger_declaration());
808 allocation_declarations.extend(
809 registered_declarations
810 .iter()
811 .map(|registration| registration.declaration().clone()),
812 );
813 let allocation_snapshot = DeclarationSnapshot::new(allocation_declarations)?;
814
815 let mut authority_records = Vec::with_capacity(registered_ranges.len() + 1);
816 authority_records.push(internal_ledger_range());
817 authority_records.extend(
818 registered_ranges
819 .iter()
820 .map(|registration| registration.record().clone()),
821 );
822 let range_authority = MemoryManagerRangeAuthority::from_records(authority_records)?;
823 let fingerprint = sealed_declaration_fingerprint(
824 &allocation_snapshot,
825 ®istered_declarations,
826 range_authority.authorities(),
827 &requests,
828 );
829
830 Ok(SealedDeclarationSnapshot {
831 inner: Arc::new(SealedDeclarationSnapshotInner {
832 allocation_snapshot,
833 requests,
834 registered_declarations,
835 registered_ranges,
836 range_authority,
837 fingerprint,
838 }),
839 })
840}
841
842#[derive(Serialize)]
843struct SealedDeclarationFingerprintMaterial<'a> {
844 format: &'static str,
845 allocation_snapshot: &'a DeclarationSnapshot,
846 registered_declarations: &'a [StaticMemoryDeclaration],
847 effective_ranges: &'a [MemoryManagerAuthorityRecord],
848 requests: &'a [MemoryRequest],
849}
850
851fn sealed_declaration_fingerprint(
852 allocation_snapshot: &DeclarationSnapshot,
853 registered_declarations: &[StaticMemoryDeclaration],
854 effective_ranges: &[MemoryManagerAuthorityRecord],
855 requests: &[MemoryRequest],
856) -> SealedDeclarationFingerprint {
857 let material = SealedDeclarationFingerprintMaterial {
858 format: "ic-memory.sealed-declaration-fingerprint.v1",
859 allocation_snapshot,
860 registered_declarations,
861 effective_ranges,
862 requests,
863 };
864 let mut bytes = Vec::new();
865 ciborium::into_writer(&material, &mut bytes)
867 .expect("sealed declaration fingerprint encodes into Vec");
868
869 SealedDeclarationFingerprint {
870 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
871 value: crate::hash::fnv64(crate::hash::FNV_OFFSET, &bytes),
872 }
873}
874
875const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
876const fn range_mode_order(mode: MemoryManagerRangeMode) -> u8 {
877 match mode {
878 MemoryManagerRangeMode::Reserved => 0,
879 MemoryManagerRangeMode::Allowed => 1,
880 }
881}
882
883fn internal_ledger_declaration() -> AllocationDeclaration {
884 AllocationDeclaration::memory_manager(
885 IC_MEMORY_LEDGER_STABLE_KEY,
886 MEMORY_MANAGER_LEDGER_ID,
887 IC_MEMORY_LEDGER_LABEL,
888 )
889 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
890}
891
892fn internal_ledger_range() -> MemoryManagerAuthorityRecord {
893 MemoryManagerAuthorityRecord::new(
894 memory_manager_governance_range(),
895 IC_MEMORY_AUTHORITY_OWNER,
896 MemoryManagerRangeMode::Reserved,
897 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
898 )
899 .unwrap_or_else(|_| unreachable!("built-in governance range metadata constants are valid"))
900}
901
902#[cfg(test)]
903pub fn reset_static_memory_declarations_for_tests() {
904 let mut registry = STATIC_MEMORY_DECLARATIONS
905 .lock()
906 .expect("static memory declaration registry poisoned");
907 registry.declarations.clear();
908 registry.requests.clear();
909 registry.ranges.clear();
910 registry.registration_hooks.clear();
911 registry.eager_init_hooks.clear();
912 registry.lifecycle = StaticRegistryLifecycle::Open;
913}
914
915#[cfg(test)]
916mod tests;