Skip to main content

ic_memory/runtime/
adoption.rs

1use super::{MemoryRuntime, RuntimeLifecycle, RuntimeOpenError};
2use crate::{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}
32
33impl<M: Memory> MemoryRuntime<M> {
34    /// Verify every logical request for one authority in
35    /// the supplied requirements against this runtime's current commitment.
36    ///
37    /// Requests must match key and authority while
38    /// retaining the host's assigned ID. Other authorities and additional
39    /// committed keys are ignored. An authority with no requirements rejects.
40    /// Grants and application schema semantics are not revalidated. Success
41    /// neither grants new access nor proves application lifecycle readiness.
42    pub fn verify_authority(
43        &self,
44        requirements: &SealedDeclarationSnapshot,
45        authority: &str,
46    ) -> Result<(), RuntimeAdoptionError> {
47        let RuntimeLifecycle::Bootstrapped {
48            binding,
49            allocations,
50            ..
51        } = &self.lifecycle
52        else {
53            return Err(RuntimeOpenError::NotBootstrapped.into());
54        };
55        let mut found = false;
56        for request in requirements.requests() {
57            if request.authority() == authority {
58                found = true;
59                verify_requirement(allocations, &binding.pool, authority, request.stable_key())?;
60            }
61        }
62        if !found {
63            return Err(RuntimeAdoptionError::UnknownAuthority {
64                authority: authority.to_string(),
65            });
66        }
67        Ok(())
68    }
69}
70
71fn verify_requirement(
72    committed: &[crate::AllocationDeclaration],
73    pool: &crate::MemoryAllocationPool,
74    authority: &str,
75    key: &StableKey,
76) -> Result<(), RuntimeAdoptionError> {
77    crate::declaration::declaration_for_key(committed, key.as_str())
78        .ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(key.to_string()))?;
79    let admitted_authority = pool
80        .authority_for_key(key)
81        .expect("committed key has an admitted owner");
82    if admitted_authority != authority {
83        return Err(RuntimeAdoptionError::AuthorityMismatch {
84            stable_key: key.to_string(),
85            committed_authority: admitted_authority.to_string(),
86            requested_authority: authority.to_string(),
87        });
88    }
89    Ok(())
90}