use crate::{
AllocationDeclaration, AllocationLedger, AllocationRecord, AllocationState,
SealedDeclarationSnapshot, StableKey,
};
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum BootstrapAdmissionError {
#[error("historical key {0} is unknown")]
Unknown(StableKey),
#[error("historical key {0} is retired")]
Retired(StableKey),
#[error("key {0} is already declared or selected")]
Duplicate(StableKey),
#[error("completed declarations exceed 254 external allocations")]
TooManyDeclarations,
#[error(transparent)]
Pool(#[from] crate::MemoryAllocationPoolError),
#[error(transparent)]
Declarations(#[from] crate::MemoryDeclarationError),
}
pub struct BootstrapAdmission<'a> {
ledger: &'a AllocationLedger,
declarations: &'a SealedDeclarationSnapshot,
pool: &'a crate::MemoryAllocationPool,
selected: Vec<AllocationDeclaration>,
failure: Option<BootstrapAdmissionError>,
}
impl<'a> BootstrapAdmission<'a> {
pub(super) const fn new(
ledger: &'a AllocationLedger,
declarations: &'a SealedDeclarationSnapshot,
pool: &'a crate::MemoryAllocationPool,
) -> Self {
Self {
ledger,
declarations,
pool,
selected: Vec::new(),
failure: None,
}
}
#[must_use]
pub const fn declarations(&self) -> &SealedDeclarationSnapshot {
self.declarations
}
#[must_use = "recovered allocation metadata is inspected only when the iterator is consumed"]
pub fn recovered_allocations(&self) -> impl ExactSizeIterator<Item = &AllocationRecord> {
self.ledger.records().iter()
}
#[must_use]
pub fn is_declared(&self, key: &StableKey) -> bool {
key.as_str() == crate::IC_MEMORY_LEDGER_STABLE_KEY
|| self
.declarations
.requests()
.binary_search_by(|request| request.stable_key().cmp(key))
.is_ok()
|| self
.selected
.iter()
.any(|declaration| declaration.stable_key() == key)
}
pub fn include_historical(
&mut self,
authority: &str,
stable_key: &str,
) -> Result<(), BootstrapAdmissionError> {
if let Some(error) = &self.failure {
return Err(error.clone());
}
let result = self.select(authority, stable_key);
if let Err(error) = &result {
self.failure = Some(error.clone());
}
result
}
fn select(&mut self, authority: &str, stable_key: &str) -> Result<(), BootstrapAdmissionError> {
let key = crate::requests::checked_request_key(authority, stable_key)?;
if self.is_declared(&key) {
return Err(BootstrapAdmissionError::Duplicate(key));
}
if self.declarations.requests().len() + self.selected.len() >= 254 {
return Err(BootstrapAdmissionError::TooManyDeclarations);
}
let Some(record) = self
.ledger
.records()
.iter()
.find(|r| r.stable_key() == &key)
else {
return Err(BootstrapAdmissionError::Unknown(key));
};
if matches!(record.state(), AllocationState::Retired) {
return Err(BootstrapAdmissionError::Retired(key));
}
self.pool.validate_authority(&key, authority)?;
self.pool.validate_id(record.slot().id())?;
self.selected.push(AllocationDeclaration {
stable_key: key,
slot: record.slot().clone(),
schema: record.schema().clone(),
});
Ok(())
}
pub(super) fn complete(self) -> Result<Vec<AllocationDeclaration>, BootstrapAdmissionError> {
if let Some(error) = self.failure {
return Err(error);
}
Ok(self.selected)
}
}