use super::{MemoryRuntime, RuntimeLifecycle, RuntimeOpenError};
use crate::{AllocationDeclaration, SchemaMetadata, SealedDeclarationSnapshot, StableKey};
use ic_stable_structures::Memory;
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum RuntimeAdoptionError {
#[error(transparent)]
Open(#[from] RuntimeOpenError),
#[error("no allocation requirements declared by authority '{authority}'")]
UnknownAuthority { authority: String },
#[error(
"stable key '{stable_key}' is committed under '{committed_authority}', not '{requested_authority}'"
)]
AuthorityMismatch {
stable_key: String,
committed_authority: String,
requested_authority: String,
},
#[error("declaration metadata for stable key '{stable_key}' differs from the commitment")]
DeclarationMetadataMismatch { stable_key: String },
}
impl From<super::RuntimeStateError> for RuntimeAdoptionError {
fn from(error: super::RuntimeStateError) -> Self {
Self::Open(RuntimeOpenError::State(error))
}
}
impl<M: Memory> MemoryRuntime<M> {
pub fn verify_authority(
&self,
requirements: &SealedDeclarationSnapshot,
authority: &str,
) -> Result<(), RuntimeAdoptionError> {
let RuntimeLifecycle::Bootstrapped { binding, .. } = &self.lifecycle else {
return Err(RuntimeOpenError::NotBootstrapped.into());
};
let committed = &binding.declarations;
let mut found = false;
for registration in requirements.registered_declarations() {
if registration.authority() == authority {
found = true;
let expected = registration.declaration();
verify_requirement(
committed,
authority,
expected.stable_key(),
expected.schema(),
Some(expected),
)?;
}
}
for request in requirements.requests() {
if request.authority() == authority {
found = true;
verify_requirement(
committed,
authority,
request.stable_key(),
request.schema(),
None,
)?;
}
}
if !found {
return Err(RuntimeAdoptionError::UnknownAuthority {
authority: authority.to_string(),
});
}
Ok(())
}
}
fn verify_requirement(
committed: &SealedDeclarationSnapshot,
authority: &str,
key: &StableKey,
schema: &SchemaMetadata,
fixed: Option<&AllocationDeclaration>,
) -> Result<(), RuntimeAdoptionError> {
let registration = committed
.registered_declaration(key)
.ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(key.to_string()))?;
if registration.authority() != authority {
return Err(RuntimeAdoptionError::AuthorityMismatch {
stable_key: key.to_string(),
committed_authority: registration.authority().to_string(),
requested_authority: authority.to_string(),
});
}
let committed = registration.declaration();
if let Some(expected) = fixed
&& expected.slot() != committed.slot()
{
return Err(RuntimeOpenError::MemoryIdMismatch {
stable_key: key.to_string(),
committed_id: committed.slot().id(),
requested_id: expected.slot().id(),
}
.into());
}
if schema != committed.schema()
|| fixed.is_some_and(|expected| expected.label() != committed.label())
{
return Err(RuntimeAdoptionError::DeclarationMetadataMismatch {
stable_key: key.to_string(),
});
}
Ok(())
}