1use crate::{
29 index::Factory as IndexFactory,
30 journal::{
31 authenticated,
32 contiguous::{fixed, variable, Contiguous, Mutable},
33 },
34 merkle::{
35 full::{self, Merkle},
36 Graftable, Location,
37 },
38 qmdb::{
39 self,
40 any::{
41 db::Db as AnyDb,
42 operation::{update::Update, Operation},
43 ordered::{
44 fixed::{Operation as OrderedFixedOp, Update as OrderedFixedUpdate},
45 variable::{Operation as OrderedVariableOp, Update as OrderedVariableUpdate},
46 },
47 unordered::{
48 fixed::{Operation as UnorderedFixedOp, Update as UnorderedFixedUpdate},
49 variable::{Operation as UnorderedVariableOp, Update as UnorderedVariableUpdate},
50 },
51 FixedValue, VariableValue,
52 },
53 bitmap::Shared,
54 current::{
55 db, grafting,
56 ordered::{
57 fixed::Db as CurrentOrderedFixedDb, variable::Db as CurrentOrderedVariableDb,
58 },
59 unordered::{
60 fixed::Db as CurrentUnorderedFixedDb, variable::Db as CurrentUnorderedVariableDb,
61 },
62 FixedConfig, VariableConfig,
63 },
64 metrics::Metrics as AnyMetrics,
65 operation::{Committable, Key, Operation as _},
66 sync::{resolver::fetch_operations, Database, DatabaseConfig as Config},
67 },
68 translator::Translator,
69 Context,
70};
71use commonware_codec::{Codec, CodecShared, Read as CodecRead};
72use commonware_cryptography::{DigestOf, Hasher};
73use commonware_parallel::Strategy;
74use commonware_utils::{bitmap::Prunable as BitMap, channel::oneshot, range::NonEmptyRange, Array};
75use core::num::NonZeroUsize;
76use std::sync::Arc;
77
78#[cfg(test)]
79pub(crate) mod tests;
80
81impl<T: Translator, J: Clone, S: Strategy> Config for super::Config<T, J, S> {
82 type JournalConfig = J;
83
84 fn journal_config(&self) -> Self::JournalConfig {
85 self.journal_config.clone()
86 }
87}
88
89#[allow(clippy::too_many_arguments)]
97async fn build_db<F, E, U, I, H, J, T, const N: usize, S>(
98 context: E,
99 merkle_config: full::Config<S>,
100 log: J,
101 translator: T,
102 pinned_nodes: Option<Vec<H::Digest>>,
103 range: NonEmptyRange<Location<F>>,
104 apply_batch_size: usize,
105 cache_size: Option<NonZeroUsize>,
106 metadata_partition: String,
107 strategy: S,
108) -> Result<db::Db<F, E, J, I, H, U, N, S>, qmdb::Error<F>>
109where
110 F: Graftable,
111 E: Context,
112 U: Update + Send + Sync + 'static,
113 I: IndexFactory<T, Value = Location<F>>,
114 H: Hasher,
115 T: Translator,
116 J: Mutable<Item = Operation<F, U>>,
117 S: Strategy,
118 Operation<F, U>: Codec + Committable + CodecShared,
119{
120 let merkle = Merkle::<F, _, _, S>::init_sync(
122 context.child("merkle"),
123 full::SyncConfig {
124 config: merkle_config,
125 range: range.clone(),
126 pinned_nodes,
127 },
128 )
129 .await?;
130 let index = I::new(context.child("index"), translator);
131 let log = authenticated::Journal::<F, _, _, _, S>::from_components(
132 merkle,
133 log,
134 qmdb::hasher::<H>(),
135 apply_batch_size as u64,
136 )
137 .await?;
138
139 let pruned_chunks = (*range.start() / BitMap::<N>::CHUNK_SIZE_BITS) as usize;
146 let bitmap = BitMap::<N>::new_with_pruned_chunks(pruned_chunks)
147 .map_err(|_| qmdb::Error::<F>::DataCorrupted("pruned chunks overflow"))?;
148 let bitmap = Arc::new(Shared::<N>::new(bitmap));
149
150 let any_metrics = AnyMetrics::new(context.child("any"));
153 let any: AnyDb<F, E, J, I, H, U, N, S> =
154 AnyDb::init_from_log(index, log, Some(bitmap), cache_size, any_metrics).await?;
155
156 let grafted_pinned_nodes = {
164 let grafted_boundary = Location::<F>::new(pruned_chunks as u64);
165 let grafting_height = grafting::height::<N>();
166 let mut pins = Vec::new();
167 for grafted_pos in F::nodes_to_pin(grafted_boundary) {
168 let ops_pos = grafting::grafted_to_ops_pos::<F>(grafted_pos, grafting_height);
169 let digest = any
170 .log
171 .merkle
172 .get_node(ops_pos)
173 .await?
174 .ok_or(qmdb::Error::<F>::DataCorrupted("missing ops pinned node"))?;
175 pins.push(digest);
176 }
177 pins
178 };
179
180 let ops_size = any.log.merkle.size();
182 let ops_leaves = Location::<F>::try_from(ops_size)?;
183 let grafted_tree = db::build_grafted_tree::<F, H, S, N>(
184 any.bitmap.as_ref(),
185 &grafted_pinned_nodes,
186 &any.log.merkle,
187 ops_leaves,
188 &strategy,
189 )
190 .await?;
191
192 let storage = grafting::Storage::<F, H, _, _>::new(
196 &grafted_tree,
197 grafting::height::<N>(),
198 &any.log.merkle,
199 );
200 let partial = db::partial_chunk(any.bitmap.as_ref());
201 let ops_root = any.root();
202 let root = db::compute_db_root::<F, H, _, _, N>(
203 any.bitmap.as_ref(),
204 &storage,
205 ops_leaves,
206 partial,
207 any.inactivity_floor_loc,
208 &ops_root,
209 )
210 .await?;
211
212 let (metadata, _, _) =
214 db::init_metadata::<F, E, DigestOf<H>>(context.child("metadata"), &metadata_partition)
215 .await?;
216
217 let metrics = db::Metrics::new(context);
218 let mut current_db = db::Db {
219 any,
220 grafted_tree: Arc::new(grafted_tree),
221 metadata,
222 strategy,
223 root,
224 metrics,
225 #[cfg(test)]
226 halt_before_prune_log: false,
227 };
228 current_db.update_metrics();
229
230 current_db.sync_metadata().await?;
232
233 Ok(current_db)
234}
235
236macro_rules! impl_current_sync_database {
239 ($db:ident, $op:ident, $update:ident,
240 $journal:ty, $config:ty,
241 $key_bound:path, $value_bound:ident
242 $(; $($where_extra:tt)+)?) => {
243 impl<F, E, K, V, H, T, const N: usize, S> Database for $db<F, E, K, V, H, T, N, S>
244 where
245 F: Graftable,
246 E: Context,
247 K: $key_bound,
248 V: $value_bound + 'static,
249 H: Hasher,
250 T: Translator,
251 S: Strategy,
252 $($($where_extra)+)?
253 {
254 type Family = F;
255 type Context = E;
256 type Op = $op<F, K, V>;
257 type Journal = $journal;
258 type Hasher = H;
259 type Config = $config;
260 type Digest = H::Digest;
261
262 async fn from_sync_result(
263 context: Self::Context,
264 config: Self::Config,
265 log: Self::Journal,
266 pinned_nodes: Option<Vec<Self::Digest>>,
267 range: NonEmptyRange<Location<F>>,
268 apply_batch_size: usize,
269 ) -> Result<Self, qmdb::Error<F>> {
270 let merkle_config = config.merkle_config.clone();
271 let metadata_partition = config.grafted_metadata_partition.clone();
272 let strategy = config.merkle_config.strategy.clone();
273 let translator = config.translator.clone();
274 let cache_size = config.init_cache_size;
275 build_db::<F, _, $update<K, V>, _, H, _, T, N, _>(
276 context,
277 merkle_config,
278 log,
279 translator,
280 pinned_nodes,
281 range,
282 apply_batch_size,
283 cache_size,
284 metadata_partition,
285 strategy,
286 )
287 .await
288 }
289
290 async fn local_boundary_nodes(
291 context: Self::Context,
292 config: &Self::Config,
293 target: &qmdb::sync::Target<Self::Family, Self::Digest>,
294 journal: &Self::Journal,
295 ) -> Result<Option<Vec<Self::Digest>>, qmdb::Error<F>> {
296 if target.range.start() == Location::new(0)
297 || !qmdb::sync::journal_covers_range(journal.bounds(), &target.range)
298 {
299 return Ok(None);
300 }
301
302 let inactivity_floor = qmdb::find_inactivity_floor_at::<F, _>(
305 journal,
306 target.range.end(),
307 |op| op.has_floor(),
308 )
309 .await?;
310
311 qmdb::sync::local_boundary_nodes::<F, _, H, S>(
312 context,
313 config.merkle_config.clone(),
314 target,
315 inactivity_floor,
316 )
317 .await
318 }
319
320 fn root(&self) -> Self::Digest {
323 self.any.root()
324 }
325 }
326 };
327}
328
329impl_current_sync_database!(
330 CurrentUnorderedFixedDb, UnorderedFixedOp, UnorderedFixedUpdate,
331 fixed::Journal<E, Self::Op>, FixedConfig<T, S>,
332 Array, FixedValue
333);
334
335impl_current_sync_database!(
336 CurrentUnorderedVariableDb, UnorderedVariableOp, UnorderedVariableUpdate,
337 variable::Journal<E, Self::Op>,
338 VariableConfig<T, <UnorderedVariableOp<F, K, V> as CodecRead>::Cfg, S>,
339 Key, VariableValue;
340 UnorderedVariableOp<F, K, V>: CodecShared
341);
342
343impl_current_sync_database!(
344 CurrentOrderedFixedDb, OrderedFixedOp, OrderedFixedUpdate,
345 fixed::Journal<E, Self::Op>, FixedConfig<T, S>,
346 Array, FixedValue
347);
348
349impl_current_sync_database!(
350 CurrentOrderedVariableDb, OrderedVariableOp, OrderedVariableUpdate,
351 variable::Journal<E, Self::Op>,
352 VariableConfig<T, <OrderedVariableOp<F, K, V> as CodecRead>::Cfg, S>,
353 Key, VariableValue;
354 OrderedVariableOp<F, K, V>: CodecShared
355);
356
357macro_rules! impl_current_resolver {
363 ($db:ident, $op:ident, $val_bound:ident, $key_bound:path $(; $($where_extra:tt)+)?) => {
364 impl<F, E, K, V, H, T, const N: usize, S> crate::qmdb::sync::Resolver
365 for std::sync::Arc<$db<F, E, K, V, H, T, N, S>>
366 where
367 F: Graftable,
368 E: Context,
369 K: $key_bound,
370 V: $val_bound + Send + Sync + 'static,
371 H: Hasher,
372 T: Translator + Send + Sync + 'static,
373 T::Key: Send + Sync,
374 S: Strategy,
375 $($($where_extra)+)?
376 {
377 type Family = F;
378 type Digest = H::Digest;
379 type Op = $op<F, K, V>;
380 type Error = qmdb::Error<F>;
381
382 async fn get_operations(
383 &self,
384 op_count: Location<F>,
385 start_loc: Location<F>,
386 max_ops: std::num::NonZeroU64,
387 include_pinned_nodes: bool,
388 _cancel_rx: oneshot::Receiver<()>,
389 ) -> Result<crate::qmdb::sync::FetchResult<F, Self::Op, Self::Digest>, Self::Error> {
390 fetch_operations(
391 op_count,
392 start_loc,
393 max_ops,
394 include_pinned_nodes,
395 |op_count, start_loc, max_ops| {
396 self.any.historical_proof(op_count, start_loc, max_ops)
397 },
398 |start_loc| self.any.pinned_nodes_at(start_loc),
399 )
400 .await
401 }
402 }
403 impl_current_resolver!(@locked AsyncRwLock, $db, $op, $val_bound, $key_bound $(; $($where_extra)+)?);
404 impl_current_resolver!(@locked TracedAsyncRwLock, $db, $op, $val_bound, $key_bound $(; $($where_extra)+)?);
405 };
406 (@locked $lock:ident, $db:ident, $op:ident, $val_bound:ident, $key_bound:path $(; $($where_extra:tt)+)?) => {
407
408 impl<F, E, K, V, H, T, const N: usize, S> crate::qmdb::sync::Resolver
409 for std::sync::Arc<
410 commonware_utils::sync::$lock<
411 $db<F, E, K, V, H, T, N, S>,
412 >,
413 >
414 where
415 F: Graftable,
416 E: Context,
417 K: $key_bound,
418 V: $val_bound + Send + Sync + 'static,
419 H: Hasher,
420 T: Translator + Send + Sync + 'static,
421 T::Key: Send + Sync,
422 S: Strategy,
423 $($($where_extra)+)?
424 {
425 type Family = F;
426 type Digest = H::Digest;
427 type Op = $op<F, K, V>;
428 type Error = qmdb::Error<F>;
429
430 async fn get_operations(
431 &self,
432 op_count: Location<F>,
433 start_loc: Location<F>,
434 max_ops: std::num::NonZeroU64,
435 include_pinned_nodes: bool,
436 _cancel_rx: oneshot::Receiver<()>,
437 ) -> Result<crate::qmdb::sync::FetchResult<F, Self::Op, Self::Digest>, qmdb::Error<F>> {
438 let db = self.read().await;
439 fetch_operations(
440 op_count,
441 start_loc,
442 max_ops,
443 include_pinned_nodes,
444 |op_count, start_loc, max_ops| {
445 db.any.historical_proof(op_count, start_loc, max_ops)
446 },
447 |start_loc| db.any.pinned_nodes_at(start_loc),
448 )
449 .await
450 }
451 }
452
453 impl<F, E, K, V, H, T, const N: usize, S> crate::qmdb::sync::Resolver
454 for std::sync::Arc<
455 commonware_utils::sync::$lock<
456 Option<$db<F, E, K, V, H, T, N, S>>,
457 >,
458 >
459 where
460 F: Graftable,
461 E: Context,
462 K: $key_bound,
463 V: $val_bound + Send + Sync + 'static,
464 H: Hasher,
465 T: Translator + Send + Sync + 'static,
466 T::Key: Send + Sync,
467 S: Strategy,
468 $($($where_extra)+)?
469 {
470 type Family = F;
471 type Digest = H::Digest;
472 type Op = $op<F, K, V>;
473 type Error = qmdb::Error<F>;
474
475 async fn get_operations(
476 &self,
477 op_count: Location<F>,
478 start_loc: Location<F>,
479 max_ops: std::num::NonZeroU64,
480 include_pinned_nodes: bool,
481 _cancel_rx: oneshot::Receiver<()>,
482 ) -> Result<crate::qmdb::sync::FetchResult<F, Self::Op, Self::Digest>, qmdb::Error<F>> {
483 let guard = self.read().await;
484 let db = guard.as_ref().ok_or(qmdb::Error::<F>::KeyNotFound)?;
485 fetch_operations(
486 op_count,
487 start_loc,
488 max_ops,
489 include_pinned_nodes,
490 |op_count, start_loc, max_ops| {
491 db.any.historical_proof(op_count, start_loc, max_ops)
492 },
493 |start_loc| db.any.pinned_nodes_at(start_loc),
494 )
495 .await
496 }
497 }
498 };
499}
500
501impl_current_resolver!(CurrentUnorderedFixedDb, UnorderedFixedOp, FixedValue, Array);
503
504impl_current_resolver!(
506 CurrentUnorderedVariableDb, UnorderedVariableOp, VariableValue, Key;
507 UnorderedVariableOp<F, K, V>: CodecShared,
508);
509
510impl_current_resolver!(CurrentOrderedFixedDb, OrderedFixedOp, FixedValue, Array);
512
513impl_current_resolver!(
515 CurrentOrderedVariableDb, OrderedVariableOp, VariableValue, Key;
516 OrderedVariableOp<F, K, V>: CodecShared,
517);