Skip to main content

ic_memory/runtime/
adoption.rs

1use super::{MemoryRuntime, RuntimeLifecycle, RuntimeOpenError};
2use crate::{SchemaMetadata, SealedDeclarationSnapshot, StableKey};
3use ic_stable_structures::Memory;
4
5///
6/// RuntimeAdoptionError
7///
8/// A consumer's declared requirements do not match the existing committed
9/// runtime. Verification never bootstraps, grants authority, opens memory,
10/// replays admission, or changes the host's policy or bucket configuration.
11///
12
13#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
14#[non_exhaustive]
15pub enum RuntimeAdoptionError {
16    /// Runtime readiness, key resolution or requirement verification failed.
17    #[error(transparent)]
18    Open(#[from] RuntimeOpenError),
19    /// The supplied snapshot contains no requests for this authority.
20    #[error("no allocation requirements declared by authority '{authority}'")]
21    UnknownAuthority { authority: String },
22    /// This key was committed under a different current declaration authority.
23    #[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    /// Declared diagnostic schema differs from the commitment.
32    #[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    /// Verify every logical request for one authority in
44    /// the supplied requirements against this runtime's current commitment.
45    ///
46    /// Requests must match key, authority and diagnostic schema while
47    /// retaining the host's assigned ID. Other authorities and additional
48    /// committed keys are ignored. An authority with no requirements rejects.
49    /// Grants and application schema semantics are not revalidated. Success
50    /// neither grants new access nor proves application lifecycle readiness.
51    pub fn verify_authority(
52        &self,
53        requirements: &SealedDeclarationSnapshot,
54        authority: &str,
55    ) -> Result<(), RuntimeAdoptionError> {
56        let RuntimeLifecycle::Bootstrapped {
57            binding,
58            committed_allocations,
59        } = &self.lifecycle
60        else {
61            return Err(RuntimeOpenError::NotBootstrapped.into());
62        };
63        let mut found = false;
64        for request in requirements.requests() {
65            if request.authority() == authority {
66                found = true;
67                verify_requirement(
68                    committed_allocations,
69                    &binding.pool,
70                    authority,
71                    request.stable_key(),
72                    request.schema(),
73                )?;
74            }
75        }
76        if !found {
77            return Err(RuntimeAdoptionError::UnknownAuthority {
78                authority: authority.to_string(),
79            });
80        }
81        Ok(())
82    }
83}
84
85fn verify_requirement(
86    committed: &crate::CommittedAllocations,
87    pool: &crate::MemoryAllocationPool,
88    authority: &str,
89    key: &StableKey,
90    schema: &SchemaMetadata,
91) -> Result<(), RuntimeAdoptionError> {
92    let declaration =
93        crate::capability::declaration_for_key(committed.declarations(), key.as_str())
94            .ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(key.to_string()))?;
95    let admitted_authority = pool
96        .authority_for_key(key)
97        .expect("committed key has an admitted owner");
98    if admitted_authority != authority {
99        return Err(RuntimeAdoptionError::AuthorityMismatch {
100            stable_key: key.to_string(),
101            committed_authority: admitted_authority.to_string(),
102            requested_authority: authority.to_string(),
103        });
104    }
105    if schema != declaration.schema() {
106        return Err(RuntimeAdoptionError::DeclarationMetadataMismatch {
107            stable_key: key.to_string(),
108        });
109    }
110    Ok(())
111}