1use crate::{
2 constants::DIAGNOSTIC_STRING_MAX_BYTES,
3 declaration::{AllocationDeclaration, DeclarationSnapshot},
4 schema::SchemaMetadata,
5 slot::{
6 IC_MEMORY_AUTHORITY_OWNER, IC_MEMORY_AUTHORITY_PURPOSE, IC_MEMORY_LEDGER_LABEL,
7 IC_MEMORY_LEDGER_STABLE_KEY, MEMORY_MANAGER_GOVERNANCE_MAX_ID, MEMORY_MANAGER_LEDGER_ID,
8 MemoryManagerAuthorityRecord, MemoryManagerIdRange, MemoryManagerRangeAuthority,
9 MemoryManagerRangeAuthorityError, MemoryManagerRangeMode, is_ic_memory_stable_key,
10 },
11};
12use serde::{Deserialize, Serialize};
13use std::{
14 collections::BTreeMap,
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)]
34pub struct StaticMemoryDeclaration {
35 authority: String,
36 declaration: AllocationDeclaration,
37}
38
39impl StaticMemoryDeclaration {
40 pub fn new(
42 authority: impl Into<String>,
43 declaration: AllocationDeclaration,
44 ) -> Result<Self, StaticMemoryDeclarationError> {
45 let authority = authority.into();
46 validate_external_authority(&authority)?;
47 declaration.validate()?;
48 if is_ic_memory_stable_key(declaration.stable_key().as_str()) {
49 return Err(StaticMemoryDeclarationError::ReservedStableKey {
50 stable_key: declaration.stable_key().as_str().to_string(),
51 });
52 }
53 Ok(Self {
54 authority,
55 declaration,
56 })
57 }
58
59 #[must_use]
61 pub fn authority(&self) -> &str {
62 &self.authority
63 }
64
65 #[must_use]
67 pub const fn declaration(&self) -> &AllocationDeclaration {
68 &self.declaration
69 }
70
71 #[must_use]
73 pub fn into_declaration(self) -> AllocationDeclaration {
74 self.declaration
75 }
76}
77
78#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
86pub struct MemoryRequest {
87 authority: String,
88 stable_key: crate::StableKey,
89 schema: SchemaMetadata,
90}
91
92impl MemoryRequest {
93 pub fn new(
95 authority: impl Into<String>,
96 stable_key: &str,
97 schema: SchemaMetadata,
98 ) -> Result<Self, StaticMemoryDeclarationError> {
99 let authority = authority.into();
100 validate_external_authority(&authority)?;
101 let stable_key =
102 crate::StableKey::parse(stable_key).map_err(crate::DeclarationSnapshotError::Key)?;
103 schema
104 .validate()
105 .map_err(crate::DeclarationSnapshotError::SchemaMetadata)?;
106 if is_ic_memory_stable_key(stable_key.as_str()) {
107 return Err(StaticMemoryDeclarationError::ReservedStableKey {
108 stable_key: stable_key.as_str().to_string(),
109 });
110 }
111 Ok(Self {
112 authority,
113 stable_key,
114 schema,
115 })
116 }
117
118 pub(crate) fn with_schema(
119 mut self,
120 schema: SchemaMetadata,
121 ) -> Result<Self, crate::DeclarationSnapshotError> {
122 schema
123 .validate()
124 .map_err(crate::DeclarationSnapshotError::SchemaMetadata)?;
125 self.schema = schema;
126 Ok(self)
127 }
128
129 #[must_use]
131 pub const fn stable_key(&self) -> &crate::StableKey {
132 &self.stable_key
133 }
134
135 #[must_use]
137 pub const fn schema(&self) -> &SchemaMetadata {
138 &self.schema
139 }
140
141 #[must_use]
143 pub fn authority(&self) -> &str {
144 &self.authority
145 }
146}
147
148pub fn register_memory_request(request: MemoryRequest) -> Result<(), StaticMemoryDeclarationError> {
150 with_unsealed_registry(|registry| registry.requests.push(request))
151}
152
153#[derive(Clone, Debug, Eq, PartialEq)]
162pub struct StaticMemoryRangeDeclaration {
163 record: MemoryManagerAuthorityRecord,
164}
165
166impl StaticMemoryRangeDeclaration {
167 pub fn new(record: MemoryManagerAuthorityRecord) -> Result<Self, StaticMemoryDeclarationError> {
169 validate_external_authority(record.authority())?;
170 record.validate()?;
171 Ok(Self { record })
172 }
173
174 #[must_use]
176 pub fn authority(&self) -> &str {
177 self.record.authority()
178 }
179
180 #[must_use]
182 pub const fn record(&self) -> &MemoryManagerAuthorityRecord {
183 &self.record
184 }
185
186 #[must_use]
188 pub fn into_record(self) -> MemoryManagerAuthorityRecord {
189 self.record
190 }
191}
192
193#[non_exhaustive]
198#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
199pub enum StaticMemoryDeclarationError {
200 #[error("at most 254 external declarations and ranges are supported")]
201 TooManyDeclarations,
202 #[error("duplicate requested stable key {stable_key}")]
203 DuplicateRequest { stable_key: crate::StableKey },
204 #[error("static memory declaration registry lock poisoned")]
206 RegistryPoisoned,
207 #[error("static memory declaration registry is already sealed")]
209 RegistrySealed,
210 #[error("static memory declaration snapshot sealing is already active on this thread")]
212 ReentrantSealing,
213 #[error("static memory declaration registry lifecycle is internally inconsistent")]
215 InconsistentLifecycle,
216 #[error("static memory declaration eager-init hook panicked")]
218 EagerInitPanicked,
219 #[error(transparent)]
221 Declaration(#[from] crate::DeclarationSnapshotError),
222 #[error(transparent)]
224 Range(#[from] MemoryManagerRangeAuthorityError),
225 #[error("failed to encode canonical sealed declaration fingerprint material: {message}")]
227 SnapshotFingerprintEncoding {
228 message: String,
230 },
231 #[error("authority {reason}")]
233 InvalidAuthority {
234 reason: &'static str,
236 },
237 #[error("authority '{authority}' is reserved for ic-memory runtime internals")]
239 ReservedAuthority {
240 authority: String,
242 },
243 #[error("stable key '{stable_key}' is reserved for ic-memory runtime internals")]
245 ReservedStableKey {
246 stable_key: String,
248 },
249}
250
251#[derive(Clone, Debug, Eq, PartialEq)]
264pub struct SealedDeclarationSnapshot {
265 inner: Arc<SealedDeclarationSnapshotInner>,
266}
267
268#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
281#[serde(deny_unknown_fields)]
282pub struct SealedDeclarationFingerprint {
283 algorithm_version: u8,
284 value: u64,
285}
286
287impl SealedDeclarationFingerprint {
288 #[must_use]
290 pub const fn algorithm_version(&self) -> u8 {
291 self.algorithm_version
292 }
293
294 #[must_use]
296 pub const fn value(&self) -> u64 {
297 self.value
298 }
299}
300
301#[derive(Debug, Eq, PartialEq)]
302struct SealedDeclarationSnapshotInner {
303 allocation_snapshot: DeclarationSnapshot,
304 requests: Vec<MemoryRequest>,
305 registered_declarations: Vec<StaticMemoryDeclaration>,
306 registered_ranges: Vec<StaticMemoryRangeDeclaration>,
307 range_authority: MemoryManagerRangeAuthority,
308 declaration_authority: BTreeMap<String, RuntimeDeclarationAuthority>,
309 fingerprint: SealedDeclarationFingerprint,
310}
311
312#[derive(Clone, Debug, Eq, PartialEq)]
313pub enum RuntimeDeclarationAuthority {
314 Internal,
315 External(String),
316}
317
318impl SealedDeclarationSnapshot {
319 pub fn new(
321 declarations: &[StaticMemoryDeclaration],
322 ranges: &[StaticMemoryRangeDeclaration],
323 requests: &[MemoryRequest],
324 ) -> Result<Self, StaticMemoryDeclarationError> {
325 build_snapshot(declarations, ranges, requests)
326 }
327
328 #[must_use]
330 pub fn requests(&self) -> &[MemoryRequest] {
331 &self.inner.requests
332 }
333
334 pub(crate) fn resolve(
335 &self,
336 ledger: &crate::AllocationLedger,
337 historical: Vec<MemoryRequest>,
338 ) -> Result<Self, crate::MemoryResolutionError> {
339 if self.requests().is_empty() && historical.is_empty() {
340 return Ok(self.clone());
341 }
342 if self.registered_declarations().len() + self.requests().len() + historical.len() > 254 {
343 return Err(StaticMemoryDeclarationError::TooManyDeclarations.into());
344 }
345 let mut declarations = self.registered_declarations().to_vec();
346 let mut occupied = [false; 255];
347 for record in ledger.allocation_history().records() {
348 occupied[usize::from(
349 record
350 .slot()
351 .memory_manager_id()
352 .expect("validated ledger slot"),
353 )] = true;
354 }
355 for fixed in &declarations {
356 occupied[usize::from(
357 fixed
358 .declaration()
359 .slot()
360 .memory_manager_id()
361 .expect("checked slot"),
362 )] = true;
363 }
364 for request in self.requests().iter().chain(&historical) {
369 let historical = ledger
370 .allocation_history()
371 .records()
372 .iter()
373 .find(|record| record.stable_key() == &request.stable_key);
374 let id = if let Some(record) = historical {
375 record
376 .slot()
377 .memory_manager_id()
378 .expect("validated ledger slot")
379 } else {
380 (0..255_u8)
381 .find(|id| {
382 !occupied[usize::from(*id)]
383 && self.range_authority().authorities().iter().any(|range| {
384 range.authority() == request.authority
385 && range.mode() == MemoryManagerRangeMode::Allowed
386 && range.range().contains(*id)
387 })
388 })
389 .ok_or_else(|| crate::MemoryResolutionError::Exhausted {
390 stable_key: request.stable_key.clone(),
391 authority: request.authority.clone(),
392 })?
393 };
394 self.range_authority()
396 .validate_slot_authority(
397 &crate::AllocationSlotDescriptor::memory_manager(id).expect("usable id"),
398 &request.authority,
399 )
400 .map_err(crate::MemoryResolutionError::Range)?;
401 occupied[usize::from(id)] = true;
402 declarations.push(StaticMemoryDeclaration::new(
403 request.authority.clone(),
404 AllocationDeclaration::memory_manager_unlabeled_with_schema(
405 request.stable_key.as_str(),
406 id,
407 request.schema.clone(),
408 )?,
409 )?);
410 }
411 Ok(build_snapshot(
412 &declarations,
413 self.registered_ranges(),
414 &[],
415 )?)
416 }
417
418 #[must_use]
422 pub fn allocation_snapshot(&self) -> &DeclarationSnapshot {
423 &self.inner.allocation_snapshot
424 }
425
426 #[must_use]
428 pub fn registered_declarations(&self) -> &[StaticMemoryDeclaration] {
429 &self.inner.registered_declarations
430 }
431
432 #[must_use]
434 pub fn registered_ranges(&self) -> &[StaticMemoryRangeDeclaration] {
435 &self.inner.registered_ranges
436 }
437
438 #[must_use]
440 pub fn range_authority(&self) -> &MemoryManagerRangeAuthority {
441 &self.inner.range_authority
442 }
443
444 #[must_use]
446 pub fn fingerprint(&self) -> SealedDeclarationFingerprint {
447 self.inner.fingerprint
448 }
449
450 pub(crate) fn declaration_authority(&self) -> &BTreeMap<String, RuntimeDeclarationAuthority> {
451 &self.inner.declaration_authority
452 }
453
454 pub(crate) fn user_ranges_registered(&self) -> bool {
455 !self.inner.registered_ranges.is_empty()
456 }
457
458 pub(crate) fn shares_storage_with(&self, other: &Self) -> bool {
459 Arc::ptr_eq(&self.inner, &other.inner)
460 }
461}
462
463type StaticRegistrationHook = fn() -> Result<(), StaticMemoryDeclarationError>;
464
465#[derive(Debug)]
466struct StaticMemoryDeclarationRegistry {
467 declarations: Vec<StaticMemoryDeclaration>,
468 requests: Vec<MemoryRequest>,
469 ranges: Vec<StaticMemoryRangeDeclaration>,
470 registration_hooks: Vec<StaticRegistrationHook>,
471 eager_init_hooks: Vec<fn()>,
472 lifecycle: StaticRegistryLifecycle,
473}
474
475#[derive(Debug)]
476enum StaticRegistryLifecycle {
477 Open,
478 Sealing {
479 owner: ThreadId,
480 deferred_error: Option<StaticMemoryDeclarationError>,
481 },
482 Sealed(SealedDeclarationSnapshot),
483 Failed(StaticMemoryDeclarationError),
484}
485
486static STATIC_MEMORY_DECLARATIONS: Mutex<StaticMemoryDeclarationRegistry> =
487 Mutex::new(StaticMemoryDeclarationRegistry {
488 declarations: Vec::new(),
489 requests: Vec::new(),
490 ranges: Vec::new(),
491 registration_hooks: Vec::new(),
492 eager_init_hooks: Vec::new(),
493 lifecycle: StaticRegistryLifecycle::Open,
494 });
495
496static STATIC_MEMORY_SEAL: Mutex<()> = Mutex::new(());
497
498fn lock_registry()
499-> Result<MutexGuard<'static, StaticMemoryDeclarationRegistry>, StaticMemoryDeclarationError> {
500 STATIC_MEMORY_DECLARATIONS
501 .lock()
502 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)
503}
504
505fn ensure_registration_open(
506 registry: &StaticMemoryDeclarationRegistry,
507) -> Result<(), StaticMemoryDeclarationError> {
508 match ®istry.lifecycle {
509 StaticRegistryLifecycle::Open => Ok(()),
510 StaticRegistryLifecycle::Sealing { owner, .. } if *owner == std::thread::current().id() => {
511 Ok(())
512 }
513 StaticRegistryLifecycle::Sealing { .. }
514 | StaticRegistryLifecycle::Sealed(_)
515 | StaticRegistryLifecycle::Failed(_) => Err(StaticMemoryDeclarationError::RegistrySealed),
516 }
517}
518
519fn with_unsealed_registry(
520 op: impl FnOnce(&mut StaticMemoryDeclarationRegistry),
521) -> Result<(), StaticMemoryDeclarationError> {
522 let mut registry = lock_registry()?;
523 ensure_registration_open(®istry)?;
524 op(&mut registry);
525 Ok(())
526}
527
528#[doc(hidden)]
533pub fn defer_static_memory_registration(hook: StaticRegistrationHook) {
534 defer_constructor_registration(|registry| {
535 registry.registration_hooks.push(hook);
536 });
537}
538
539#[doc(hidden)]
544pub fn defer_eager_init(hook: fn()) {
545 defer_constructor_registration(|registry| {
546 registry.eager_init_hooks.push(hook);
547 });
548}
549
550fn defer_constructor_registration(op: impl FnOnce(&mut StaticMemoryDeclarationRegistry)) {
551 let Ok(mut registry) = STATIC_MEMORY_DECLARATIONS.lock() else {
552 return;
555 };
556 if matches!(registry.lifecycle, StaticRegistryLifecycle::Open) {
557 op(&mut registry);
558 return;
559 }
560 match &mut registry.lifecycle {
561 StaticRegistryLifecycle::Sealing { deferred_error, .. } => {
562 if deferred_error.is_none() {
563 *deferred_error = Some(StaticMemoryDeclarationError::RegistrySealed);
564 }
565 }
566 StaticRegistryLifecycle::Sealed(_) => {
567 registry.lifecycle =
568 StaticRegistryLifecycle::Failed(StaticMemoryDeclarationError::RegistrySealed);
569 }
570 StaticRegistryLifecycle::Failed(_) | StaticRegistryLifecycle::Open => {}
571 }
572}
573
574pub fn register_static_memory_declaration(
576 authority: impl Into<String>,
577 declaration: AllocationDeclaration,
578) -> Result<(), StaticMemoryDeclarationError> {
579 let registration = StaticMemoryDeclaration::new(authority, declaration)?;
580 with_unsealed_registry(|registry| {
581 registry.declarations.push(registration);
582 })
583}
584
585pub fn register_static_memory_manager_range(
587 start: u8,
588 end: u8,
589 authority: impl Into<String>,
590 mode: MemoryManagerRangeMode,
591 purpose: Option<String>,
592) -> Result<(), StaticMemoryDeclarationError> {
593 let authority = authority.into();
594 let record = MemoryManagerAuthorityRecord::new(
595 MemoryManagerIdRange::new(start, end).map_err(MemoryManagerRangeAuthorityError::Range)?,
596 authority,
597 mode,
598 purpose,
599 )?;
600 register_static_memory_range_declaration(StaticMemoryRangeDeclaration::new(record)?)
601}
602
603pub fn register_static_memory_range_declaration(
605 declaration: StaticMemoryRangeDeclaration,
606) -> Result<(), StaticMemoryDeclarationError> {
607 validate_external_authority(declaration.authority())?;
608 with_unsealed_registry(|registry| {
609 registry.ranges.push(declaration);
610 })
611}
612
613fn validate_external_authority(value: &str) -> Result<(), StaticMemoryDeclarationError> {
614 if value == IC_MEMORY_AUTHORITY_OWNER {
615 return Err(StaticMemoryDeclarationError::ReservedAuthority {
616 authority: value.to_string(),
617 });
618 }
619 if value.is_empty() {
620 return Err(StaticMemoryDeclarationError::InvalidAuthority {
621 reason: "must not be empty",
622 });
623 }
624 if value.len() > DIAGNOSTIC_STRING_MAX_BYTES {
625 return Err(StaticMemoryDeclarationError::InvalidAuthority {
626 reason: "must be at most 256 bytes",
627 });
628 }
629 if !value.is_ascii() {
630 return Err(StaticMemoryDeclarationError::InvalidAuthority {
631 reason: "must be ASCII",
632 });
633 }
634 if value.bytes().any(|byte| byte.is_ascii_control()) {
635 return Err(StaticMemoryDeclarationError::InvalidAuthority {
636 reason: "must not contain ASCII control characters",
637 });
638 }
639 Ok(())
640}
641
642pub fn register_static_memory_manager_declaration(
644 id: u8,
645 authority: impl Into<String>,
646 label: impl Into<String>,
647 stable_key: impl AsRef<str>,
648) -> Result<(), StaticMemoryDeclarationError> {
649 register_static_memory_manager_declaration_with_schema(
650 id,
651 authority,
652 label,
653 stable_key,
654 SchemaMetadata::default(),
655 )
656}
657
658pub fn register_static_memory_manager_declaration_with_schema(
660 id: u8,
661 authority: impl Into<String>,
662 label: impl Into<String>,
663 stable_key: impl AsRef<str>,
664 schema: SchemaMetadata,
665) -> Result<(), StaticMemoryDeclarationError> {
666 let declaration =
667 AllocationDeclaration::memory_manager_with_schema(stable_key, id, label, schema)?;
668 register_static_memory_declaration(authority, declaration)
669}
670
671pub fn sealed_declaration_snapshot()
678-> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
679 {
680 let registry = lock_registry()?;
681 match ®istry.lifecycle {
682 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
683 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
684 StaticRegistryLifecycle::Sealing { owner, .. }
685 if *owner == std::thread::current().id() =>
686 {
687 return Err(StaticMemoryDeclarationError::ReentrantSealing);
688 }
689 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealing { .. } => {}
690 }
691 }
692
693 let _seal = STATIC_MEMORY_SEAL
694 .lock()
695 .map_err(|_| StaticMemoryDeclarationError::RegistryPoisoned)?;
696 let (registration_hooks, eager_init_hooks) = {
697 let mut registry = lock_registry()?;
698 match ®istry.lifecycle {
699 StaticRegistryLifecycle::Sealed(snapshot) => return Ok(snapshot.clone()),
700 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
701 StaticRegistryLifecycle::Sealing { .. } => {
702 return Err(StaticMemoryDeclarationError::ReentrantSealing);
703 }
704 StaticRegistryLifecycle::Open => {}
705 }
706 registry.lifecycle = StaticRegistryLifecycle::Sealing {
707 owner: std::thread::current().id(),
708 deferred_error: None,
709 };
710 (
711 std::mem::take(&mut registry.registration_hooks),
712 std::mem::take(&mut registry.eager_init_hooks),
713 )
714 };
715
716 for hook in registration_hooks {
717 let result = catch_unwind(AssertUnwindSafe(hook))
718 .map_err(|_| StaticMemoryDeclarationError::EagerInitPanicked)
719 .and_then(std::convert::identity);
720 if let Err(err) = result {
721 return fail_sealing(err);
722 }
723 }
724 for hook in eager_init_hooks {
725 if catch_unwind(AssertUnwindSafe(hook)).is_err() {
726 return fail_sealing(StaticMemoryDeclarationError::EagerInitPanicked);
727 }
728 }
729
730 let mut registry = lock_registry()?;
731 let deferred_error = match ®istry.lifecycle {
732 StaticRegistryLifecycle::Sealing { deferred_error, .. } => deferred_error.clone(),
733 StaticRegistryLifecycle::Failed(err) => return Err(err.clone()),
734 StaticRegistryLifecycle::Open | StaticRegistryLifecycle::Sealed(_) => {
735 return Err(StaticMemoryDeclarationError::InconsistentLifecycle);
736 }
737 };
738 if let Some(err) = deferred_error {
739 registry.lifecycle = StaticRegistryLifecycle::Failed(err.clone());
740 return Err(err);
741 }
742 let snapshot =
743 match build_snapshot(®istry.declarations, ®istry.ranges, ®istry.requests) {
744 Ok(snapshot) => snapshot,
745 Err(err) => {
746 registry.lifecycle = StaticRegistryLifecycle::Failed(err.clone());
747 return Err(err);
748 }
749 };
750 registry.lifecycle = StaticRegistryLifecycle::Sealed(snapshot.clone());
751 Ok(snapshot)
752}
753
754fn fail_sealing<T>(err: StaticMemoryDeclarationError) -> Result<T, StaticMemoryDeclarationError> {
755 let mut registry = lock_registry()?;
756 let failure = match ®istry.lifecycle {
757 StaticRegistryLifecycle::Sealing {
758 deferred_error: Some(deferred_error),
759 ..
760 } => deferred_error.clone(),
761 StaticRegistryLifecycle::Open
762 | StaticRegistryLifecycle::Sealing {
763 deferred_error: None,
764 ..
765 }
766 | StaticRegistryLifecycle::Sealed(_) => err,
767 StaticRegistryLifecycle::Failed(failure) => failure.clone(),
768 };
769 registry.lifecycle = StaticRegistryLifecycle::Failed(failure.clone());
770 Err(failure)
771}
772
773fn build_snapshot(
774 declarations: &[StaticMemoryDeclaration],
775 ranges: &[StaticMemoryRangeDeclaration],
776 requests: &[MemoryRequest],
777) -> Result<SealedDeclarationSnapshot, StaticMemoryDeclarationError> {
778 if declarations.len().saturating_add(requests.len()) > 254 || ranges.len() > 254 {
779 return Err(StaticMemoryDeclarationError::TooManyDeclarations);
780 }
781 let mut requests = requests.to_vec();
782 requests.sort_unstable_by(|a, b| a.stable_key.cmp(&b.stable_key));
784 let mut keys = std::collections::BTreeSet::new();
785 keys.extend(declarations.iter().map(|d| d.declaration().stable_key()));
786 for key in requests.iter().map(|r| &r.stable_key) {
787 if !keys.insert(key) {
788 return Err(StaticMemoryDeclarationError::DuplicateRequest {
789 stable_key: key.clone(),
790 });
791 }
792 }
793 let mut registered_declarations = declarations.to_vec();
794 registered_declarations.sort_by(|left, right| {
795 left.declaration()
796 .stable_key()
797 .cmp(right.declaration().stable_key())
798 .then_with(|| left.declaration().slot().cmp(right.declaration().slot()))
799 .then_with(|| left.authority().cmp(right.authority()))
800 });
801
802 let mut registered_ranges = ranges.to_vec();
803 registered_ranges.sort_by(|left, right| {
804 let left = left.record();
805 let right = right.record();
806 left.range()
807 .start()
808 .cmp(&right.range().start())
809 .then_with(|| left.range().end().cmp(&right.range().end()))
810 .then_with(|| left.authority().cmp(right.authority()))
811 .then_with(|| range_mode_order(left.mode()).cmp(&range_mode_order(right.mode())))
812 .then_with(|| left.purpose().cmp(&right.purpose()))
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 let mut declaration_authority = BTreeMap::new();
840 declaration_authority.insert(
841 IC_MEMORY_LEDGER_STABLE_KEY.to_string(),
842 RuntimeDeclarationAuthority::Internal,
843 );
844 for registration in ®istered_declarations {
845 declaration_authority.insert(
846 registration.declaration().stable_key().as_str().to_string(),
847 RuntimeDeclarationAuthority::External(registration.authority().to_string()),
848 );
849 }
850
851 Ok(SealedDeclarationSnapshot {
852 inner: Arc::new(SealedDeclarationSnapshotInner {
853 allocation_snapshot,
854 requests,
855 registered_declarations,
856 registered_ranges,
857 range_authority,
858 declaration_authority,
859 fingerprint,
860 }),
861 })
862}
863
864#[derive(Serialize)]
865struct FingerprintDeclaration<'a> {
866 authority: &'a str,
867 declaration: &'a AllocationDeclaration,
868}
869
870#[derive(Serialize)]
871struct SealedDeclarationFingerprintMaterial<'a> {
872 format: &'static str,
873 allocation_snapshot: &'a DeclarationSnapshot,
874 registered_declarations: Vec<FingerprintDeclaration<'a>>,
875 effective_ranges: &'a [MemoryManagerAuthorityRecord],
876 requests: &'a [MemoryRequest],
877}
878
879fn sealed_declaration_fingerprint(
880 allocation_snapshot: &DeclarationSnapshot,
881 registered_declarations: &[StaticMemoryDeclaration],
882 effective_ranges: &[MemoryManagerAuthorityRecord],
883 requests: &[MemoryRequest],
884) -> Result<SealedDeclarationFingerprint, StaticMemoryDeclarationError> {
885 let material = SealedDeclarationFingerprintMaterial {
886 format: "ic-memory.sealed-declaration-fingerprint.v1",
887 allocation_snapshot,
888 registered_declarations: registered_declarations
889 .iter()
890 .map(|registration| FingerprintDeclaration {
891 authority: registration.authority(),
892 declaration: registration.declaration(),
893 })
894 .collect(),
895 effective_ranges,
896 requests,
897 };
898 let mut bytes = Vec::new();
899 ciborium::into_writer(&material, &mut bytes).map_err(|err| {
900 StaticMemoryDeclarationError::SnapshotFingerprintEncoding {
901 message: err.to_string(),
902 }
903 })?;
904
905 let value = bytes
906 .into_iter()
907 .fold(FINGERPRINT_FNV_OFFSET, fingerprint_hash_byte);
908 Ok(SealedDeclarationFingerprint {
909 algorithm_version: SEALED_DECLARATION_FINGERPRINT_VERSION,
910 value,
911 })
912}
913
914const SEALED_DECLARATION_FINGERPRINT_VERSION: u8 = 1;
915const FINGERPRINT_FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
916const FINGERPRINT_FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
917
918const fn fingerprint_hash_byte(hash: u64, byte: u8) -> u64 {
919 (hash ^ byte as u64).wrapping_mul(FINGERPRINT_FNV_PRIME)
920}
921
922const fn range_mode_order(mode: MemoryManagerRangeMode) -> u8 {
923 match mode {
924 MemoryManagerRangeMode::Reserved => 0,
925 MemoryManagerRangeMode::Allowed => 1,
926 }
927}
928
929fn internal_ledger_declaration() -> Result<AllocationDeclaration, crate::DeclarationSnapshotError> {
930 AllocationDeclaration::memory_manager(
931 IC_MEMORY_LEDGER_STABLE_KEY,
932 MEMORY_MANAGER_LEDGER_ID,
933 IC_MEMORY_LEDGER_LABEL,
934 )
935}
936
937fn internal_ledger_range() -> Result<MemoryManagerAuthorityRecord, MemoryManagerRangeAuthorityError>
938{
939 MemoryManagerAuthorityRecord::new(
940 MemoryManagerIdRange::new(MEMORY_MANAGER_LEDGER_ID, MEMORY_MANAGER_GOVERNANCE_MAX_ID)?,
941 IC_MEMORY_AUTHORITY_OWNER,
942 MemoryManagerRangeMode::Reserved,
943 Some(IC_MEMORY_AUTHORITY_PURPOSE.to_string()),
944 )
945}
946
947#[cfg(test)]
948pub fn reset_static_memory_declarations_for_tests() {
949 let mut registry = STATIC_MEMORY_DECLARATIONS
950 .lock()
951 .expect("static memory declaration registry poisoned");
952 registry.declarations.clear();
953 registry.requests.clear();
954 registry.ranges.clear();
955 registry.registration_hooks.clear();
956 registry.eager_init_hooks.clear();
957 registry.lifecycle = StaticRegistryLifecycle::Open;
958}
959
960#[cfg(test)]
961mod tests;