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