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