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)]
33pub struct StaticMemoryDeclaration {
34 authority: String,
35 declaration: AllocationDeclaration,
36}
37
38impl StaticMemoryDeclaration {
39 pub fn new(
41 authority: impl Into<String>,
42 declaration: AllocationDeclaration,
43 ) -> Result<Self, StaticMemoryDeclarationError> {
44 let authority = authority.into();
45 validate_external_authority(&authority)?;
46 declaration.validate()?;
47 if is_ic_memory_stable_key(declaration.stable_key().as_str()) {
48 return Err(StaticMemoryDeclarationError::ReservedStableKey {
49 stable_key: declaration.stable_key().as_str().to_string(),
50 });
51 }
52 Ok(Self {
53 authority,
54 declaration,
55 })
56 }
57
58 #[must_use]
60 pub fn authority(&self) -> &str {
61 &self.authority
62 }
63
64 #[must_use]
66 pub const fn declaration(&self) -> &AllocationDeclaration {
67 &self.declaration
68 }
69
70 #[must_use]
72 pub fn into_declaration(self) -> AllocationDeclaration {
73 self.declaration
74 }
75}
76
77#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
85pub struct MemoryRequest {
86 authority: String,
87 stable_key: crate::StableKey,
88 schema: SchemaMetadata,
89}
90
91impl MemoryRequest {
92 pub fn new(
94 authority: impl Into<String>,
95 stable_key: &str,
96 schema: SchemaMetadata,
97 ) -> Result<Self, StaticMemoryDeclarationError> {
98 let authority = authority.into();
99 validate_external_authority(&authority)?;
100 let stable_key =
101 crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
102 schema
103 .validate()
104 .map_err(crate::DeclarationSnapshotError::SchemaMetadata)?;
105 if is_ic_memory_stable_key(stable_key.as_str()) {
106 return Err(StaticMemoryDeclarationError::ReservedStableKey {
107 stable_key: stable_key.as_str().to_string(),
108 });
109 }
110 Ok(Self {
111 authority,
112 stable_key,
113 schema,
114 })
115 }
116
117 pub(crate) const fn with_schema(mut self, schema: SchemaMetadata) -> Self {
119 self.schema = schema;
120 self
121 }
122
123 #[must_use]
125 pub const fn stable_key(&self) -> &crate::StableKey {
126 &self.stable_key
127 }
128
129 #[must_use]
131 pub const fn schema(&self) -> &SchemaMetadata {
132 &self.schema
133 }
134
135 #[must_use]
137 pub fn authority(&self) -> &str {
138 &self.authority
139 }
140}
141
142pub fn register_memory_request(request: MemoryRequest) -> Result<(), StaticMemoryDeclarationError> {
144 with_unsealed_registry(|registry| registry.requests.push(request))
145}
146
147#[derive(Clone, Debug, Eq, PartialEq)]
156pub struct StaticMemoryRangeDeclaration {
157 record: MemoryManagerAuthorityRecord,
158}
159
160impl StaticMemoryRangeDeclaration {
161 pub fn new(record: MemoryManagerAuthorityRecord) -> Result<Self, StaticMemoryDeclarationError> {
163 validate_external_authority(record.authority())?;
164 record.validate()?;
165 Ok(Self { record })
166 }
167
168 #[must_use]
170 pub fn authority(&self) -> &str {
171 self.record.authority()
172 }
173
174 #[must_use]
176 pub const fn record(&self) -> &MemoryManagerAuthorityRecord {
177 &self.record
178 }
179
180 #[must_use]
182 pub fn into_record(self) -> MemoryManagerAuthorityRecord {
183 self.record
184 }
185}
186
187#[non_exhaustive]
192#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
193pub enum StaticMemoryDeclarationError {
194 #[error("at most 254 external declarations and ranges are supported")]
195 TooManyDeclarations,
196 #[error("duplicate requested stable key {stable_key}")]
197 DuplicateRequest { stable_key: crate::StableKey },
198 #[error("static memory declaration registry lock poisoned")]
200 RegistryPoisoned,
201 #[error("static memory declaration registry is already sealed")]
203 RegistrySealed,
204 #[error("static memory declaration snapshot sealing is already active on this thread")]
206 ReentrantSealing,
207 #[error("static memory declaration eager-init hook panicked")]
209 EagerInitPanicked,
210 #[error(transparent)]
212 Declaration(#[from] crate::DeclarationSnapshotError),
213 #[error(transparent)]
215 Range(#[from] MemoryManagerRangeAuthorityError),
216 #[error("authority {reason}")]
218 InvalidAuthority {
219 reason: &'static str,
221 },
222 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
224 ReservedAuthority {
225 authority: String,
227 },
228 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
230 ReservedStableKey {
231 stable_key: String,
233 },
234}
235
236#[derive(Clone, Debug, Eq, PartialEq)]
249pub struct SealedDeclarationSnapshot {
250 inner: Arc<SealedDeclarationSnapshotInner>,
251}
252
253#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
266#[serde(deny_unknown_fields)]
267pub struct SealedDeclarationFingerprint {
268 algorithm_version: u8,
269 value: u64,
270}
271
272impl SealedDeclarationFingerprint {
273 #[must_use]
275 pub const fn algorithm_version(&self) -> u8 {
276 self.algorithm_version
277 }
278
279 #[must_use]
281 pub const fn value(&self) -> u64 {
282 self.value
283 }
284}
285
286#[derive(Debug, Eq, PartialEq)]
287struct SealedDeclarationSnapshotInner {
288 allocation_snapshot: DeclarationSnapshot,
289 requests: Vec<MemoryRequest>,
290 registered_declarations: Vec<StaticMemoryDeclaration>,
291 registered_ranges: Vec<StaticMemoryRangeDeclaration>,
292 range_authority: MemoryManagerRangeAuthority,
293 fingerprint: SealedDeclarationFingerprint,
294}
295
296impl SealedDeclarationSnapshot {
297 pub fn new(
299 declarations: &[StaticMemoryDeclaration],
300 ranges: &[StaticMemoryRangeDeclaration],
301 requests: &[MemoryRequest],
302 ) -> Result<Self, StaticMemoryDeclarationError> {
303 build_snapshot(declarations, ranges, requests)
304 }
305
306 #[must_use]
308 pub fn requests(&self) -> &[MemoryRequest] {
309 &self.inner.requests
310 }
311
312 pub(crate) fn resolve(
313 &self,
314 ledger: &crate::AllocationLedger,
315 historical: Vec<MemoryRequest>,
316 ) -> Result<Self, crate::MemoryResolutionError> {
317 if self.requests().is_empty() && historical.is_empty() {
318 return Ok(self.clone());
319 }
320 if self.registered_declarations().len() + self.requests().len() + historical.len() > 254 {
321 return Err(StaticMemoryDeclarationError::TooManyDeclarations.into());
322 }
323 let mut declarations = self.registered_declarations().to_vec();
324 let mut occupied = [false; 255];
325 for record in ledger.allocation_history().records() {
326 occupied[usize::from(
327 record
328 .slot()
329 .memory_manager_id()
330 .expect("validated ledger slot"),
331 )] = true;
332 }
333 for fixed in &declarations {
334 occupied[usize::from(
335 fixed
336 .declaration()
337 .slot()
338 .memory_manager_id()
339 .expect("checked slot"),
340 )] = true;
341 }
342 for request in self.requests().iter().chain(&historical) {
347 let historical = ledger
348 .allocation_history()
349 .records()
350 .iter()
351 .find(|record| record.stable_key() == &request.stable_key);
352 let id = if let Some(record) = historical {
353 record
354 .slot()
355 .memory_manager_id()
356 .expect("validated ledger slot")
357 } else {
358 (0..255_u8)
359 .find(|id| {
360 !occupied[usize::from(*id)]
361 && self.range_authority().authorities().iter().any(|range| {
362 range.authority() == request.authority
363 && range.mode() == MemoryManagerRangeMode::Allowed
364 && range.range().contains(*id)
365 })
366 })
367 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
368 stable_key: request.stable_key.clone(),
369 authority: request.authority.clone(),
370 })?
371 };
372 let slot = crate::AllocationSlotDescriptor::memory_manager(id).expect("usable id");
373 self.range_authority()
375 .validate_slot_authority(&slot, &request.authority)
376 .map_err(crate::MemoryResolutionError::Range)?;
377 occupied[usize::from(id)] = true;
378 declarations.push(StaticMemoryDeclaration {
382 authority: request.authority.clone(),
383 declaration: AllocationDeclaration {
384 stable_key: request.stable_key.clone(),
385 slot,
386 label: None,
387 schema: request.schema.clone(),
388 },
389 });
390 }
391 Ok(build_snapshot(
392 &declarations,
393 self.registered_ranges(),
394 &[],
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 validate_external_authority(declaration.authority())?;
616 with_unsealed_registry(|registry| {
617 registry.ranges.push(declaration);
618 })
619}
620
621fn validate_external_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
622 if value == IC_MEMORY_AUTHORITY_OWNER {
623 return Err(StaticMemoryDeclarationError::ReservedAuthority {
624 authority: value.to_string(),
625 });
626 }
627 validate_diagnostic_text(value).map_err(|error| {
628 StaticMemoryDeclarationError::InvalidAuthority {
629 reason: error.reason(),
630 }
631 })
632}
633
634pub fn register_static_memory_manager_declaration(
636 id: u8,
637 authority: impl Into<String>,
638 label: impl Into<String>,
639 stable_key: impl AsRef<str>,
640) -> Result<(), StaticMemoryDeclarationError> {
641 register_static_memory_manager_declaration_with_schema(
642 id,
643 authority,
644 label,
645 stable_key,
646 SchemaMetadata::default(),
647 )
648}
649
650pub fn register_static_memory_manager_declaration_with_schema(
652 id: u8,
653 authority: impl Into<String>,
654 label: impl Into<String>,
655 stable_key: impl AsRef<str>,
656 schema: SchemaMetadata,
657) -> Result<(), StaticMemoryDeclarationError> {
658 let declaration =
659 AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
660 register_static_memory_declaration(authority, declaration)
661}
662
663pub fn sealed_declaration_snapshot()
675-> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
676 {
677 let registry = lock_registry()?;
678 match ®istry.lifecycle {
679 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
680 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
681 StaticRegistryLifecycle::Sealing { owner, .. }
682 if *owner == std::thread::current().id() =>
683 {
684 return Err(StaticMemoryDeclarationError::ReentrantSealing);
685 }
686 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealing { .. } => {}
687 }
688 }
689
690 let _seal = STATIC_MEMORY_SEAL
691 .lock()
692 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
693 let (registration_hooks, eager_init_hooks) = {
694 let mut registry = lock_registry()?;
695 match ®istry.lifecycle {
696 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
697 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
698 StaticRegistryLifecycle::Sealing { .. } => {
699 return Err(StaticMemoryDeclarationError::ReentrantSealing);
700 }
701 StaticRegistryLifecycle::Open => {}
702 }
703 registry.lifecycle = StaticRegistryLifecycle::Sealing {
704 owner: std::thread::current().id(),
705 deferred_error: None,
706 };
707 (
708 std::mem::take(&mut registry.registration_hooks),
709 std::mem::take(&mut registry.eager_init_hooks),
710 )
711 };
712
713 for hook in registration_hooks {
714 let result = catch_unwind(AssertUnwindSafe(hook))
715 .map_err(|_| StaticMemoryDeclarationError::EagerInitPanicked)
716 .and_then(std::convert::identity);
717 if let Err(err) = result {
718 return fail_sealing(err);
719 }
720 }
721 for hook in eager_init_hooks {
722 if catch_unwind(AssertUnwindSafe(hook)).is_err() {
723 return fail_sealing(StaticMemoryDeclarationError::EagerInitPanicked);
724 }
725 }
726
727 let mut registry = lock_registry()?;
728 let deferred_error = match ®istry.lifecycle {
729 StaticRegistryLifecycle::Sealing { deferred_error, .. } => deferred_error.clone(),
730 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
731 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealed(_) => {
732 unreachable!("seal lock preserves the in-progress registry lifecycle");
733 }
734 };
735 let result = match deferred_error {
736 Some(error) => Err(error),
737 None => build_snapshot(®istry.declarations, ®istry.ranges, ®istry.requests),
738 };
739 registry.finish_sealing(result)
740}
741
742fn fail_sealing(
743 err: StaticMemoryDeclarationError,
744) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
745 let mut registry = lock_registry()?;
746 let failure = match ®istry.lifecycle {
747 StaticRegistryLifecycle::Sealing {
748 deferred_error: Some(deferred_error),
749 ..
750 } => deferred_error.clone(),
751 StaticRegistryLifecycle::Open
752 | StaticRegistryLifecycle::Sealing {
753 deferred_error: None,
754 ..
755 }
756 | StaticRegistryLifecycle::Sealed(_) => err,
757 StaticRegistryLifecycle::Failed(failure) => failure.clone(),
758 };
759 registry.finish_sealing(Err(failure))
760}
761
762fn build_snapshot(
763 declarations: &[StaticMemoryDeclaration],
764 ranges: &[StaticMemoryRangeDeclaration],
765 requests: &[MemoryRequest],
766) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
767 if declarations.len().saturating_add(requests.len()) > 254 || ranges.len() > 254 {
768 return Err(StaticMemoryDeclarationError::TooManyDeclarations);
769 }
770 let mut requests = requests.to_vec();
771 requests.sort_unstable_by(|a, b| a.stable_key.cmp(&b.stable_key));
773 let mut keys = std::collections::BTreeSet::new();
774 keys.extend(declarations.iter().map(|d| d.declaration().stable_key()));
775 for key in requests.iter().map(|r| &r.stable_key) {
776 if !keys.insert(key) {
777 return Err(StaticMemoryDeclarationError::DuplicateRequest {
778 stable_key: key.clone(),
779 });
780 }
781 }
782 let mut registered_declarations = declarations.to_vec();
783 registered_declarations.sort_by(|left, right| {
784 left.declaration()
785 .stable_key()
786 .cmp(right.declaration().stable_key())
787 .then_with(|| left.declaration().slot().cmp(right.declaration().slot()))
788 .then_with(|| left.authority().cmp(right.authority()))
789 });
790
791 let mut registered_ranges = ranges.to_vec();
792 registered_ranges.sort_by(|left, right| {
793 let left = left.record();
794 let right = right.record();
795 left.range()
796 .start()
797 .cmp(&right.range().start())
798 .then_with(|| left.range().end().cmp(&right.range().end()))
799 .then_with(|| left.authority().cmp(right.authority()))
800 .then_with(|| range_mode_order(left.mode()).cmp(&range_mode_order(right.mode())))
801 .then_with(|| left.purpose().cmp(&right.purpose()))
802 });
803
804 let mut allocation_declarations = Vec::with_capacity(registered_declarations.len() + 1);
805 allocation_declarations.push(internal_ledger_declaration());
806 allocation_declarations.extend(
807 registered_declarations
808 .iter()
809 .map(|registration| registration.declaration().clone()),
810 );
811 let allocation_snapshot = DeclarationSnapshot::new(allocation_declarations)?;
812
813 let mut authority_records = Vec::with_capacity(registered_ranges.len() + 1);
814 authority_records.push(internal_ledger_range());
815 authority_records.extend(
816 registered_ranges
817 .iter()
818 .map(|registration| registration.record().clone()),
819 );
820 let range_authority = MemoryManagerRangeAuthority::from_records(authority_records)?;
821 let fingerprint = sealed_declaration_fingerprint(
822 &allocation_snapshot,
823 ®istered_declarations,
824 range_authority.authorities(),
825 &requests,
826 );
827
828 Ok(SealedDeclarationSnapshot {
829 inner: Arc::new(SealedDeclarationSnapshotInner {
830 allocation_snapshot,
831 requests,
832 registered_declarations,
833 registered_ranges,
834 range_authority,
835 fingerprint,
836 }),
837 })
838}
839
840#[derive(Serialize)]
841struct FingerprintDeclaration<'a> {
842 authority: &'a str,
843 declaration: &'a AllocationDeclaration,
844}
845
846#[derive(Serialize)]
847struct SealedDeclarationFingerprintMaterial<'a> {
848 format: &'static str,
849 allocation_snapshot: &'a DeclarationSnapshot,
850 registered_declarations: Vec<FingerprintDeclaration<'a>>,
851 effective_ranges: &'a [MemoryManagerAuthorityRecord],
852 requests: &'a [MemoryRequest],
853}
854
855fn sealed_declaration_fingerprint(
856 allocation_snapshot: &DeclarationSnapshot,
857 registered_declarations: &[StaticMemoryDeclaration],
858 effective_ranges: &[MemoryManagerAuthorityRecord],
859 requests: &[MemoryRequest],
860) -> SealedDeclarationFingerprint {
861 let material = SealedDeclarationFingerprintMaterial {
862 format: "ic-memory.sealed-declaration-fingerprint.v1",
863 allocation_snapshot,
864 registered_declarations: registered_declarations
865 .iter()
866 .map(|registration| FingerprintDeclaration {
867 authority: registration.authority(),
868 declaration: registration.declaration(),
869 })
870 .collect(),
871 effective_ranges,
872 requests,
873 };
874 let mut bytes = Vec::new();
875 ciborium::into_writer(&material, &mut bytes)
877 .expect("sealed declaration fingerprint encodes into Vec");
878
879 SealedDeclarationFingerprint {
880 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
881 value: crate::hash::fnv64(crate::hash::FNV_OFFSET, &bytes),
882 }
883}
884
885const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
886const fn range_mode_order(mode: MemoryManagerRangeMode) -> u8 {
887 match mode {
888 MemoryManagerRangeMode::Reserved => 0,
889 MemoryManagerRangeMode::Allowed => 1,
890 }
891}
892
893fn internal_ledger_declaration() -> AllocationDeclaration {
894 AllocationDeclaration::memory_manager(
895 IC_MEMORY_LEDGER_STABLE_KEY,
896 MEMORY_MANAGER_LEDGER_ID,
897 IC_MEMORY_LEDGER_LABEL,
898 )
899 .unwrap_or_else(|_| unreachable!("built-in ledger declaration constants are valid"))
900}
901
902fn internal_ledger_range() -> MemoryManagerAuthorityRecord {
903 MemoryManagerAuthorityRecord::new(
904 memory_manager_governance_range(),
905 IC_MEMORY_AUTHORITY_OWNER,
906 MemoryManagerRangeMode::Reserved,
907 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
908 )
909 .unwrap_or_else(|_| unreachable!("built-in governance range metadata constants are valid"))
910}
911
912#[cfg(test)]
913pub fn reset_static_memory_declarations_for_tests() {
914 let mut registry = STATIC_MEMORY_DECLARATIONS
915 .lock()
916 .expect("static memory declaration registry poisoned");
917 registry.declarations.clear();
918 registry.requests.clear();
919 registry.ranges.clear();
920 registry.registration_hooks.clear();
921 registry.eager_init_hooks.clear();
922 registry.lifecycle = StaticRegistryLifecycle::Open;
923}
924
925#[cfg(test)]
926mod tests;