1use crate::{
29 Context,
30 index::{Factory as IndexFactory, Unordered as UnorderedIndex},
31 journal::{
32 authenticated,
33 contiguous::{Contiguous, Mutable, fixed, variable},
34 },
35 merkle::{
36 Graftable, Location,
37 full::{self, Merkle},
38 },
39 qmdb::{
40 self,
41 any::{
42 FixedValue, VariableValue,
43 db::Db as AnyDb,
44 operation::{Operation, update::Update},
45 ordered::{
46 fixed::{Operation as OrderedFixedOp, Update as OrderedFixedUpdate},
47 variable::{Operation as OrderedVariableOp, Update as OrderedVariableUpdate},
48 },
49 unordered::{
50 fixed::{Operation as UnorderedFixedOp, Update as UnorderedFixedUpdate},
51 variable::{Operation as UnorderedVariableOp, Update as UnorderedVariableUpdate},
52 },
53 },
54 bitmap::Shared,
55 current::{
56 FixedConfig, VariableConfig, db, grafting,
57 ordered::{
58 fixed::Db as CurrentOrderedFixedDb, variable::Db as CurrentOrderedVariableDb,
59 },
60 unordered::{
61 fixed::Db as CurrentUnorderedFixedDb, variable::Db as CurrentUnorderedVariableDb,
62 },
63 },
64 metrics::Metrics as AnyMetrics,
65 operation::Key,
66 sync::{Database, DatabaseConfig as Config, FeedbackTx, Request, Response},
67 },
68 translator::Translator,
69};
70use commonware_codec::{Codec, CodecShared, Read as CodecRead};
71use commonware_cryptography::{DigestOf, Hasher};
72use commonware_parallel::Strategy;
73use commonware_runtime::Spawner;
74use commonware_utils::{Array, bitmap::Prunable as BitMap, range::NonEmptyRange};
75use core::num::NonZeroUsize;
76use std::{num::NonZeroU64, 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, const N: usize, S>(
98 context: E,
99 merkle_config: full::Config<S>,
100 log: J,
101 translator: I::Translator,
102 pinned_nodes: Option<Vec<H::Digest>>,
103 range: NonEmptyRange<Location<F>>,
104 apply_batch_size: NonZeroU64,
105 init_concurrency: <I as crate::qmdb::SnapshotBuild<F>>::Concurrency,
106 init_buffer: NonZeroUsize,
107 cache_size: Option<NonZeroUsize>,
108 metadata_partition: String,
109 strategy: S,
110) -> Result<db::Db<F, E, J, I, H, U, N, S>, qmdb::Error<F>>
111where
112 F: Graftable,
113 E: Context + Spawner,
114 U: Update,
115 I: IndexFactory + crate::qmdb::SnapshotBuild<F>,
116 H: Hasher,
117 J: Mutable<Item = Operation<F, U>> + 'static,
118 S: Strategy,
119 Operation<F, U>: Codec,
120{
121 let merkle = Merkle::<F, _, _, S>::init_sync(
123 context.child("merkle"),
124 full::SyncConfig {
125 config: merkle_config,
126 range: range.clone(),
127 pinned_nodes,
128 },
129 )
130 .await?;
131 let index = I::new(context.child("index"), translator);
132 let log = authenticated::Journal::<F, _, _, _, S>::from_components(
133 merkle,
134 log,
135 qmdb::hasher::<H>(),
136 apply_batch_size.get(),
137 )
138 .await?;
139
140 let pruned_chunks = (*range.start() / BitMap::<N>::CHUNK_SIZE_BITS) as usize;
147 let bitmap = BitMap::<N>::new_with_pruned_chunks(pruned_chunks)
148 .map_err(|_| qmdb::Error::<F>::DataCorrupted("pruned chunks overflow"))?;
149 let bitmap = Arc::new(Shared::<N>::new(bitmap));
150
151 let snapshot_context = context.child("any_snapshot");
154 let any_metrics = AnyMetrics::new(context.child("any"));
155 let any: AnyDb<F, E, J, I, H, U, N, S> = AnyDb::init_from_log(
156 snapshot_context,
157 index,
158 log,
159 Some(bitmap),
160 init_concurrency,
161 init_buffer,
162 cache_size,
163 any_metrics,
164 )
165 .await?;
166
167 let grafted_pinned_nodes = {
175 let grafted_boundary = Location::<F>::new(pruned_chunks as u64);
176 let grafting_height = grafting::height::<N>();
177 let mut pinned_nodes = Vec::new();
178 for grafted_pos in F::nodes_to_pin(grafted_boundary) {
179 let ops_pos = grafting::grafted_to_ops_pos::<F>(grafted_pos, grafting_height);
180 let digest = any
181 .log
182 .merkle
183 .get_node(ops_pos)
184 .await?
185 .ok_or(qmdb::Error::<F>::DataCorrupted("missing ops pinned node"))?;
186 pinned_nodes.push(digest);
187 }
188 pinned_nodes
189 };
190
191 let (grafted_tree, root) = db::rebuild_grafted_tree::<F, H, S, N>(
195 any.bitmap.as_ref(),
196 &grafted_pinned_nodes,
197 &any.log.merkle,
198 any.inactivity_floor_loc,
199 any.root(),
200 &strategy,
201 )
202 .await?;
203
204 let (metadata, _, _) =
206 db::init_metadata::<F, E, DigestOf<H>>(context.child("metadata"), &metadata_partition)
207 .await?;
208
209 let metrics = db::Metrics::new(context);
210 let current_db = db::Db {
211 any,
212 grafted_tree: Arc::new(grafted_tree),
213 metadata,
214 strategy,
215 root,
216 metrics,
217 #[cfg(test)]
218 halt_before_prune_log: false,
219 };
220 current_db.update_metrics();
221
222 let current_db = current_db.sync_metadata().await?;
224
225 Ok(current_db)
226}
227
228macro_rules! impl_current_sync_database {
231 ($db:ident, $op:ident, $update:ident,
232 $journal:ty, $config:ty,
233 $key_bound:path, $value_bound:ident
234 $(; $($where_extra:tt)+)?) => {
235 impl<F, E, K, V, H, T, const N: usize, S> Database for $db<F, E, K, V, H, T, N, S>
236 where
237 F: Graftable,
238 E: Context + Spawner,
239 K: $key_bound,
240 V: $value_bound + 'static,
241 H: Hasher,
242 T: Translator,
243 S: Strategy,
244 $($($where_extra)+)?
245 {
246 type Family = F;
247 type Context = E;
248 type Op = $op<F, K, V>;
249 type Journal = $journal;
250 type Hasher = H;
251 type Config = $config;
252 type Digest = H::Digest;
253
254 async fn from_sync_result(
255 context: Self::Context,
256 config: Self::Config,
257 log: Self::Journal,
258 pinned_nodes: Option<Vec<Self::Digest>>,
259 range: NonEmptyRange<Location<F>>,
260 apply_batch_size: NonZeroU64,
261 ) -> Result<Self, qmdb::Error<F>> {
262 let merkle_config = config.merkle_config.clone();
263 let metadata_partition = config.grafted_metadata_partition.clone();
264 let strategy = config.merkle_config.strategy.clone();
265 let translator = config.translator.clone();
266 let cache_size = config.init_cache_size;
267 let init_buffer = config.init_buffer;
268 let init_concurrency = config.init_concurrency;
269 build_db::<F, _, $update<K, V>, _, H, _, N, _>(
270 context,
271 merkle_config,
272 log,
273 translator,
274 pinned_nodes,
275 range,
276 apply_batch_size,
277 init_concurrency,
278 init_buffer,
279 cache_size,
280 metadata_partition,
281 strategy,
282 )
283 .await
284 }
285
286 async fn persist_sync_result(self) -> Result<Self, qmdb::Error<F>> {
287 Ok(self)
288 }
289
290 async fn local_pinned_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 =
305 qmdb::find_inactivity_floor_at::<F, _>(journal, target.range.end()).await?;
306
307 qmdb::sync::local_pinned_nodes::<F, _, H, S>(
308 context,
309 config.merkle_config.clone(),
310 target,
311 inactivity_floor,
312 )
313 .await
314 }
315
316 fn root(&self) -> Self::Digest {
319 self.any.root()
320 }
321 }
322 };
323}
324
325impl_current_sync_database!(
326 CurrentUnorderedFixedDb, UnorderedFixedOp, UnorderedFixedUpdate,
327 fixed::Journal<E, Self::Op>, FixedConfig<T, S>,
328 Array, FixedValue
329);
330
331impl_current_sync_database!(
332 CurrentUnorderedVariableDb, UnorderedVariableOp, UnorderedVariableUpdate,
333 variable::Journal<E, Self::Op>,
334 VariableConfig<T, <UnorderedVariableOp<F, K, V> as CodecRead>::Cfg, S>,
335 Key, VariableValue;
336 UnorderedVariableOp<F, K, V>: CodecShared
337);
338
339impl_current_sync_database!(
340 CurrentOrderedFixedDb, OrderedFixedOp, OrderedFixedUpdate,
341 fixed::Journal<E, Self::Op>, FixedConfig<T, S>,
342 Array, FixedValue
343);
344
345impl_current_sync_database!(
346 CurrentOrderedVariableDb, OrderedVariableOp, OrderedVariableUpdate,
347 variable::Journal<E, Self::Op>,
348 VariableConfig<T, <OrderedVariableOp<F, K, V> as CodecRead>::Cfg, S>,
349 Key, VariableValue;
350 OrderedVariableOp<F, K, V>: CodecShared
351);
352
353impl<F, E, C, I, H, U, const N: usize, S> crate::qmdb::sync::Source
356 for db::Db<F, E, C, I, H, U, N, S>
357where
358 F: Graftable,
359 E: Context,
360 C: Mutable<Item = Operation<F, U>>,
361 I: UnorderedIndex<Value = Location<F>>,
362 H: Hasher,
363 U: Update,
364 S: Strategy,
365 Operation<F, U>: Codec,
366{
367 type Family = F;
368 type Digest = H::Digest;
369 type Op = Operation<F, U>;
370 type Error = qmdb::Error<F>;
371
372 async fn serve(
373 &self,
374 request: Request<F>,
375 ) -> Result<(Response<F, Self::Op, H::Digest>, FeedbackTx), qmdb::Error<F>> {
376 self.any.serve(request).await
377 }
378}