1pub use super::db::KeyValueProof;
11use crate::{
12 bitmap::MerkleizedBitMap,
13 journal::contiguous::fixed::Journal,
14 mmr::{Location, StandardHasher},
15 qmdb::{
16 any::{
17 ordered::fixed::{Db as AnyDb, Operation},
18 value::FixedEncoding,
19 FixedValue,
20 },
21 current::{
22 db::{merkleize_grafted_bitmap, root, Merkleized},
23 FixedConfig as Config,
24 },
25 Durable, Error,
26 },
27 translator::Translator,
28};
29use commonware_codec::FixedSize;
30use commonware_cryptography::{DigestOf, Hasher};
31use commonware_runtime::{Clock, Metrics, Storage as RStorage};
32use commonware_utils::Array;
33
34pub type Db<E, K, V, H, T, const N: usize, S = Merkleized<DigestOf<H>>, D = Durable> =
35 super::db::Db<E, Journal<E, Operation<K, V>>, K, FixedEncoding<V>, H, T, N, S, D>;
36
37impl<
39 E: RStorage + Clock + Metrics,
40 K: Array,
41 V: FixedValue,
42 H: Hasher,
43 T: Translator,
44 const N: usize,
45 > Db<E, K, V, H, T, N, Merkleized<DigestOf<H>>, Durable>
46{
47 pub async fn init(context: E, config: Config<T>) -> Result<Self, Error> {
50 const {
52 assert!(
56 N.is_multiple_of(H::Digest::SIZE),
57 "chunk size must be some multiple of the digest size",
58 );
59 assert!(N.is_power_of_two(), "chunk size must be a power of 2");
62 }
63
64 let thread_pool = config.thread_pool.clone();
65 let bitmap_metadata_partition = config.bitmap_metadata_partition.clone();
66
67 let mut hasher = StandardHasher::<H>::new();
68 let mut status = MerkleizedBitMap::init(
69 context.with_label("bitmap"),
70 &bitmap_metadata_partition,
71 thread_pool,
72 &mut hasher,
73 )
74 .await?
75 .into_dirty();
76
77 let last_known_inactivity_floor = Location::new_unchecked(status.len());
79 let any = AnyDb::init_with_callback(
80 context.with_label("any"),
81 config.into(),
82 Some(last_known_inactivity_floor),
83 |append: bool, loc: Option<Location>| {
84 status.push(append);
85 if let Some(loc) = loc {
86 status.set_bit(*loc, false);
87 }
88 },
89 )
90 .await?;
91
92 let status = merkleize_grafted_bitmap(&mut hasher, status, &any.log.mmr).await?;
93
94 let root = root(&mut hasher, &status, &any.log.mmr).await?;
96
97 Ok(Self {
98 any,
99 status,
100 state: Merkleized { root },
101 })
102 }
103}
104
105#[cfg(test)]
106pub mod test {
107 use super::*;
108 use crate::{
109 kv::tests::{assert_batchable, assert_deletable, assert_gettable, assert_send},
110 mmr::hasher::Hasher as _,
111 qmdb::{
112 any::ordered::Update,
113 current::{
114 db::Unmerkleized,
115 proof::{OperationProof, RangeProof},
116 tests::{self, apply_random_ops},
117 },
118 store::{
119 batch_tests,
120 tests::{assert_log_store, assert_merkleized_store, assert_prunable_store},
121 LogStore,
122 },
123 NonDurable,
124 },
125 translator::OneCap,
126 };
127 use commonware_cryptography::{sha256::Digest, Sha256};
128 use commonware_macros::test_traced;
129 use commonware_runtime::{buffer::paged::CacheRef, deterministic, Runner as _};
130 use commonware_utils::{NZUsize, NZU16, NZU64};
131 use rand::RngCore;
132 use std::num::{NonZeroU16, NonZeroUsize};
133
134 const PAGE_SIZE: NonZeroU16 = NZU16!(88);
135 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(8);
136
137 fn current_db_config(partition_prefix: &str) -> Config<OneCap> {
138 Config {
139 mmr_journal_partition: format!("{partition_prefix}_journal_partition"),
140 mmr_metadata_partition: format!("{partition_prefix}_metadata_partition"),
141 mmr_items_per_blob: NZU64!(11),
142 mmr_write_buffer: NZUsize!(1024),
143 log_journal_partition: format!("{partition_prefix}_partition_prefix"),
144 log_items_per_blob: NZU64!(7),
145 log_write_buffer: NZUsize!(1024),
146 bitmap_metadata_partition: format!("{partition_prefix}_bitmap_metadata_partition"),
147 translator: OneCap,
148 thread_pool: None,
149 page_cache: CacheRef::new(PAGE_SIZE, PAGE_CACHE_SIZE),
150 }
151 }
152
153 type CleanCurrentTest =
155 Db<deterministic::Context, Digest, Digest, Sha256, OneCap, 32, Merkleized<Digest>, Durable>;
156 type MutableCurrentTest =
157 Db<deterministic::Context, Digest, Digest, Sha256, OneCap, 32, Unmerkleized, NonDurable>;
158
159 async fn open_db(
161 context: deterministic::Context,
162 partition_prefix: String,
163 ) -> CleanCurrentTest {
164 CleanCurrentTest::init(context, current_db_config(&partition_prefix))
165 .await
166 .unwrap()
167 }
168
169 #[test_traced("DEBUG")]
170 pub fn test_current_db_build_small_close_reopen() {
171 super::super::tests::test_build_small_close_reopen::<CleanCurrentTest, _, _>(open_db);
172 }
173
174 #[test_traced("WARN")]
175 fn test_current_db_build_big() {
176 tests::test_current_db_build_big::<CleanCurrentTest, _, _>(open_db, 4241, 3383);
178 }
179
180 #[test_traced("DEBUG")]
182 fn test_current_ordered_fixed_db_steps_not_reset() {
183 let executor = deterministic::Runner::default();
184 executor.start(|context| async move {
185 let db = open_db(context, "steps_test".to_string()).await;
186 crate::qmdb::any::test::test_any_db_steps_not_reset(db).await;
187 });
188 }
189
190 #[test_traced("DEBUG")]
194 pub fn test_current_db_verify_proof_over_bits_in_uncommitted_chunk() {
195 let executor = deterministic::Runner::default();
196 executor.start(|context| async move {
197 let mut hasher = StandardHasher::<Sha256>::new();
198 let partition = "build_small".to_string();
199 let mut db = open_db(context, partition).await.into_mutable();
200
201 let k = Sha256::fill(0x01);
203 let v1 = Sha256::fill(0xA1);
204 db.update(k, v1).await.unwrap();
205 let (db, _) = db.commit(None).await.unwrap();
206 let db = db.into_merkleized().await.unwrap();
207
208 let (_, op_loc) = db.any.get_with_loc(&k).await.unwrap().unwrap();
209 let proof = db.key_value_proof(hasher.inner(), k).await.unwrap();
210
211 let root = db.root();
213 assert!(CleanCurrentTest::verify_key_value_proof(
214 hasher.inner(),
215 k,
216 v1,
217 &proof,
218 &root,
219 ));
220
221 let v2 = Sha256::fill(0xA2);
222 assert!(!CleanCurrentTest::verify_key_value_proof(
224 hasher.inner(),
225 k,
226 v2,
227 &proof,
228 &root,
229 ));
230 let mut mangled_proof = proof.clone();
232 mangled_proof.next_key = Sha256::fill(0xFF);
233 assert!(!CleanCurrentTest::verify_key_value_proof(
234 hasher.inner(),
235 k,
236 v1,
237 &mangled_proof,
238 &root,
239 ));
240
241 let mut db = db.into_mutable();
243 db.update(k, v2).await.unwrap();
244 let (db, _) = db.commit(None).await.unwrap();
245 let db = db.into_merkleized().await.unwrap();
246 let root = db.root();
247
248 assert!(!CleanCurrentTest::verify_key_value_proof(
250 hasher.inner(),
251 k,
252 v2,
253 &proof,
254 &root,
255 ));
256
257 let proof = db.key_value_proof(hasher.inner(), k).await.unwrap();
259 assert!(CleanCurrentTest::verify_key_value_proof(
260 hasher.inner(),
261 k,
262 v2,
263 &proof,
264 &root,
265 ));
266 assert!(!CleanCurrentTest::verify_key_value_proof(
268 hasher.inner(),
269 k,
270 v1,
271 &proof,
272 &root,
273 ));
274
275 let (p, _, chunks) = db
278 .range_proof(hasher.inner(), op_loc, NZU64!(1))
279 .await
280 .unwrap();
281 let proof_inactive = KeyValueProof {
282 proof: OperationProof {
283 loc: op_loc,
284 chunk: chunks[0],
285 range_proof: p,
286 },
287 next_key: k,
288 };
289 let op = Operation::Update(Update {
292 key: k,
293 value: v1,
294 next_key: k,
295 });
296 assert!(CleanCurrentTest::verify_range_proof(
297 hasher.inner(),
298 &proof_inactive.proof.range_proof,
299 proof_inactive.proof.loc,
300 &[op],
301 &[proof_inactive.proof.chunk],
302 &root,
303 ));
304 assert!(!CleanCurrentTest::verify_key_value_proof(
307 hasher.inner(),
308 k,
309 v1,
310 &proof_inactive,
311 &root,
312 ));
313
314 let (_, active_loc) = db.any.get_with_loc(&k).await.unwrap().unwrap();
318 assert_ne!(active_loc, proof_inactive.proof.loc);
320 assert_eq!(
321 MerkleizedBitMap::<deterministic::Context, Digest, 32>::leaf_pos(*active_loc),
322 MerkleizedBitMap::<deterministic::Context, Digest, 32>::leaf_pos(
323 *proof_inactive.proof.loc
324 )
325 );
326 let mut fake_proof = proof_inactive.clone();
327 fake_proof.proof.loc = active_loc;
328 assert!(!CleanCurrentTest::verify_key_value_proof(
329 hasher.inner(),
330 k,
331 v1,
332 &fake_proof,
333 &root,
334 ));
335
336 let mut modified_chunk = proof_inactive.proof.chunk;
341 let bit_pos = *proof_inactive.proof.loc;
342 let byte_idx = bit_pos / 8;
343 let bit_idx = bit_pos % 8;
344 modified_chunk[byte_idx as usize] |= 1 << bit_idx;
345
346 let mut fake_proof = proof_inactive.clone();
347 fake_proof.proof.chunk = modified_chunk;
348 assert!(!CleanCurrentTest::verify_key_value_proof(
349 hasher.inner(),
350 k,
351 v1,
352 &fake_proof,
353 &root,
354 ));
355
356 db.destroy().await.unwrap();
357 });
358 }
359
360 #[test_traced("DEBUG")]
361 pub fn test_current_db_range_proofs() {
362 let executor = deterministic::Runner::default();
363 executor.start(|mut context| async move {
364 let partition = "range_proofs".to_string();
365 let mut hasher = StandardHasher::<Sha256>::new();
366 let db = open_db(context.with_label("db"), partition).await;
367 let root = db.root();
368
369 let proof = RangeProof {
372 proof: crate::mmr::Proof::default(),
373 partial_chunk_digest: None,
374 };
375 assert!(!CleanCurrentTest::verify_range_proof(
376 hasher.inner(),
377 &proof,
378 Location::new_unchecked(0),
379 &[],
380 &[],
381 &root,
382 ));
383
384 let rng_seed = context.next_u64();
385 let db = apply_random_ops::<CleanCurrentTest>(200, true, rng_seed, db.into_mutable())
386 .await
387 .unwrap();
388 let (db, _) = db.commit(None).await.unwrap();
389 let db = db.into_merkleized().await.unwrap();
390 let root = db.root();
391
392 let max_ops = 4;
395 let end_loc = db.size();
396 let start_loc = db.any.inactivity_floor_loc();
397
398 for loc in *start_loc..*end_loc {
399 let loc = Location::new_unchecked(loc);
400 let (proof, ops, chunks) = db
401 .range_proof(hasher.inner(), loc, NZU64!(max_ops))
402 .await
403 .unwrap();
404 assert!(
405 CleanCurrentTest::verify_range_proof(
406 hasher.inner(),
407 &proof,
408 loc,
409 &ops,
410 &chunks,
411 &root
412 ),
413 "failed to verify range at start_loc {start_loc}",
414 );
415 let mut chunks_with_extra = chunks.to_vec();
417 chunks_with_extra.push(chunks[chunks.len() - 1]);
418 assert!(!CleanCurrentTest::verify_range_proof(
419 hasher.inner(),
420 &proof,
421 loc,
422 &ops,
423 &chunks_with_extra,
424 &root,
425 ));
426 }
427
428 db.destroy().await.unwrap();
429 });
430 }
431
432 #[test_traced("DEBUG")]
433 pub fn test_current_db_key_value_proof() {
434 let executor = deterministic::Runner::default();
435 executor.start(|mut context| async move {
436 let partition = "range_proofs".to_string();
437 let mut hasher = StandardHasher::<Sha256>::new();
438 let db = open_db(context.with_label("db"), partition.clone())
439 .await
440 .into_mutable();
441 let db = apply_random_ops::<CleanCurrentTest>(500, true, context.next_u64(), db)
442 .await
443 .unwrap();
444 let (db, _) = db.commit(None).await.unwrap();
445 let db = db.into_merkleized().await.unwrap();
446 let root = db.root();
447
448 let bad_key = Sha256::fill(0xAA);
450 let res = db.key_value_proof(hasher.inner(), bad_key).await;
451 assert!(matches!(res, Err(Error::KeyNotFound)));
452
453 let start = *db.inactivity_floor_loc();
454 for i in start..db.status.len() {
455 if !db.status.get_bit(i) {
456 continue;
457 }
458 let op = db.any.log.read(Location::new_unchecked(i)).await.unwrap();
461 let (key, value) = match op {
462 Operation::Update(key_data) => (key_data.key, key_data.value),
463 Operation::CommitFloor(_, _) => continue,
464 _ => unreachable!("expected update or commit floor operation"),
465 };
466 let proof = db.key_value_proof(hasher.inner(), key).await.unwrap();
467
468 assert!(CleanCurrentTest::verify_key_value_proof(
470 hasher.inner(),
471 key,
472 value,
473 &proof,
474 &root
475 ));
476 let wrong_val = Sha256::hash(&[0xFF]);
480 assert!(!CleanCurrentTest::verify_key_value_proof(
481 hasher.inner(),
482 key,
483 wrong_val,
484 &proof,
485 &root
486 ));
487 let wrong_key = Sha256::hash(&[0xEE]);
489 assert!(!CleanCurrentTest::verify_key_value_proof(
490 hasher.inner(),
491 wrong_key,
492 value,
493 &proof,
494 &root
495 ));
496 let wrong_root = Sha256::hash(&[0xDD]);
498 assert!(!CleanCurrentTest::verify_key_value_proof(
499 hasher.inner(),
500 key,
501 value,
502 &proof,
503 &wrong_root,
504 ));
505 let mut bad_proof = proof.clone();
507 bad_proof.next_key = wrong_key;
508 assert!(!CleanCurrentTest::verify_key_value_proof(
509 hasher.inner(),
510 key,
511 value,
512 &bad_proof,
513 &root,
514 ));
515 }
516
517 db.destroy().await.unwrap();
518 });
519 }
520
521 #[test_traced("WARN")]
524 pub fn test_current_db_build_random_close_reopen() {
525 crate::qmdb::current::tests::test_build_random_close_reopen(open_db);
526 }
527
528 #[test_traced("WARN")]
529 pub fn test_current_db_sync_persists_bitmap_pruning_boundary() {
530 tests::test_sync_persists_bitmap_pruning_boundary::<CleanCurrentTest, _, _>(open_db);
531 }
532
533 #[test_traced("WARN")]
536 pub fn test_current_db_proving_repeated_updates() {
537 let executor = deterministic::Runner::default();
538 executor.start(|context| async move {
539 let mut hasher = StandardHasher::<Sha256>::new();
540 let partition = "build_small".to_string();
541 let mut db = open_db(context, partition).await;
542
543 let k = Sha256::fill(0x00);
545 let mut old_val = Sha256::fill(0x00);
546 for i in 1u8..=255 {
547 let v = Sha256::fill(i);
548 let mut dirty_db = db.into_mutable();
549 dirty_db.update(k, v).await.unwrap();
550 assert_eq!(dirty_db.get(&k).await.unwrap().unwrap(), v);
551 let (dirty_db, _) = dirty_db.commit(None).await.unwrap();
552 let clean_db = dirty_db.into_merkleized().await.unwrap();
553 db = clean_db;
554 let root = db.root();
555
556 let proof = db.key_value_proof(hasher.inner(), k).await.unwrap();
558 assert!(
559 CleanCurrentTest::verify_key_value_proof(hasher.inner(), k, v, &proof, &root),
560 "proof of update {i} failed to verify"
561 );
562 assert!(
564 !CleanCurrentTest::verify_key_value_proof(
565 hasher.inner(),
566 k,
567 old_val,
568 &proof,
569 &root
570 ),
571 "proof of update {i} verified when it should not have"
572 );
573 old_val = v;
574 }
575
576 db.destroy().await.unwrap();
577 });
578 }
579
580 #[test_traced("WARN")]
583 pub fn test_current_db_simulate_write_failures() {
584 crate::qmdb::current::tests::test_simulate_write_failures(open_db);
585 }
586
587 #[test_traced("WARN")]
588 pub fn test_current_db_different_pruning_delays_same_root() {
589 tests::test_different_pruning_delays_same_root::<CleanCurrentTest, _, _>(open_db);
590 }
591
592 #[test_traced("DEBUG")]
594 pub fn test_current_db_exclusion_proofs() {
595 let executor = deterministic::Runner::default();
596 executor.start(|context| async move {
597 let mut hasher = StandardHasher::<Sha256>::new();
598 let partition = "exclusion_proofs".to_string();
599 let db = open_db(context, partition).await;
600
601 let key_exists_1 = Sha256::fill(0x10);
602
603 let empty_root = db.root();
605 let empty_proof = db
606 .exclusion_proof(hasher.inner(), &key_exists_1)
607 .await
608 .unwrap();
609 assert!(CleanCurrentTest::verify_exclusion_proof(
610 hasher.inner(),
611 &key_exists_1,
612 &empty_proof,
613 &empty_root,
614 ));
615
616 let v1 = Sha256::fill(0xA1);
618 let mut db = db.into_mutable();
619 db.update(key_exists_1, v1).await.unwrap();
620 let (db, _) = db.commit(None).await.unwrap();
621 let db = db.into_merkleized().await.unwrap();
622 let root = db.root();
623
624 let result = db.exclusion_proof(hasher.inner(), &key_exists_1).await;
626 assert!(matches!(result, Err(Error::KeyExists)));
627
628 let greater_key = Sha256::fill(0xFF);
630 let lesser_key = Sha256::fill(0x00);
631 let proof = db
632 .exclusion_proof(hasher.inner(), &greater_key)
633 .await
634 .unwrap();
635 let proof2 = db
636 .exclusion_proof(hasher.inner(), &lesser_key)
637 .await
638 .unwrap();
639
640 assert_eq!(proof, proof2);
643 assert!(CleanCurrentTest::verify_exclusion_proof(
645 hasher.inner(),
646 &greater_key,
647 &proof,
648 &root,
649 ));
650 assert!(CleanCurrentTest::verify_exclusion_proof(
651 hasher.inner(),
652 &lesser_key,
653 &proof,
654 &root,
655 ));
656 assert!(!CleanCurrentTest::verify_exclusion_proof(
658 hasher.inner(),
659 &key_exists_1,
660 &proof,
661 &root,
662 ));
663
664 let key_exists_2 = Sha256::fill(0x30);
666 let v2 = Sha256::fill(0xB2);
667
668 let mut db = db.into_mutable();
669 db.update(key_exists_2, v2).await.unwrap();
670 let (db, _) = db.commit(None).await.unwrap();
671 let db = db.into_merkleized().await.unwrap();
672 let root = db.root();
673
674 let lesser_key = Sha256::fill(0x0F); let greater_key = Sha256::fill(0x31); let middle_key = Sha256::fill(0x20); let proof = db
680 .exclusion_proof(hasher.inner(), &greater_key)
681 .await
682 .unwrap();
683 assert!(CleanCurrentTest::verify_exclusion_proof(
686 hasher.inner(),
687 &greater_key,
688 &proof,
689 &root,
690 ));
691 assert!(CleanCurrentTest::verify_exclusion_proof(
692 hasher.inner(),
693 &lesser_key,
694 &proof,
695 &root,
696 ));
697 assert!(!CleanCurrentTest::verify_exclusion_proof(
698 hasher.inner(),
699 &middle_key,
700 &proof,
701 &root,
702 ));
703
704 let new_proof = db
706 .exclusion_proof(hasher.inner(), &lesser_key)
707 .await
708 .unwrap();
709 assert_eq!(proof, new_proof);
710
711 let proof = db
713 .exclusion_proof(hasher.inner(), &middle_key)
714 .await
715 .unwrap();
716 assert!(!CleanCurrentTest::verify_exclusion_proof(
718 hasher.inner(),
719 &key_exists_1,
720 &proof,
721 &root,
722 ));
723 assert!(CleanCurrentTest::verify_exclusion_proof(
725 hasher.inner(),
726 &middle_key,
727 &proof,
728 &root,
729 ));
730 assert!(!CleanCurrentTest::verify_exclusion_proof(
731 hasher.inner(),
732 &key_exists_2,
733 &proof,
734 &root,
735 ));
736
737 let conflicting_middle_key = Sha256::fill(0x11); assert!(CleanCurrentTest::verify_exclusion_proof(
739 hasher.inner(),
740 &conflicting_middle_key,
741 &proof,
742 &root,
743 ));
744
745 assert!(!CleanCurrentTest::verify_exclusion_proof(
747 hasher.inner(),
748 &greater_key,
749 &proof,
750 &root,
751 ));
752 assert!(!CleanCurrentTest::verify_exclusion_proof(
753 hasher.inner(),
754 &lesser_key,
755 &proof,
756 &root,
757 ));
758
759 let mut db = db.into_mutable();
762 db.delete(key_exists_1).await.unwrap();
763 db.delete(key_exists_2).await.unwrap();
764 let (db, _) = db.commit(None).await.unwrap();
765 let mut db = db.into_merkleized().await.unwrap();
766 db.sync().await.unwrap();
767 let root = db.root();
768 assert!(db.is_empty());
771 assert_ne!(db.bounds().end, 0);
772 assert_ne!(root, empty_root);
773
774 let proof = db
775 .exclusion_proof(hasher.inner(), &key_exists_1)
776 .await
777 .unwrap();
778 assert!(CleanCurrentTest::verify_exclusion_proof(
779 hasher.inner(),
780 &key_exists_1,
781 &proof,
782 &root,
783 ));
784 assert!(CleanCurrentTest::verify_exclusion_proof(
785 hasher.inner(),
786 &key_exists_2,
787 &proof,
788 &root,
789 ));
790
791 assert!(!CleanCurrentTest::verify_exclusion_proof(
793 hasher.inner(),
794 &key_exists_1,
795 &empty_proof, &root,
797 ));
798 assert!(!CleanCurrentTest::verify_exclusion_proof(
799 hasher.inner(),
800 &key_exists_1,
801 &proof,
802 &empty_root, ));
804 });
805 }
806
807 #[test_traced("DEBUG")]
808 fn test_batch() {
809 batch_tests::test_batch(|mut ctx| async move {
810 let seed = ctx.next_u64();
811 let partition = format!("current_ordered_batch_{seed}");
812 open_db(ctx, partition).await.into_mutable()
813 });
814 }
815
816 #[allow(dead_code)]
817 fn assert_merkleized_db_futures_are_send(
818 db: &mut CleanCurrentTest,
819 key: Digest,
820 loc: Location,
821 ) {
822 assert_gettable(db, &key);
823 assert_log_store(db);
824 assert_prunable_store(db, loc);
825 assert_merkleized_store(db, loc);
826 assert_send(db.sync());
827 }
828
829 #[allow(dead_code)]
830 fn assert_mutable_db_futures_are_send(db: &mut MutableCurrentTest, key: Digest, value: Digest) {
831 assert_gettable(db, &key);
832 assert_log_store(db);
833 assert_send(db.update(key, value));
834 assert_send(db.create(key, value));
835 assert_deletable(db, key);
836 assert_batchable(db, key, value);
837 }
838
839 #[allow(dead_code)]
840 fn assert_mutable_db_commit_is_send(db: MutableCurrentTest) {
841 assert_send(db.commit(None));
842 }
843}