1use crate::{
7 db::{
8 DbSession, QuickIntegrityResult,
9 commit::database_incarnation_id,
10 integrity::{
11 IntegrityAuthorityDiagnostic, IntegrityCheckRequest, IntegrityCheckResult,
12 IntegrityDeepError, IntegrityEntityIdentity, IntegrityJobError, IntegrityJobId,
13 IntegrityJobOwner, IntegrityJobReceipt, IntegritySubmissionKey,
14 abort_deep_integrity_job, capture_integrity_proof_vector, continue_deep_integrity_job,
15 execute_quick_integrity, 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 Ok(uninspectable_quick_integrity(
98 identity,
99 database_incarnation_id()?,
100 &error,
101 ))
102 }
103 Err(AcceptedInspectionPlanLoadError::Unselected(error)) => {
104 Err(IntegrityDeepError::from(error))
105 }
106 }
107 }
108
109 fn execute_quick_integrity_with_catalog(
110 &self,
111 entity: &IntegrityEntityIdentity,
112 catalog: &AcceptedSchemaCatalogContext,
113 ) -> Result<QuickIntegrityResult, IntegrityDeepError> {
114 let plan = catalog.inspection_plan();
115 Self::validate_integrity_plan_identity(entity, plan)?;
116 execute_quick_integrity(
117 &self.db,
118 plan,
119 catalog.runtime_root_identity().database_incarnation(),
120 )
121 .map_err(IntegrityDeepError::from)
122 }
123
124 fn integrity_target(
125 &self,
126 entity: &IntegrityEntityIdentity,
127 ) -> Result<(AcceptedRuntimeEntity, crate::db::registry::StoreHandle), IntegrityDeepError> {
128 entity.validate()?;
129 let runtime_entity = self
130 .db
131 .accepted_runtime_entity_for_path(entity.entity_path())?;
132 if runtime_entity.entity_tag().value() != entity.entity_tag()
133 || runtime_entity.store_path() != entity.store_path()
134 {
135 return Err(IntegrityJobError::EntityIdentityMismatch.into());
136 }
137 let store = self.db.store_handle(runtime_entity.store_path())?;
138
139 Ok((runtime_entity, store))
140 }
141
142 fn validate_integrity_plan_identity(
143 entity: &IntegrityEntityIdentity,
144 plan: &AcceptedInspectionPlan,
145 ) -> Result<(), IntegrityDeepError> {
146 if *entity != IntegrityEntityIdentity::from_accepted_identity(plan.identity_ref()) {
147 return Err(IntegrityJobError::EntityIdentityMismatch.into());
148 }
149 Ok(())
150 }
151
152 fn start_deep_integrity_for_identity(
154 &self,
155 entity: &IntegrityEntityIdentity,
156 owner: IntegrityJobOwner,
157 submission_key: IntegritySubmissionKey,
158 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
159 self.start_deep_integrity_for_identity_with_plan_loader(
160 entity,
161 owner,
162 submission_key,
163 |runtime_entity, store| {
164 self.accepted_inspection_plan_for_runtime_entity(runtime_entity, store)
165 },
166 )
167 }
168
169 fn start_deep_integrity_for_identity_with_plan_loader(
170 &self,
171 entity: &IntegrityEntityIdentity,
172 owner: IntegrityJobOwner,
173 submission_key: IntegritySubmissionKey,
174 mut load_plan: impl FnMut(
175 AcceptedRuntimeEntity,
176 crate::db::registry::StoreHandle,
177 )
178 -> Result<AcceptedInspectionPlan, AcceptedInspectionPlanLoadError>,
179 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
180 submission_key.validate()?;
181 let (runtime_entity, store) = self.integrity_target(entity)?;
182 let first_plan = load_plan(runtime_entity.clone(), store)
183 .map_err(|error| Self::deep_start_plan_load_error(entity, error))?;
184 Self::validate_integrity_plan_identity(entity, &first_plan)?;
185 let proof_a = capture_integrity_proof_vector(&self.db, &first_plan)?;
186
187 let store = self.db.store_handle(runtime_entity.store_path())?;
188 let second_plan = load_plan(runtime_entity, store)
189 .map_err(|error| Self::deep_start_plan_load_error(entity, error))?;
190 Self::validate_integrity_plan_identity(entity, &second_plan)?;
191 let proof_b = capture_integrity_proof_vector(&self.db, &second_plan)?;
192 start_deep_integrity_job(
193 &self.db,
194 &second_plan,
195 owner,
196 submission_key,
197 proof_a,
198 proof_b,
199 )
200 }
201
202 fn deep_start_plan_load_error(
203 entity: &IntegrityEntityIdentity,
204 error: AcceptedInspectionPlanLoadError,
205 ) -> IntegrityDeepError {
206 match error {
207 AcceptedInspectionPlanLoadError::Selected { identity, error } => {
208 if entity != &IntegrityEntityIdentity::from_accepted_identity(&identity) {
209 return IntegrityJobError::EntityIdentityMismatch.into();
210 }
211 IntegrityDeepError::Uninspectable(IntegrityAuthorityDiagnostic::from_internal(
212 &error,
213 ))
214 }
215 AcceptedInspectionPlanLoadError::Unselected(error) => {
216 IntegrityDeepError::Uninspectable(IntegrityAuthorityDiagnostic::from_internal(
217 &error,
218 ))
219 }
220 }
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}