1use crate::{
7 db::{
8 DbSession, QuickIntegrityResult,
9 commit::database_incarnation_id,
10 integrity::{
11 IntegrityCheckRequest, IntegrityCheckResult, IntegrityDeepError,
12 IntegrityEntityIdentity, IntegrityJobError, IntegrityJobId, IntegrityJobOwner,
13 IntegrityJobReceipt, IntegritySubmissionKey, abort_deep_integrity_job,
14 capture_integrity_proof_vector, continue_deep_integrity_job, execute_quick_integrity,
15 run_next_integrity_retention_page, start_deep_integrity_job,
16 uninspectable_quick_integrity,
17 },
18 runtime_entity_catalog::AcceptedRuntimeEntity,
19 schema::AcceptedInspectionPlan,
20 session::{AcceptedSchemaCatalogContext, accepted_schema::AcceptedInspectionPlanLoadError},
21 },
22 traits::CanisterKind,
23};
24
25impl<C: CanisterKind> DbSession<C> {
26 pub fn execute_admin_integrity(
39 &self,
40 request: IntegrityCheckRequest,
41 owner: IntegrityJobOwner,
42 ) -> Result<IntegrityCheckResult, IntegrityDeepError> {
43 self.execute_admin_integrity_with_quick_catalog(request, owner, None)
44 }
45
46 pub(in crate::db::session) fn execute_admin_integrity_with_quick_catalog(
47 &self,
48 request: IntegrityCheckRequest,
49 owner: IntegrityJobOwner,
50 quick_catalog: Option<AcceptedSchemaCatalogContext>,
51 ) -> Result<IntegrityCheckResult, IntegrityDeepError> {
52 self.db.ensure_recovered_control_state()?;
53 owner.validate()?;
54 let result = match request {
55 IntegrityCheckRequest::Quick { entity } => self
56 .execute_quick_integrity_for_identity(&entity, quick_catalog.as_ref())
57 .map(IntegrityCheckResult::Quick),
58 IntegrityCheckRequest::DeepStart {
59 entity,
60 submission_key,
61 } => self
62 .start_deep_integrity_for_identity(&entity, owner, submission_key)
63 .map(IntegrityCheckResult::Deep),
64 IntegrityCheckRequest::DeepContinue {
65 job_id,
66 acknowledged_sequence,
67 } => {
68 job_id.validate()?;
69 self.continue_deep_integrity(job_id, &owner, acknowledged_sequence)
70 .map(IntegrityCheckResult::Deep)
71 }
72 IntegrityCheckRequest::DeepAbort { job_id } => {
73 job_id.validate()?;
74 Self::abort_deep_integrity(job_id, &owner).map(IntegrityCheckResult::Deep)
75 }
76 };
77 run_next_integrity_retention_page::<C>()?;
78 result
79 }
80
81 fn execute_quick_integrity_for_identity(
82 &self,
83 entity: &IntegrityEntityIdentity,
84 catalog: Option<&AcceptedSchemaCatalogContext>,
85 ) -> Result<QuickIntegrityResult, IntegrityDeepError> {
86 if let Some(catalog) = catalog {
87 return self.execute_quick_integrity_with_catalog(entity, catalog);
88 }
89 let (runtime_entity, store) = self.integrity_target(entity)?;
90 match self.accepted_integrity_catalog_context_for_runtime_entity(runtime_entity, store) {
91 Ok(catalog) => self.execute_quick_integrity_with_catalog(entity, &catalog),
92 Err(AcceptedInspectionPlanLoadError::Selected { identity, error }) => {
93 let accepted = IntegrityEntityIdentity::from_accepted_identity(&identity);
94 if entity != &accepted {
95 return Err(IntegrityJobError::EntityIdentityMismatch.into());
96 }
97 match IntegrityDeepError::from_plan_load(error) {
98 IntegrityDeepError::Uninspectable(diagnostic) => {
99 Ok(uninspectable_quick_integrity(
100 identity,
101 database_incarnation_id()?,
102 diagnostic,
103 ))
104 }
105 error => Err(error),
106 }
107 }
108 Err(AcceptedInspectionPlanLoadError::Unselected(error)) => {
109 Err(IntegrityDeepError::from(error))
110 }
111 }
112 }
113
114 fn execute_quick_integrity_with_catalog(
115 &self,
116 entity: &IntegrityEntityIdentity,
117 catalog: &AcceptedSchemaCatalogContext,
118 ) -> Result<QuickIntegrityResult, IntegrityDeepError> {
119 let plan = catalog.inspection_plan();
120 Self::validate_integrity_plan_identity(entity, plan)?;
121 execute_quick_integrity(
122 &self.db,
123 plan,
124 catalog.runtime_root_identity().database_incarnation(),
125 )
126 .map_err(IntegrityDeepError::from)
127 }
128
129 fn integrity_target(
130 &self,
131 entity: &IntegrityEntityIdentity,
132 ) -> Result<(AcceptedRuntimeEntity, crate::db::registry::StoreHandle), IntegrityDeepError> {
133 entity.validate()?;
134 let runtime_entity = self
135 .db
136 .accepted_runtime_entity_for_path(entity.entity_path())?;
137 if runtime_entity.entity_tag().value() != entity.entity_tag()
138 || runtime_entity.store_path() != entity.store_path()
139 {
140 return Err(IntegrityJobError::EntityIdentityMismatch.into());
141 }
142 let store = self.db.store_handle(runtime_entity.store_path())?;
143
144 Ok((runtime_entity, store))
145 }
146
147 fn validate_integrity_plan_identity(
148 entity: &IntegrityEntityIdentity,
149 plan: &AcceptedInspectionPlan,
150 ) -> Result<(), IntegrityDeepError> {
151 if *entity != IntegrityEntityIdentity::from_accepted_identity(plan.identity_ref()) {
152 return Err(IntegrityJobError::EntityIdentityMismatch.into());
153 }
154 Ok(())
155 }
156
157 fn start_deep_integrity_for_identity(
159 &self,
160 entity: &IntegrityEntityIdentity,
161 owner: IntegrityJobOwner,
162 submission_key: IntegritySubmissionKey,
163 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
164 self.start_deep_integrity_for_identity_with_plan_loader(
165 entity,
166 owner,
167 submission_key,
168 |runtime_entity, store| {
169 self.accepted_inspection_plan_for_runtime_entity(runtime_entity, store)
170 },
171 )
172 }
173
174 fn start_deep_integrity_for_identity_with_plan_loader(
175 &self,
176 entity: &IntegrityEntityIdentity,
177 owner: IntegrityJobOwner,
178 submission_key: IntegritySubmissionKey,
179 mut load_plan: impl FnMut(
180 AcceptedRuntimeEntity,
181 crate::db::registry::StoreHandle,
182 )
183 -> Result<AcceptedInspectionPlan, AcceptedInspectionPlanLoadError>,
184 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
185 submission_key.validate()?;
186 let (runtime_entity, store) = self.integrity_target(entity)?;
187 let first_plan = load_plan(runtime_entity.clone(), store)
188 .map_err(|error| Self::deep_start_plan_load_error(entity, error))?;
189 Self::validate_integrity_plan_identity(entity, &first_plan)?;
190 let proof_a = capture_integrity_proof_vector(&self.db, &first_plan)?;
191
192 let store = self.db.store_handle(runtime_entity.store_path())?;
193 let second_plan = load_plan(runtime_entity, store)
194 .map_err(|error| Self::deep_start_plan_load_error(entity, error))?;
195 Self::validate_integrity_plan_identity(entity, &second_plan)?;
196 let proof_b = capture_integrity_proof_vector(&self.db, &second_plan)?;
197 start_deep_integrity_job(
198 &self.db,
199 &second_plan,
200 owner,
201 submission_key,
202 proof_a,
203 proof_b,
204 )
205 }
206
207 fn deep_start_plan_load_error(
208 entity: &IntegrityEntityIdentity,
209 error: AcceptedInspectionPlanLoadError,
210 ) -> IntegrityDeepError {
211 let error = match error {
212 AcceptedInspectionPlanLoadError::Selected { identity, error } => {
213 if entity != &IntegrityEntityIdentity::from_accepted_identity(&identity) {
214 return IntegrityJobError::EntityIdentityMismatch.into();
215 }
216 error
217 }
218 AcceptedInspectionPlanLoadError::Unselected(error) => error,
219 };
220 IntegrityDeepError::from_plan_load(error)
221 }
222
223 fn continue_deep_integrity(
225 &self,
226 job_id: IntegrityJobId,
227 owner: &IntegrityJobOwner,
228 acknowledged_sequence: u64,
229 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
230 continue_deep_integrity_job(
231 &self.db,
232 job_id,
233 owner,
234 acknowledged_sequence,
235 |entity_path| {
236 let runtime_entity = self.db.accepted_runtime_entity_for_path(entity_path)?;
237 let store = self.db.store_handle(runtime_entity.store_path())?;
238 self.accepted_inspection_plan_for_runtime_entity(runtime_entity, store)
239 .map_err(AcceptedInspectionPlanLoadError::into_internal)
240 },
241 )
242 }
243
244 fn abort_deep_integrity(
246 job_id: IntegrityJobId,
247 owner: &IntegrityJobOwner,
248 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
249 abort_deep_integrity_job::<C>(job_id, owner)
250 }
251}