ic_memory/runtime/
adoption.rs1use super::{MemoryRuntime, RuntimeLifecycle, RuntimeOpenError};
2use crate::{AllocationDeclaration, SchemaMetadata, SealedDeclarationSnapshot, StableKey};
3use ic_stable_structures::Memory;
4
5#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
14#[non_exhaustive]
15pub enum RuntimeAdoptionError {
16 #[error(transparent)]
18 Open(#[from] RuntimeOpenError),
19 #[error("no allocation requirements declared by authority '{authority}'")]
21 UnknownAuthority { authority: String },
22 #[error(
24 "stable key '{stable_key}' is committed under '{committed_authority}', not '{requested_authority}'"
25 )]
26 AuthorityMismatch {
27 stable_key: String,
28 committed_authority: String,
29 requested_authority: String,
30 },
31 #[error("declaration metadata for stable key '{stable_key}' differs from the commitment")]
33 DeclarationMetadataMismatch { stable_key: String },
34}
35
36impl From<super::RuntimeStateError> for RuntimeAdoptionError {
37 fn from(error: super::RuntimeStateError) -> Self {
38 Self::Open(RuntimeOpenError::State(error))
39 }
40}
41
42impl<M: Memory> MemoryRuntime<M> {
43 pub fn verify_authority(
53 &self,
54 requirements: &SealedDeclarationSnapshot,
55 authority: &str,
56 ) -> Result<(), RuntimeAdoptionError> {
57 self.committed_allocations()?;
58 let mut found = false;
59 for registration in requirements.registered_declarations() {
60 if registration.authority() == authority {
61 found = true;
62 let expected = registration.declaration();
63 self.verify_requirement(
64 authority,
65 expected.stable_key(),
66 expected.schema(),
67 Some(expected),
68 )?;
69 }
70 }
71 for request in requirements.requests() {
72 if request.authority() == authority {
73 found = true;
74 self.verify_requirement(authority, request.stable_key(), request.schema(), None)?;
75 }
76 }
77 if !found {
78 return Err(RuntimeAdoptionError::UnknownAuthority {
79 authority: authority.to_string(),
80 });
81 }
82 Ok(())
83 }
84
85 fn verify_requirement(
86 &self,
87 authority: &str,
88 key: &StableKey,
89 schema: &SchemaMetadata,
90 fixed: Option<&AllocationDeclaration>,
91 ) -> Result<(), RuntimeAdoptionError> {
92 let RuntimeLifecycle::Bootstrapped { binding, .. } = &self.lifecycle else {
93 return Err(RuntimeOpenError::NotBootstrapped.into());
94 };
95 let registration = binding
96 .declarations
97 .registered_declaration(key)
98 .ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(key.to_string()))?;
99 if registration.authority() != authority {
100 return Err(RuntimeAdoptionError::AuthorityMismatch {
101 stable_key: key.to_string(),
102 committed_authority: registration.authority().to_string(),
103 requested_authority: authority.to_string(),
104 });
105 }
106 let committed = registration.declaration();
107 if let Some(expected) = fixed
108 && expected.slot() != committed.slot()
109 {
110 return Err(RuntimeOpenError::MemoryIdMismatch {
111 stable_key: key.to_string(),
112 committed_id: committed
113 .slot()
114 .memory_manager_id()
115 .map_err(RuntimeOpenError::from)?,
116 requested_id: expected
117 .slot()
118 .memory_manager_id()
119 .map_err(RuntimeOpenError::from)?,
120 }
121 .into());
122 }
123 if schema != committed.schema()
124 || fixed.is_some_and(|expected| expected.label() != committed.label())
125 {
126 return Err(RuntimeAdoptionError::DeclarationMetadataMismatch {
127 stable_key: key.to_string(),
128 });
129 }
130 Ok(())
131 }
132}