coven_database/store/store_session/retained_merge_replay/
circle_coverage.rs1use super::*;
2use crate::query_mapped_rows;
3#[cfg(any(test, feature = "test-utils"))]
4use crate::store::store_session::StoreRecords;
5
6impl StoreSession<'_> {
7 fn prepare_circle_restore_selection(
8 &mut self,
9 root: &coven_protocol::store_commit::StoreRootRef,
10 ) -> Result<CircleRestoreSelectionIndex, DbError> {
11 let tx = self.conn.unchecked_transaction().map_err(DbError::from)?;
12 crate::store::store_session::StoreTransaction::new(&tx, self.store_dir)
13 .seed_stream_activation_index_from_retained(self.verified_store_authority, root)?;
14 let rows = query_mapped_rows(
15 &tx,
16 "SELECT circle_id, control_coord FROM circle_control_activations
17 ORDER BY circle_id, control_coord",
18 [],
19 |row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)),
20 )?;
21 let mut circles: Vec<(
22 coven_protocol::circle::CircleId,
23 Vec<coven_protocol::circle::CircleControlCoord>,
24 )> = Vec::new();
25 for (circle_id, control_coord) in rows {
26 let circle_id: coven_protocol::circle::CircleId = circle_id
27 .parse()
28 .map_err(|error| DbError::context("parse retained Circle id", error))?;
29 let control: coven_protocol::circle::CircleControlCoord =
30 serde_json::from_str(&control_coord).map_err(|error| {
31 DbError::context("parse retained Circle control coordinate", error)
32 })?;
33 match circles.last_mut() {
34 Some((last_circle, controls)) if *last_circle == circle_id => {
35 controls.push(control)
36 }
37 _ => circles.push((circle_id, vec![control])),
38 }
39 }
40 let preserved_bootstraps = circle_bootstrap_coverage_refs_on(&tx)?;
41 tx.commit().map_err(DbError::from)?;
42 Ok(CircleRestoreSelectionIndex {
43 circles,
44 preserved_bootstraps,
45 })
46 }
47
48 fn retained_merge_materialization_by_ref(
49 &mut self,
50 root: &coven_protocol::store_commit::StoreRootRef,
51 reference: &StoreBatchCommitRef,
52 ) -> Result<OwnedVerifiedMergeMaterialization, DbError> {
53 let retained = self
54 .verified_store_authority
55 .retained_materialization_by_ref_on(
56 crate::store::store_session::StoreRecords::new(self.conn, self.store_dir),
57 reference,
58 )?;
59 if retained.root() != root {
60 return Err(DbError::Message(
61 "retained Merge materialization belongs to another Store root".to_string(),
62 ));
63 }
64 Ok(retained)
65 }
66
67 fn circle_replay_epoch_index(
68 &mut self,
69 root: &coven_protocol::store_commit::StoreRootRef,
70 ) -> Result<CircleReplayEpochIndex, DbError> {
71 self.verified_store_authority.retained_replay_inputs_on(
72 crate::store::store_session::StoreRecords::new(self.conn, self.store_dir),
73 root,
74 )?;
75 self.verified_store_authority.circle_replay_epoch_index_on(
76 crate::store::store_session::StoreRecords::new(self.conn, self.store_dir),
77 )
78 }
79}
80
81pub(crate) fn circle_bootstrap_coverage_ref_on(
82 conn: &Connection,
83 circle_id: coven_protocol::circle::CircleId,
84) -> Result<Option<coven_protocol::circle::CircleBootstrapCoverageRef>, DbError> {
85 let row: Option<(String, String, String, String, Vec<u8>)> = conn
86 .query_row(
87 "SELECT control_coord, activation_commit, exact_cut, image_hash, bootstrap_ref
88 FROM circle_bootstrap_coverage WHERE circle_id = ?1",
89 [circle_id.to_string()],
90 |row| {
91 Ok((
92 row.get(0)?,
93 row.get(1)?,
94 row.get(2)?,
95 row.get(3)?,
96 row.get(4)?,
97 ))
98 },
99 )
100 .optional()
101 .map_err(DbError::from)?;
102 let Some((control, activation_commit, exact_cut, image_hash, bootstrap_ref)) = row else {
103 return Ok(None);
104 };
105 decode_circle_bootstrap_coverage_ref(
106 circle_id,
107 control,
108 activation_commit,
109 exact_cut,
110 image_hash,
111 bootstrap_ref,
112 )
113 .map(Some)
114}
115
116pub(crate) fn circle_bootstrap_coverage_refs_on(
117 conn: &Connection,
118) -> Result<Vec<coven_protocol::circle::CircleBootstrapCoverageRef>, DbError> {
119 let rows = query_mapped_rows(
120 conn,
121 "SELECT circle_id, control_coord, activation_commit, exact_cut,
122 image_hash, bootstrap_ref
123 FROM circle_bootstrap_coverage ORDER BY circle_id",
124 [],
125 |row| {
126 Ok((
127 row.get::<_, String>(0)?,
128 row.get::<_, String>(1)?,
129 row.get::<_, String>(2)?,
130 row.get::<_, String>(3)?,
131 row.get::<_, String>(4)?,
132 row.get::<_, Vec<u8>>(5)?,
133 ))
134 },
135 )?;
136 let mut bootstraps = Vec::with_capacity(rows.len());
137 for (circle_id, control, activation_commit, exact_cut, image_hash, encoded_reference) in rows {
138 let circle_id: coven_protocol::circle::CircleId = circle_id
139 .parse()
140 .map_err(|error| DbError::context("parse retained Circle bootstrap id", error))?;
141 bootstraps.push(decode_circle_bootstrap_coverage_ref(
142 circle_id,
143 control,
144 activation_commit,
145 exact_cut,
146 image_hash,
147 encoded_reference,
148 )?);
149 }
150 Ok(bootstraps)
151}
152
153fn decode_circle_bootstrap_coverage_ref(
154 circle_id: coven_protocol::circle::CircleId,
155 control: String,
156 activation_commit: String,
157 exact_cut: String,
158 image_hash: String,
159 encoded_reference: Vec<u8>,
160) -> Result<coven_protocol::circle::CircleBootstrapCoverageRef, DbError> {
161 let control = serde_json::from_str(&control)
162 .map_err(|error| DbError::context("parse retained Circle bootstrap control", error))?;
163 let activation_commit = serde_json::from_str(&activation_commit)
164 .map_err(|error| DbError::context("parse retained Circle bootstrap activation", error))?;
165 let exact_cut: CommitFrontier = serde_json::from_str(&exact_cut)
166 .map_err(|error| DbError::context("parse retained Circle bootstrap coverage", error))?;
167 let bootstrap: coven_protocol::circle::CircleBootstrapRef =
168 serde_json::from_slice(&encoded_reference).map_err(|error| {
169 DbError::context("parse retained Circle bootstrap reference", error)
170 })?;
171 if serde_json::to_vec(&bootstrap)
172 .map_err(|error| DbError::context("serialize retained Circle bootstrap reference", error))?
173 != encoded_reference
174 || bootstrap.coverage != exact_cut
175 || bootstrap.image.image_hash.to_string() != image_hash
176 {
177 return Err(DbError::Message(
178 "retained Circle bootstrap row differs from its exact reference".to_string(),
179 ));
180 }
181 Ok(coven_protocol::circle::CircleBootstrapCoverageRef {
182 circle_id,
183 control,
184 activation_commit,
185 bootstrap,
186 })
187}
188
189impl StoreDatabase {
190 pub async fn prepare_circle_restore_selection(
191 &self,
192 root: coven_protocol::store_commit::StoreRootRef,
193 ) -> Result<CircleRestoreSelectionIndex, DbError> {
194 self.call_store(move |session| session.prepare_circle_restore_selection(&root))
195 .await
196 }
197
198 pub async fn retained_merge_materialization_by_ref(
199 &self,
200 root: coven_protocol::store_commit::StoreRootRef,
201 reference: StoreBatchCommitRef,
202 ) -> Result<OwnedVerifiedMergeMaterialization, DbError> {
203 self.call_store(move |session| {
204 session.retained_merge_materialization_by_ref(&root, &reference)
205 })
206 .await
207 }
208
209 pub async fn circle_replay_epoch_index(
210 &self,
211 root: coven_protocol::store_commit::StoreRootRef,
212 ) -> Result<CircleReplayEpochIndex, DbError> {
213 self.call_store(move |session| session.circle_replay_epoch_index(&root))
214 .await
215 }
216
217 #[cfg(any(test, feature = "test-utils"))]
218 pub(crate) fn circle_bootstrap_replay_inputs_on(
219 records: StoreRecords<'_>,
220 ) -> Result<
221 Vec<(
222 StoreBatchCommitRef,
223 coven_protocol::circle_activation::VerifiedCircleImage,
224 )>,
225 DbError,
226 > {
227 records
228 .claimed_circle_bootstrap_coverage_refs()?
229 .into_iter()
230 .map(|coverage| {
231 let image_bytes = records.payload(coverage.bootstrap.image.image_hash)?;
232 let image =
233 coven_protocol::circle_activation::VerifiedCircleImage::from_stored_image(
234 coverage.circle_id,
235 coverage.control,
236 coverage.bootstrap,
237 image_bytes,
238 )
239 .map_err(DbError::from)?;
240 Ok((coverage.activation_commit, image))
241 })
242 .collect()
243 }
244}
245
246#[cfg(any(test, feature = "test-utils"))]
247impl crate::store::store_session::StoreTransaction<'_, '_> {
248 pub(crate) fn circle_bootstrap_replay_inputs(
249 self,
250 ) -> Result<
251 Vec<(
252 StoreBatchCommitRef,
253 coven_protocol::circle_activation::VerifiedCircleImage,
254 )>,
255 DbError,
256 > {
257 StoreDatabase::circle_bootstrap_replay_inputs_on(
258 crate::store::store_session::StoreRecords::new(self.transaction, self.store_dir),
259 )
260 }
261}