use super::{MemoryRuntime, RuntimeLifecycle, RuntimeOpenError};
use crate::{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,
committed_allocations,
} = &self.lifecycle
else {
return Err(RuntimeOpenError::NotBootstrapped.into());
};
let mut found = false;
for request in requirements.requests() {
if request.authority() == authority {
found = true;
verify_requirement(
committed_allocations,
&binding.pool,
authority,
request.stable_key(),
request.schema(),
)?;
}
}
if !found {
return Err(RuntimeAdoptionError::UnknownAuthority {
authority: authority.to_string(),
});
}
Ok(())
}
}
fn verify_requirement(
committed: &crate::CommittedAllocations,
pool: &crate::MemoryAllocationPool,
authority: &str,
key: &StableKey,
schema: &SchemaMetadata,
) -> Result<(), RuntimeAdoptionError> {
let declaration =
crate::capability::declaration_for_key(committed.declarations(), key.as_str())
.ok_or_else(|| RuntimeOpenError::StableKeyNotCommitted(key.to_string()))?;
let admitted_authority = pool
.authority_for_key(key)
.expect("committed key has an admitted owner");
if admitted_authority != authority {
return Err(RuntimeAdoptionError::AuthorityMismatch {
stable_key: key.to_string(),
committed_authority: admitted_authority.to_string(),
requested_authority: authority.to_string(),
});
}
if schema != declaration.schema() {
return Err(RuntimeAdoptionError::DeclarationMetadataMismatch {
stable_key: key.to_string(),
});
}
Ok(())
}