1pub use super::db::KeyValueProof;
10use crate::{
11 index::ordered::Index,
12 journal::contiguous::variable::Journal,
13 mmr::Location,
14 qmdb::{
15 any::{ordered::variable::Operation, value::VariableEncoding, VariableValue},
16 current::VariableConfig as Config,
17 operation::Key,
18 Error,
19 },
20 translator::Translator,
21};
22use commonware_codec::{Codec, Read};
23use commonware_cryptography::Hasher;
24use commonware_runtime::{Clock, Metrics, Storage as RStorage};
25
26pub type Db<E, K, V, H, T, const N: usize> =
27 super::db::Db<E, Journal<E, Operation<K, V>>, K, VariableEncoding<V>, Index<T, Location>, H, N>;
28
29impl<
30 E: RStorage + Clock + Metrics,
31 K: Key,
32 V: VariableValue,
33 H: Hasher,
34 T: Translator,
35 const N: usize,
36 > Db<E, K, V, H, T, N>
37where
38 Operation<K, V>: Codec,
39{
40 pub async fn init(
43 context: E,
44 config: Config<T, <Operation<K, V> as Read>::Cfg>,
45 ) -> Result<Self, Error> {
46 crate::qmdb::current::init_variable(context, config, |ctx, t| Index::new(ctx, t)).await
47 }
48}
49
50pub mod partitioned {
51 pub use super::KeyValueProof;
54 use crate::{
55 index::partitioned::ordered::Index,
56 journal::contiguous::variable::Journal,
57 mmr::Location,
58 qmdb::{
59 any::{
60 ordered::variable::partitioned::Operation, value::VariableEncoding, VariableValue,
61 },
62 current::VariableConfig as Config,
63 operation::Key,
64 Error,
65 },
66 translator::Translator,
67 };
68 use commonware_codec::{Codec, Read};
69 use commonware_cryptography::Hasher;
70 use commonware_runtime::{Clock, Metrics, Storage as RStorage};
71
72 pub type Db<E, K, V, H, T, const P: usize, const N: usize> =
79 crate::qmdb::current::ordered::db::Db<
80 E,
81 Journal<E, Operation<K, V>>,
82 K,
83 VariableEncoding<V>,
84 Index<T, Location, P>,
85 H,
86 N,
87 >;
88
89 impl<
90 E: RStorage + Clock + Metrics,
91 K: Key,
92 V: VariableValue,
93 H: Hasher,
94 T: Translator,
95 const P: usize,
96 const N: usize,
97 > Db<E, K, V, H, T, P, N>
98 where
99 Operation<K, V>: Codec,
100 {
101 pub async fn init(
104 context: E,
105 config: Config<T, <Operation<K, V> as Read>::Cfg>,
106 ) -> Result<Self, Error> {
107 crate::qmdb::current::init_variable(context, config, |ctx, t| Index::new(ctx, t)).await
108 }
109 }
110}
111
112#[cfg(test)]
113mod test {
114 use crate::{
115 kv::tests::{assert_gettable, assert_send},
116 mmr::{hasher::Hasher as _, Location, StandardHasher},
117 qmdb::{
118 any::ordered::variable::Operation,
119 current::{
120 ordered::{db::KeyValueProof, variable::Db},
121 proof::{OperationProof, RangeProof},
122 tests::{apply_random_ops, variable_config},
123 },
124 store::{
125 tests::{assert_log_store, assert_merkleized_store, assert_prunable_store},
126 LogStore,
127 },
128 Error,
129 },
130 translator::OneCap,
131 };
132 use commonware_cryptography::{sha256::Digest, Digest as _, Hasher as _, Sha256};
133 use commonware_macros::test_traced;
134 use commonware_runtime::{deterministic, Runner as _};
135 use commonware_utils::{bitmap::Prunable as BitMap, NZU64};
136 use rand::RngCore;
137
138 type CurrentTest = Db<deterministic::Context, Digest, Digest, Sha256, OneCap, 32>;
140
141 async fn open_db(context: deterministic::Context, partition_prefix: String) -> CurrentTest {
143 let cfg = variable_config::<OneCap>(&partition_prefix, &context);
144 CurrentTest::init(context, cfg).await.unwrap()
145 }
146
147 #[test_traced("DEBUG")]
151 pub fn test_current_db_verify_proof_over_bits_in_uncommitted_chunk() {
152 let executor = deterministic::Runner::default();
153 executor.start(|context| async move {
154 let mut hasher = StandardHasher::<Sha256>::new();
155 let partition = "build-small".into();
156 let mut db = open_db(context, partition).await;
157
158 let k = Sha256::fill(0x01);
160 let v1 = Sha256::fill(0xA1);
161 let finalized = {
162 let mut batch = db.new_batch();
163 batch.write(k, Some(v1));
164 batch.merkleize(None).await.unwrap().finalize()
165 };
166 db.apply_batch(finalized).await.unwrap();
167
168 let (_, op_loc) = db.any.get_with_loc(&k).await.unwrap().unwrap();
169 let proof = db.key_value_proof(hasher.inner(), k).await.unwrap();
170
171 let root = db.root();
173 assert!(CurrentTest::verify_key_value_proof(
174 hasher.inner(),
175 k,
176 v1,
177 &proof,
178 &root,
179 ));
180
181 let v2 = Sha256::fill(0xA2);
182 assert!(!CurrentTest::verify_key_value_proof(
184 hasher.inner(),
185 k,
186 v2,
187 &proof,
188 &root,
189 ));
190 let mut mangled_proof = proof.clone();
192 mangled_proof.next_key = Sha256::fill(0xFF);
193 assert!(!CurrentTest::verify_key_value_proof(
194 hasher.inner(),
195 k,
196 v1,
197 &mangled_proof,
198 &root,
199 ));
200
201 let finalized = {
203 let mut batch = db.new_batch();
204 batch.write(k, Some(v2));
205 batch.merkleize(None).await.unwrap().finalize()
206 };
207 db.apply_batch(finalized).await.unwrap();
208 let root = db.root();
209
210 assert!(!CurrentTest::verify_key_value_proof(
212 hasher.inner(),
213 k,
214 v2,
215 &proof,
216 &root,
217 ));
218
219 let proof = db.key_value_proof(hasher.inner(), k).await.unwrap();
221 assert!(CurrentTest::verify_key_value_proof(
222 hasher.inner(),
223 k,
224 v2,
225 &proof,
226 &root,
227 ));
228 assert!(!CurrentTest::verify_key_value_proof(
230 hasher.inner(),
231 k,
232 v1,
233 &proof,
234 &root,
235 ));
236
237 let (p, _, chunks) = db
240 .range_proof(hasher.inner(), op_loc, NZU64!(1))
241 .await
242 .unwrap();
243 let proof_inactive = KeyValueProof {
244 proof: OperationProof {
245 loc: op_loc,
246 chunk: chunks[0],
247 range_proof: p,
248 },
249 next_key: k,
250 };
251 let op = Operation::Update(crate::qmdb::any::ordered::Update {
254 key: k,
255 value: v1,
256 next_key: k,
257 });
258 assert!(CurrentTest::verify_range_proof(
259 hasher.inner(),
260 &proof_inactive.proof.range_proof,
261 proof_inactive.proof.loc,
262 &[op],
263 &[proof_inactive.proof.chunk],
264 &root,
265 ));
266 assert!(!CurrentTest::verify_key_value_proof(
269 hasher.inner(),
270 k,
271 v1,
272 &proof_inactive,
273 &root,
274 ));
275
276 let (_, active_loc) = db.any.get_with_loc(&k).await.unwrap().unwrap();
280 assert_ne!(active_loc, proof_inactive.proof.loc);
282 assert_eq!(
283 BitMap::<32>::to_chunk_index(*active_loc),
284 BitMap::<32>::to_chunk_index(*proof_inactive.proof.loc)
285 );
286 let mut fake_proof = proof_inactive.clone();
287 fake_proof.proof.loc = active_loc;
288 assert!(!CurrentTest::verify_key_value_proof(
289 hasher.inner(),
290 k,
291 v1,
292 &fake_proof,
293 &root,
294 ));
295
296 let mut modified_chunk = proof_inactive.proof.chunk;
301 let bit_pos = *proof_inactive.proof.loc;
302 let byte_idx = bit_pos / 8;
303 let bit_idx = bit_pos % 8;
304 modified_chunk[byte_idx as usize] |= 1 << bit_idx;
305
306 let mut fake_proof = proof_inactive.clone();
307 fake_proof.proof.chunk = modified_chunk;
308 assert!(!CurrentTest::verify_key_value_proof(
309 hasher.inner(),
310 k,
311 v1,
312 &fake_proof,
313 &root,
314 ));
315
316 db.destroy().await.unwrap();
317 });
318 }
319
320 #[test_traced("DEBUG")]
321 pub fn test_current_db_range_proofs() {
322 let executor = deterministic::Runner::default();
323 executor.start(|mut context| async move {
324 let partition = "range-proofs".into();
325 let mut hasher = StandardHasher::<Sha256>::new();
326 let db = open_db(context.clone(), partition).await;
327 let root = db.root();
328
329 let proof = RangeProof {
332 proof: crate::mmr::Proof::default(),
333 partial_chunk_digest: None,
334 ops_root: Digest::EMPTY,
335 };
336 assert!(!CurrentTest::verify_range_proof(
337 hasher.inner(),
338 &proof,
339 Location::new(0),
340 &[],
341 &[],
342 &root,
343 ));
344
345 let rng_seed = context.next_u64();
346 let mut db = apply_random_ops::<CurrentTest>(200, true, rng_seed, db)
347 .await
348 .unwrap();
349 let finalized = db.new_batch().merkleize(None).await.unwrap().finalize();
350 db.apply_batch(finalized).await.unwrap();
351 let root = db.root();
352
353 let max_ops = 4;
356 let end_loc = db.size().await;
357 let start_loc = db.any.inactivity_floor_loc();
358
359 for loc in *start_loc..*end_loc {
360 let loc = Location::new(loc);
361 let (proof, ops, chunks) = db
362 .range_proof(hasher.inner(), loc, NZU64!(max_ops))
363 .await
364 .unwrap();
365 assert!(
366 CurrentTest::verify_range_proof(
367 hasher.inner(),
368 &proof,
369 loc,
370 &ops,
371 &chunks,
372 &root
373 ),
374 "failed to verify range at start_loc {start_loc}",
375 );
376 let mut chunks_with_extra = chunks.clone();
378 chunks_with_extra.push(chunks[chunks.len() - 1]);
379 assert!(!CurrentTest::verify_range_proof(
380 hasher.inner(),
381 &proof,
382 loc,
383 &ops,
384 &chunks_with_extra,
385 &root,
386 ));
387 }
388
389 db.destroy().await.unwrap();
390 });
391 }
392
393 #[test_traced("DEBUG")]
394 pub fn test_current_db_key_value_proof() {
395 let executor = deterministic::Runner::default();
396 executor.start(|mut context| async move {
397 let partition = "range-proofs".to_string();
398 let mut hasher = StandardHasher::<Sha256>::new();
399 let db = open_db(context.clone(), partition.clone()).await;
400 let mut db = apply_random_ops::<CurrentTest>(500, true, context.next_u64(), db)
401 .await
402 .unwrap();
403 let finalized = db.new_batch().merkleize(None).await.unwrap().finalize();
404 db.apply_batch(finalized).await.unwrap();
405 let root = db.root();
406
407 let bad_key = Sha256::fill(0xAA);
409 let res = db.key_value_proof(hasher.inner(), bad_key).await;
410 assert!(matches!(res, Err(Error::KeyNotFound)));
411
412 let start = *db.inactivity_floor_loc();
413 for i in start..db.status.len() {
414 if !db.status.get_bit(i) {
415 continue;
416 }
417 let op = db.any.log.read(Location::new(i)).await.unwrap();
420 let (key, value) = match op {
421 Operation::Update(key_data) => (key_data.key, key_data.value),
422 Operation::CommitFloor(_, _) => continue,
423 _ => unreachable!("expected update or commit floor operation"),
424 };
425 let proof = db.key_value_proof(hasher.inner(), key).await.unwrap();
426
427 assert!(CurrentTest::verify_key_value_proof(
429 hasher.inner(),
430 key,
431 value,
432 &proof,
433 &root
434 ));
435 let wrong_val = Sha256::hash(&[0xFF]);
439 assert!(!CurrentTest::verify_key_value_proof(
440 hasher.inner(),
441 key,
442 wrong_val,
443 &proof,
444 &root
445 ));
446 let wrong_key = Sha256::hash(&[0xEE]);
448 assert!(!CurrentTest::verify_key_value_proof(
449 hasher.inner(),
450 wrong_key,
451 value,
452 &proof,
453 &root
454 ));
455 let wrong_root = Sha256::hash(&[0xDD]);
457 assert!(!CurrentTest::verify_key_value_proof(
458 hasher.inner(),
459 key,
460 value,
461 &proof,
462 &wrong_root,
463 ));
464 let mut bad_proof = proof.clone();
466 bad_proof.next_key = wrong_key;
467 assert!(!CurrentTest::verify_key_value_proof(
468 hasher.inner(),
469 key,
470 value,
471 &bad_proof,
472 &root,
473 ));
474 }
475
476 db.destroy().await.unwrap();
477 });
478 }
479
480 #[test_traced("WARN")]
483 pub fn test_current_db_proving_repeated_updates() {
484 let executor = deterministic::Runner::default();
485 executor.start(|context| async move {
486 let mut hasher = StandardHasher::<Sha256>::new();
487 let partition = "build-small".into();
488 let mut db = open_db(context, partition).await;
489
490 let k = Sha256::fill(0x00);
492 let mut old_val = Sha256::fill(0x00);
493 for i in 1u8..=255 {
494 let v = Sha256::fill(i);
495 let finalized = {
496 let mut batch = db.new_batch();
497 batch.write(k, Some(v));
498 batch.merkleize(None).await.unwrap().finalize()
499 };
500 db.apply_batch(finalized).await.unwrap();
501 assert_eq!(db.get(&k).await.unwrap().unwrap(), v);
502 let root = db.root();
503
504 let proof = db.key_value_proof(hasher.inner(), k).await.unwrap();
506 assert!(
507 CurrentTest::verify_key_value_proof(hasher.inner(), k, v, &proof, &root),
508 "proof of update {i} failed to verify"
509 );
510 assert!(
512 !CurrentTest::verify_key_value_proof(hasher.inner(), k, old_val, &proof, &root),
513 "proof of update {i} verified when it should not have"
514 );
515 old_val = v;
516 }
517
518 db.destroy().await.unwrap();
519 });
520 }
521
522 #[test_traced("DEBUG")]
524 pub fn test_current_db_exclusion_proofs() {
525 let executor = deterministic::Runner::default();
526 executor.start(|context| async move {
527 let mut hasher = StandardHasher::<Sha256>::new();
528 let partition = "exclusion-proofs".into();
529 let mut db = open_db(context, partition).await;
530
531 let key_exists_1 = Sha256::fill(0x10);
532
533 let empty_root = db.root();
535 let empty_proof = db
536 .exclusion_proof(hasher.inner(), &key_exists_1)
537 .await
538 .unwrap();
539 assert!(CurrentTest::verify_exclusion_proof(
540 hasher.inner(),
541 &key_exists_1,
542 &empty_proof,
543 &empty_root,
544 ));
545
546 let v1 = Sha256::fill(0xA1);
548 let finalized = {
549 let mut batch = db.new_batch();
550 batch.write(key_exists_1, Some(v1));
551 batch.merkleize(None).await.unwrap().finalize()
552 };
553 db.apply_batch(finalized).await.unwrap();
554 let root = db.root();
555
556 let result = db.exclusion_proof(hasher.inner(), &key_exists_1).await;
558 assert!(matches!(result, Err(Error::KeyExists)));
559
560 let greater_key = Sha256::fill(0xFF);
562 let lesser_key = Sha256::fill(0x00);
563 let proof = db
564 .exclusion_proof(hasher.inner(), &greater_key)
565 .await
566 .unwrap();
567 let proof2 = db
568 .exclusion_proof(hasher.inner(), &lesser_key)
569 .await
570 .unwrap();
571
572 assert_eq!(proof, proof2);
575 assert!(CurrentTest::verify_exclusion_proof(
577 hasher.inner(),
578 &greater_key,
579 &proof,
580 &root,
581 ));
582 assert!(CurrentTest::verify_exclusion_proof(
583 hasher.inner(),
584 &lesser_key,
585 &proof,
586 &root,
587 ));
588 assert!(!CurrentTest::verify_exclusion_proof(
590 hasher.inner(),
591 &key_exists_1,
592 &proof,
593 &root,
594 ));
595
596 let key_exists_2 = Sha256::fill(0x30);
598 let v2 = Sha256::fill(0xB2);
599
600 let finalized = {
601 let mut batch = db.new_batch();
602 batch.write(key_exists_2, Some(v2));
603 batch.merkleize(None).await.unwrap().finalize()
604 };
605 db.apply_batch(finalized).await.unwrap();
606 let root = db.root();
607
608 let lesser_key = Sha256::fill(0x0F); let greater_key = Sha256::fill(0x31); let middle_key = Sha256::fill(0x20); let proof = db
614 .exclusion_proof(hasher.inner(), &greater_key)
615 .await
616 .unwrap();
617 assert!(CurrentTest::verify_exclusion_proof(
620 hasher.inner(),
621 &greater_key,
622 &proof,
623 &root,
624 ));
625 assert!(CurrentTest::verify_exclusion_proof(
626 hasher.inner(),
627 &lesser_key,
628 &proof,
629 &root,
630 ));
631 assert!(!CurrentTest::verify_exclusion_proof(
632 hasher.inner(),
633 &middle_key,
634 &proof,
635 &root,
636 ));
637
638 let new_proof = db
640 .exclusion_proof(hasher.inner(), &lesser_key)
641 .await
642 .unwrap();
643 assert_eq!(proof, new_proof);
644
645 let proof = db
647 .exclusion_proof(hasher.inner(), &middle_key)
648 .await
649 .unwrap();
650 assert!(!CurrentTest::verify_exclusion_proof(
652 hasher.inner(),
653 &key_exists_1,
654 &proof,
655 &root,
656 ));
657 assert!(CurrentTest::verify_exclusion_proof(
659 hasher.inner(),
660 &middle_key,
661 &proof,
662 &root,
663 ));
664 assert!(!CurrentTest::verify_exclusion_proof(
665 hasher.inner(),
666 &key_exists_2,
667 &proof,
668 &root,
669 ));
670
671 let conflicting_middle_key = Sha256::fill(0x11); assert!(CurrentTest::verify_exclusion_proof(
673 hasher.inner(),
674 &conflicting_middle_key,
675 &proof,
676 &root,
677 ));
678
679 assert!(!CurrentTest::verify_exclusion_proof(
681 hasher.inner(),
682 &greater_key,
683 &proof,
684 &root,
685 ));
686 assert!(!CurrentTest::verify_exclusion_proof(
687 hasher.inner(),
688 &lesser_key,
689 &proof,
690 &root,
691 ));
692
693 let finalized = {
696 let mut batch = db.new_batch();
697 batch.write(key_exists_1, None);
698 batch.write(key_exists_2, None);
699 batch.merkleize(None).await.unwrap().finalize()
700 };
701 db.apply_batch(finalized).await.unwrap();
702 db.sync().await.unwrap();
703 let root = db.root();
704 assert!(db.is_empty());
707 assert_ne!(db.bounds().await.end, 0);
708 assert_ne!(root, empty_root);
709
710 let proof = db
711 .exclusion_proof(hasher.inner(), &key_exists_1)
712 .await
713 .unwrap();
714 assert!(CurrentTest::verify_exclusion_proof(
715 hasher.inner(),
716 &key_exists_1,
717 &proof,
718 &root,
719 ));
720 assert!(CurrentTest::verify_exclusion_proof(
721 hasher.inner(),
722 &key_exists_2,
723 &proof,
724 &root,
725 ));
726
727 assert!(!CurrentTest::verify_exclusion_proof(
729 hasher.inner(),
730 &key_exists_1,
731 &empty_proof, &root,
733 ));
734 assert!(!CurrentTest::verify_exclusion_proof(
735 hasher.inner(),
736 &key_exists_1,
737 &proof,
738 &empty_root, ));
740 });
741 }
742
743 #[allow(dead_code)]
744 fn assert_db_futures_are_send(db: &mut CurrentTest, key: Digest, loc: Location) {
745 assert_gettable(db, &key);
746 assert_log_store(db);
747 assert_prunable_store(db, loc);
748 assert_merkleized_store(db, loc);
749 assert_send(db.sync());
750 }
751}