1use crate::{
67 Context,
68 index::Factory as IndexFactory,
69 journal::{
70 authenticated,
71 contiguous::{fixed::Config as FConfig, variable::Config as VConfig},
72 },
73 merkle::{Family, Location, full::Config as MerkleConfig},
74 qmdb::{
75 ROOT_BAGGING,
76 any::operation::{Operation, Update},
77 bitmap::Shared,
78 metrics::Metrics,
79 operation::Committable,
80 single_operation_root,
81 },
82 translator::Translator,
83};
84use commonware_codec::Codec;
85use commonware_cryptography::Hasher;
86use commonware_macros::boxed;
87use commonware_parallel::Strategy;
88use commonware_runtime::Spawner;
89use core::num::NonZeroUsize;
90use std::sync::Arc;
91use tracing::warn;
92
93pub mod batch;
94pub mod db;
95pub mod operation;
96#[cfg(any(test, feature = "test-traits"))]
97pub mod traits;
98pub mod value;
99pub use value::{FixedValue, ValueEncoding, VariableValue};
100pub mod ordered;
101pub(crate) mod sync;
102pub mod unordered;
103
104pub fn initial_root<F, U, H>() -> H::Digest
109where
110 F: Family,
111 H: Hasher,
112 U: Update,
113 Operation<F, U>: Codec,
114{
115 single_operation_root::<F, H>(&Operation::<F, U>::CommitFloor(None, Location::new(0)))
116}
117
118pub(crate) const BITMAP_CHUNK_BYTES: usize = 64;
119
120#[derive(Clone)]
122pub struct Config<T: Translator, J, S: Strategy, B = ()> {
123 pub merkle_config: MerkleConfig<S>,
125
126 pub journal_config: J,
128
129 pub translator: T,
131
132 pub init_cache_size: Option<NonZeroUsize>,
135
136 pub init_buffer: NonZeroUsize,
138
139 pub init_concurrency: B,
144}
145
146pub type FixedConfig<T, S, B = ()> = Config<T, FConfig, S, B>;
148
149pub type VariableConfig<T, C, S, B = ()> = Config<T, VConfig<C>, S, B>;
151
152pub async fn init<F, E, U, H, I, J, S>(
154 context: E,
155 cfg: Config<I::Translator, J::Config, S, <I as crate::qmdb::SnapshotBuild<F>>::Concurrency>,
156) -> Result<db::Db<F, E, J, I, H, U, BITMAP_CHUNK_BYTES, S>, crate::qmdb::Error<F>>
157where
158 F: Family,
159 E: Context + Spawner,
160 U: Update,
161 H: Hasher,
162 I: IndexFactory<Value = Location<F>> + crate::qmdb::SnapshotBuild<F>,
163 J: authenticated::Backing<E, Item = Operation<F, U>> + 'static,
164 S: Strategy,
165 Operation<F, U>: Codec,
166{
167 init_with_bitmap::<F, E, U, H, I, J, S, BITMAP_CHUNK_BYTES>(context, cfg, None).await
168}
169
170#[boxed]
173pub(crate) async fn init_with_bitmap<F, E, U, H, I, J, S, const N: usize>(
174 context: E,
175 cfg: Config<I::Translator, J::Config, S, <I as crate::qmdb::SnapshotBuild<F>>::Concurrency>,
176 bitmap: Option<Arc<Shared<N>>>,
177) -> Result<db::Db<F, E, J, I, H, U, N, S>, crate::qmdb::Error<F>>
178where
179 F: Family,
180 E: Context + Spawner,
181 U: Update,
182 H: Hasher,
183 I: IndexFactory<Value = Location<F>> + crate::qmdb::SnapshotBuild<F>,
184 J: authenticated::Backing<E, Item = Operation<F, U>> + 'static,
185 S: Strategy,
186 Operation<F, U>: Codec,
187{
188 let mut log = authenticated::Journal::<F, E, J, H, S>::new(
189 context.child("log"),
190 cfg.merkle_config,
191 cfg.journal_config,
192 Operation::is_commit,
193 ROOT_BAGGING,
194 )
195 .await?;
196
197 if log.size() == 0 {
198 warn!("Authenticated log is empty, initializing new db");
199 let commit_floor = Operation::CommitFloor(None, Location::new(0));
200 (log, _) = log.append(&commit_floor).await?;
201 log = log.sync().await?;
202 }
203
204 let index = I::new(context.child("index"), cfg.translator);
205 let snapshot_context = context.child("snapshot");
206 let metrics = Metrics::new(context);
207 db::Db::init_from_log(
208 snapshot_context,
209 index,
210 log,
211 bitmap,
212 cfg.init_concurrency,
213 cfg.init_buffer,
214 cfg.init_cache_size,
215 metrics,
216 )
217 .await
218}
219
220#[cfg(test)]
221pub(crate) mod test {
223 use super::*;
224 use crate::{
225 journal::contiguous::{fixed::Config as FConfig, variable::Config as VConfig},
226 qmdb::any::{FixedConfig, MerkleConfig, VariableConfig},
227 translator::OneCap,
228 };
229 use commonware_codec::{Codec, CodecShared};
230 use commonware_cryptography::{Hasher, Sha256, sha256::Digest};
231 use commonware_runtime::{
232 BufferPooler, Supervisor as _, buffer::paged::CacheRef, deterministic::Context,
233 };
234 use commonware_utils::{NZU16, NZU64, NZUsize};
235 use core::{future::Future, pin::Pin};
236 use std::{
237 collections::HashMap,
238 num::{NonZeroU16, NonZeroUsize},
239 };
240
241 pub(crate) fn colliding_digest(prefix: u8, suffix: u64) -> Digest {
242 let mut bytes = [0u8; 32];
243 bytes[0] = prefix;
244 bytes[24..].copy_from_slice(&suffix.to_be_bytes());
245 Digest::from(bytes)
246 }
247
248 const PAGE_SIZE: NonZeroU16 = NZU16!(101);
250 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11);
251
252 pub(crate) fn fixed_db_config<T: Translator + Default>(
253 suffix: &str,
254 pooler: &impl BufferPooler,
255 ) -> FixedConfig<T, Sequential> {
256 fixed_db_config_with_strategy(suffix, pooler, Sequential)
257 }
258
259 pub(crate) fn fixed_db_config_with_strategy<
260 T: Translator + Default,
261 S: commonware_parallel::Strategy,
262 >(
263 suffix: &str,
264 pooler: &impl BufferPooler,
265 strategy: S,
266 ) -> FixedConfig<T, S> {
267 fixed_db_config_full(suffix, pooler, strategy, ())
268 }
269
270 pub(crate) fn fixed_db_config_full<
273 T: Translator + Default,
274 S: commonware_parallel::Strategy,
275 B,
276 >(
277 suffix: &str,
278 pooler: &impl BufferPooler,
279 strategy: S,
280 init_concurrency: B,
281 ) -> FixedConfig<T, S, B> {
282 let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
283 FixedConfig {
284 merkle_config: MerkleConfig {
285 journal_partition: format!("journal-{suffix}"),
286 metadata_partition: format!("metadata-{suffix}"),
287 items_per_blob: NZU64!(11),
288 write_buffer: NZUsize!(1024),
289 replay_buffer: NZUsize!(1024),
290 strategy,
291 page_cache: page_cache.clone(),
292 },
293 journal_config: FConfig {
294 partition: format!("log-journal-{suffix}"),
295 items_per_blob: NZU64!(7),
296 page_cache,
297 write_buffer: NZUsize!(1024),
298 replay_buffer: NZUsize!(1024),
299 },
300 translator: T::default(),
301 init_cache_size: Some(NZUsize!(1024)),
302 init_buffer: NZUsize!(1 << 21),
303 init_concurrency,
304 }
305 }
306
307 pub(crate) fn fixed_db_config_partitioned<T: Translator + Default>(
309 suffix: &str,
310 pooler: &impl BufferPooler,
311 ) -> FixedConfig<T, Sequential, NonZeroUsize> {
312 fixed_db_config_full(suffix, pooler, Sequential, NZUsize!(1))
313 }
314
315 pub(crate) fn variable_db_config_partitioned<T: Translator + Default>(
317 suffix: &str,
318 pooler: &impl BufferPooler,
319 ) -> VariableConfig<T, ((), ()), Sequential, NonZeroUsize> {
320 variable_db_config_full(suffix, pooler, NZUsize!(1))
321 }
322
323 pub(crate) fn variable_db_config<T: Translator + Default>(
324 suffix: &str,
325 pooler: &impl BufferPooler,
326 ) -> VariableConfig<T, ((), ()), Sequential> {
327 variable_db_config_full(suffix, pooler, ())
328 }
329
330 pub(crate) fn variable_db_config_full<T: Translator + Default, B>(
333 suffix: &str,
334 pooler: &impl BufferPooler,
335 init_concurrency: B,
336 ) -> VariableConfig<T, ((), ()), Sequential, B> {
337 let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
338 VariableConfig {
339 merkle_config: MerkleConfig {
340 journal_partition: format!("journal-{suffix}"),
341 metadata_partition: format!("metadata-{suffix}"),
342 items_per_blob: NZU64!(11),
343 write_buffer: NZUsize!(1024),
344 replay_buffer: NZUsize!(1024),
345 strategy: Sequential,
346 page_cache: page_cache.clone(),
347 },
348 journal_config: VConfig {
349 partition: format!("log-journal-{suffix}"),
350 items_per_section: NZU64!(7),
351 compression: None,
352 codec_config: ((), ()),
353 page_cache,
354 write_buffer: NZUsize!(1024),
355 replay_buffer: NZUsize!(1024),
356 },
357 translator: T::default(),
358 init_cache_size: Some(NZUsize!(1024)),
359 init_buffer: NZUsize!(1 << 21),
360 init_concurrency,
361 }
362 }
363
364 use crate::{
365 index::Unordered as UnorderedIndex,
366 journal::contiguous::Mutable,
367 merkle::mmr,
368 qmdb::any::{
369 db::Db as AnyDb,
370 operation::{Operation as AnyOperation, update::Update as UpdateTrait},
371 traits::{DbAny, Provable, UnmerkleizedBatch as _},
372 },
373 };
374
375 type Error = crate::qmdb::Error<mmr::Family>;
376 type Location = mmr::Location;
377
378 pub(crate) trait RewindableDb: Sized {
379 fn rewind_to_size(self, size: Location)
380 -> impl Future<Output = Result<Self, Error>> + Send;
381 }
382
383 impl<E, C, I, H, U, const N: usize, S> RewindableDb for AnyDb<mmr::Family, E, C, I, H, U, N, S>
384 where
385 E: crate::Context,
386 C: Mutable<Item = AnyOperation<mmr::Family, U>>,
387 I: UnorderedIndex<Value = Location>,
388 H: Hasher,
389 U: UpdateTrait,
390 S: Strategy,
391 AnyOperation<mmr::Family, U>: Codec,
392 {
393 async fn rewind_to_size(self, size: Location) -> Result<Self, Error> {
394 self.rewind(size).await
395 }
396 }
397
398 pub(crate) async fn test_any_db_non_empty_recovery<F: Family, D, V: Clone + CodecShared>(
400 context: Context,
401 mut db: D,
402 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
403 make_value: impl Fn(u64) -> V,
404 ) where
405 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
406 {
407 const ELEMENTS: u64 = 1000;
408
409 {
411 let mut batch = db.new_batch();
412 for i in 0u64..ELEMENTS {
413 let k = Sha256::hash(&[&i.to_be_bytes()]);
414 let v = make_value(i * 1000);
415 batch = batch.write(k, Some(v));
416 }
417 let merkleized = batch.merkleize(&db, None).await.unwrap();
418 (db, _) = db.apply_batch(merkleized).await.unwrap();
419 }
420 let db = db.commit().await.unwrap();
421 let boundary = db.sync_boundary();
422 let db = db.prune(boundary).await.unwrap();
423 let root = db.root();
424 let op_count = db.size();
425 let inactivity_floor_loc = db.inactivity_floor_loc();
426
427 let db = reopen_db(context.child("reopen").with_attribute("index", 1)).await;
428 assert_eq!(db.size(), op_count);
429 assert_eq!(db.inactivity_floor_loc(), inactivity_floor_loc);
430 assert_eq!(db.root(), root);
431
432 {
434 let mut batch = db.new_batch();
435 for i in 0u64..ELEMENTS {
436 let k = Sha256::hash(&[&i.to_be_bytes()]);
437 let v = make_value((i + 1) * 10000);
438 batch = batch.write(k, Some(v));
439 }
440 let _merkleized = batch.merkleize(&db, None).await.unwrap();
441 }
442 let db = reopen_db(context.child("reopen").with_attribute("index", 2)).await;
443 assert_eq!(db.size(), op_count);
444 assert_eq!(db.inactivity_floor_loc(), inactivity_floor_loc);
445 assert_eq!(db.root(), root);
446
447 {
449 let mut batch = db.new_batch();
450 for i in 0u64..ELEMENTS {
451 let k = Sha256::hash(&[&i.to_be_bytes()]);
452 let v = make_value((i + 1) * 10000);
453 batch = batch.write(k, Some(v));
454 }
455 let _merkleized = batch.merkleize(&db, None).await.unwrap();
456 }
457 let db = reopen_db(context.child("reopen").with_attribute("index", 3)).await;
458 assert_eq!(db.size(), op_count);
459 assert_eq!(db.root(), root);
460
461 for _ in 0..3 {
463 let mut batch = db.new_batch();
464 for i in 0u64..ELEMENTS {
465 let k = Sha256::hash(&[&i.to_be_bytes()]);
466 let v = make_value((i + 1) * 10000);
467 batch = batch.write(k, Some(v));
468 }
469 let _merkleized = batch.merkleize(&db, None).await.unwrap();
470 }
471 let mut db = reopen_db(context.child("reopen").with_attribute("index", 4)).await;
472 assert_eq!(db.size(), op_count);
473 assert_eq!(db.root(), root);
474
475 {
477 let mut batch = db.new_batch();
478 for i in 0u64..ELEMENTS {
479 let k = Sha256::hash(&[&i.to_be_bytes()]);
480 let v = make_value((i + 1) * 10000);
481 batch = batch.write(k, Some(v));
482 }
483 let merkleized = batch.merkleize(&db, None).await.unwrap();
484 (db, _) = db.apply_batch(merkleized).await.unwrap();
485 }
486 db.commit().await.unwrap();
487 let db = reopen_db(context.child("reopen").with_attribute("index", 5)).await;
488 assert!(db.size() > op_count);
489 assert_ne!(db.inactivity_floor_loc(), inactivity_floor_loc);
490 assert_ne!(db.root(), root);
491
492 db.destroy().await.unwrap();
493 }
494
495 pub(crate) async fn test_any_db_empty_recovery<F: Family, D, V: Clone + CodecShared>(
497 context: Context,
498 db: D,
499 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
500 make_value: impl Fn(u64) -> V,
501 ) where
502 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
503 {
504 let root = db.root();
505
506 let db = reopen_db(context.child("reopen").with_attribute("index", 1)).await;
507 assert_eq!(db.size(), 1);
508 assert_eq!(db.root(), root);
509
510 {
512 let mut batch = db.new_batch();
513 for i in 0u64..1000 {
514 let k = Sha256::hash(&[&i.to_be_bytes()]);
515 let v = make_value((i + 1) * 10000);
516 batch = batch.write(k, Some(v));
517 }
518 let _merkleized = batch.merkleize(&db, None).await.unwrap();
519 }
520 let db = reopen_db(context.child("reopen").with_attribute("index", 2)).await;
521 assert_eq!(db.size(), 1);
522 assert_eq!(db.root(), root);
523
524 {
526 let mut batch = db.new_batch();
527 for i in 0u64..1000 {
528 let k = Sha256::hash(&[&i.to_be_bytes()]);
529 let v = make_value((i + 1) * 10000);
530 batch = batch.write(k, Some(v));
531 }
532 let _merkleized = batch.merkleize(&db, None).await.unwrap();
533 }
534 drop(db);
535 let db = reopen_db(context.child("reopen").with_attribute("index", 3)).await;
536 assert_eq!(db.size(), 1);
537 assert_eq!(db.root(), root);
538
539 for _ in 0..3 {
541 let mut batch = db.new_batch();
542 for i in 0u64..1000 {
543 let k = Sha256::hash(&[&i.to_be_bytes()]);
544 let v = make_value((i + 1) * 10000);
545 batch = batch.write(k, Some(v));
546 }
547 let _merkleized = batch.merkleize(&db, None).await.unwrap();
548 }
549 drop(db);
550 let mut db = reopen_db(context.child("reopen").with_attribute("index", 4)).await;
551 assert_eq!(db.size(), 1);
552 assert_eq!(db.root(), root);
553
554 {
556 let mut batch = db.new_batch();
557 for i in 0u64..1000 {
558 let k = Sha256::hash(&[&i.to_be_bytes()]);
559 let v = make_value((i + 1) * 10000);
560 batch = batch.write(k, Some(v));
561 }
562 let merkleized = batch.merkleize(&db, None).await.unwrap();
563 (db, _) = db.apply_batch(merkleized).await.unwrap();
564 }
565 db.commit().await.unwrap();
566 let db = reopen_db(context.child("reopen").with_attribute("index", 5)).await;
567 assert!(db.size() > 1);
568 assert_ne!(db.root(), root);
569
570 db.destroy().await.unwrap();
571 }
572
573 pub(crate) async fn test_any_db_commit_after_sync_recovery<F: Family, D, V>(
575 context: Context,
576 db: D,
577 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
578 make_value: impl Fn(u64) -> V,
579 ) where
580 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
581 V: Clone + CodecShared + Eq + std::fmt::Debug,
582 {
583 let key0 = Sha256::hash(&[&0u64.to_be_bytes()]);
584 let key1 = Sha256::hash(&[&1u64.to_be_bytes()]);
585 let value0 = make_value(100);
586 let value1 = make_value(200);
587
588 let merkleized = db
590 .new_batch()
591 .write(key0, Some(value0.clone()))
592 .merkleize(&db, None)
593 .await
594 .unwrap();
595 let (db, _) = db.apply_batch(merkleized).await.unwrap();
596 let db = db.commit().await.unwrap();
597 let db = db.sync().await.unwrap();
598
599 let merkleized = db
601 .new_batch()
602 .write(key1, Some(value1.clone()))
603 .merkleize(&db, None)
604 .await
605 .unwrap();
606 let (db, _) = db.apply_batch(merkleized).await.unwrap();
607 let db = db.commit().await.unwrap();
608 let committed_root = db.root();
609 let committed_size = db.size();
610 drop(db);
611
612 let db = reopen_db(context.child("reopen").with_attribute("index", 1)).await;
613 assert_eq!(db.root(), committed_root);
614 assert_eq!(db.size(), committed_size);
615 assert_eq!(db.get(&key0).await.unwrap(), Some(value0));
616 assert_eq!(db.get(&key1).await.unwrap(), Some(value1));
617
618 db.destroy().await.unwrap();
619 }
620
621 pub(crate) async fn test_any_db_start_sync_recovery<F: Family, D, V>(
623 context: Context,
624 db: D,
625 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
626 make_value: impl Fn(u64) -> V,
627 ) where
628 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
629 V: Clone + CodecShared + Eq + std::fmt::Debug,
630 {
631 let key0 = Sha256::hash(&[&0u64.to_be_bytes()]);
632 let value0 = make_value(100);
633
634 let merkleized = db
636 .new_batch()
637 .write(key0, Some(value0.clone()))
638 .merkleize(&db, None)
639 .await
640 .unwrap();
641 let (db, _) = db.apply_batch(merkleized).await.unwrap();
642 let (db, handle) = db.start_sync().await.unwrap();
643 handle.await.unwrap();
644 let committed_root = db.root();
645 let committed_size = db.size();
646 drop(db);
647
648 let db = reopen_db(context.child("reopen").with_attribute("index", 1)).await;
649 assert_eq!(db.root(), committed_root);
650 assert_eq!(db.size(), committed_size);
651 assert_eq!(db.get(&key0).await.unwrap(), Some(value0));
652
653 db.destroy().await.unwrap();
654 }
655
656 pub(crate) async fn test_any_db_prune_after_unsynced_floor_recovery<
660 F: Family,
661 D,
662 V: Clone + CodecShared,
663 >(
664 context: Context,
665 db: D,
666 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
667 make_value: impl Fn(u64) -> V,
668 ) where
669 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
670 {
671 const ELEMENTS: u64 = 1000;
672
673 let mut batch = db.new_batch();
675 for i in 0u64..ELEMENTS {
676 let k = Sha256::hash(&[&i.to_be_bytes()]);
677 batch = batch.write(k, Some(make_value(i)));
678 }
679 let merkleized = batch.merkleize(&db, None).await.unwrap();
680 let (db, _) = db.apply_batch(merkleized).await.unwrap();
681 let db = db.commit().await.unwrap();
682 let durable_floor = db.inactivity_floor_loc();
683
684 let mut batch = db.new_batch();
687 for i in 0u64..ELEMENTS {
688 let k = Sha256::hash(&[&i.to_be_bytes()]);
689 batch = batch.write(k, Some(make_value(i + 1)));
690 }
691 let merkleized = batch.merkleize(&db, None).await.unwrap();
692 let (db, _) = db.apply_batch(merkleized).await.unwrap();
693 let unsynced_floor = db.inactivity_floor_loc();
694 assert!(unsynced_floor > durable_floor);
695
696 let boundary = db.sync_boundary();
698 let db = db.prune(boundary).await.unwrap();
699 let root = db.root();
700 let op_count = db.size();
701 drop(db);
702
703 let db = reopen_db(context.child("reopen").with_attribute("index", 1)).await;
706 assert_eq!(db.size(), op_count);
707 assert_eq!(db.inactivity_floor_loc(), unsynced_floor);
708 assert_eq!(db.root(), root);
709
710 db.destroy().await.unwrap();
711 }
712
713 pub(crate) async fn test_any_db_rewind_recovery<D, V>(
715 context: Context,
716 db: D,
717 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
718 make_value: impl Fn(u64) -> V,
719 ) where
720 D: DbAny<mmr::Family, Key = Digest, Value = V, Digest = Digest> + RewindableDb,
721 V: Clone + CodecShared + Eq + std::fmt::Debug,
722 {
723 let key0 = Sha256::hash(&[&0u64.to_be_bytes()]);
724 let key1 = Sha256::hash(&[&1u64.to_be_bytes()]);
725 let key2 = Sha256::hash(&[&2u64.to_be_bytes()]);
726 let initial_root = db.root();
727 let initial_size = db.size();
728 let initial_floor = db.inactivity_floor_loc();
729
730 let merkleized = db.new_batch().merkleize(&db, None).await.unwrap();
732 let (db, empty_range) = db.apply_batch(merkleized).await.unwrap();
733 let db = db.commit().await.unwrap();
734 assert_eq!(empty_range.start, initial_size);
735 assert_eq!(db.size(), empty_range.end);
736 let db = db.rewind_to_size(initial_size).await.unwrap();
737 assert_eq!(db.root(), initial_root);
738 assert_eq!(db.size(), initial_size);
739 assert_eq!(db.inactivity_floor_loc(), initial_floor);
740 assert_eq!(db.get_metadata().await.unwrap(), None);
741
742 let value0_a = make_value(10);
743 let value1_a = make_value(11);
744 let metadata_a = make_value(12);
745
746 let merkleized = db
747 .new_batch()
748 .write(key0, Some(value0_a.clone()))
749 .write(key1, Some(value1_a.clone()))
750 .merkleize(&db, Some(metadata_a.clone()))
751 .await
752 .unwrap();
753 let (db, range_a) = db.apply_batch(merkleized).await.unwrap();
754 let db = db.commit().await.unwrap();
755
756 let root_a = db.root();
757 let size_a = db.size();
758 let floor_a = db.inactivity_floor_loc();
759 assert_eq!(size_a, range_a.end);
760
761 let value0_b = make_value(20);
762 let value2_b = make_value(21);
763 let metadata_b = make_value(22);
764
765 let merkleized = db
766 .new_batch()
767 .write(key0, Some(value0_b))
768 .write(key1, None)
769 .write(key2, Some(value2_b))
770 .merkleize(&db, Some(metadata_b))
771 .await
772 .unwrap();
773 let (db, range_b) = db.apply_batch(merkleized).await.unwrap();
774 let db = db.commit().await.unwrap();
775 assert_eq!(range_b.start, size_a);
776 assert_ne!(db.root(), root_a);
777
778 let value0_c = make_value(30);
779 let value1_c = make_value(31);
780 let metadata_c = make_value(32);
781 let merkleized = db
782 .new_batch()
783 .write(key0, Some(value0_c))
784 .write(key1, Some(value1_c))
785 .write(key2, None)
786 .merkleize(&db, Some(metadata_c))
787 .await
788 .unwrap();
789 let (db, _) = db.apply_batch(merkleized).await.unwrap();
790 let db = db.commit().await.unwrap();
791
792 let db = db.rewind_to_size(size_a).await.unwrap();
796 assert_eq!(db.root(), root_a);
797 assert_eq!(db.size(), size_a);
798 assert_eq!(db.inactivity_floor_loc(), floor_a);
799 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a.clone()));
800 assert_eq!(db.get(&key0).await.unwrap(), Some(value0_a));
801 assert_eq!(db.get(&key1).await.unwrap(), Some(value1_a));
802 assert_eq!(db.get(&key2).await.unwrap(), None);
803
804 db.commit().await.unwrap();
805 let db = reopen_db(context.child("reopen_after_rewind")).await;
806 assert_eq!(db.root(), root_a);
807 assert_eq!(db.size(), size_a);
808 assert_eq!(db.inactivity_floor_loc(), floor_a);
809 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a));
810 assert_eq!(db.get(&key0).await.unwrap(), Some(make_value(10)));
811 assert_eq!(db.get(&key1).await.unwrap(), Some(make_value(11)));
812 assert_eq!(db.get(&key2).await.unwrap(), None);
813
814 let value2_d = make_value(40);
817 let metadata_d = make_value(41);
818 let merkleized = db
819 .new_batch()
820 .write(key2, Some(value2_d.clone()))
821 .merkleize(&db, Some(metadata_d.clone()))
822 .await
823 .unwrap();
824 let (db, _) = db.apply_batch(merkleized).await.unwrap();
825 let db = db.commit().await.unwrap();
826 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_d.clone()));
827 assert_eq!(db.get(&key0).await.unwrap(), Some(make_value(10)));
828 assert_eq!(db.get(&key1).await.unwrap(), Some(make_value(11)));
829 assert_eq!(db.get(&key2).await.unwrap(), Some(value2_d.clone()));
830
831 drop(db);
832 let db = reopen_db(context.child("reopen_after_rewind_new_writes")).await;
833 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_d));
834 assert_eq!(db.get(&key0).await.unwrap(), Some(make_value(10)));
835 assert_eq!(db.get(&key1).await.unwrap(), Some(make_value(11)));
836 assert_eq!(db.get(&key2).await.unwrap(), Some(value2_d));
837
838 let db = db.rewind_to_size(initial_size).await.unwrap();
840 assert_eq!(db.root(), initial_root);
841 assert_eq!(db.size(), initial_size);
842 assert_eq!(db.inactivity_floor_loc(), initial_floor);
843 assert_eq!(db.get_metadata().await.unwrap(), None);
844 assert_eq!(db.get(&key0).await.unwrap(), None);
845 assert_eq!(db.get(&key1).await.unwrap(), None);
846 assert_eq!(db.get(&key2).await.unwrap(), None);
847
848 db.commit().await.unwrap();
849 let db = reopen_db(context.child("reopen_initial_boundary")).await;
850 assert_eq!(db.root(), initial_root);
851 assert_eq!(db.size(), initial_size);
852 assert_eq!(db.inactivity_floor_loc(), initial_floor);
853 assert_eq!(db.get_metadata().await.unwrap(), None);
854 assert_eq!(db.get(&key0).await.unwrap(), None);
855 assert_eq!(db.get(&key1).await.unwrap(), None);
856 assert_eq!(db.get(&key2).await.unwrap(), None);
857
858 db.destroy().await.unwrap();
859 }
860
861 #[boxed]
863 pub(crate) async fn test_any_db_build_and_authenticate<D, V>(
864 context: Context,
865 mut db: D,
866 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
867 make_value: impl Fn(u64) -> V,
868 ) where
869 D: DbAny<mmr::Family, Key = Digest, Value = V, Digest = Digest> + Provable<mmr::Family>,
870 V: CodecShared + Clone + Eq + std::hash::Hash + std::fmt::Debug,
871 <D as Provable<mmr::Family>>::Operation: Codec,
872 {
873 use crate::qmdb::verify_proof;
874
875 const ELEMENTS: u64 = 1000;
876
877 let mut map = HashMap::<Digest, V>::default();
878 {
879 let mut batch = db.new_batch();
880 for i in 0u64..ELEMENTS {
881 let k = Sha256::hash(&[&i.to_be_bytes()]);
882 let v = make_value(i * 1000);
883 batch = batch.write(k, Some(v.clone()));
884 map.insert(k, v);
885 }
886
887 for i in 0u64..ELEMENTS {
889 if i % 3 != 0 {
890 continue;
891 }
892 let k = Sha256::hash(&[&i.to_be_bytes()]);
893 let v = make_value((i + 1) * 10000);
894 batch = batch.write(k, Some(v.clone()));
895 map.insert(k, v);
896 }
897
898 for i in 0u64..ELEMENTS {
900 if i % 7 != 1 {
901 continue;
902 }
903 let k = Sha256::hash(&[&i.to_be_bytes()]);
904 batch = batch.write(k, None);
905 map.remove(&k);
906 }
907
908 let merkleized = batch.merkleize(&db, None).await.unwrap();
909 (db, _) = db.apply_batch(merkleized).await.unwrap();
910 }
911 let db = db.sync().await.unwrap();
913 let boundary = db.sync_boundary();
914 let db = db.prune(boundary).await.unwrap();
915
916 let root = db.root();
918 db.sync().await.unwrap();
919 let db = reopen_db(context.child("reopened")).await;
920 assert_eq!(root, db.root());
921
922 for i in 0u64..ELEMENTS {
924 let k = Sha256::hash(&[&i.to_be_bytes()]);
925 if let Some(map_value) = map.get(&k) {
926 let Some(db_value) = db.get(&k).await.unwrap() else {
927 panic!("key not found in db: {k}");
928 };
929 assert_eq!(*map_value, db_value);
930 } else {
931 assert!(db.get(&k).await.unwrap().is_none());
932 }
933 }
934 let bounds = db.bounds();
935 let inactivity_floor = db.inactivity_floor_loc();
936 for loc in *inactivity_floor..*bounds.end {
937 let loc = Location::new(loc);
938 let (proof, ops) = db.proof(loc, NZU64!(10)).await.unwrap();
939 assert!(verify_proof::<Sha256, _, _>(&proof, loc, &ops, &root));
940 }
941
942 db.destroy().await.unwrap();
943 }
944
945 pub(crate) async fn test_any_db_log_replay<
947 F: Family,
948 D,
949 V: Clone + CodecShared + PartialEq + std::fmt::Debug,
950 >(
951 context: Context,
952 mut db: D,
953 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
954 make_value: impl Fn(u64) -> V,
955 ) where
956 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
957 {
958 const UPDATES: u64 = 100;
960 let k = Sha256::hash(&[&UPDATES.to_be_bytes()]);
961 let mut last_value = None;
962 {
963 let mut batch = db.new_batch();
964 for i in 0u64..UPDATES {
965 let v = make_value(i * 1000);
966 last_value = Some(v.clone());
967 batch = batch.write(k, Some(v));
968 }
969 let merkleized = batch.merkleize(&db, None).await.unwrap();
970 (db, _) = db.apply_batch(merkleized).await.unwrap();
971 }
972 let db = db.commit().await.unwrap();
973 let root = db.root();
974
975 drop(db);
977 let db = reopen_db(context.child("reopened")).await;
978 assert_eq!(db.root(), root);
979 assert_eq!(db.get(&k).await.unwrap(), last_value);
980
981 db.destroy().await.unwrap();
982 }
983
984 pub(crate) async fn test_any_db_historical_proof_basic<D, V: Clone + CodecShared>(
986 _context: Context,
987 mut db: D,
988 make_value: impl Fn(u64) -> V,
989 ) where
990 D: DbAny<mmr::Family, Key = Digest, Value = V, Digest = Digest> + Provable<mmr::Family>,
991 <D as Provable<mmr::Family>>::Operation: Codec + PartialEq + std::fmt::Debug,
992 {
993 use crate::qmdb::verify_proof;
994 use commonware_utils::NZU64;
995
996 const OPS: u64 = 20;
998 {
999 let mut batch = db.new_batch();
1000 for i in 0u64..OPS {
1001 let k = Sha256::hash(&[&i.to_be_bytes()]);
1002 let v = make_value(i * 1000);
1003 batch = batch.write(k, Some(v));
1004 }
1005 let merkleized = batch.merkleize(&db, None).await.unwrap();
1006 (db, _) = db.apply_batch(merkleized).await.unwrap();
1007 }
1008 let root_hash = db.root();
1009 let original_op_count = db.size();
1010
1011 let max_ops = NZU64!(10);
1013 let start_loc = Location::new(5);
1014 let (historical_proof, historical_ops) = db
1015 .historical_proof(original_op_count, start_loc, max_ops)
1016 .await
1017 .unwrap();
1018 let (regular_proof, regular_ops) = db.proof(start_loc, max_ops).await.unwrap();
1019
1020 assert_eq!(historical_proof.leaves, regular_proof.leaves);
1021 assert_eq!(historical_proof.digests, regular_proof.digests);
1022 assert_eq!(historical_ops, regular_ops);
1023 assert!(verify_proof::<Sha256, _, _>(
1024 &historical_proof,
1025 start_loc,
1026 &historical_ops,
1027 &root_hash,
1028 ));
1029
1030 {
1032 let mut batch = db.new_batch();
1033 for i in OPS..(OPS + 5) {
1034 let k = Sha256::hash(&[&(i + 1000).to_be_bytes()]); let v = make_value(i * 1000);
1036 batch = batch.write(k, Some(v));
1037 }
1038 let merkleized = batch.merkleize(&db, None).await.unwrap();
1039 (db, _) = db.apply_batch(merkleized).await.unwrap();
1040 }
1041
1042 let (historical_proof2, historical_ops2) = db
1044 .historical_proof(original_op_count, start_loc, max_ops)
1045 .await
1046 .unwrap();
1047 assert_eq!(historical_proof2.leaves, original_op_count);
1048 assert_eq!(historical_proof2.digests, regular_proof.digests);
1049 assert_eq!(historical_ops2, regular_ops);
1050 assert!(verify_proof::<Sha256, _, _>(
1051 &historical_proof2,
1052 start_loc,
1053 &historical_ops2,
1054 &root_hash,
1055 ));
1056
1057 db.destroy().await.unwrap();
1058 }
1059
1060 pub(crate) async fn test_any_db_historical_proof_invalid<D, V: Clone + CodecShared>(
1062 _context: Context,
1063 mut db: D,
1064 make_value: impl Fn(u64) -> V,
1065 ) where
1066 D: DbAny<mmr::Family, Key = Digest, Value = V, Digest = Digest> + Provable<mmr::Family>,
1067 <D as Provable<mmr::Family>>::Operation: Codec + PartialEq + std::fmt::Debug + Clone,
1068 {
1069 use crate::qmdb::verify_proof;
1070 use commonware_utils::NZU64;
1071
1072 let mut historical_op_count = Location::new(0);
1076 for i in 0u64..2 {
1077 let k = Sha256::hash(&[&i.to_be_bytes()]);
1078 let v = make_value(i * 1000);
1079 let merkleized = db
1080 .new_batch()
1081 .write(k, Some(v))
1082 .merkleize(&db, None)
1083 .await
1084 .unwrap();
1085 (db, _) = db.apply_batch(merkleized).await.unwrap();
1086 if i == 0 {
1087 historical_op_count = db.bounds().end;
1088 }
1089 }
1090
1091 let expected_ops_len = (*historical_op_count - 1) as usize;
1092 let (proof, ops) = db
1093 .historical_proof(historical_op_count, Location::new(1), NZU64!(10))
1094 .await
1095 .unwrap();
1096 assert_eq!(proof.leaves, historical_op_count);
1097 assert_eq!(ops.len(), expected_ops_len);
1098
1099 {
1101 let mut tampered_proof = proof.clone();
1102 tampered_proof.digests[0] = Sha256::hash(&[b"invalid"]);
1103 let root_hash = db.root();
1104 assert!(!verify_proof::<Sha256, _, _>(
1105 &tampered_proof,
1106 Location::new(1),
1107 &ops,
1108 &root_hash,
1109 ));
1110 }
1111
1112 {
1114 let mut tampered_proof = proof.clone();
1115 tampered_proof.digests.push(Sha256::hash(&[b"invalid"]));
1116 let root_hash = db.root();
1117 assert!(!verify_proof::<Sha256, _, _>(
1118 &tampered_proof,
1119 Location::new(1),
1120 &ops,
1121 &root_hash,
1122 ));
1123 }
1124
1125 {
1127 let root_hash = db.root();
1128 let mut tampered_ops = ops.clone();
1129 if tampered_ops.len() >= 2 {
1131 tampered_ops.swap(0, 1);
1132 assert!(!verify_proof::<Sha256, _, _>(
1133 &proof,
1134 Location::new(1),
1135 &tampered_ops,
1136 &root_hash,
1137 ));
1138 }
1139 }
1140
1141 {
1143 let root_hash = db.root();
1144 let mut tampered_ops = ops.clone();
1145 tampered_ops.push(tampered_ops[0].clone());
1146 assert!(!verify_proof::<Sha256, _, _>(
1147 &proof,
1148 Location::new(1),
1149 &tampered_ops,
1150 &root_hash,
1151 ));
1152 }
1153
1154 {
1156 let root_hash = db.root();
1157 assert!(!verify_proof::<Sha256, _, _>(
1158 &proof,
1159 Location::new(2),
1160 &ops,
1161 &root_hash,
1162 ));
1163 }
1164
1165 {
1167 let invalid_root = Sha256::hash(&[b"invalid"]);
1168 assert!(!verify_proof::<Sha256, _, _>(
1169 &proof,
1170 Location::new(1),
1171 &ops,
1172 &invalid_root,
1173 ));
1174 }
1175
1176 {
1178 let mut tampered_proof = proof.clone();
1179 tampered_proof.leaves = Location::new(100);
1180 let root_hash = db.root();
1181 assert!(!verify_proof::<Sha256, _, _>(
1182 &tampered_proof,
1183 Location::new(1),
1184 &ops,
1185 &root_hash,
1186 ));
1187 }
1188
1189 db.destroy().await.unwrap();
1190 }
1191
1192 pub(crate) async fn test_any_db_historical_proof_edge_cases<D, V: Clone + CodecShared>(
1194 _context: Context,
1195 mut db: D,
1196 make_value: impl Fn(u64) -> V,
1197 ) where
1198 D: DbAny<mmr::Family, Key = Digest, Value = V, Digest = Digest> + Provable<mmr::Family>,
1199 <D as Provable<mmr::Family>>::Operation: Codec + PartialEq + std::fmt::Debug,
1200 {
1201 use commonware_utils::NZU64;
1202
1203 let initial_size = db.bounds().end;
1209 let mut boundaries = vec![initial_size];
1210 for i in 0u64..5 {
1211 let k = Sha256::hash(&[&i.to_be_bytes()]);
1212 let v = make_value(i * 1000);
1213 let merkleized = db
1214 .new_batch()
1215 .write(k, Some(v))
1216 .merkleize(&db, None)
1217 .await
1218 .unwrap();
1219 (db, _) = db.apply_batch(merkleized).await.unwrap();
1220 boundaries.push(db.bounds().end);
1221 }
1222
1223 let singleton_size = boundaries[0];
1225 let (single_proof, single_ops) = db
1226 .historical_proof(singleton_size, Location::new(0), NZU64!(1))
1227 .await
1228 .unwrap();
1229 assert_eq!(single_proof.leaves, singleton_size);
1230 assert_eq!(single_ops.len(), 1);
1231
1232 let limited_size = boundaries[2];
1236 let limited_start = boundaries[1];
1237 let expected_limited = (*limited_size - *limited_start) as usize;
1238 assert!(expected_limited > 0);
1239 let (_limited_proof, limited_ops) = db
1240 .historical_proof(limited_size, limited_start, NZU64!(20))
1241 .await
1242 .unwrap();
1243 assert_eq!(limited_ops.len(), expected_limited);
1244
1245 let min_size = boundaries[2];
1248 let max_ops = NZU64!(3);
1249 let expected_min = core::cmp::min(max_ops.get(), *min_size - 1) as usize;
1250 let (min_proof, min_ops) = db
1251 .historical_proof(min_size, Location::new(1), max_ops)
1252 .await
1253 .unwrap();
1254 assert_eq!(min_proof.leaves, min_size);
1255 assert_eq!(min_ops.len(), expected_min);
1256
1257 db.destroy().await.unwrap();
1258 }
1259
1260 pub(crate) async fn test_any_db_multiple_commits_delete_replayed<F: Family, D, V>(
1262 context: Context,
1263 mut db: D,
1264 reopen_db: impl Fn(Context) -> Pin<Box<dyn Future<Output = D> + Send>>,
1265 make_value: impl Fn(u64) -> V,
1266 ) where
1267 D: DbAny<F, Key = Digest, Value = V, Digest = Digest>,
1268 V: Clone + CodecShared + Eq + std::fmt::Debug,
1269 {
1270 let mut map = HashMap::<Digest, V>::default();
1271 const ELEMENTS: u64 = 10;
1272 let metadata_value = make_value(42);
1273 let key_at = |j: u64, i: u64| Sha256::hash(&[&(j * 1000 + i).to_be_bytes()]);
1274 for j in 0u64..ELEMENTS {
1275 let mut batch = db.new_batch();
1276 for i in 0u64..ELEMENTS {
1277 let k = key_at(j, i);
1278 let v = make_value(i * 1000);
1279 batch = batch.write(k, Some(v.clone()));
1280 map.insert(k, v);
1281 }
1282 let merkleized = batch
1283 .merkleize(&db, Some(metadata_value.clone()))
1284 .await
1285 .unwrap();
1286 (db, _) = db.apply_batch(merkleized).await.unwrap();
1287 db = db.commit().await.unwrap();
1288 }
1289 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_value));
1290 let k = key_at(ELEMENTS - 1, ELEMENTS - 1);
1291
1292 let merkleized = db
1293 .new_batch()
1294 .write(k, None)
1295 .merkleize(&db, None)
1296 .await
1297 .unwrap();
1298 let (db, _) = db.apply_batch(merkleized).await.unwrap();
1299 let db = db.commit().await.unwrap();
1300 assert_eq!(db.get_metadata().await.unwrap(), None);
1301 assert!(db.get(&k).await.unwrap().is_none());
1302
1303 let root = db.root();
1304 drop(db);
1305 let db = reopen_db(context.child("reopened")).await;
1306 assert_eq!(root, db.root());
1307 assert_eq!(db.get_metadata().await.unwrap(), None);
1308 assert!(db.get(&k).await.unwrap().is_none());
1309
1310 db.destroy().await.unwrap();
1311 }
1312
1313 use crate::qmdb::any::{
1314 ordered::{fixed::Db as OrderedFixedDb, variable::Db as OrderedVariableDb},
1315 unordered::{fixed::Db as UnorderedFixedDb, variable::Db as UnorderedVariableDb},
1316 };
1317 use commonware_macros::{test_group, test_traced};
1318 use commonware_parallel::{Sequential, Strategy};
1319 use commonware_runtime::{Runner as _, deterministic};
1320
1321 type UnorderedFixed =
1323 UnorderedFixedDb<mmr::Family, Context, Digest, Digest, Sha256, OneCap, Sequential>;
1324 type UnorderedVariable =
1325 UnorderedVariableDb<mmr::Family, Context, Digest, Digest, Sha256, OneCap, Sequential>;
1326 type OrderedFixed =
1327 OrderedFixedDb<mmr::Family, Context, Digest, Digest, Sha256, OneCap, Sequential>;
1328 type OrderedVariable =
1329 OrderedVariableDb<mmr::Family, Context, Digest, Digest, Sha256, OneCap, Sequential>;
1330 type UnorderedFixedP1 = unordered::fixed::partitioned::Db<
1331 mmr::Family,
1332 Context,
1333 Digest,
1334 Digest,
1335 Sha256,
1336 OneCap,
1337 1,
1338 Sequential,
1339 >;
1340 type UnorderedVariableP1 = unordered::variable::partitioned::Db<
1341 mmr::Family,
1342 Context,
1343 Digest,
1344 Digest,
1345 Sha256,
1346 OneCap,
1347 1,
1348 Sequential,
1349 >;
1350 type OrderedFixedP1 = ordered::fixed::partitioned::Db<
1351 mmr::Family,
1352 Context,
1353 Digest,
1354 Digest,
1355 Sha256,
1356 OneCap,
1357 1,
1358 Sequential,
1359 >;
1360 type OrderedVariableP1 = ordered::variable::partitioned::Db<
1361 mmr::Family,
1362 Context,
1363 Digest,
1364 Digest,
1365 Sha256,
1366 OneCap,
1367 1,
1368 Sequential,
1369 >;
1370 type UnorderedFixedP2 = unordered::fixed::partitioned::Db<
1371 mmr::Family,
1372 Context,
1373 Digest,
1374 Digest,
1375 Sha256,
1376 OneCap,
1377 2,
1378 Sequential,
1379 >;
1380 type UnorderedVariableP2 = unordered::variable::partitioned::Db<
1381 mmr::Family,
1382 Context,
1383 Digest,
1384 Digest,
1385 Sha256,
1386 OneCap,
1387 2,
1388 Sequential,
1389 >;
1390 type OrderedFixedP2 = ordered::fixed::partitioned::Db<
1391 mmr::Family,
1392 Context,
1393 Digest,
1394 Digest,
1395 Sha256,
1396 OneCap,
1397 2,
1398 Sequential,
1399 >;
1400 type OrderedVariableP2 = ordered::variable::partitioned::Db<
1401 mmr::Family,
1402 Context,
1403 Digest,
1404 Digest,
1405 Sha256,
1406 OneCap,
1407 2,
1408 Sequential,
1409 >;
1410
1411 mod mmb_types {
1413 use super::*;
1414 use crate::{
1415 index::{ordered::Index as OrderedIndex, unordered::Index as UnorderedIndex},
1416 journal::contiguous::{fixed::Journal as FJournal, variable::Journal as VJournal},
1417 merkle::{Location, mmb},
1418 qmdb::any::{
1419 operation::{Operation, update},
1420 value::{FixedEncoding, VariableEncoding},
1421 },
1422 };
1423
1424 type MmbLocation = Location<mmb::Family>;
1425
1426 pub type MmbUnorderedFixed = super::super::db::Db<
1427 mmb::Family,
1428 Context,
1429 FJournal<
1430 Context,
1431 Operation<mmb::Family, update::Unordered<Digest, FixedEncoding<Digest>>>,
1432 >,
1433 UnorderedIndex<OneCap, MmbLocation>,
1434 Sha256,
1435 update::Unordered<Digest, FixedEncoding<Digest>>,
1436 { crate::qmdb::any::BITMAP_CHUNK_BYTES },
1437 Sequential,
1438 >;
1439
1440 pub type MmbUnorderedVariable = super::super::db::Db<
1441 mmb::Family,
1442 Context,
1443 VJournal<
1444 Context,
1445 Operation<mmb::Family, update::Unordered<Digest, VariableEncoding<Digest>>>,
1446 >,
1447 UnorderedIndex<OneCap, MmbLocation>,
1448 Sha256,
1449 update::Unordered<Digest, VariableEncoding<Digest>>,
1450 { crate::qmdb::any::BITMAP_CHUNK_BYTES },
1451 Sequential,
1452 >;
1453
1454 pub type MmbOrderedFixed = super::super::db::Db<
1455 mmb::Family,
1456 Context,
1457 FJournal<
1458 Context,
1459 Operation<mmb::Family, update::Ordered<Digest, FixedEncoding<Digest>>>,
1460 >,
1461 OrderedIndex<OneCap, MmbLocation>,
1462 Sha256,
1463 update::Ordered<Digest, FixedEncoding<Digest>>,
1464 { crate::qmdb::any::BITMAP_CHUNK_BYTES },
1465 Sequential,
1466 >;
1467
1468 pub type MmbOrderedVariable = super::super::db::Db<
1469 mmb::Family,
1470 Context,
1471 VJournal<
1472 Context,
1473 Operation<mmb::Family, update::Ordered<Digest, VariableEncoding<Digest>>>,
1474 >,
1475 OrderedIndex<OneCap, MmbLocation>,
1476 Sha256,
1477 update::Ordered<Digest, VariableEncoding<Digest>>,
1478 { crate::qmdb::any::BITMAP_CHUNK_BYTES },
1479 Sequential,
1480 >;
1481 }
1482 use mmb_types::*;
1483
1484 #[inline]
1485 fn to_digest(i: u64) -> Digest {
1486 Sha256::hash(&[&i.to_be_bytes()])
1487 }
1488
1489 macro_rules! with_mmr_variants {
1491 ($cb:ident!($($args:tt)*)) => {
1492 $cb!($($args)*, uf, UnorderedFixed, mmr::Family, fixed_db_config);
1493 $cb!($($args)*, uv, UnorderedVariable, mmr::Family, variable_db_config);
1494 $cb!($($args)*, of, OrderedFixed, mmr::Family, fixed_db_config);
1495 $cb!($($args)*, ov, OrderedVariable, mmr::Family, variable_db_config);
1496 $cb!($($args)*, ufp1, UnorderedFixedP1, mmr::Family, fixed_db_config_partitioned);
1497 $cb!($($args)*, uvp1, UnorderedVariableP1, mmr::Family, variable_db_config_partitioned);
1498 $cb!($($args)*, ofp1, OrderedFixedP1, mmr::Family, fixed_db_config_partitioned);
1499 $cb!($($args)*, ovp1, OrderedVariableP1, mmr::Family, variable_db_config_partitioned);
1500 $cb!($($args)*, ufp2, UnorderedFixedP2, mmr::Family, fixed_db_config_partitioned);
1501 $cb!($($args)*, uvp2, UnorderedVariableP2, mmr::Family, variable_db_config_partitioned);
1502 $cb!($($args)*, ofp2, OrderedFixedP2, mmr::Family, fixed_db_config_partitioned);
1503 $cb!($($args)*, ovp2, OrderedVariableP2, mmr::Family, variable_db_config_partitioned);
1504 };
1505 }
1506
1507 macro_rules! with_all_variants {
1509 ($cb:ident!($($args:tt)*)) => {
1510 $cb!($($args)*, uf, UnorderedFixed, mmr::Family, fixed_db_config);
1511 $cb!($($args)*, uv, UnorderedVariable, mmr::Family, variable_db_config);
1512 $cb!($($args)*, of, OrderedFixed, mmr::Family, fixed_db_config);
1513 $cb!($($args)*, ov, OrderedVariable, mmr::Family, variable_db_config);
1514 $cb!($($args)*, ufp1, UnorderedFixedP1, mmr::Family, fixed_db_config_partitioned);
1515 $cb!($($args)*, uvp1, UnorderedVariableP1, mmr::Family, variable_db_config_partitioned);
1516 $cb!($($args)*, ofp1, OrderedFixedP1, mmr::Family, fixed_db_config_partitioned);
1517 $cb!($($args)*, ovp1, OrderedVariableP1, mmr::Family, variable_db_config_partitioned);
1518 $cb!($($args)*, ufp2, UnorderedFixedP2, mmr::Family, fixed_db_config_partitioned);
1519 $cb!($($args)*, uvp2, UnorderedVariableP2, mmr::Family, variable_db_config_partitioned);
1520 $cb!($($args)*, ofp2, OrderedFixedP2, mmr::Family, fixed_db_config_partitioned);
1521 $cb!($($args)*, ovp2, OrderedVariableP2, mmr::Family, variable_db_config_partitioned);
1522 $cb!($($args)*, uf_mmb, MmbUnorderedFixed, mmb::Family, fixed_db_config);
1523 $cb!($($args)*, uv_mmb, MmbUnorderedVariable, mmb::Family, variable_db_config);
1524 $cb!($($args)*, of_mmb, MmbOrderedFixed, mmb::Family, fixed_db_config);
1525 $cb!($($args)*, ov_mmb, MmbOrderedVariable, mmb::Family, variable_db_config);
1526 };
1527 }
1528
1529 macro_rules! test_for_variant {
1533 (with_reopen: $f:ident, $traced:literal, $l:ident, $db:ty, $family:ty, $cfg:ident) => {
1534 paste::paste! {
1535 #[test_group("slow")]
1536 #[test_traced($traced)]
1537 fn [<$f _ $l>]() {
1538 let executor = deterministic::Runner::default();
1539 executor.start(|context| async move {
1540 let ctx = context.child(stringify!($l));
1541 let db = <$db>::init(ctx.child("storage"), $cfg::<OneCap>("db", &ctx))
1542 .await
1543 .unwrap();
1544 $f(
1545 ctx,
1546 db,
1547 |ctx| {
1548 Box::pin(async move {
1549 <$db>::init(ctx.child("storage"), $cfg::<OneCap>("db", &ctx))
1550 .await
1551 .unwrap()
1552 })
1553 },
1554 to_digest,
1555 )
1556 .await;
1557 });
1558 }
1559 }
1560 };
1561 (with_make_value: $f:ident, $traced:literal, $l:ident, $db:ty, $family:ty, $cfg:ident) => {
1562 paste::paste! {
1563 #[test_group("slow")]
1564 #[test_traced($traced)]
1565 fn [<$f _ $l>]() {
1566 let executor = deterministic::Runner::default();
1567 executor.start(|context| async move {
1568 let ctx = context.child(stringify!($l));
1569 let db = <$db>::init(ctx.child("storage"), $cfg::<OneCap>("db", &ctx))
1570 .await
1571 .unwrap();
1572 $f(ctx, db, to_digest).await;
1573 });
1574 }
1575 }
1576 };
1577 }
1578
1579 macro_rules! test_for_all_variants {
1581 (with_reopen: $f:ident, $traced:literal) => {
1582 with_all_variants!(test_for_variant!(with_reopen: $f, $traced));
1583 };
1584 (with_make_value: $f:ident, $traced:literal) => {
1585 with_all_variants!(test_for_variant!(with_make_value: $f, $traced));
1586 };
1587 }
1588
1589 macro_rules! test_for_mmr_variants {
1593 (with_reopen: $f:ident, $traced:literal) => {
1594 with_mmr_variants!(test_for_variant!(with_reopen: $f, $traced));
1595 };
1596 (with_make_value: $f:ident, $traced:literal) => {
1597 with_mmr_variants!(test_for_variant!(with_make_value: $f, $traced));
1598 };
1599 }
1600
1601 test_for_all_variants!(with_reopen: test_any_db_log_replay, "WARN");
1602 test_for_mmr_variants!(with_reopen: test_any_db_build_and_authenticate, "WARN");
1603 test_for_mmr_variants!(with_make_value: test_any_db_historical_proof_basic, "WARN");
1604 test_for_mmr_variants!(with_make_value: test_any_db_historical_proof_invalid, "WARN");
1605 test_for_mmr_variants!(with_make_value: test_any_db_historical_proof_edge_cases, "WARN");
1606 test_for_all_variants!(with_reopen: test_any_db_multiple_commits_delete_replayed, "WARN");
1607 test_for_all_variants!(with_reopen: test_any_db_non_empty_recovery, "WARN");
1608 test_for_all_variants!(with_reopen: test_any_db_empty_recovery, "WARN");
1609 test_for_all_variants!(with_reopen: test_any_db_commit_after_sync_recovery, "WARN");
1610 test_for_all_variants!(with_reopen: test_any_db_start_sync_recovery, "WARN");
1611 test_for_all_variants!(with_reopen: test_any_db_prune_after_unsynced_floor_recovery, "WARN");
1612 test_for_mmr_variants!(with_reopen: test_any_db_rewind_recovery, "WARN");
1613
1614 fn key(i: u64) -> Digest {
1615 Sha256::hash(&[&i.to_be_bytes()])
1616 }
1617
1618 fn val(i: u64) -> Digest {
1619 Sha256::hash(&[&(i + 10000).to_be_bytes()])
1620 }
1621
1622 async fn commit_writes(
1624 db: UnorderedVariable,
1625 writes: impl IntoIterator<Item = (Digest, Option<Digest>)>,
1626 metadata: Option<Digest>,
1627 ) -> (UnorderedVariable, std::ops::Range<crate::mmr::Location>) {
1628 let mut batch = db.new_batch();
1629 for (k, v) in writes {
1630 batch = batch.write(k, v);
1631 }
1632 let merkleized = batch.merkleize(&db, metadata).await.unwrap();
1633 let (db, range) = db.apply_batch(merkleized).await.unwrap();
1634 let db = db.commit().await.unwrap();
1635 (db, range)
1636 }
1637
1638 #[test_traced("INFO")]
1640 fn test_any_batch_empty() {
1641 let executor = deterministic::Runner::default();
1642 executor.start(|context| async move {
1643 let ctx = context.child("db");
1644 let db: UnorderedVariable = UnorderedVariableDb::init(
1645 ctx.child("storage"),
1646 variable_db_config::<OneCap>("e", &ctx),
1647 )
1648 .await
1649 .unwrap();
1650
1651 let root_before = db.root();
1652 let batch = db.new_batch();
1653 let merkleized = batch.merkleize(&db, None).await.unwrap();
1654 let (db, _) = db.apply_batch(merkleized).await.unwrap();
1655
1656 assert_ne!(db.root(), root_before);
1658
1659 let (db, _) = commit_writes(db, [(key(0), Some(val(0)))], None).await;
1661 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
1662
1663 db.destroy().await.unwrap();
1664 });
1665 }
1666
1667 #[test_traced("INFO")]
1669 fn test_any_batch_metadata() {
1670 let executor = deterministic::Runner::default();
1671 executor.start(|context| async move {
1672 let ctx = context.child("db");
1673 let db: UnorderedVariable = UnorderedVariableDb::init(
1674 ctx.child("storage"),
1675 variable_db_config::<OneCap>("m", &ctx),
1676 )
1677 .await
1678 .unwrap();
1679
1680 let metadata = val(42);
1681
1682 let (db, _) = commit_writes(db, [(key(0), Some(val(0)))], Some(metadata)).await;
1684 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata));
1685
1686 let batch = db.new_batch();
1688 let merkleized = batch.merkleize(&db, None).await.unwrap();
1689 let (db, _) = db.apply_batch(merkleized).await.unwrap();
1690 assert_eq!(db.get_metadata().await.unwrap(), None);
1691
1692 db.destroy().await.unwrap();
1693 });
1694 }
1695
1696 #[test_traced("INFO")]
1699 fn test_any_batch_get_read_through() {
1700 let executor = deterministic::Runner::default();
1701 executor.start(|context| async move {
1702 let ctx = context.child("db");
1703 let db: UnorderedVariable = UnorderedVariableDb::init(
1704 ctx.child("storage"),
1705 variable_db_config::<OneCap>("g", &ctx),
1706 )
1707 .await
1708 .unwrap();
1709
1710 let ka = key(0);
1712 let va = val(0);
1713 let (db, _) = commit_writes(db, [(ka, Some(va))], None).await;
1714
1715 let kb = key(1);
1716 let vb = val(1);
1717 let kc = key(2);
1718
1719 let mut batch = db.new_batch();
1720
1721 assert_eq!(batch.get(&ka, &db).await.unwrap(), Some(va));
1723
1724 batch = batch.write(kb, Some(vb));
1726 assert_eq!(batch.get(&kb, &db).await.unwrap(), Some(vb));
1727
1728 assert_eq!(batch.get(&kc, &db).await.unwrap(), None);
1730
1731 let va2 = val(100);
1733 batch = batch.write(ka, Some(va2));
1734 assert_eq!(batch.get(&ka, &db).await.unwrap(), Some(va2));
1735
1736 batch = batch.write(ka, None);
1738 assert_eq!(batch.get(&ka, &db).await.unwrap(), None);
1739
1740 db.destroy().await.unwrap();
1741 });
1742 }
1743
1744 #[test_traced("INFO")]
1746 fn test_any_batch_get_on_merkleized() {
1747 let executor = deterministic::Runner::default();
1748 executor.start(|context| async move {
1749 let ctx = context.child("db");
1750 let db: UnorderedVariable = UnorderedVariableDb::init(
1751 ctx.child("storage"),
1752 variable_db_config::<OneCap>("mg", &ctx),
1753 )
1754 .await
1755 .unwrap();
1756
1757 let ka = key(0);
1758 let kb = key(1);
1759 let kc = key(2);
1760 let kd = key(3);
1761
1762 let (db, _) = commit_writes(db, [(ka, Some(val(0))), (kb, Some(val(1)))], None).await;
1764
1765 let va2 = val(100);
1767 let vc = val(2);
1768 let mut batch = db.new_batch();
1769 batch = batch.write(ka, Some(va2));
1770 batch = batch.write(kb, None);
1771 batch = batch.write(kc, Some(vc));
1772 let merkleized = batch.merkleize(&db, None).await.unwrap();
1773
1774 assert_eq!(merkleized.get(&ka, &db).await.unwrap(), Some(va2));
1775 assert_eq!(merkleized.get(&kb, &db).await.unwrap(), None);
1776 assert_eq!(merkleized.get(&kc, &db).await.unwrap(), Some(vc));
1777 assert_eq!(merkleized.get(&kd, &db).await.unwrap(), None);
1778
1779 db.destroy().await.unwrap();
1780 });
1781 }
1782
1783 #[test_traced("INFO")]
1785 fn test_any_batch_stacked_get() {
1786 let executor = deterministic::Runner::default();
1787 executor.start(|context| async move {
1788 let ctx = context.child("db");
1789 let db: UnorderedVariable = UnorderedVariableDb::init(
1790 ctx.child("storage"),
1791 variable_db_config::<OneCap>("sg", &ctx),
1792 )
1793 .await
1794 .unwrap();
1795
1796 let ka = key(0);
1797 let kb = key(1);
1798
1799 let mut batch = db.new_batch();
1801 batch = batch.write(ka, Some(val(0)));
1802 let merkleized = batch.merkleize(&db, None).await.unwrap();
1803
1804 let mut child = merkleized.new_batch::<Sha256>();
1806 assert_eq!(child.get(&ka, &db).await.unwrap(), Some(val(0)));
1807
1808 child = child.write(ka, Some(val(100)));
1810 assert_eq!(child.get(&ka, &db).await.unwrap(), Some(val(100)));
1811
1812 child = child.write(kb, Some(val(1)));
1814 assert_eq!(child.get(&kb, &db).await.unwrap(), Some(val(1)));
1815
1816 child = child.write(ka, None);
1818 assert_eq!(child.get(&ka, &db).await.unwrap(), None);
1819
1820 db.destroy().await.unwrap();
1821 });
1822 }
1823
1824 #[test_traced("INFO")]
1826 fn test_any_batch_stacked_delete_recreate() {
1827 let executor = deterministic::Runner::default();
1828 executor.start(|context| async move {
1829 let ctx = context.child("db");
1830 let db: UnorderedVariable = UnorderedVariableDb::init(
1831 ctx.child("storage"),
1832 variable_db_config::<OneCap>("dr", &ctx),
1833 )
1834 .await
1835 .unwrap();
1836
1837 let ka = key(0);
1838
1839 let (db, _) = commit_writes(db, [(ka, Some(val(0)))], None).await;
1841
1842 let mut parent = db.new_batch();
1844 parent = parent.write(ka, None);
1845 let parent_m = parent.merkleize(&db, None).await.unwrap();
1846 assert_eq!(parent_m.get(&ka, &db).await.unwrap(), None);
1847
1848 let mut child = parent_m.new_batch::<Sha256>();
1850 child = child.write(ka, Some(val(200)));
1851 let child_m = child.merkleize(&db, None).await.unwrap();
1852 assert_eq!(child_m.get(&ka, &db).await.unwrap(), Some(val(200)));
1853
1854 let (db, _) = db.apply_batch(child_m).await.unwrap();
1856 assert_eq!(db.get(&ka).await.unwrap(), Some(val(200)));
1857
1858 db.destroy().await.unwrap();
1859 });
1860 }
1861
1862 #[test_traced("INFO")]
1865 fn test_any_batch_floor_raise() {
1866 let executor = deterministic::Runner::default();
1867 executor.start(|context| async move {
1868 let ctx = context.child("db");
1869 let db: UnorderedVariable = UnorderedVariableDb::init(
1870 ctx.child("storage"),
1871 variable_db_config::<OneCap>("fr", &ctx),
1872 )
1873 .await
1874 .unwrap();
1875
1876 let init: Vec<_> = (0..100).map(|i| (key(i), Some(val(i)))).collect();
1878 let (db, _) = commit_writes(db, init, None).await;
1879
1880 let floor_before = db.inactivity_floor_loc();
1881
1882 let updates: Vec<_> = (0..30).map(|i| (key(i), Some(val(i + 500)))).collect();
1884 let (db, _) = commit_writes(db, updates, None).await;
1885
1886 assert!(db.inactivity_floor_loc() > floor_before);
1888
1889 for i in 0..30 {
1891 assert_eq!(
1892 db.get(&key(i)).await.unwrap(),
1893 Some(val(i + 500)),
1894 "updated key {i} mismatch"
1895 );
1896 }
1897 for i in 30..100 {
1898 assert_eq!(
1899 db.get(&key(i)).await.unwrap(),
1900 Some(val(i)),
1901 "untouched key {i} mismatch"
1902 );
1903 }
1904
1905 db.destroy().await.unwrap();
1906 });
1907 }
1908
1909 #[test_traced("INFO")]
1911 fn test_any_batch_apply_returns_range() {
1912 let executor = deterministic::Runner::default();
1913 executor.start(|context| async move {
1914 let ctx = context.child("db");
1915 let db: UnorderedVariable = UnorderedVariableDb::init(
1916 ctx.child("storage"),
1917 variable_db_config::<OneCap>("ar", &ctx),
1918 )
1919 .await
1920 .unwrap();
1921
1922 let writes: Vec<_> = (0..5).map(|i| (key(i), Some(val(i)))).collect();
1924 let (db, range1) = commit_writes(db, writes, None).await;
1925
1926 assert_eq!(range1.start, crate::mmr::Location::new(1));
1928 assert!(range1.end.saturating_sub(*range1.start) >= 6);
1930
1931 let writes: Vec<_> = (5..10).map(|i| (key(i), Some(val(i)))).collect();
1933 let (db, range2) = commit_writes(db, writes, None).await;
1934 assert_eq!(range2.start, range1.end);
1935
1936 db.destroy().await.unwrap();
1937 });
1938 }
1939
1940 #[test_traced("INFO")]
1942 fn test_any_batch_deep_chain() {
1943 let executor = deterministic::Runner::default();
1944 executor.start(|context| async move {
1945 let ctx = context.child("db");
1946 let db: UnorderedVariable = UnorderedVariableDb::init(
1947 ctx.child("storage"),
1948 variable_db_config::<OneCap>("dc", &ctx),
1949 )
1950 .await
1951 .unwrap();
1952
1953 let init: Vec<_> = (0..5).map(|i| (key(i), Some(val(i)))).collect();
1955 let (db, _) = commit_writes(db, init, None).await;
1956
1957 let mut parent = db.new_batch();
1959 parent = parent.write(key(0), Some(val(100)));
1960 parent = parent.write(key(5), Some(val(5)));
1961 let parent_m = parent.merkleize(&db, None).await.unwrap();
1962
1963 let mut child = parent_m.new_batch::<Sha256>();
1965 child = child.write(key(1), Some(val(101)));
1966 child = child.write(key(6), Some(val(6)));
1967 let child_m = child.merkleize(&db, None).await.unwrap();
1968
1969 let mut grandchild = child_m.new_batch::<Sha256>();
1971 grandchild = grandchild.write(key(2), None);
1972 grandchild = grandchild.write(key(7), Some(val(7)));
1973 let grandchild_m = grandchild.merkleize(&db, None).await.unwrap();
1974
1975 assert_eq!(
1977 grandchild_m.get(&key(0), &db).await.unwrap(),
1978 Some(val(100))
1979 );
1980 assert_eq!(
1981 grandchild_m.get(&key(1), &db).await.unwrap(),
1982 Some(val(101))
1983 );
1984 assert_eq!(grandchild_m.get(&key(2), &db).await.unwrap(), None);
1985 assert_eq!(grandchild_m.get(&key(7), &db).await.unwrap(), Some(val(7)));
1986
1987 let (db, _) = db.apply_batch(grandchild_m).await.unwrap();
1989
1990 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(100)));
1991 assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(101)));
1992 assert_eq!(db.get(&key(2)).await.unwrap(), None);
1993 assert_eq!(db.get(&key(3)).await.unwrap(), Some(val(3)));
1994 assert_eq!(db.get(&key(4)).await.unwrap(), Some(val(4)));
1995 assert_eq!(db.get(&key(5)).await.unwrap(), Some(val(5)));
1996 assert_eq!(db.get(&key(6)).await.unwrap(), Some(val(6)));
1997 assert_eq!(db.get(&key(7)).await.unwrap(), Some(val(7)));
1998
1999 db.destroy().await.unwrap();
2000 });
2001 }
2002
2003 #[test_traced("INFO")]
2005 fn test_any_batch_chain_matches_sequential() {
2006 let executor = deterministic::Runner::default();
2007 executor.start(|context| async move {
2008 let ctx = context.child("db");
2009
2010 let ctx_a = ctx.child("a");
2012 let db_a: UnorderedVariable = UnorderedVariableDb::init(
2013 ctx_a.child("db"),
2014 variable_db_config::<OneCap>("cms-a", &ctx_a),
2015 )
2016 .await
2017 .unwrap();
2018
2019 let ctx_b = ctx.child("b");
2021 let db_b: UnorderedVariable = UnorderedVariableDb::init(
2022 ctx_b.child("db"),
2023 variable_db_config::<OneCap>("cms-b", &ctx_b),
2024 )
2025 .await
2026 .unwrap();
2027
2028 let writes1: Vec<_> = (0..5).map(|i| (key(i), Some(val(i)))).collect();
2030
2031 let writes2 = vec![
2033 (key(0), Some(val(100))),
2034 (key(1), None),
2035 (key(5), Some(val(5))),
2036 ];
2037
2038 let (db_a, _) = commit_writes(db_a, writes1.clone(), None).await;
2040 let (db_a, _) = commit_writes(db_a, writes2.clone(), None).await;
2041
2042 let mut parent = db_b.new_batch();
2044 for (k, v) in &writes1 {
2045 parent = parent.write(*k, *v);
2046 }
2047 let parent_m = parent.merkleize(&db_b, None).await.unwrap();
2048
2049 let mut child = parent_m.new_batch::<Sha256>();
2050 for (k, v) in &writes2 {
2051 child = child.write(*k, *v);
2052 }
2053 let child_m = child.merkleize(&db_b, None).await.unwrap();
2054 let (db_b, _) = db_b.apply_batch(child_m).await.unwrap();
2055
2056 assert_eq!(db_a.root(), db_b.root());
2058 for i in 0..6 {
2059 assert_eq!(
2060 db_a.get(&key(i)).await.unwrap(),
2061 db_b.get(&key(i)).await.unwrap(),
2062 "key {i} mismatch"
2063 );
2064 }
2065
2066 db_a.destroy().await.unwrap();
2067 db_b.destroy().await.unwrap();
2068 });
2069 }
2070
2071 #[test_traced("INFO")]
2073 fn test_any_batch_create_then_delete_same_batch() {
2074 let executor = deterministic::Runner::default();
2075 executor.start(|context| async move {
2076 let ctx = context.child("db");
2077 let db: UnorderedVariable = UnorderedVariableDb::init(
2078 ctx.child("storage"),
2079 variable_db_config::<OneCap>("cd", &ctx),
2080 )
2081 .await
2082 .unwrap();
2083
2084 let (db, _) = commit_writes(db, [(key(0), Some(val(0)))], None).await;
2086
2087 let mut batch = db.new_batch();
2089 batch = batch.write(key(1), Some(val(1))); batch = batch.write(key(1), None); batch = batch.write(key(2), Some(val(2))); batch = batch.write(key(0), None); let merkleized = batch.merkleize(&db, None).await.unwrap();
2094 let (db, _) = db.apply_batch(merkleized).await.unwrap();
2095
2096 assert_eq!(db.get(&key(0)).await.unwrap(), None);
2097 assert_eq!(db.get(&key(1)).await.unwrap(), None);
2098 assert_eq!(db.get(&key(2)).await.unwrap(), Some(val(2)));
2099
2100 db.destroy().await.unwrap();
2101 });
2102 }
2103
2104 #[test_traced("INFO")]
2106 fn test_any_batch_delete_all_keys() {
2107 let executor = deterministic::Runner::default();
2108 executor.start(|context| async move {
2109 let ctx = context.child("db");
2110 let db: UnorderedVariable = UnorderedVariableDb::init(
2111 ctx.child("storage"),
2112 variable_db_config::<OneCap>("da", &ctx),
2113 )
2114 .await
2115 .unwrap();
2116
2117 let init: Vec<_> = (0..5).map(|i| (key(i), Some(val(i)))).collect();
2119 let (db, _) = commit_writes(db, init, None).await;
2120
2121 let deletes: Vec<_> = (0..5).map(|i| (key(i), None)).collect();
2123 let (db, _) = commit_writes(db, deletes, None).await;
2124
2125 for i in 0..5 {
2126 assert_eq!(db.get(&key(i)).await.unwrap(), None, "key {i} not deleted");
2127 }
2128
2129 let (db, _) = commit_writes(db, [(key(10), Some(val(10)))], None).await;
2131 assert_eq!(db.get(&key(10)).await.unwrap(), Some(val(10)));
2132
2133 db.destroy().await.unwrap();
2134 });
2135 }
2136
2137 #[test_traced("INFO")]
2139 fn test_any_batch_parallel_forks() {
2140 let executor = deterministic::Runner::default();
2141 executor.start(|context| async move {
2142 let ctx = context.child("db");
2143 let db: UnorderedVariable = UnorderedVariableDb::init(
2144 ctx.child("storage"),
2145 variable_db_config::<OneCap>("pf", &ctx),
2146 )
2147 .await
2148 .unwrap();
2149
2150 let (db, _) = commit_writes(db, [(key(0), Some(val(0)))], None).await;
2152 let root_before = db.root();
2153
2154 let fork_a_m = db
2156 .new_batch()
2157 .write(key(0), Some(val(100)))
2158 .write(key(1), Some(val(1)))
2159 .merkleize(&db, None)
2160 .await
2161 .unwrap();
2162
2163 let fork_b_m = db
2165 .new_batch()
2166 .write(key(0), None)
2167 .write(key(2), Some(val(2)))
2168 .merkleize(&db, None)
2169 .await
2170 .unwrap();
2171
2172 assert_ne!(fork_a_m.root(), fork_b_m.root());
2174
2175 assert_eq!(db.root(), root_before);
2177 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2178 assert_eq!(db.get(&key(1)).await.unwrap(), None);
2179
2180 let (db, _) = db.apply_batch(fork_a_m).await.unwrap();
2182 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(100)));
2183 assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
2184 assert_eq!(db.get(&key(2)).await.unwrap(), None);
2185
2186 db.destroy().await.unwrap();
2187 });
2188 }
2189
2190 #[test_traced("INFO")]
2192 fn test_any_batch_floor_raise_chained() {
2193 let executor = deterministic::Runner::default();
2194 executor.start(|context| async move {
2195 let ctx = context.child("db");
2196 let db: UnorderedVariable = UnorderedVariableDb::init(
2197 ctx.child("storage"),
2198 variable_db_config::<OneCap>("frc", &ctx),
2199 )
2200 .await
2201 .unwrap();
2202
2203 let init: Vec<_> = (0..50).map(|i| (key(i), Some(val(i)))).collect();
2205 let (db, _) = commit_writes(db, init, None).await;
2206 let floor_before = db.inactivity_floor_loc();
2207
2208 let mut parent = db.new_batch();
2210 for i in 0..20 {
2211 parent = parent.write(key(i), Some(val(i + 500)));
2212 }
2213 let parent_m = parent.merkleize(&db, None).await.unwrap();
2214
2215 let mut child = parent_m.new_batch::<Sha256>();
2217 for i in 20..30 {
2218 child = child.write(key(i), Some(val(i + 500)));
2219 }
2220 let child_m = child.merkleize(&db, None).await.unwrap();
2221 let (db, _) = db.apply_batch(child_m).await.unwrap();
2222
2223 assert!(db.inactivity_floor_loc() > floor_before);
2225
2226 for i in 0..30 {
2228 assert_eq!(
2229 db.get(&key(i)).await.unwrap(),
2230 Some(val(i + 500)),
2231 "updated key {i} mismatch"
2232 );
2233 }
2234 for i in 30..50 {
2235 assert_eq!(
2236 db.get(&key(i)).await.unwrap(),
2237 Some(val(i)),
2238 "untouched key {i} mismatch"
2239 );
2240 }
2241
2242 db.destroy().await.unwrap();
2243 });
2244 }
2245
2246 #[test_traced("INFO")]
2248 fn test_any_batch_abandoned() {
2249 let executor = deterministic::Runner::default();
2250 executor.start(|context| async move {
2251 let ctx = context.child("db");
2252 let db: UnorderedVariable = UnorderedVariableDb::init(
2253 ctx.child("storage"),
2254 variable_db_config::<OneCap>("ab", &ctx),
2255 )
2256 .await
2257 .unwrap();
2258
2259 let (db, _) = commit_writes(db, [(key(0), Some(val(0)))], None).await;
2260 let root_before = db.root();
2261
2262 {
2264 let mut batch = db.new_batch();
2265 batch = batch.write(key(0), Some(val(999)));
2266 batch = batch.write(key(1), Some(val(1)));
2267 let _merkleized = batch.merkleize(&db, None).await.unwrap();
2268 }
2270
2271 assert_eq!(db.root(), root_before);
2273 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2274 assert_eq!(db.get(&key(1)).await.unwrap(), None);
2275
2276 db.destroy().await.unwrap();
2277 });
2278 }
2279
2280 #[test_traced("INFO")]
2282 fn test_any_batch_apply_requires_commit_for_recovery() {
2283 let executor = deterministic::Runner::default();
2284 executor.start(|context| async move {
2285 let partition = "apply_requires_commit";
2286 let ctx = context.child("db");
2287 let db: UnorderedVariable = UnorderedVariableDb::init(
2288 ctx.child("storage"),
2289 variable_db_config::<OneCap>(partition, &ctx),
2290 )
2291 .await
2292 .unwrap();
2293
2294 let committed_root = db.root();
2295
2296 let merkleized = db
2297 .new_batch()
2298 .write(key(0), Some(val(0)))
2299 .merkleize(&db, None)
2300 .await
2301 .unwrap();
2302 let (db, _) = db.apply_batch(merkleized).await.unwrap();
2303
2304 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2305
2306 drop(db);
2307
2308 let reopened: UnorderedVariable = UnorderedVariableDb::init(
2309 context.child("reopen"),
2310 variable_db_config::<OneCap>(partition, &context),
2311 )
2312 .await
2313 .unwrap();
2314 assert_eq!(reopened.root(), committed_root);
2315 assert_eq!(reopened.get(&key(0)).await.unwrap(), None);
2316
2317 reopened.destroy().await.unwrap();
2318 });
2319 }
2320
2321 #[test_traced("INFO")]
2323 fn test_any_rewind_pruned_target_errors() {
2324 let executor = deterministic::Runner::default();
2325 executor.start(|context| async move {
2326 const KEYS: u64 = 64;
2327
2328 let ctx = context.child("db");
2329 let db: UnorderedVariable = UnorderedVariableDb::init(
2330 ctx.child("storage"),
2331 variable_db_config::<OneCap>("rp", &ctx),
2332 )
2333 .await
2334 .unwrap();
2335
2336 let initial: Vec<_> = (0..KEYS).map(|i| (key(i), Some(val(i)))).collect();
2337 let (mut db, first_range) = commit_writes(db, initial, None).await;
2338
2339 let mut round = 0u64;
2340 loop {
2341 round += 1;
2342 assert!(
2343 round <= 64,
2344 "failed to prune enough history for rewind test"
2345 );
2346
2347 let updates: Vec<_> = (0..KEYS)
2348 .map(|i| (key(i), Some(val(1000 + round * KEYS + i))))
2349 .collect();
2350 (db, _) = commit_writes(db, updates, None).await;
2351
2352 let boundary = db.sync_boundary();
2353 db = db.prune(boundary).await.unwrap();
2354 let bounds = db.bounds();
2355 if bounds.start > first_range.start {
2356 break;
2357 }
2358 }
2359
2360 let oldest_retained = db.bounds().start;
2361 let Err(boundary_err) = db.rewind(oldest_retained).await else {
2362 panic!("expected rewind at retained boundary to fail");
2363 };
2364 assert!(
2365 matches!(
2366 boundary_err,
2367 crate::qmdb::Error::Journal(crate::journal::Error::ItemPruned(_))
2368 ),
2369 "unexpected rewind error at retained boundary: {boundary_err:?}"
2370 );
2371
2372 let db: UnorderedVariable = UnorderedVariableDb::init(
2373 ctx.child("reopen"),
2374 variable_db_config::<OneCap>("rp", &ctx),
2375 )
2376 .await
2377 .unwrap();
2378 let Err(err) = db.rewind(first_range.start).await else {
2379 panic!("expected rewind to pruned target to fail");
2380 };
2381 assert!(
2382 matches!(
2383 err,
2384 crate::qmdb::Error::Journal(crate::journal::Error::ItemPruned(_))
2385 ),
2386 "unexpected rewind error: {err:?}"
2387 );
2388 });
2389 }
2390
2391 #[test_traced("INFO")]
2393 fn test_any_rewind_invalid_target_errors() {
2394 let executor = deterministic::Runner::default();
2395 executor.start(|context| async move {
2396 let ctx = context.child("db");
2397 let db: UnorderedVariable = UnorderedVariableDb::init(
2398 ctx.child("storage"),
2399 variable_db_config::<OneCap>("ri", &ctx),
2400 )
2401 .await
2402 .unwrap();
2403
2404 let (db, _) = commit_writes(db, [(key(0), Some(val(0)))], None).await;
2406
2407 let root_before = db.root();
2408 let size_before = db.size();
2409 let db = db.rewind(size_before).await.unwrap();
2410 assert_eq!(db.root(), root_before);
2411 assert_eq!(db.size(), size_before);
2412
2413 let Err(zero_err) = db.rewind(Location::new(0)).await else {
2414 panic!("expected rewind to zero to fail");
2415 };
2416 assert!(
2417 matches!(
2418 zero_err,
2419 crate::qmdb::Error::Journal(crate::journal::Error::InvalidRewind(0))
2420 ),
2421 "unexpected rewind error: {zero_err:?}"
2422 );
2423
2424 let db: UnorderedVariable = UnorderedVariableDb::init(
2425 ctx.child("reopen"),
2426 variable_db_config::<OneCap>("ri", &ctx),
2427 )
2428 .await
2429 .unwrap();
2430 assert_eq!(db.root(), root_before);
2431 assert_eq!(db.size(), size_before);
2432
2433 let too_large_target = size_before + 1;
2434 let Err(too_large_err) = db.rewind(too_large_target).await else {
2435 panic!("expected rewind past size to fail");
2436 };
2437 assert!(
2438 matches!(
2439 too_large_err,
2440 crate::qmdb::Error::Journal(crate::journal::Error::InvalidRewind(size))
2441 if size == *too_large_target
2442 ),
2443 "unexpected rewind error: {too_large_err:?}"
2444 );
2445
2446 let db: UnorderedVariable = UnorderedVariableDb::init(
2447 ctx.child("reopen2"),
2448 variable_db_config::<OneCap>("ri", &ctx),
2449 )
2450 .await
2451 .unwrap();
2452 assert_eq!(db.root(), root_before);
2453 assert_eq!(db.size(), size_before);
2454
2455 db.destroy().await.unwrap();
2456 });
2457 }
2458
2459 #[test_traced("INFO")]
2462 fn test_any_rewind_rejects_target_with_pruned_floor() {
2463 let executor = deterministic::Runner::default();
2464 executor.start(|context| async move {
2465 const KEYS: u64 = 64;
2466
2467 let ctx = context.child("db");
2468 let db: UnorderedVariable =
2469 UnorderedVariableDb::init(ctx.child("storage"), variable_db_config::<OneCap>("rf", &ctx))
2470 .await
2471 .unwrap();
2472
2473 let (db, _) = commit_writes(db, (0..KEYS).map(|i| (key(i), Some(val(i)))), None).await;
2474 let (mut db, _) = commit_writes(
2475 db,
2476 (0..KEYS).map(|i| (key(i), Some(val(1_000 + i)))),
2477 None,
2478 )
2479 .await;
2480
2481 let rewind_target = db.size();
2482 let target_floor = db.inactivity_floor_loc();
2483 let prune_loc = target_floor + (KEYS / 2);
2484 assert!(
2485 rewind_target > *prune_loc,
2486 "test setup expected target size > prune_loc; target={rewind_target:?}, floor={target_floor:?}"
2487 );
2488
2489 let mut round = 0u64;
2490 while db.inactivity_floor_loc() < prune_loc {
2491 round += 1;
2492 assert!(
2493 round <= 8,
2494 "failed to advance inactivity floor enough for floor-pruned rewind test"
2495 );
2496 (db, _) = commit_writes(
2497 db,
2498 (0..KEYS).map(|i| (key(i), Some(val(10_000 + round * KEYS + i)))),
2499 None,
2500 )
2501 .await;
2502 }
2503
2504 let db = db.prune(prune_loc).await.unwrap();
2505 let bounds = db.bounds();
2506 assert!(
2507 bounds.start > *target_floor,
2508 "test setup expected pruned start beyond target floor; bounds={bounds:?}, target_floor={target_floor:?}"
2509 );
2510 assert!(
2511 rewind_target > bounds.start,
2512 "test setup expected target commit retained; target={rewind_target:?}, bounds={bounds:?}"
2513 );
2514
2515 let Err(err) = db.rewind(rewind_target).await else {
2516 panic!("expected rewind to floor-pruned target to fail");
2517 };
2518 assert!(
2519 matches!(
2520 err,
2521 crate::qmdb::Error::Journal(crate::journal::Error::ItemPruned(_))
2522 ),
2523 "unexpected rewind error: {err:?}"
2524 );
2525 });
2526 }
2527
2528 #[test_traced("INFO")]
2534 fn test_any_prune_keeps_bitmap_aligned_with_journal() {
2535 let executor = deterministic::Runner::default();
2536 executor.start(|context| async move {
2537 const BITMAP_CHUNK_BITS: u64 =
2540 commonware_utils::bitmap::Prunable::<BITMAP_CHUNK_BYTES>::CHUNK_SIZE_BITS;
2541 const ITEMS_PER_SECTION: u64 = 2048;
2545 const { assert!(ITEMS_PER_SECTION > BITMAP_CHUNK_BITS) };
2546
2547 let ctx = context.child("db");
2548 let mut cfg = variable_db_config::<OneCap>("rg", &ctx);
2549 cfg.journal_config.items_per_section = NZU64!(ITEMS_PER_SECTION);
2550
2551 let db: UnorderedVariable =
2552 UnorderedVariableDb::init(ctx.child("storage"), cfg).await.unwrap();
2553
2554 let (db, _) = commit_writes(db, (0..100).map(|i| (key(i), Some(val(i)))), None).await;
2555 let rewind_target = db.size();
2556 assert!(
2559 *rewind_target < BITMAP_CHUNK_BITS,
2560 "rewind_target {rewind_target:?} must be < {BITMAP_CHUNK_BITS} for the bug to manifest"
2561 );
2562 let root_at_target = db.root();
2563
2564 let (db, _) = commit_writes(
2565 db,
2566 (0..700).map(|i| (key(i), Some(val(1_000 + i)))),
2567 None,
2568 )
2569 .await;
2570 let (db, _) = commit_writes(
2571 db,
2572 (0..700).map(|i| (key(i), Some(val(10_000 + i)))),
2573 None,
2574 )
2575 .await;
2576
2577 let pre_prune_size = db.size();
2580 assert!(pre_prune_size > rewind_target);
2581
2582 let prune_loc = Location::new(600);
2583 assert!(
2586 *prune_loc > BITMAP_CHUNK_BITS,
2587 "prune_loc {prune_loc:?} must exceed one bitmap chunk ({BITMAP_CHUNK_BITS} bits)"
2588 );
2589 assert!(
2592 *prune_loc < ITEMS_PER_SECTION,
2593 "prune_loc {prune_loc:?} must be < {ITEMS_PER_SECTION} so the journal retains section 0"
2594 );
2595 assert!(db.inactivity_floor_loc() >= prune_loc);
2596
2597 let db = db.prune(prune_loc).await.unwrap();
2598
2599 let bounds = db.bounds();
2602 assert_eq!(bounds.start, Location::new(0));
2603 assert_eq!(
2604 db.bitmap.pruned_bits(),
2605 0,
2606 "bitmap pruned past journal retained start"
2607 );
2608
2609 let db = db.rewind(rewind_target).await.unwrap();
2612 assert_eq!(db.size(), rewind_target);
2613 assert_eq!(db.root(), root_at_target);
2614
2615 db.destroy().await.unwrap();
2616 });
2617 }
2618
2619 type MmbVariable = super::db::Db<
2626 crate::merkle::mmb::Family,
2627 Context,
2628 crate::journal::contiguous::variable::Journal<
2629 Context,
2630 super::operation::Operation<
2631 crate::merkle::mmb::Family,
2632 super::operation::update::Unordered<Digest, super::value::VariableEncoding<Digest>>,
2633 >,
2634 >,
2635 crate::index::unordered::Index<OneCap, crate::merkle::Location<crate::merkle::mmb::Family>>,
2636 Sha256,
2637 super::operation::update::Unordered<Digest, super::value::VariableEncoding<Digest>>,
2638 { crate::qmdb::any::BITMAP_CHUNK_BYTES },
2639 Sequential,
2640 >;
2641
2642 async fn open_mmb_db(context: Context, suffix: &str) -> MmbVariable {
2643 let cfg = variable_db_config::<OneCap>(suffix, &context);
2644 super::init(context, cfg).await.unwrap()
2645 }
2646
2647 async fn commit_writes_mmb(
2648 db: MmbVariable,
2649 writes: impl IntoIterator<Item = (Digest, Option<Digest>)>,
2650 metadata: Option<Digest>,
2651 ) -> MmbVariable {
2652 let mut batch = db.new_batch();
2653 for (k, v) in writes {
2654 batch = batch.write(k, v);
2655 }
2656 let merkleized = batch.merkleize(&db, metadata).await.unwrap();
2657 let (db, _) = db.apply_batch(merkleized).await.unwrap();
2658 db.commit().await.unwrap()
2659 }
2660
2661 #[test_traced("INFO")]
2662 fn test_mmb_batch_crud() {
2663 let executor = deterministic::Runner::default();
2664 executor.start(|context| async move {
2665 let db = open_mmb_db(context.child("db"), "crud").await;
2666
2667 let db = commit_writes_mmb(db, [(key(0), Some(val(0)))], None).await;
2669 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2670
2671 let db = commit_writes_mmb(db, [(key(0), Some(val(1)))], None).await;
2673 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(1)));
2674
2675 let db = commit_writes_mmb(db, [(key(0), None)], None).await;
2677 assert!(db.get(&key(0)).await.unwrap().is_none());
2678
2679 let db =
2681 commit_writes_mmb(db, [(key(1), Some(val(1))), (key(2), Some(val(2)))], None).await;
2682 assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
2683 assert_eq!(db.get(&key(2)).await.unwrap(), Some(val(2)));
2684
2685 db.destroy().await.unwrap();
2686 });
2687 }
2688
2689 #[test_traced("INFO")]
2690 fn test_mmb_batch_empty() {
2691 let executor = deterministic::Runner::default();
2692 executor.start(|context| async move {
2693 let db = open_mmb_db(context.child("db"), "empty").await;
2694 let root_before = db.root();
2695
2696 let merkleized = db.new_batch().merkleize(&db, None).await.unwrap();
2697 let (db, _) = db.apply_batch(merkleized).await.unwrap();
2698 assert_ne!(db.root(), root_before);
2699
2700 let db = commit_writes_mmb(db, [(key(0), Some(val(0)))], None).await;
2701 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2702
2703 db.destroy().await.unwrap();
2704 });
2705 }
2706
2707 #[test_traced("INFO")]
2708 fn test_mmb_batch_metadata() {
2709 let executor = deterministic::Runner::default();
2710 executor.start(|context| async move {
2711 let db = open_mmb_db(context.child("db"), "meta").await;
2712
2713 let metadata = val(42);
2714 let db = commit_writes_mmb(db, [(key(0), Some(val(0)))], Some(metadata)).await;
2715 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata));
2716
2717 let merkleized = db.new_batch().merkleize(&db, None).await.unwrap();
2718 let (db, _) = db.apply_batch(merkleized).await.unwrap();
2719 assert_eq!(db.get_metadata().await.unwrap(), None);
2720
2721 db.destroy().await.unwrap();
2722 });
2723 }
2724
2725 #[test_traced("WARN")]
2726 fn test_mmb_recovery() {
2727 let executor = deterministic::Runner::default();
2728 executor.start(|context| async move {
2729 let db = open_mmb_db(context.child("db").with_attribute("index", 0), "recovery").await;
2730
2731 let db = commit_writes_mmb(db, [(key(0), Some(val(0)))], Some(val(99))).await;
2732 let db = commit_writes_mmb(db, [(key(1), Some(val(1)))], None).await;
2733
2734 let root = db.root();
2735 let bounds = db.bounds();
2736 db.sync().await.unwrap();
2737
2738 let db = open_mmb_db(context.child("db").with_attribute("index", 1), "recovery").await;
2740 assert_eq!(db.root(), root);
2741 assert_eq!(db.bounds(), bounds);
2742 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2743 assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
2744 assert_eq!(db.get_metadata().await.unwrap(), None);
2745
2746 db.destroy().await.unwrap();
2747 });
2748 }
2749
2750 #[test_traced("INFO")]
2751 fn test_mmb_prune() {
2752 let executor = deterministic::Runner::default();
2753 executor.start(|context| async move {
2754 let mut db = open_mmb_db(context.child("db"), "prune").await;
2755
2756 for i in 0u64..20 {
2757 db = commit_writes_mmb(db, [(key(i), Some(val(i)))], None).await;
2758 }
2759
2760 let floor = db.inactivity_floor_loc();
2761 let db = db.prune(floor).await.unwrap();
2762
2763 for i in 0u64..20 {
2765 assert_eq!(db.get(&key(i)).await.unwrap(), Some(val(i)));
2766 }
2767
2768 db.destroy().await.unwrap();
2769 });
2770 }
2771
2772 #[test_traced("INFO")]
2774 fn test_any_batch_single_stage_pipeline() {
2775 let executor = deterministic::Runner::default();
2776 executor.start(|context| async move {
2777 let ctx = context.child("db");
2778 let mut db: UnorderedVariable = UnorderedVariableDb::init(
2779 ctx.child("storage"),
2780 variable_db_config::<OneCap>("pipe", &ctx),
2781 )
2782 .await
2783 .unwrap();
2784
2785 {
2786 let mut batch = db.new_batch();
2787 batch = batch.write(key(0), Some(val(0)));
2788 let merkleized = batch.merkleize(&db, None).await.unwrap();
2789 (db, _) = db.apply_batch(merkleized).await.unwrap();
2790 }
2791
2792 let child_merkleized = {
2793 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2794 let mut child = db.new_batch();
2795 child = child.write(key(1), Some(val(1)));
2796 child.merkleize(&db, None).await.unwrap()
2797 };
2798 let db = db.commit().await.unwrap();
2799
2800 let (db, _) = db.apply_batch(child_merkleized).await.unwrap();
2801 let db = db.commit().await.unwrap();
2802
2803 assert_eq!(db.get(&key(0)).await.unwrap(), Some(val(0)));
2804 assert_eq!(db.get(&key(1)).await.unwrap(), Some(val(1)));
2805
2806 db.destroy().await.unwrap();
2807 });
2808 }
2809}
2810
2811#[cfg(test)]
2812mod bitmap_tests {
2813 use crate::qmdb::any::unordered::variable::test::{AnyTest, create_test_config};
2817 use commonware_cryptography::{Hasher as _, Sha256};
2818 use commonware_macros::{boxed, test_traced};
2819 use commonware_runtime::{
2820 Runner as _, Supervisor as _,
2821 deterministic::{self, Context},
2822 };
2823 use commonware_utils::bitmap::Readable as _;
2824
2825 async fn open_db(context: Context) -> AnyTest {
2827 let cfg = create_test_config(0, &context);
2828 AnyTest::init(context, cfg).await.unwrap()
2829 }
2830
2831 fn bitmap_active_locs(db: &AnyTest) -> Vec<u64> {
2833 let b = &db.bitmap;
2834 (b.pruned_bits()..b.len())
2835 .filter(|loc| b.get_bit(*loc))
2836 .collect()
2837 }
2838
2839 #[boxed]
2841 async fn assert_oracle_round_trip(db: AnyTest, context: Context, label: &str) -> AnyTest {
2842 let pre_active = bitmap_active_locs(&db);
2843 let pre_len = db.bitmap.len();
2844 let pre_pruned = db.bitmap.pruned_bits();
2845
2846 db.commit().await.unwrap();
2847
2848 let db = open_db(context.child("reopen").with_attribute("case", label)).await;
2849
2850 assert_eq!(
2851 db.bitmap.pruned_bits(),
2852 pre_pruned,
2853 "pruned_bits diverged on reopen",
2854 );
2855 assert_eq!(db.bitmap.len(), pre_len, "bitmap len diverged on reopen");
2856 assert_eq!(
2857 bitmap_active_locs(&db),
2858 pre_active,
2859 "active locations diverged on reopen",
2860 );
2861 db
2862 }
2863
2864 #[test_traced]
2870 fn current_commit_floor_bit_is_one_others_zero() {
2871 deterministic::Runner::default().start(|context| async move {
2872 let mut db = open_db(context.child("db")).await;
2873
2874 let mut commit_locs = Vec::new();
2876 for i in 0..3u64 {
2877 let key = Sha256::hash(&[&i.to_be_bytes()]);
2878 let batch = db
2879 .new_batch()
2880 .write(key, Some(vec![i as u8]))
2881 .merkleize(&db, None)
2882 .await
2883 .unwrap();
2884 commit_locs.push(batch.bounds.tip.size - 1);
2885 (db, _) = db.apply_batch(batch).await.unwrap();
2886 }
2887 let db = db.commit().await.unwrap();
2888
2889 assert_eq!(commit_locs.len(), 3);
2891 assert!(*commit_locs[0] < *commit_locs[1]);
2892 assert!(*commit_locs[1] < *commit_locs[2]);
2893 assert!(*commit_locs[2] < db.bitmap.len());
2894
2895 assert!(!db.bitmap.get_bit(*commit_locs[0]));
2897 assert!(!db.bitmap.get_bit(*commit_locs[1]));
2898 assert!(db.bitmap.get_bit(*commit_locs[2]));
2900
2901 let db = assert_oracle_round_trip(db, context, "commit_floor").await;
2902 db.destroy().await.unwrap();
2903 });
2904 }
2905
2906 #[test_traced]
2917 fn rewind_restores_bitmap_to_target_commit() {
2918 deterministic::Runner::default().start(|context| async move {
2919 let db = open_db(context.child("db")).await;
2920 let k1 = Sha256::hash(&[&[1]]);
2921 let k2 = Sha256::hash(&[&[2]]);
2922
2923 let b1 = db
2925 .new_batch()
2926 .write(k1, Some(vec![10]))
2927 .merkleize(&db, None)
2928 .await
2929 .unwrap();
2930 let (db, _) = db.apply_batch(b1).await.unwrap();
2931 let db = db.commit().await.unwrap();
2932 let size_after_first = db.last_commit_loc + 1;
2933
2934 let b2 = db
2935 .new_batch()
2936 .write(k2, Some(vec![20]))
2937 .merkleize(&db, None)
2938 .await
2939 .unwrap();
2940 let (db, _) = db.apply_batch(b2).await.unwrap();
2941
2942 assert_eq!(db.get(&k1).await.unwrap(), Some(vec![10]));
2944 assert_eq!(db.get(&k2).await.unwrap(), Some(vec![20]));
2945 assert!(*db.last_commit_loc + 1 > *size_after_first);
2946
2947 let db = db.rewind(size_after_first).await.unwrap();
2949
2950 assert_eq!(db.get(&k1).await.unwrap(), Some(vec![10]));
2952 assert!(db.get(&k2).await.unwrap().is_none());
2953
2954 let db = assert_oracle_round_trip(db, context, "rewind").await;
2955 db.destroy().await.unwrap();
2956 });
2957 }
2958
2959 #[test_traced]
2979 fn floor_scan_falls_through_to_uncommitted_tail() {
2980 deterministic::Runner::default().start(|context| async move {
2981 let db = open_db(context.child("db")).await;
2982 let anchor = Sha256::hash(&[&[0xAA]]);
2983
2984 let b = db
2986 .new_batch()
2987 .write(anchor, Some(vec![1]))
2988 .merkleize(&db, None)
2989 .await
2990 .unwrap();
2991 let (db, _) = db.apply_batch(b).await.unwrap();
2992
2993 assert_eq!(db.get(&anchor).await.unwrap(), Some(vec![1]));
2995 let committed_bitmap_len = db.bitmap.len();
2996
2997 let parent = db
2999 .new_batch()
3000 .write(anchor, Some(vec![2]))
3001 .merkleize(&db, None)
3002 .await
3003 .unwrap();
3004 assert!(
3005 parent.bounds.tip.size > committed_bitmap_len,
3006 "parent must extend past committed bitmap to exercise the tail path",
3007 );
3008
3009 let mut child_batch = parent.new_batch::<Sha256>();
3013 child_batch = child_batch.write(anchor, Some(vec![3]));
3014 for i in 0..16u64 {
3015 let k = Sha256::hash(&[&(1000 + i).to_be_bytes()]);
3016 child_batch = child_batch.write(k, Some(vec![i as u8]));
3017 }
3018 let child = child_batch.merkleize(&db, None).await.unwrap();
3019 assert!(
3020 child.bounds.tip.size > committed_bitmap_len,
3021 "child must include an uncommitted tail beyond committed bitmap",
3022 );
3023 let expected_root = child.root();
3024
3025 let (db, _) = db.apply_batch(child).await.unwrap();
3028 assert_eq!(db.root(), expected_root);
3029 assert_eq!(db.get(&anchor).await.unwrap(), Some(vec![3]));
3030
3031 let db = assert_oracle_round_trip(db, context, "tail").await;
3032 db.destroy().await.unwrap();
3033 });
3034 }
3035}