1#[cfg(test)]
7use crate::{
8 db::integrity::{
9 DerivedInspectionLimits, DerivedIntegrityPage, IntegrityProofVector,
10 PhysicalUnitCheckpoint, RowInspectionLimits, RowIntegrityPage,
11 execute_index_integrity_page, execute_reverse_integrity_page, execute_row_integrity_page,
12 },
13 error::InternalError,
14};
15use crate::{
16 db::{
17 DbSession, QuickIntegrityResult,
18 commit::database_incarnation_id,
19 integrity::{
20 IntegrityCheckRequest, IntegrityCheckResult, IntegrityDeepError,
21 IntegrityEntityIdentity, IntegrityJobError, IntegrityJobId, IntegrityJobOwner,
22 IntegrityJobReceipt, IntegritySubmissionKey, abort_deep_integrity_job,
23 capture_integrity_proof_vector, continue_deep_integrity_job, execute_quick_integrity,
24 run_next_integrity_retention_page, start_deep_integrity_job,
25 uninspectable_quick_integrity,
26 },
27 runtime_hooks::EntityRuntimeHooks,
28 schema::AcceptedInspectionPlan,
29 session::accepted_schema::AcceptedInspectionPlanLoadError,
30 },
31 traits::CanisterKind,
32};
33
34impl<C: CanisterKind> DbSession<C> {
35 pub fn execute_admin_integrity(
48 &self,
49 request: IntegrityCheckRequest,
50 owner: IntegrityJobOwner,
51 ) -> Result<IntegrityCheckResult, IntegrityDeepError> {
52 owner.validate()?;
53 let result = match request {
54 IntegrityCheckRequest::Quick { entity } => self
55 .execute_quick_integrity_for_identity(&entity)
56 .map(IntegrityCheckResult::Quick),
57 IntegrityCheckRequest::DeepStart {
58 entity,
59 submission_key,
60 } => self
61 .start_deep_integrity_for_identity(&entity, owner, submission_key)
62 .map(IntegrityCheckResult::Deep),
63 IntegrityCheckRequest::DeepContinue {
64 job_id,
65 acknowledged_sequence,
66 } => {
67 job_id.validate()?;
68 self.continue_deep_integrity(job_id, &owner, acknowledged_sequence)
69 .map(IntegrityCheckResult::Deep)
70 }
71 IntegrityCheckRequest::DeepAbort { job_id } => {
72 job_id.validate()?;
73 Self::abort_deep_integrity(job_id, &owner).map(IntegrityCheckResult::Deep)
74 }
75 };
76 run_next_integrity_retention_page::<C>()?;
77 result
78 }
79
80 fn execute_quick_integrity_for_identity(
81 &self,
82 entity: &IntegrityEntityIdentity,
83 ) -> Result<QuickIntegrityResult, IntegrityDeepError> {
84 let (hooks, store) = self.integrity_target(entity)?;
85 let incarnation = database_incarnation_id()?;
86 match self.accepted_inspection_plan_for_runtime_hook(hooks, store) {
87 Ok(plan) => {
88 Self::validate_integrity_plan_identity(entity, &plan)?;
89 execute_quick_integrity(&self.db, &plan).map_err(IntegrityDeepError::from)
90 }
91 Err(AcceptedInspectionPlanLoadError::Selected { identity, error }) => {
92 let accepted = IntegrityEntityIdentity::from_accepted_identity(identity);
93 if entity != &accepted {
94 return Err(IntegrityJobError::EntityIdentityMismatch.into());
95 }
96 Ok(uninspectable_quick_integrity(identity, incarnation, &error))
97 }
98 Err(AcceptedInspectionPlanLoadError::Unselected(error)) => {
99 Err(IntegrityDeepError::from(error))
100 }
101 }
102 }
103
104 fn integrity_target(
105 &self,
106 entity: &IntegrityEntityIdentity,
107 ) -> Result<(&EntityRuntimeHooks<C>, crate::db::registry::StoreHandle), IntegrityDeepError>
108 {
109 entity.validate()?;
110 let hooks = self.db.runtime_hook_for_entity_path(entity.entity_path())?;
111 if hooks.entity_tag.value() != entity.entity_tag()
112 || hooks.store_path != entity.store_path()
113 {
114 return Err(IntegrityJobError::EntityIdentityMismatch.into());
115 }
116 let store = self.db.recovered_store(hooks.store_path)?;
117
118 Ok((hooks, store))
119 }
120
121 fn validate_integrity_plan_identity(
122 entity: &IntegrityEntityIdentity,
123 plan: &AcceptedInspectionPlan,
124 ) -> Result<(), IntegrityDeepError> {
125 if entity != &IntegrityEntityIdentity::from_accepted_identity(plan.identity()) {
126 return Err(IntegrityJobError::EntityIdentityMismatch.into());
127 }
128 Ok(())
129 }
130
131 #[cfg(test)]
137 pub(in crate::db) fn execute_integrity_row_page_for_entity(
138 &self,
139 entity_path: &str,
140 checkpoint: PhysicalUnitCheckpoint,
141 limits: RowInspectionLimits,
142 ) -> Result<RowIntegrityPage, InternalError> {
143 let hooks = self.db.runtime_hook_for_entity_path(entity_path)?;
144 let store = self.db.recovered_store(hooks.store_path)?;
145 let plan = self
146 .accepted_inspection_plan_for_runtime_hook(hooks, store)
147 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
148 execute_row_integrity_page(&self.db, &plan, checkpoint, limits)
149 }
150
151 #[cfg(test)]
156 pub(in crate::db) fn execute_integrity_index_page_for_entity(
157 &self,
158 entity_path: &str,
159 index_ordinal: usize,
160 checkpoint: PhysicalUnitCheckpoint,
161 limits: DerivedInspectionLimits,
162 ) -> Result<DerivedIntegrityPage, InternalError> {
163 let hooks = self.db.runtime_hook_for_entity_path(entity_path)?;
164 let store = self.db.recovered_store(hooks.store_path)?;
165 let plan = self
166 .accepted_inspection_plan_for_runtime_hook(hooks, store)
167 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
168 execute_index_integrity_page(&self.db, &plan, index_ordinal, checkpoint, limits)
169 }
170
171 #[cfg(test)]
176 pub(in crate::db) fn execute_integrity_reverse_page_for_entity(
177 &self,
178 entity_path: &str,
179 relation_ordinal: usize,
180 checkpoint: PhysicalUnitCheckpoint,
181 limits: DerivedInspectionLimits,
182 ) -> Result<DerivedIntegrityPage, InternalError> {
183 let hooks = self.db.runtime_hook_for_entity_path(entity_path)?;
184 let store = self.db.recovered_store(hooks.store_path)?;
185 let plan = self
186 .accepted_inspection_plan_for_runtime_hook(hooks, store)
187 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
188 execute_reverse_integrity_page(&self.db, &plan, relation_ordinal, checkpoint, limits)
189 }
190
191 #[cfg(test)]
193 pub(in crate::db) fn capture_integrity_proof_for_entity(
194 &self,
195 entity_path: &str,
196 ) -> Result<IntegrityProofVector, InternalError> {
197 let hooks = self.db.runtime_hook_for_entity_path(entity_path)?;
198 let store = self.db.recovered_store(hooks.store_path)?;
199 let plan = self
200 .accepted_inspection_plan_for_runtime_hook(hooks, store)
201 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
202 capture_integrity_proof_vector(&self.db, &plan)
203 }
204
205 fn start_deep_integrity_for_identity(
207 &self,
208 entity: &IntegrityEntityIdentity,
209 owner: IntegrityJobOwner,
210 submission_key: IntegritySubmissionKey,
211 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
212 submission_key.validate()?;
213 let (hooks, store) = self.integrity_target(entity)?;
214 let first_plan = self
215 .accepted_inspection_plan_for_runtime_hook(hooks, store)
216 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
217 Self::validate_integrity_plan_identity(entity, &first_plan)?;
218 let proof_a = capture_integrity_proof_vector(&self.db, &first_plan)?;
219
220 let store = self.db.recovered_store(hooks.store_path)?;
221 let second_plan = self
222 .accepted_inspection_plan_for_runtime_hook(hooks, store)
223 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
224 Self::validate_integrity_plan_identity(entity, &second_plan)?;
225 let proof_b = capture_integrity_proof_vector(&self.db, &second_plan)?;
226 start_deep_integrity_job(
227 &self.db,
228 &second_plan,
229 owner,
230 submission_key,
231 proof_a,
232 proof_b,
233 )
234 }
235
236 fn continue_deep_integrity(
238 &self,
239 job_id: IntegrityJobId,
240 owner: &IntegrityJobOwner,
241 acknowledged_sequence: u64,
242 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
243 continue_deep_integrity_job(
244 &self.db,
245 job_id,
246 owner,
247 acknowledged_sequence,
248 |entity_path| {
249 let hooks = self.db.runtime_hook_for_entity_path(entity_path)?;
250 let store = self.db.recovered_store(hooks.store_path)?;
251 self.accepted_inspection_plan_for_runtime_hook(hooks, store)
252 .map_err(AcceptedInspectionPlanLoadError::into_internal)
253 },
254 )
255 }
256
257 fn abort_deep_integrity(
259 job_id: IntegrityJobId,
260 owner: &IntegrityJobOwner,
261 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
262 abort_deep_integrity_job::<C>(job_id, owner)
263 }
264
265 #[cfg(test)]
266 pub(in crate::db) fn start_deep_integrity_for_entity(
267 &self,
268 entity_path: &str,
269 owner: IntegrityJobOwner,
270 submission_key: IntegritySubmissionKey,
271 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
272 let hooks = self.db.runtime_hook_for_entity_path(entity_path)?;
273 let store = self.db.recovered_store(hooks.store_path)?;
274 let plan = self
275 .accepted_inspection_plan_for_runtime_hook(hooks, store)
276 .map_err(AcceptedInspectionPlanLoadError::into_internal)?;
277 let entity = IntegrityEntityIdentity::from_accepted_identity(plan.identity());
278
279 self.start_deep_integrity_for_identity(&entity, owner, submission_key)
280 }
281
282 #[cfg(test)]
283 pub(in crate::db) fn continue_deep_integrity_for_tests(
284 &self,
285 job_id: IntegrityJobId,
286 owner: &IntegrityJobOwner,
287 acknowledged_sequence: u64,
288 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
289 self.continue_deep_integrity(job_id, owner, acknowledged_sequence)
290 }
291}