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(
41 authority: impl Into<String>,
42 prefix: impl Into<String>,
43 ) -> Result<Self, AdmissionError> {
44 let authority = authority.into();
45 let prefix = prefix.into();
46 if !owner_valid(&authority) || !crate::key::is_key_prefix(&prefix) {
47 return Err(AdmissionError::Invalid);
48 }
49 Ok(Self { authority, prefix })
50 }
51}
52
53#[derive(Clone, Debug, Eq, PartialEq)]
55pub struct AllocationGrants(Vec<MemoryAuthority>);
56impl AllocationGrants {
57 pub fn new(mut grants: Vec<MemoryAuthority>) -> Result<Self, AdmissionError> {
59 grants.sort_unstable_by(|a, b| a.prefix.cmp(&b.prefix));
60 if grants
61 .windows(2)
62 .any(|pair| pair[1].prefix.starts_with(&pair[0].prefix))
63 {
64 return Err(AdmissionError::Duplicate);
65 }
66 Ok(Self(grants))
67 }
68 pub(super) fn check(&self, request: &MemoryRequest) -> Result<(), AdmissionError> {
69 if self.0.iter().any(|grant| {
70 grant.authority == request.authority && request.key.as_str().starts_with(&grant.prefix)
71 }) {
72 Ok(())
73 } else {
74 Err(AdmissionError::Unauthorized)
75 }
76 }
77}
78
79#[derive(Clone, Debug, Eq, PartialEq)]
81pub struct MemoryRequest {
82 authority: String,
83 key: StableKey,
84}
85impl MemoryRequest {
86 pub fn new(authority: impl Into<String>, key: &str) -> Result<Self, AdmissionError> {
88 let authority = authority.into();
89 if !owner_valid(&authority) {
90 return Err(AdmissionError::Invalid);
91 }
92 Ok(Self {
93 authority,
94 key: StableKey::parse(key)?,
95 })
96 }
97 #[must_use]
99 pub fn authority(&self) -> &str {
100 &self.authority
101 }
102 #[must_use]
104 pub const fn stable_key(&self) -> &StableKey {
105 &self.key
106 }
107}
108
109#[derive(Clone, Debug, Eq, PartialEq)]
111pub struct SealedDeclarationSnapshot(Vec<MemoryRequest>);
112impl SealedDeclarationSnapshot {
113 pub fn new(mut requests: Vec<MemoryRequest>) -> Result<Self, AdmissionError> {
115 requests.sort_unstable_by(|a, b| a.key.cmp(&b.key));
116 if requests.windows(2).any(|pair| pair[0].key == pair[1].key) {
117 return Err(AdmissionError::Duplicate);
118 }
119 Ok(Self(requests))
120 }
121 #[must_use]
123 pub fn requests(&self) -> &[MemoryRequest] {
124 &self.0
125 }
126}
127
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130pub struct PolicyIdentity([u8; 32]);
131impl PolicyIdentity {
132 #[must_use]
134 pub const fn new(bytes: [u8; 32]) -> Self {
135 Self(bytes)
136 }
137}
138
139pub struct BootstrapAdmission<'a> {
141 grants: &'a AllocationGrants,
142 recovered: &'a [(StableKey, AllocationId, u64)],
143 selected: Vec<MemoryRequest>,
144 keys: BTreeSet<StableKey>,
145 maximum: usize,
146 failure: Option<AdmissionError>,
147}
148impl<'a> BootstrapAdmission<'a> {
149 pub(super) fn new(
150 source: &SealedDeclarationSnapshot,
151 grants: &'a AllocationGrants,
152 recovered: &'a [(StableKey, AllocationId, u64)],
153 maximum: usize,
154 ) -> Result<Self, AdmissionError> {
155 if source.0.len() > maximum {
156 return Err(AdmissionError::Limit);
157 }
158 for request in &source.0 {
159 grants.check(request)?;
160 }
161 Ok(Self {
162 grants,
163 recovered,
164 selected: source.0.clone(),
165 keys: source.0.iter().map(|request| request.key.clone()).collect(),
166 maximum,
167 failure: None,
168 })
169 }
170 #[must_use]
172 pub const fn recovered_allocations(&self) -> &[(StableKey, AllocationId, u64)] {
173 self.recovered
174 }
175 #[must_use]
177 pub fn is_declared(&self, key: &StableKey) -> bool {
178 self.keys.contains(key)
179 }
180 pub fn include_historical(&mut self, authority: &str, key: &str) -> Result<(), AdmissionError> {
182 if let Some(error) = &self.failure {
183 return Err(error.clone());
184 }
185 let result = self.include(authority, key);
186 if let Err(error) = &result {
187 self.failure = Some(error.clone());
188 }
189 result
190 }
191 fn include(&mut self, authority: &str, key: &str) -> Result<(), AdmissionError> {
192 let request = MemoryRequest::new(authority, key)?;
193 self.grants.check(&request)?;
194 if self.selected.len() >= self.maximum
195 || !self.recovered.iter().any(|(key, _, _)| key == &request.key)
196 {
197 return Err(AdmissionError::Limit);
198 }
199 if !self.keys.insert(request.key.clone()) {
200 return Err(AdmissionError::Duplicate);
201 }
202 self.selected.push(request);
203 Ok(())
204 }
205 pub(super) fn complete(self) -> Result<Vec<MemoryRequest>, AdmissionError> {
206 if let Some(error) = self.failure {
207 Err(error)
208 } else {
209 Ok(self.selected)
210 }
211 }
212}
213
214pub trait RuntimeBootstrapPolicy {
216 type Error;
218 fn validate_allocation(&self, key: &StableKey) -> Result<(), Self::Error>;
220 fn prepare_bootstrap(
222 &self,
223 _admission: &mut BootstrapAdmission<'_>,
224 ) -> Result<(), Self::Error> {
225 Ok(())
226 }
227 fn runtime_bootstrap_identity(&self) -> PolicyIdentity;
229}
230pub struct GenericAllocationPolicy;
232impl RuntimeBootstrapPolicy for GenericAllocationPolicy {
233 type Error = std::convert::Infallible;
234 fn validate_allocation(&self, _key: &StableKey) -> Result<(), Self::Error> {
235 Ok(())
236 }
237 fn runtime_bootstrap_identity(&self) -> PolicyIdentity {
238 PolicyIdentity::new(*b"ic-storage.noop-policy..........")
239 }
240}