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