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