ic_memory/runtime/
admission.rs1use crate::{
2 AllocationLedger, AllocationState, MemoryManagerSlot, MemoryRequest, SchemaMetadata,
3 SealedDeclarationSnapshot, StableKey,
4};
5
6#[derive(Clone, Copy, Debug)]
15pub struct RecoveredAllocationMetadata<'a> {
16 pub stable_key: &'a StableKey,
18 pub slot: &'a MemoryManagerSlot,
20 pub state: AllocationState,
22 pub schema: &'a SchemaMetadata,
24}
25
26#[non_exhaustive]
33#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
34pub enum BootstrapAdmissionError {
35 #[error("historical key {0} is unknown")]
36 Unknown(StableKey),
37 #[error("historical key {0} is retired")]
38 Retired(StableKey),
39 #[error("key {0} is already declared or selected")]
40 Duplicate(StableKey),
41 #[error("completed declarations exceed 254 external allocations")]
42 TooManyDeclarations,
43 #[error("historical selection {stable_key} by {authority} lacks a current grant: {source}")]
44 Range {
45 stable_key: StableKey,
46 authority: String,
47 source: crate::MemoryManagerRangeAuthorityError,
48 },
49 #[error(transparent)]
50 Registry(#[from] crate::StaticMemoryDeclarationError),
51}
52
53pub struct BootstrapAdmission<'a> {
64 ledger: &'a AllocationLedger,
65 declarations: &'a SealedDeclarationSnapshot,
66 selected: Vec<MemoryRequest>,
67 failure: Option<BootstrapAdmissionError>,
68}
69
70impl<'a> BootstrapAdmission<'a> {
71 pub(super) const fn new(
72 ledger: &'a AllocationLedger,
73 declarations: &'a SealedDeclarationSnapshot,
74 ) -> Self {
75 Self {
76 ledger,
77 declarations,
78 selected: Vec::new(),
79 failure: None,
80 }
81 }
82
83 #[must_use]
85 pub const fn declarations(&self) -> &SealedDeclarationSnapshot {
86 self.declarations
87 }
88
89 #[must_use = "recovered allocation metadata is inspected only when the iterator is consumed"]
95 pub fn recovered_allocations(
96 &self,
97 ) -> impl ExactSizeIterator<Item = RecoveredAllocationMetadata<'_>> {
98 self.ledger
99 .allocation_history()
100 .records()
101 .iter()
102 .map(|record| RecoveredAllocationMetadata {
103 stable_key: record.stable_key(),
104 slot: record.slot(),
105 state: record.state(),
106 schema: record
107 .schema_history()
108 .last()
109 .expect("validated schema history")
110 .schema(),
111 })
112 }
113
114 #[must_use]
116 pub fn is_declared(&self, key: &StableKey) -> bool {
117 key.as_str() == crate::IC_MEMORY_LEDGER_STABLE_KEY
120 || self.declarations.registered_declaration(key).is_some()
121 || self
122 .declarations
123 .requests()
124 .binary_search_by(|request| request.stable_key().cmp(key))
125 .is_ok()
126 || self
127 .selected
128 .iter()
129 .any(|request| request.stable_key() == key)
130 }
131
132 pub fn include_historical(
136 &mut self,
137 authority: &str,
138 stable_key: &str,
139 ) -> Result<(), BootstrapAdmissionError> {
140 if let Some(error) = &self.failure {
141 return Err(error.clone());
142 }
143 let result = self.select(authority, stable_key);
144 if let Err(error) = &result {
145 self.failure = Some(error.clone());
146 }
147 result
148 }
149
150 fn select(&mut self, authority: &str, stable_key: &str) -> Result<(), BootstrapAdmissionError> {
151 let request = MemoryRequest::new(authority, stable_key, SchemaMetadata::default())?;
153 let key = request.stable_key();
154 if self.is_declared(key) {
155 return Err(BootstrapAdmissionError::Duplicate(key.clone()));
156 }
157 if self.declarations.registered_declarations().len()
158 + self.declarations.requests().len()
159 + self.selected.len()
160 >= 254
161 {
162 return Err(BootstrapAdmissionError::TooManyDeclarations);
163 }
164 let record = self
165 .ledger
166 .allocation_history()
167 .records()
168 .iter()
169 .find(|r| r.stable_key() == key)
170 .ok_or_else(|| BootstrapAdmissionError::Unknown(key.clone()))?;
171 if matches!(record.state(), AllocationState::Retired { .. }) {
172 return Err(BootstrapAdmissionError::Retired(key.clone()));
173 }
174 self.declarations
175 .range_authority()
176 .validate_slot_authority(record.slot(), authority)
177 .map_err(|source| BootstrapAdmissionError::Range {
178 stable_key: key.clone(),
179 authority: authority.to_string(),
180 source,
181 })?;
182 self.selected.push(
183 request.with_schema(
184 record
185 .schema_history()
186 .last()
187 .expect("validated schema history")
188 .schema()
189 .clone(),
190 ),
191 );
192 Ok(())
193 }
194
195 pub(super) fn complete(self) -> Result<Vec<MemoryRequest>, BootstrapAdmissionError> {
196 if let Some(error) = self.failure {
197 return Err(error);
198 }
199 Ok(self.selected)
200 }
201}