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