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