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::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 owner.validate()?;
44 let result = match request {
45 IntegrityCheckRequest::Quick { entity } => self
46 .execute_quick_integrity_for_identity(&entity)
47 .map(IntegrityCheckResult::Quick),
48 IntegrityCheckRequest::DeepStart {
49 entity,
50 submission_key,
51 } => self
52 .start_deep_integrity_for_identity(&entity, owner, submission_key)
53 .map(IntegrityCheckResult::Deep),
54 IntegrityCheckRequest::DeepContinue {
55 job_id,
56 acknowledged_sequence,
57 } => {
58 job_id.validate()?;
59 self.continue_deep_integrity(job_id, &owner, acknowledged_sequence)
60 .map(IntegrityCheckResult::Deep)
61 }
62 IntegrityCheckRequest::DeepAbort { job_id } => {
63 job_id.validate()?;
64 Self::abort_deep_integrity(job_id, &owner).map(IntegrityCheckResult::Deep)
65 }
66 };
67 run_next_integrity_retention_page::<C>()?;
68 result
69 }
70
71 fn execute_quick_integrity_for_identity(
72 &self,
73 entity: &IntegrityEntityIdentity,
74 ) -> Result<QuickIntegrityResult, IntegrityDeepError> {
75 let (runtime_entity, store) = self.integrity_target(entity)?;
76 let incarnation = database_incarnation_id()?;
77 match self.accepted_inspection_plan_for_runtime_entity(runtime_entity, store) {
78 Ok(plan) => {
79 Self::validate_integrity_plan_identity(entity, &plan)?;
80 execute_quick_integrity(&self.db, &plan).map_err(IntegrityDeepError::from)
81 }
82 Err(AcceptedInspectionPlanLoadError::Selected { identity, error }) => {
83 let accepted = IntegrityEntityIdentity::from_accepted_identity(&identity);
84 if entity != &accepted {
85 return Err(IntegrityJobError::EntityIdentityMismatch.into());
86 }
87 Ok(uninspectable_quick_integrity(identity, incarnation, &error))
88 }
89 Err(AcceptedInspectionPlanLoadError::Unselected(error)) => {
90 Err(IntegrityDeepError::from(error))
91 }
92 }
93 }
94
95 fn integrity_target(
96 &self,
97 entity: &IntegrityEntityIdentity,
98 ) -> Result<(AcceptedRuntimeEntity, crate::db::registry::StoreHandle), IntegrityDeepError> {
99 entity.validate()?;
100 let runtime_entity = self
101 .db
102 .accepted_runtime_entity_for_path(entity.entity_path())?;
103 if runtime_entity.entity_tag().value() != entity.entity_tag()
104 || runtime_entity.store_path() != entity.store_path()
105 {
106 return Err(IntegrityJobError::EntityIdentityMismatch.into());
107 }
108 let store = self.db.recovered_store(runtime_entity.store_path())?;
109
110 Ok((runtime_entity, store))
111 }
112
113 fn validate_integrity_plan_identity(
114 entity: &IntegrityEntityIdentity,
115 plan: &AcceptedInspectionPlan,
116 ) -> Result<(), IntegrityDeepError> {
117 if *entity != IntegrityEntityIdentity::from_accepted_identity(plan.identity_ref()) {
118 return Err(IntegrityJobError::EntityIdentityMismatch.into());
119 }
120 Ok(())
121 }
122
123 fn start_deep_integrity_for_identity(
125 &self,
126 entity: &IntegrityEntityIdentity,
127 owner: IntegrityJobOwner,
128 submission_key: IntegritySubmissionKey,
129 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
130 self.start_deep_integrity_for_identity_with_plan_loader(
131 entity,
132 owner,
133 submission_key,
134 |runtime_entity, store| {
135 self.accepted_inspection_plan_for_runtime_entity(runtime_entity, store)
136 },
137 )
138 }
139
140 fn start_deep_integrity_for_identity_with_plan_loader(
141 &self,
142 entity: &IntegrityEntityIdentity,
143 owner: IntegrityJobOwner,
144 submission_key: IntegritySubmissionKey,
145 mut load_plan: impl FnMut(
146 AcceptedRuntimeEntity,
147 crate::db::registry::StoreHandle,
148 )
149 -> Result<AcceptedInspectionPlan, AcceptedInspectionPlanLoadError>,
150 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
151 submission_key.validate()?;
152 let (runtime_entity, store) = self.integrity_target(entity)?;
153 let first_plan = load_plan(runtime_entity.clone(), store)
154 .map_err(|error| Self::deep_start_plan_load_error(entity, error))?;
155 Self::validate_integrity_plan_identity(entity, &first_plan)?;
156 let proof_a = capture_integrity_proof_vector(&self.db, &first_plan)?;
157
158 let store = self.db.recovered_store(runtime_entity.store_path())?;
159 let second_plan = load_plan(runtime_entity, store)
160 .map_err(|error| Self::deep_start_plan_load_error(entity, error))?;
161 Self::validate_integrity_plan_identity(entity, &second_plan)?;
162 let proof_b = capture_integrity_proof_vector(&self.db, &second_plan)?;
163 start_deep_integrity_job(
164 &self.db,
165 &second_plan,
166 owner,
167 submission_key,
168 proof_a,
169 proof_b,
170 )
171 }
172
173 fn deep_start_plan_load_error(
174 entity: &IntegrityEntityIdentity,
175 error: AcceptedInspectionPlanLoadError,
176 ) -> IntegrityDeepError {
177 match error {
178 AcceptedInspectionPlanLoadError::Selected { identity, error } => {
179 if entity != &IntegrityEntityIdentity::from_accepted_identity(&identity) {
180 return IntegrityJobError::EntityIdentityMismatch.into();
181 }
182 IntegrityDeepError::Uninspectable(IntegrityAuthorityDiagnostic::from_internal(
183 &error,
184 ))
185 }
186 AcceptedInspectionPlanLoadError::Unselected(error) => {
187 IntegrityDeepError::Uninspectable(IntegrityAuthorityDiagnostic::from_internal(
188 &error,
189 ))
190 }
191 }
192 }
193
194 fn continue_deep_integrity(
196 &self,
197 job_id: IntegrityJobId,
198 owner: &IntegrityJobOwner,
199 acknowledged_sequence: u64,
200 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
201 continue_deep_integrity_job(
202 &self.db,
203 job_id,
204 owner,
205 acknowledged_sequence,
206 |entity_path| {
207 let runtime_entity = self.db.accepted_runtime_entity_for_path(entity_path)?;
208 let store = self.db.recovered_store(runtime_entity.store_path())?;
209 self.accepted_inspection_plan_for_runtime_entity(runtime_entity, store)
210 .map_err(AcceptedInspectionPlanLoadError::into_internal)
211 },
212 )
213 }
214
215 fn abort_deep_integrity(
217 job_id: IntegrityJobId,
218 owner: &IntegrityJobOwner,
219 ) -> Result<IntegrityJobReceipt, IntegrityDeepError> {
220 abort_deep_integrity_job::<C>(job_id, owner)
221 }
222}