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