1use crate::{
6 bitmap,
7 index::Unordered as UnorderedIndex,
8 journal::{
9 contiguous::{Contiguous, MutableContiguous},
10 Error as JournalError,
11 },
12 mmr::{
13 self,
14 grafting::{Hasher as GraftingHasher, Storage as GraftingStorage},
15 hasher::Hasher as MmrHasher,
16 journaled::Mmr,
17 Location, Proof, StandardHasher,
18 },
19 qmdb::{
20 any::{
21 self,
22 operation::{update::Update, Operation},
23 ValueEncoding,
24 },
25 current::proof::RangeProof,
26 store::{self, LogStore, MerkleizedStore, PrunableStore},
27 DurabilityState, Durable, Error, NonDurable,
28 },
29 Persistable,
30};
31use commonware_codec::{Codec, CodecShared};
32use commonware_cryptography::{Digest, DigestOf, Hasher};
33use commonware_runtime::{Clock, Metrics, Storage};
34use commonware_utils::{bitmap::Prunable as PrunableBitMap, Array};
35use core::{num::NonZeroU64, ops::Range};
36
37const fn grafting_height<const N: usize>() -> u32 {
39 PrunableBitMap::<N>::CHUNK_SIZE_BITS.trailing_zeros()
40}
41
42mod private {
43 pub trait Sealed {}
44}
45
46pub trait State<D: Digest>: private::Sealed + Sized + Send + Sync {
48 type AnyState: mmr::mem::State<D>;
50 type BitMapState: bitmap::State<D>;
52}
53
54pub struct Merkleized<D: Digest> {
56 pub(super) root: D,
58}
59
60impl<D: Digest> private::Sealed for Merkleized<D> {}
61impl<D: Digest> State<D> for Merkleized<D> {
62 type AnyState = mmr::mem::Clean<D>;
63 type BitMapState = bitmap::Merkleized<D>;
64}
65
66pub struct Unmerkleized;
68
69impl private::Sealed for Unmerkleized {}
70impl<D: Digest> State<D> for Unmerkleized {
71 type AnyState = mmr::mem::Dirty;
72 type BitMapState = bitmap::Unmerkleized;
73}
74
75pub struct Db<
77 E: Storage + Clock + Metrics,
78 C: Contiguous<Item: CodecShared>,
79 I: UnorderedIndex<Value = Location>,
80 H: Hasher,
81 U: Send + Sync,
82 const N: usize,
83 S: State<DigestOf<H>> = Merkleized<DigestOf<H>>,
84 D: DurabilityState = Durable,
85> {
86 pub(super) any: any::db::Db<E, C, I, H, U, S::AnyState, D>,
89
90 pub(super) status: bitmap::BitMap<E, H::Digest, N, S::BitMapState>,
93
94 pub(super) state: S,
96}
97
98impl<E, K, V, C, I, H, U, const N: usize, S, D> Db<E, C, I, H, U, N, S, D>
100where
101 E: Storage + Clock + Metrics,
102 K: Array,
103 V: ValueEncoding,
104 U: Update<K, V>,
105 C: Contiguous<Item = Operation<K, V, U>>,
106 I: UnorderedIndex<Value = Location>,
107 H: Hasher,
108 S: State<DigestOf<H>>,
109 D: DurabilityState,
110 Operation<K, V, U>: Codec,
111{
112 pub const fn inactivity_floor_loc(&self) -> Location {
115 self.any.inactivity_floor_loc()
116 }
117
118 pub const fn is_empty(&self) -> bool {
120 self.any.is_empty()
121 }
122
123 pub async fn get_metadata(&self) -> Result<Option<V::Value>, Error> {
125 self.any.get_metadata().await
126 }
127
128 pub const fn grafting_height() -> u32 {
133 grafting_height::<N>()
134 }
135
136 pub fn verify_range_proof(
148 hasher: &mut H,
149 proof: &RangeProof<H::Digest>,
150 start_loc: Location,
151 ops: &[Operation<K, V, U>],
152 chunks: &[[u8; N]],
153 root: &H::Digest,
154 ) -> bool {
155 let height = Self::grafting_height();
156
157 proof.verify(hasher, height, start_loc, ops, chunks, root)
158 }
159}
160
161impl<E, K, V, U, C, I, H, D, const N: usize> Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, D>
164where
165 E: Storage + Clock + Metrics,
166 K: Array,
167 V: ValueEncoding,
168 U: Update<K, V>,
169 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
170 I: UnorderedIndex<Value = Location>,
171 H: Hasher,
172 D: DurabilityState,
173 Operation<K, V, U>: Codec,
174{
175 pub const fn root(&self) -> H::Digest {
176 self.state.root
177 }
178
179 pub async fn range_proof(
189 &self,
190 hasher: &mut H,
191 start_loc: Location,
192 max_ops: NonZeroU64,
193 ) -> Result<(RangeProof<H::Digest>, Vec<Operation<K, V, U>>, Vec<[u8; N]>), Error> {
194 RangeProof::<H::Digest>::new_with_ops(
195 hasher,
196 &self.status,
197 Self::grafting_height(),
198 &self.any.log.mmr,
199 &self.any.log,
200 start_loc,
201 max_ops,
202 )
203 .await
204 }
205
206 pub async fn prune(&mut self, prune_loc: Location) -> Result<(), Error> {
214 self.status.write_pruned().await?;
218
219 self.any.prune(prune_loc).await
220 }
221}
222
223impl<E, K, V, U, C, I, H, const N: usize> Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, Durable>
225where
226 E: Storage + Clock + Metrics,
227 K: Array,
228 V: ValueEncoding,
229 U: Update<K, V>,
230 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
231 I: UnorderedIndex<Value = Location>,
232 H: Hasher,
233 Operation<K, V, U>: Codec,
234{
235 pub async fn sync(&mut self) -> Result<(), Error> {
237 self.any.sync().await?;
238
239 self.status.write_pruned().await.map_err(Into::into)
242 }
243
244 pub async fn destroy(self) -> Result<(), Error> {
246 self.status.destroy().await?;
248
249 self.any.destroy().await
251 }
252
253 pub fn into_mutable(self) -> Db<E, C, I, H, U, N, Unmerkleized, NonDurable> {
255 Db {
256 any: self.any.into_mutable(),
257 status: self.status.into_dirty(),
258 state: Unmerkleized,
259 }
260 }
261}
262
263impl<E, K, V, U, C, I, H, const N: usize, D> Db<E, C, I, H, U, N, Unmerkleized, D>
265where
266 E: Storage + Clock + Metrics,
267 K: Array,
268 V: ValueEncoding,
269 U: Update<K, V>,
270 C: Contiguous<Item = Operation<K, V, U>>,
271 I: UnorderedIndex<Value = Location>,
272 H: Hasher,
273 D: DurabilityState,
274 Operation<K, V, U>: Codec,
275{
276 pub async fn into_merkleized(
278 self,
279 ) -> Result<Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, D>, Error> {
280 let mut any = self.any.into_merkleized();
282
283 let hasher = &mut any.log.hasher;
285 let mut status = merkleize_grafted_bitmap(hasher, self.status, &any.log.mmr).await?;
286
287 status.prune_to_bit(*any.inactivity_floor_loc)?;
289
290 let root = root(hasher, &status, &any.log.mmr).await?;
292
293 Ok(Db {
294 any,
295 status,
296 state: Merkleized { root },
297 })
298 }
299}
300
301impl<E, K, V, U, C, I, H, const N: usize> Db<E, C, I, H, U, N, Unmerkleized, Durable>
303where
304 E: Storage + Clock + Metrics,
305 K: Array,
306 V: ValueEncoding,
307 U: Update<K, V>,
308 C: Contiguous<Item = Operation<K, V, U>>,
309 I: UnorderedIndex<Value = Location>,
310 H: Hasher,
311 Operation<K, V, U>: Codec,
312{
313 pub fn into_mutable(self) -> Db<E, C, I, H, U, N, Unmerkleized, NonDurable> {
315 Db {
316 any: self.any.into_mutable(),
317 status: self.status,
318 state: Unmerkleized,
319 }
320 }
321}
322
323impl<E, K, V, U, C, I, H, const N: usize> Db<E, C, I, H, U, N, Unmerkleized, NonDurable>
325where
326 E: Storage + Clock + Metrics,
327 K: Array,
328 V: ValueEncoding,
329 U: Update<K, V>,
330 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
331 I: UnorderedIndex<Value = Location>,
332 H: Hasher,
333 Operation<K, V, U>: Codec,
334{
335 async fn apply_commit_op(
339 &mut self,
340 metadata: Option<V::Value>,
341 ) -> Result<Range<Location>, Error> {
342 let start_loc = self.any.last_commit_loc + 1;
343
344 self.status.set_bit(*self.any.last_commit_loc, false);
346
347 let inactivity_floor_loc = self.any.raise_floor_with_bitmap(&mut self.status).await?;
350
351 self.status.push(true);
353 let commit_op = Operation::CommitFloor(metadata, inactivity_floor_loc);
354
355 self.any.apply_commit_op(commit_op).await?;
356
357 Ok(start_loc..self.any.log.bounds().end)
358 }
359
360 pub async fn commit(
364 mut self,
365 metadata: Option<V::Value>,
366 ) -> Result<(Db<E, C, I, H, U, N, Unmerkleized, Durable>, Range<Location>), Error> {
367 let range = self.apply_commit_op(metadata).await?;
368
369 let any = any::db::Db {
371 log: self.any.log,
372 inactivity_floor_loc: self.any.inactivity_floor_loc,
373 last_commit_loc: self.any.last_commit_loc,
374 snapshot: self.any.snapshot,
375 durable_state: store::Durable,
376 active_keys: self.any.active_keys,
377 _update: core::marker::PhantomData,
378 };
379
380 Ok((
381 Db {
382 any,
383 status: self.status,
384 state: Unmerkleized,
385 },
386 range,
387 ))
388 }
389}
390
391impl<E, K, V, U, C, I, H, const N: usize> Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, NonDurable>
393where
394 E: Storage + Clock + Metrics,
395 K: Array,
396 V: ValueEncoding,
397 U: Update<K, V>,
398 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
399 I: UnorderedIndex<Value = Location>,
400 H: Hasher,
401 Operation<K, V, U>: Codec,
402{
403 pub fn into_mutable(self) -> Db<E, C, I, H, U, N, Unmerkleized, NonDurable> {
405 Db {
406 any: self.any.into_mutable(),
407 status: self.status.into_dirty(),
408 state: Unmerkleized,
409 }
410 }
411}
412
413impl<E, K, V, U, C, I, H, const N: usize> Persistable
414 for Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, Durable>
415where
416 E: Storage + Clock + Metrics,
417 K: Array,
418 V: ValueEncoding,
419 U: Update<K, V>,
420 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
421 I: UnorderedIndex<Value = Location>,
422 H: Hasher,
423 Operation<K, V, U>: Codec,
424{
425 type Error = Error;
426
427 async fn commit(&mut self) -> Result<(), Error> {
428 Ok(())
430 }
431
432 async fn sync(&mut self) -> Result<(), Error> {
433 self.sync().await
434 }
435
436 async fn destroy(self) -> Result<(), Error> {
437 self.destroy().await
438 }
439}
440
441impl<E, K, V, U, C, I, H, D, const N: usize> MerkleizedStore
446 for Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, D>
447where
448 E: Storage + Clock + Metrics,
449 K: Array,
450 V: ValueEncoding,
451 U: Update<K, V>,
452 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
453 I: UnorderedIndex<Value = Location>,
454 H: Hasher,
455 D: DurabilityState,
456 Operation<K, V, U>: Codec,
457{
458 type Digest = H::Digest;
459 type Operation = Operation<K, V, U>;
460
461 fn root(&self) -> H::Digest {
462 self.root()
463 }
464
465 async fn historical_proof(
466 &self,
467 historical_size: Location,
468 start_loc: Location,
469 max_ops: NonZeroU64,
470 ) -> Result<(Proof<Self::Digest>, Vec<Self::Operation>), Error> {
471 self.any
472 .historical_proof(historical_size, start_loc, max_ops)
473 .await
474 }
475}
476
477impl<E, K, V, U, C, I, H, const N: usize, S, D> LogStore for Db<E, C, I, H, U, N, S, D>
478where
479 E: Storage + Clock + Metrics,
480 K: Array,
481 V: ValueEncoding,
482 U: Update<K, V>,
483 C: Contiguous<Item = Operation<K, V, U>>,
484 I: UnorderedIndex<Value = Location>,
485 H: Hasher,
486 S: State<DigestOf<H>>,
487 D: DurabilityState,
488 Operation<K, V, U>: Codec,
489{
490 type Value = V::Value;
491
492 fn bounds(&self) -> std::ops::Range<Location> {
493 self.any.bounds()
494 }
495
496 fn inactivity_floor_loc(&self) -> Location {
497 self.inactivity_floor_loc()
498 }
499
500 async fn get_metadata(&self) -> Result<Option<V::Value>, Error> {
501 self.get_metadata().await
502 }
503
504 fn is_empty(&self) -> bool {
505 self.is_empty()
506 }
507}
508
509impl<E, K, V, U, C, I, H, const N: usize, D> PrunableStore
510 for Db<E, C, I, H, U, N, Merkleized<DigestOf<H>>, D>
511where
512 E: Storage + Clock + Metrics,
513 K: Array,
514 V: ValueEncoding,
515 U: Update<K, V>,
516 C: MutableContiguous<Item = Operation<K, V, U>> + Persistable<Error = JournalError>,
517 I: UnorderedIndex<Value = Location>,
518 H: Hasher,
519 D: DurabilityState,
520 Operation<K, V, U>: Codec,
521{
522 async fn prune(&mut self, prune_loc: Location) -> Result<(), Error> {
523 self.prune(prune_loc).await
524 }
525}
526
527pub(super) async fn root<E: Storage + Clock + Metrics, H: Hasher, const N: usize>(
529 hasher: &mut StandardHasher<H>,
530 status: &bitmap::MerkleizedBitMap<E, H::Digest, N>,
531 mmr: &Mmr<E, H::Digest, mmr::mem::Clean<DigestOf<H>>>,
532) -> Result<H::Digest, Error> {
533 let grafted_mmr = GraftingStorage::<'_, H, _, _>::new(status, mmr, grafting_height::<N>());
534 let mmr_root = grafted_mmr.root(hasher).await?;
535
536 let (last_chunk, next_bit) = status.last_chunk();
538 if next_bit == PrunableBitMap::<N>::CHUNK_SIZE_BITS {
539 return Ok(mmr_root);
541 }
542
543 hasher.inner().update(last_chunk);
547 let last_chunk_digest = hasher.inner().finalize();
548
549 Ok(bitmap::partial_chunk_root::<H, N>(
550 hasher.inner(),
551 &mmr_root,
552 next_bit,
553 &last_chunk_digest,
554 ))
555}
556
557pub(super) async fn merkleize_grafted_bitmap<E, H, const N: usize>(
565 hasher: &mut StandardHasher<H>,
566 status: bitmap::UnmerkleizedBitMap<E, H::Digest, N>,
567 mmr: &impl mmr::storage::Storage<H::Digest>,
568) -> Result<bitmap::MerkleizedBitMap<E, H::Digest, N>, Error>
569where
570 E: Storage + Clock + Metrics,
571 H: Hasher,
572{
573 let mut grafter = GraftingHasher::new(hasher, grafting_height::<N>());
574 grafter
575 .load_grafted_digests(&status.dirty_chunks(), mmr)
576 .await?;
577 status.merkleize(&mut grafter).await.map_err(Into::into)
578}