1use crate::{
30 Context,
31 index::{Factory as IndexFactory, Unordered as UnorderedIndex},
32 journal::{authenticated, contiguous::Mutable},
33 merkle::{
34 Graftable, Location,
35 full::{self, Merkle},
36 },
37 qmdb::{
38 self,
39 any::{
40 db::Db as AnyDb,
41 operation::{Operation, update::Update},
42 },
43 bitmap::Shared,
44 current::{db, grafting},
45 metrics::Metrics as AnyMetrics,
46 sync::{Database, DatabaseConfig as Config, Request, source},
47 },
48 translator::Translator,
49};
50use commonware_codec::Codec;
51use commonware_cryptography::{DigestOf, Hasher};
52use commonware_parallel::Strategy;
53use commonware_runtime::Spawner;
54use commonware_utils::{bitmap::Prunable as BitMap, range::NonEmptyRange};
55use core::num::NonZeroUsize;
56use std::{num::NonZeroU64, sync::Arc};
57
58#[cfg(test)]
59pub(crate) mod tests;
60
61impl<T: Translator, J: Clone, S: Strategy, B> Config for super::Config<T, J, S, B> {
62 type JournalConfig = J;
63
64 fn journal_config(&self) -> Self::JournalConfig {
65 self.journal_config.clone()
66 }
67}
68
69#[allow(clippy::too_many_arguments)]
77async fn build_db<F, E, U, I, H, J, const N: usize, S>(
78 context: E,
79 merkle_config: full::Config<S>,
80 log: J,
81 translator: I::Translator,
82 pinned_nodes: Option<Vec<H::Digest>>,
83 range: NonEmptyRange<Location<F>>,
84 apply_batch_size: NonZeroU64,
85 init_concurrency: <I as crate::qmdb::SnapshotBuild<F>>::Concurrency,
86 init_buffer: NonZeroUsize,
87 cache_size: Option<NonZeroUsize>,
88 metadata_partition: String,
89 strategy: S,
90) -> Result<db::Db<F, E, J, I, H, U, N, S>, qmdb::Error<F>>
91where
92 F: Graftable,
93 E: Context + Spawner,
94 U: Update,
95 I: IndexFactory + crate::qmdb::SnapshotBuild<F>,
96 H: Hasher,
97 J: Mutable<Item = Operation<F, U>> + 'static,
98 S: Strategy,
99 Operation<F, U>: Codec,
100{
101 let merkle = Merkle::<F, _, _, S>::init_sync(
103 context.child("merkle"),
104 full::SyncConfig {
105 config: merkle_config,
106 range: range.clone(),
107 pinned_nodes,
108 },
109 )
110 .await?;
111 let index = I::new(context.child("index"), translator);
112 let log = authenticated::Journal::<F, _, _, _, S>::from_components(
113 merkle,
114 log,
115 qmdb::hasher::<H>(),
116 apply_batch_size,
117 )
118 .await?;
119
120 let pruned_chunks = (*range.start() / BitMap::<N>::CHUNK_SIZE_BITS) as usize;
127 let bitmap = BitMap::<N>::new_with_pruned_chunks(pruned_chunks)
128 .map_err(|_| qmdb::Error::<F>::DataCorrupted("pruned chunks overflow"))?;
129 let bitmap = Arc::new(Shared::<N>::new(bitmap));
130
131 let snapshot_context = context.child("any_snapshot");
134 let any_metrics = AnyMetrics::new(context.child("any"));
135 let any: AnyDb<F, E, J, I, H, U, N, S> = AnyDb::init_from_log(
136 snapshot_context,
137 index,
138 log,
139 Some(bitmap),
140 init_concurrency,
141 init_buffer,
142 cache_size,
143 any_metrics,
144 )
145 .await?;
146
147 let grafted_pinned_nodes = {
155 let grafted_boundary = Location::<F>::new(pruned_chunks as u64);
156 let grafting_height = grafting::height::<N>();
157 let mut pinned_nodes = Vec::new();
158 for grafted_pos in F::nodes_to_pin(grafted_boundary) {
159 let ops_pos = grafting::grafted_to_ops_pos::<F>(grafted_pos, grafting_height);
160 let digest = any
161 .log
162 .merkle
163 .get_node(ops_pos)
164 .await?
165 .ok_or(qmdb::Error::<F>::DataCorrupted("missing ops pinned node"))?;
166 pinned_nodes.push(digest);
167 }
168 pinned_nodes
169 };
170
171 let (grafted_tree, graft_roots, root) = db::rebuild_grafted_tree::<F, H, S, N>(
175 any.bitmap.as_ref(),
176 &grafted_pinned_nodes,
177 &any.log.merkle,
178 any.inactivity_floor_loc,
179 any.root(),
180 &strategy,
181 )
182 .await?;
183
184 let (metadata, _, _) =
186 db::init_metadata::<F, E, DigestOf<H>>(context.child("metadata"), &metadata_partition)
187 .await?;
188
189 let metrics = db::Metrics::new(context);
190 let current_db = db::Db {
191 any,
192 grafted_tree: Arc::new(grafted_tree),
193 graft_roots,
194 metadata,
195 strategy,
196 root,
197 metrics,
198 #[cfg(test)]
199 halt_before_prune_log: false,
200 };
201 current_db.update_metrics();
202
203 let current_db = current_db.sync_metadata().await?;
205
206 Ok(current_db)
207}
208
209impl<F, E, C, I, H, U, const N: usize, S> Database for db::Db<F, E, C, I, H, U, N, S>
210where
211 F: Graftable,
212 E: Context + Spawner,
213 C: Mutable<Item = Operation<F, U>>
214 + crate::qmdb::sync::Journal<F, Context = E, Op = Operation<F, U>>
215 + 'static,
216 I: IndexFactory + crate::qmdb::SnapshotBuild<F> + UnorderedIndex<Value = Location<F>>,
217 H: Hasher,
218 U: Update,
219 S: Strategy,
220 Operation<F, U>: Codec,
221{
222 type Family = F;
223 type Context = E;
224 type Op = Operation<F, U>;
225 type Journal = C;
226 type Hasher = H;
227 type Config = super::Config<
228 I::Translator,
229 <C as crate::qmdb::sync::Journal<F>>::Config,
230 S,
231 <I as crate::qmdb::SnapshotBuild<F>>::Concurrency,
232 >;
233 type Digest = H::Digest;
234
235 async fn from_sync_result(
236 context: Self::Context,
237 config: Self::Config,
238 log: Self::Journal,
239 pinned_nodes: Option<Vec<Self::Digest>>,
240 range: NonEmptyRange<Location<F>>,
241 apply_batch_size: NonZeroU64,
242 ) -> Result<Self, qmdb::Error<F>> {
243 let strategy = config.merkle_config.strategy.clone();
244 build_db::<F, _, U, _, H, _, N, _>(
245 context,
246 config.merkle_config,
247 log,
248 config.translator,
249 pinned_nodes,
250 range,
251 apply_batch_size,
252 config.init_concurrency,
253 config.init_buffer,
254 config.init_cache,
255 config.grafted_metadata_partition,
256 strategy,
257 )
258 .await
259 }
260
261 async fn persist_sync_result(self) -> Result<Self, qmdb::Error<F>> {
262 Ok(self)
263 }
264
265 async fn local_pinned_nodes(
266 context: Self::Context,
267 config: &Self::Config,
268 target: &qmdb::sync::Target<Self::Family, Self::Digest>,
269 journal: &Self::Journal,
270 ) -> Result<Option<Vec<Self::Digest>>, qmdb::Error<F>> {
271 if target.range.start() == Location::new(0)
272 || !qmdb::sync::journal_covers_range(journal.bounds(), &target.range)
273 {
274 return Ok(None);
275 }
276
277 let inactivity_floor =
280 qmdb::find_inactivity_floor_at::<F, _>(journal, target.range.end()).await?;
281
282 qmdb::sync::local_pinned_nodes::<F, _, H, S>(
283 context,
284 config.merkle_config.clone(),
285 target,
286 inactivity_floor,
287 )
288 .await
289 }
290
291 fn root(&self) -> Self::Digest {
294 self.any.root()
295 }
296}
297
298impl<F, E, C, I, H, U, const N: usize, S> crate::qmdb::sync::Source
301 for db::Db<F, E, C, I, H, U, N, S>
302where
303 F: Graftable,
304 E: Context,
305 C: Mutable<Item = Operation<F, U>>,
306 I: UnorderedIndex<Value = Location<F>>,
307 H: Hasher,
308 U: Update,
309 S: Strategy,
310 Operation<F, U>: Codec,
311{
312 type Family = F;
313 type Digest = H::Digest;
314 type Op = Operation<F, U>;
315 type Error = qmdb::Error<F>;
316
317 async fn serve(&self, request: Request<F>) -> source::Result<Self> {
318 self.any.serve(request).await
319 }
320}