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