1use crate::{AllocationId, StableKey};
3use std::collections::BTreeSet;
4
5#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
7pub enum AdmissionError {
8 #[error("invalid authority or namespace")]
10 Invalid,
11 #[error(transparent)]
13 Key(#[from] crate::StableKeyError),
14 #[error("duplicate key or overlapping namespace")]
16 Duplicate,
17 #[error("namespace is not granted to this authority")]
19 Unauthorized,
20 #[error("unknown historical allocation or declaration budget exceeded")]
22 Limit,
23}
24fn owner_valid(owner: &str) -> bool {
25 !owner.is_empty()
26 && owner.len() <= 128
27 && owner
28 .bytes()
29 .all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-')
30}
31
32#[derive(Clone, Debug, Eq, PartialEq)]
34pub struct MemoryAuthority {
35 authority: String,
36 prefix: String,
37}
38impl MemoryAuthority {
39 pub fn new(
44 authority: impl Into<String>,
45 prefix: impl Into<String>,
46 ) -> Result<Self, AdmissionError> {
47 let authority = authority.into();
48 let prefix = prefix.into();
49 if !owner_valid(&authority) || !crate::key::is_key_prefix(&prefix) {
50 return Err(AdmissionError::Invalid);
51 }
52 Ok(Self { authority, prefix })
53 }
54}
55
56#[derive(Clone, Debug, Eq, PartialEq)]
58pub struct AllocationGrants(Vec<MemoryAuthority>);
59impl AllocationGrants {
60 pub fn new(mut grants: Vec<MemoryAuthority>) -> Result<Self, AdmissionError> {
65 grants.sort_unstable_by(|a, b| a.prefix.cmp(&b.prefix));
66 if grants
67 .windows(2)
68 .any(|pair| pair[1].prefix.starts_with(&pair[0].prefix))
69 {
70 return Err(AdmissionError::Duplicate);
71 }
72 Ok(Self(grants))
73 }
74 pub(super) fn check(&self, request: &MemoryRequest) -> Result<(), AdmissionError> {
75 if self.0.iter().any(|grant| {
76 grant.authority == request.authority && request.key.as_str().starts_with(&grant.prefix)
77 }) {
78 Ok(())
79 } else {
80 Err(AdmissionError::Unauthorized)
81 }
82 }
83}
84
85#[derive(Clone, Debug, Eq, PartialEq)]
87pub struct MemoryRequest {
88 authority: String,
89 key: StableKey,
90}
91impl MemoryRequest {
92 pub fn new(authority: impl Into<String>, key: &str) -> Result<Self, AdmissionError> {
97 let authority = authority.into();
98 if !owner_valid(&authority) {
99 return Err(AdmissionError::Invalid);
100 }
101 Ok(Self {
102 authority,
103 key: StableKey::parse(key)?,
104 })
105 }
106 #[must_use]
108 pub fn authority(&self) -> &str {
109 &self.authority
110 }
111 #[must_use]
113 pub const fn stable_key(&self) -> &StableKey {
114 &self.key
115 }
116}
117
118#[derive(Clone, Debug, Eq, PartialEq)]
120pub struct SealedDeclarationSnapshot(Vec<MemoryRequest>);
121impl SealedDeclarationSnapshot {
122 pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, AdmissionError> {
127 requests.sort_unstable_by(|a, b| a.key.cmp(&b.key));
128 if requests.windows(2).any(|pair| pair[0].key == pair[1].key) {
129 return Err(AdmissionError::Duplicate);
130 }
131 Ok(Self(requests))
132 }
133 #[must_use]
135 pub fn requests(&self) -> &[MemoryRequest] {
136 &self.0
137 }
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142pub struct PolicyIdentity([u8; 32]);
143impl PolicyIdentity {
144 #[must_use]
146 pub const fn new(bytes: [u8; 32]) -> Self {
147 Self(bytes)
148 }
149}
150
151pub struct BootstrapAdmission<'a> {
153 grants: &'a AllocationGrants,
154 recovered: &'a [(StableKey, AllocationId, u64)],
155 selected: Vec<MemoryRequest>,
156 keys: BTreeSet<StableKey>,
157 maximum: usize,
158 failure: Option<AdmissionError>,
159}
160impl<'a> BootstrapAdmission<'a> {
161 pub(super) fn new(
162 source: &SealedDeclarationSnapshot,
163 grants: &'a AllocationGrants,
164 recovered: &'a [(StableKey, AllocationId, u64)],
165 maximum: usize,
166 ) -> Result<Self, AdmissionError> {
167 if source.0.len() > maximum {
168 return Err(AdmissionError::Limit);
169 }
170 for request in &source.0 {
171 grants.check(request)?;
172 }
173 Ok(Self {
174 grants,
175 recovered,
176 selected: source.0.clone(),
177 keys: source.0.iter().map(|request| request.key.clone()).collect(),
178 maximum,
179 failure: None,
180 })
181 }
182 #[must_use]
184 pub const fn recovered_allocations(&self) -> &[(StableKey, AllocationId, u64)] {
185 self.recovered
186 }
187 #[must_use]
189 pub fn is_declared(&self, key: &StableKey) -> bool {
190 self.keys.contains(key)
191 }
192 pub fn include_historical(&mut self, authority: &str, key: &str) -> Result<(), AdmissionError> {
197 if let Some(error) = &self.failure {
198 return Err(error.clone());
199 }
200 let result = self.include(authority, key);
201 if let Err(error) = &result {
202 self.failure = Some(error.clone());
203 }
204 result
205 }
206 fn include(&mut self, authority: &str, key: &str) -> Result<(), AdmissionError> {
207 let request = MemoryRequest::new(authority, key)?;
208 self.grants.check(&request)?;
209 if self.selected.len() >= self.maximum
210 || !self.recovered.iter().any(|(key, _, _)| key == &request.key)
211 {
212 return Err(AdmissionError::Limit);
213 }
214 if !self.keys.insert(request.key.clone()) {
215 return Err(AdmissionError::Duplicate);
216 }
217 self.selected.push(request);
218 Ok(())
219 }
220 pub(super) fn complete(self) -> Result<Vec<MemoryRequest>, AdmissionError> {
221 if let Some(error) = self.failure {
222 Err(error)
223 } else {
224 Ok(self.selected)
225 }
226 }
227}
228
229pub trait RuntimeBootstrapPolicy {
231 type Error;
233 fn validate_allocation(&self, key: &StableKey) -> Result<(), Self::Error>;
238 fn prepare_bootstrap(
243 &self,
244 _admission: &mut BootstrapAdmission<'_>,
245 ) -> Result<(), Self::Error> {
246 Ok(())
247 }
248 fn runtime_bootstrap_identity(&self) -> PolicyIdentity;
250}
251pub struct GenericAllocationPolicy;
253impl RuntimeBootstrapPolicy for GenericAllocationPolicy {
254 type Error = std::convert::Infallible;
255 fn validate_allocation(&self, _key: &StableKey) -> Result<(), Self::Error> {
256 Ok(())
257 }
258 fn runtime_bootstrap_identity(&self) -> PolicyIdentity {
259 PolicyIdentity::new(*b"ic-storage.noop-policy..........")
260 }
261}