1use crate::{
47 journal::{
48 authenticated,
49 contiguous::{Contiguous, Mutable},
50 },
51 merkle::{full::Config as MerkleConfig, Family, Location, Proof},
52 qmdb::{
53 any::value::ValueEncoding, batch_chain, metrics::Metrics, single_operation_root, Error,
54 },
55 Context,
56};
57use commonware_codec::EncodeShared;
58use commonware_cryptography::Hasher;
59use commonware_macros::boxed;
60use commonware_parallel::Strategy;
61use std::{num::NonZeroU64, sync::Arc};
62use tracing::{debug, warn};
63
64pub mod batch;
65mod compact;
66pub mod fixed;
67mod operation;
68pub(crate) mod sync;
69pub mod variable;
70pub use compact::{
71 Config as CompactConfig, Db as CompactDb, MerkleizedBatch as CompactMerkleizedBatch,
72 UnmerkleizedBatch as CompactUnmerkleizedBatch,
73};
74pub use operation::Operation;
75
76pub fn initial_root<F, V, H>() -> H::Digest
80where
81 F: Family,
82 V: ValueEncoding,
83 H: Hasher,
84 Operation<F, V>: EncodeShared,
85{
86 single_operation_root::<F, H>(&Operation::<F, V>::Commit(None, Location::new(0)))
87}
88
89#[derive(Clone)]
91pub struct Config<J, S: Strategy> {
92 pub merkle: MerkleConfig<S>,
94
95 pub log: J,
97}
98
99pub struct Keyless<F, E, V, C, H, S>
101where
102 F: Family,
103 E: Context,
104 V: ValueEncoding,
105 C: Contiguous<Item = Operation<F, V>>,
106 H: Hasher,
107 S: Strategy,
108 Operation<F, V>: EncodeShared,
109{
110 journal: authenticated::Journal<F, E, C, H, S>,
112
113 root: H::Digest,
115
116 last_commit_loc: Location<F>,
118
119 inactivity_floor_loc: Location<F>,
122
123 metrics: Metrics<E>,
125}
126
127impl<F, E, V, C, H, S> Keyless<F, E, V, C, H, S>
128where
129 F: Family,
130 E: Context,
131 V: ValueEncoding,
132 C: Mutable<Item = Operation<F, V>>,
133 H: Hasher,
134 S: Strategy,
135 Operation<F, V>: EncodeShared,
136{
137 #[boxed]
138 pub(crate) async fn init_from_journal(
139 mut journal: authenticated::Journal<F, E, C, H, S>,
140 context: E,
141 ) -> Result<Self, Error<F>> {
142 let metrics = Metrics::new(context);
143 if journal.size() == 0 {
144 warn!("no operations found in log, creating initial commit");
145 journal
146 .append(&Operation::Commit(None, Location::new(0)))
147 .await?;
148 journal.sync().await?;
149 }
150
151 let (last_commit_loc, inactivity_floor_loc) = {
152 let bounds = journal.bounds();
153 let last_commit_loc = Location::new(
154 bounds
155 .end
156 .checked_sub(1)
157 .expect("at least one commit should exist"),
158 );
159 let op = journal.read(*last_commit_loc).await?;
160 let inactivity_floor_loc = op
161 .has_floor()
162 .expect("last operation should be a commit with floor");
163 (last_commit_loc, inactivity_floor_loc)
164 };
165 let inactive_peaks = F::inactive_peaks(
166 F::location_to_position(Location::new(*last_commit_loc + 1)),
167 inactivity_floor_loc,
168 );
169 let root = journal.root(inactive_peaks)?;
170
171 let db = Self {
172 journal,
173 root,
174 last_commit_loc,
175 inactivity_floor_loc,
176 metrics,
177 };
178 db.update_metrics();
179 Ok(db)
180 }
181
182 pub async fn get(&self, loc: Location<F>) -> Result<Option<V::Value>, Error<F>> {
189 let _timer = self.metrics.get_timer();
190 self.metrics.get_calls.inc();
191 self.metrics.lookups_requested.inc();
192 let op_count = self.journal.bounds().end;
193 if loc >= op_count {
194 return Err(Error::LocationOutOfBounds(loc, Location::new(op_count)));
195 }
196 let op = self.journal.read(*loc).await?;
197
198 let result = op.into_value();
199 Ok(result)
200 }
201
202 pub async fn get_many(&self, locs: &[Location<F>]) -> Result<Vec<Option<V::Value>>, Error<F>> {
211 if locs.is_empty() {
212 return Ok(Vec::new());
213 }
214
215 let _timer = self.metrics.get_many_timer();
216 self.metrics.get_many_calls.inc();
217 self.metrics.lookups_requested.inc_by(locs.len() as u64);
218 assert!(
219 locs.is_sorted_by(|a, b| a < b),
220 "locations must be strictly increasing"
221 );
222 let op_count = self.journal.bounds().end;
223 for &loc in locs {
224 if loc >= op_count {
225 return Err(Error::LocationOutOfBounds(loc, Location::new(op_count)));
226 }
227 }
228 let positions: Vec<u64> = locs.iter().map(|loc| **loc).collect();
229 let ops = self.journal.read_many(&positions).await?;
230 let result = ops.into_iter().map(|op| op.into_value()).collect();
231 Ok(result)
232 }
233
234 pub const fn last_commit_loc(&self) -> Location<F> {
236 self.last_commit_loc
237 }
238
239 pub const fn inactivity_floor_loc(&self) -> Location<F> {
241 self.inactivity_floor_loc
242 }
243
244 pub fn bounds(&self) -> std::ops::Range<Location<F>> {
247 let bounds = self.journal.bounds();
248 Location::new(bounds.start)..Location::new(bounds.end)
249 }
250
251 fn update_metrics(&self) {
253 let bounds = self.journal.bounds();
254 self.metrics.update(
255 bounds.end,
256 bounds.start,
257 *self.inactivity_floor_loc,
258 *self.last_commit_loc,
259 );
260 }
261
262 pub const fn sync_boundary(&self) -> Location<F> {
266 self.inactivity_floor_loc
267 }
268
269 pub async fn get_metadata(&self) -> Result<Option<V::Value>, Error<F>> {
271 let op = self.journal.read(*self.last_commit_loc).await?;
272 let Operation::Commit(metadata, _floor) = op else {
273 return Ok(None);
274 };
275
276 Ok(metadata)
277 }
278
279 pub const fn root(&self) -> H::Digest {
281 self.root
282 }
283
284 pub const fn strategy(&self) -> &S {
286 self.journal.strategy()
287 }
288
289 pub async fn proof(
303 &self,
304 start_loc: Location<F>,
305 max_ops: NonZeroU64,
306 ) -> Result<(Proof<F, H::Digest>, Vec<Operation<F, V>>), Error<F>> {
307 self.historical_proof(self.bounds().end, start_loc, max_ops)
308 .await
309 }
310
311 #[allow(clippy::type_complexity)]
325 #[tracing::instrument(
326 name = "qmdb.keyless.db.historical_proof",
327 level = "info",
328 skip_all,
329 fields(
330 op_count = *op_count,
331 start_loc = *start_loc,
332 max_ops = max_ops.get(),
333 ),
334 )]
335 pub async fn historical_proof(
336 &self,
337 op_count: Location<F>,
338 start_loc: Location<F>,
339 max_ops: NonZeroU64,
340 ) -> Result<(Proof<F, H::Digest>, Vec<Operation<F, V>>), Error<F>> {
341 if op_count > self.journal.size() {
342 return Err(crate::merkle::Error::RangeOutOfBounds(op_count).into());
343 }
344
345 let inactive_peaks =
346 crate::qmdb::inactive_peaks_at::<F, _>(&self.journal, op_count, |op| op.has_floor())
347 .await?;
348
349 Ok(self
350 .journal
351 .historical_proof(op_count, start_loc, max_ops, inactive_peaks)
352 .await?)
353 }
354
355 pub async fn pinned_nodes_at(&self, loc: Location<F>) -> Result<Vec<H::Digest>, Error<F>> {
357 self.journal
358 .merkle
359 .pinned_nodes_at(loc)
360 .await
361 .map_err(Into::into)
362 }
363
364 #[tracing::instrument(name = "qmdb.keyless.db.prune", level = "info", skip_all)]
373 pub async fn prune(&mut self, loc: Location<F>) -> Result<(), Error<F>> {
374 let _timer = self.metrics.prune_timer();
375 self.metrics.prune_calls.inc();
376 if loc > self.inactivity_floor_loc {
377 return Err(Error::PruneBeyondMinRequired(
378 loc,
379 self.inactivity_floor_loc,
380 ));
381 }
382 self.journal.prune(loc).await?;
383 self.update_metrics();
384 Ok(())
385 }
386
387 #[tracing::instrument(name = "qmdb.keyless.db.rewind", level = "info", skip_all)]
408 pub async fn rewind(&mut self, size: Location<F>) -> Result<(), Error<F>> {
409 let rewind_size = *size;
410 let current_size = *self.last_commit_loc + 1;
411 if rewind_size == current_size {
412 return Ok(());
413 }
414 if rewind_size == 0 || rewind_size > current_size {
415 return Err(Error::Journal(crate::journal::Error::InvalidRewind(
416 rewind_size,
417 )));
418 }
419
420 let rewind_last_loc = Location::new(rewind_size - 1);
421 let rewind_floor = {
422 let bounds = self.journal.bounds();
423 if rewind_size <= bounds.start {
424 return Err(Error::Journal(crate::journal::Error::ItemPruned(
425 *rewind_last_loc,
426 )));
427 }
428 let rewind_last_op = self.journal.read(*rewind_last_loc).await?;
429 let Operation::Commit(_, floor) = rewind_last_op else {
430 return Err(Error::UnexpectedData(rewind_last_loc));
431 };
432 floor
433 };
434
435 self.journal.rewind(rewind_size).await?;
438 self.last_commit_loc = rewind_last_loc;
439 self.inactivity_floor_loc = rewind_floor;
440 let inactive_peaks = F::inactive_peaks(F::location_to_position(size), rewind_floor);
441 self.root = self.journal.root(inactive_peaks)?;
442 self.update_metrics();
443 Ok(())
444 }
445
446 #[tracing::instrument(name = "qmdb.keyless.db.sync", level = "info", skip_all)]
450 pub async fn sync(&mut self) -> Result<(), Error<F>> {
451 let _timer = self.metrics.sync_timer();
452 self.metrics.sync_calls.inc();
453 self.journal.sync().await?;
454 Ok(())
455 }
456
457 #[tracing::instrument(name = "qmdb.keyless.db.commit", level = "info", skip_all)]
459 pub async fn commit(&mut self) -> Result<(), Error<F>> {
460 let _timer = self.metrics.commit_timer();
461 self.metrics.commit_calls.inc();
462 self.journal.commit().await?;
463 Ok(())
464 }
465
466 #[boxed]
468 pub async fn destroy(self) -> Result<(), Error<F>> {
469 Ok(self.journal.destroy().await?)
470 }
471
472 pub fn new_batch(&self) -> batch::UnmerkleizedBatch<F, H, V, S> {
474 let journal_size = *self.last_commit_loc + 1;
475 batch::UnmerkleizedBatch::new(self, journal_size)
476 }
477
478 pub fn to_batch(&self) -> Arc<batch::MerkleizedBatch<F, H::Digest, V, S>> {
480 let journal_size = *self.last_commit_loc + 1;
481 Arc::new(batch::MerkleizedBatch {
482 journal_batch: self.journal.to_merkleized_batch(),
483 root: self.root,
484 parent: None,
485 bounds: batch_chain::Bounds {
486 base_size: journal_size,
487 db_size: journal_size,
488 total_size: journal_size,
489 ancestors: Vec::new(),
490 inactivity_floor: self.inactivity_floor_loc,
491 },
492 })
493 }
494
495 #[tracing::instrument(name = "qmdb.keyless.db.apply_batch", level = "info", skip_all)]
522 pub async fn apply_batch(
523 &mut self,
524 batch: Arc<batch::MerkleizedBatch<F, H::Digest, V, S>>,
525 ) -> Result<core::ops::Range<Location<F>>, Error<F>> {
526 let _timer = self.metrics.apply_batch_timer();
527 self.metrics.apply_batch_calls.inc();
528 let db_size = *self.last_commit_loc + 1;
529 batch
530 .bounds
531 .validate_apply_to(db_size, self.inactivity_floor_loc)?;
532 let start_loc = self.last_commit_loc + 1;
533
534 self.journal.apply_batch(&batch.journal_batch).await?;
535
536 self.last_commit_loc = Location::new(batch.bounds.total_size - 1);
537 self.inactivity_floor_loc = batch.bounds.inactivity_floor;
538 self.root = batch.root;
539 let end_loc = Location::new(batch.bounds.total_size);
540 debug!(size = ?end_loc, "applied batch");
541 let range = start_loc..end_loc;
542 self.update_metrics();
543 self.metrics
544 .operations_applied
545 .inc_by(*range.end - *range.start);
546 Ok(range)
547 }
548}
549
550#[cfg(test)]
551pub(crate) mod tests {
552 use super::*;
553 use crate::{journal::contiguous::Mutable, qmdb::verify_proof};
554 use commonware_cryptography::Sha256;
555 use commonware_parallel::Strategy;
556 use commonware_runtime::{deterministic, Supervisor as _};
557 use commonware_utils::NZU64;
558 use std::{future::Future, pin::Pin};
559
560 pub(crate) type Reopen<D> =
561 Box<dyn Fn(deterministic::Context) -> Pin<Box<dyn Future<Output = D> + Send>>>;
562
563 type TestKeyless<F, V, C, H, S> = Keyless<F, deterministic::Context, V, C, H, S>;
564
565 pub(crate) trait TestValue: Clone + PartialEq + std::fmt::Debug + Send + Sync {
567 fn make(i: u64) -> Self;
568 }
569
570 impl TestValue for Vec<u8> {
571 fn make(i: u64) -> Self {
572 vec![(i % 255) as u8; ((i % 13) + 7) as usize]
573 }
574 }
575
576 impl TestValue for commonware_utils::sequence::U64 {
577 fn make(i: u64) -> Self {
578 Self::new(i * 10 + 1)
579 }
580 }
581
582 #[boxed]
583 pub(crate) async fn test_keyless_db_empty<F: Family, V, C, H, S: Strategy>(
584 context: deterministic::Context,
585 db: TestKeyless<F, V, C, H, S>,
586 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
587 ) where
588 V: ValueEncoding<Value: TestValue>,
589 C: Mutable<Item = Operation<F, V>>,
590 H: Hasher,
591 Operation<F, V>: EncodeShared,
592 {
593 let bounds = db.bounds();
594 assert_eq!(bounds.end, 1); assert_eq!(bounds.start, Location::new(0));
596 assert_eq!(db.get_metadata().await.unwrap(), None);
597 assert_eq!(db.last_commit_loc(), Location::new(0));
598
599 let root = db.root();
601 {
602 db.new_batch().append(V::Value::make(1));
603 }
605 drop(db);
606
607 let mut db = reopen(context.child("db").with_attribute("index", 2)).await;
608 assert_eq!(db.root(), root);
609 assert_eq!(db.bounds().end, 1);
610 assert_eq!(db.get_metadata().await.unwrap(), None);
611
612 let metadata = V::Value::make(99);
614 let merkleized = db
615 .new_batch()
616 .merkleize(&db, Some(metadata.clone()), db.inactivity_floor_loc())
617 .await;
618 db.apply_batch(merkleized).await.unwrap();
619 db.commit().await.unwrap();
620 assert_eq!(db.bounds().end, 2); assert_eq!(db.get_metadata().await.unwrap(), Some(metadata.clone()));
622 assert_eq!(
623 db.get(Location::new(1)).await.unwrap(),
624 Some(metadata.clone())
625 ); let root = db.root();
627
628 let db = reopen(context.child("db").with_attribute("index", 3)).await;
630 assert_eq!(db.bounds().end, 2); assert_eq!(db.get_metadata().await.unwrap(), Some(metadata));
632 assert_eq!(db.root(), root);
633 assert_eq!(db.last_commit_loc(), Location::new(1));
634
635 db.destroy().await.unwrap();
636 }
637
638 #[boxed]
639 pub(crate) async fn test_keyless_db_commit_after_sync_recovery<
640 F: Family,
641 V,
642 C,
643 H,
644 S: Strategy,
645 >(
646 context: deterministic::Context,
647 mut db: TestKeyless<F, V, C, H, S>,
648 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
649 ) where
650 V: ValueEncoding<Value: TestValue>,
651 C: Mutable<Item = Operation<F, V>>,
652 H: Hasher,
653 Operation<F, V>: EncodeShared,
654 {
655 let value0 = V::Value::make(10);
656 let value1 = V::Value::make(20);
657
658 let first_loc = Location::new(1);
660 let merkleized = db
661 .new_batch()
662 .append(value0.clone())
663 .merkleize(&db, None, db.inactivity_floor_loc())
664 .await;
665 db.apply_batch(merkleized).await.unwrap();
666 db.commit().await.unwrap();
667 db.sync().await.unwrap();
668
669 let second_loc = db.bounds().end;
671 let merkleized = db
672 .new_batch()
673 .append(value1.clone())
674 .merkleize(&db, None, db.inactivity_floor_loc())
675 .await;
676 db.apply_batch(merkleized).await.unwrap();
677 db.commit().await.unwrap();
678 let committed_bounds = db.bounds();
679 let committed_root = db.root();
680 drop(db);
681
682 let db = reopen(context.child("db").with_attribute("index", 2)).await;
683 assert_eq!(db.bounds(), committed_bounds);
684 assert_eq!(db.root(), committed_root);
685 assert_eq!(db.get(first_loc).await.unwrap(), Some(value0));
686 assert_eq!(db.get(second_loc).await.unwrap(), Some(value1));
687
688 db.destroy().await.unwrap();
689 }
690
691 #[boxed]
692 pub(crate) async fn test_keyless_db_build_basic<F: Family, V, C, H, S: Strategy>(
693 context: deterministic::Context,
694 mut db: TestKeyless<F, V, C, H, S>,
695 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
696 ) where
697 V: ValueEncoding<Value: TestValue>,
698 C: Mutable<Item = Operation<F, V>>,
699 H: Hasher,
700 Operation<F, V>: EncodeShared,
701 {
702 let v1 = V::Value::make(1);
704 let v2 = V::Value::make(2);
705
706 {
707 let batch = db.new_batch();
708 let loc1 = batch.size();
709 let batch = batch.append(v1.clone());
710 let loc2 = batch.size();
711 let batch = batch.append(v2.clone());
712 assert_eq!(loc1, Location::new(1));
713 assert_eq!(loc2, Location::new(2));
714 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
715 .await
716 .unwrap();
717 }
718
719 assert_eq!(db.bounds().end, 4); assert_eq!(db.get_metadata().await.unwrap(), None);
722 assert_eq!(db.get(Location::new(3)).await.unwrap(), None); let root = db.root();
724 db.sync().await.unwrap();
725 drop(db);
726
727 let db = reopen(context.child("db").with_attribute("index", 2)).await;
728 assert_eq!(db.bounds().end, 4);
729 assert_eq!(db.root(), root);
730 assert_eq!(db.get(Location::new(1)).await.unwrap().unwrap(), v1);
731 assert_eq!(db.get(Location::new(2)).await.unwrap().unwrap(), v2);
732
733 drop(db);
735 let db = reopen(context.child("db").with_attribute("index", 3)).await;
736 assert_eq!(db.bounds().end, 4);
737 assert_eq!(db.root(), root);
738
739 db.destroy().await.unwrap();
740 }
741
742 #[boxed]
743 pub(crate) async fn test_keyless_db_recovery<F: Family, V, C, H, S: Strategy>(
744 context: deterministic::Context,
745 db: TestKeyless<F, V, C, H, S>,
746 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
747 ) where
748 V: ValueEncoding<Value: TestValue>,
749 C: Mutable<Item = Operation<F, V>>,
750 H: Hasher,
751 Operation<F, V>: EncodeShared,
752 {
753 let root = db.root();
754 const ELEMENTS: u64 = 100;
755
756 {
758 let mut batch = db.new_batch();
759 for i in 0..ELEMENTS {
760 batch = batch.append(V::Value::make(i));
761 }
762 }
764 drop(db);
765 let mut db = reopen(context.child("db").with_attribute("index", 2)).await;
767 assert_eq!(root, db.root());
768
769 {
771 let mut batch = db.new_batch();
772 for i in 0..ELEMENTS {
773 batch = batch.append(V::Value::make(i + 100));
774 }
775 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
776 .await
777 .unwrap();
778 }
779 db.commit().await.unwrap();
780 let root = db.root();
781
782 {
784 let mut batch = db.new_batch();
785 for i in 0..ELEMENTS {
786 batch = batch.append(V::Value::make(i + 200));
787 }
788 }
790 drop(db);
791 let mut db = reopen(context.child("db").with_attribute("index", 3)).await;
793 assert_eq!(root, db.root());
794
795 {
797 let mut batch = db.new_batch();
798 for i in 0..ELEMENTS {
799 batch = batch.append(V::Value::make(i + 300));
800 }
801 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
802 .await
803 .unwrap();
804 }
805 db.commit().await.unwrap();
806 let root = db.root();
807
808 drop(db);
810 let db = reopen(context.child("db").with_attribute("index", 4)).await;
811 assert_eq!(db.bounds().end, 2 * ELEMENTS + 3);
812 assert_eq!(db.root(), root);
813
814 db.destroy().await.unwrap();
815 }
816
817 #[boxed]
818 pub(crate) async fn test_keyless_db_proof<F: Family, V, C, S: Strategy>(
819 mut db: TestKeyless<F, V, C, Sha256, S>,
820 ) where
821 V: ValueEncoding<Value: TestValue>,
822 C: Mutable<Item = Operation<F, V>>,
823 Operation<F, V>: EncodeShared + std::fmt::Debug,
824 {
825 const ELEMENTS: u64 = 50;
826
827 {
828 let mut batch = db.new_batch();
829 for i in 0..ELEMENTS {
830 batch = batch.append(V::Value::make(i));
831 }
832 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
833 .await
834 .unwrap();
835 }
836 let root = db.root();
837
838 let (proof, ops) = db.proof(Location::new(0), NZU64!(100)).await.unwrap();
839 assert!(verify_proof::<Sha256, _, _>(
840 &proof,
841 Location::new(0),
842 &ops,
843 &root,
844 ));
845 assert_eq!(ops.len() as u64, 1 + ELEMENTS + 1);
846
847 let (proof, ops) = db.proof(Location::new(10), NZU64!(5)).await.unwrap();
848 assert!(verify_proof::<Sha256, _, _>(
849 &proof,
850 Location::new(10),
851 &ops,
852 &root,
853 ));
854 assert_eq!(ops.len(), 5);
855
856 db.destroy().await.unwrap();
857 }
858
859 #[boxed]
860 pub(crate) async fn test_keyless_db_metadata<F: Family, V, C, H, S: Strategy>(
861 mut db: TestKeyless<F, V, C, H, S>,
862 ) where
863 V: ValueEncoding<Value: TestValue>,
864 C: Mutable<Item = Operation<F, V>>,
865 H: Hasher,
866 Operation<F, V>: EncodeShared,
867 {
868 let metadata = V::Value::make(99);
869 let merkleized = db
870 .new_batch()
871 .append(V::Value::make(1))
872 .merkleize(&db, Some(metadata.clone()), db.inactivity_floor_loc())
873 .await;
874 db.apply_batch(merkleized).await.unwrap();
875 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata));
876
877 let merkleized = db
878 .new_batch()
879 .merkleize(&db, None, db.inactivity_floor_loc())
880 .await;
881 db.apply_batch(merkleized).await.unwrap();
882 assert_eq!(db.get_metadata().await.unwrap(), None);
883
884 db.destroy().await.unwrap();
885 }
886
887 #[boxed]
888 pub(crate) async fn test_keyless_db_pruning<F: Family, V, C, H, S: Strategy>(
889 mut db: TestKeyless<F, V, C, H, S>,
890 ) where
891 V: ValueEncoding<Value: TestValue>,
892 C: Mutable<Item = Operation<F, V>>,
893 H: Hasher,
894 Operation<F, V>: EncodeShared,
895 {
896 assert_eq!(db.inactivity_floor_loc(), Location::new(0));
898 let result = db.prune(Location::new(1)).await;
899 assert!(
900 matches!(result, Err(Error::PruneBeyondMinRequired(prune_loc, floor))
901 if prune_loc == Location::new(1) && floor == Location::new(0))
902 );
903
904 let first_commit_loc = Location::<F>::new(3);
906 let merkleized = db
907 .new_batch()
908 .append(V::Value::make(1))
909 .append(V::Value::make(2))
910 .merkleize(&db, None, first_commit_loc)
911 .await;
912 db.apply_batch(merkleized).await.unwrap();
913 assert_eq!(db.last_commit_loc(), first_commit_loc);
914 assert_eq!(db.inactivity_floor_loc(), first_commit_loc);
915
916 let second_commit_loc = Location::<F>::new(5);
918 let merkleized = db
919 .new_batch()
920 .append(V::Value::make(3))
921 .merkleize(&db, None, second_commit_loc)
922 .await;
923 db.apply_batch(merkleized).await.unwrap();
924
925 let root = db.root();
927 assert!(db.prune(first_commit_loc).await.is_ok());
928 assert_eq!(db.root(), root);
929
930 let new_floor = db.inactivity_floor_loc();
932 let beyond = Location::new(*new_floor + 1);
933 let result = db.prune(beyond).await;
934 assert!(
935 matches!(result, Err(Error::PruneBeyondMinRequired(prune_loc, floor))
936 if prune_loc == beyond && floor == new_floor)
937 );
938
939 db.destroy().await.unwrap();
940 }
941
942 #[boxed]
943 pub(crate) async fn test_keyless_db_empty_db_recovery<F: Family, V, C, H, S: Strategy>(
944 context: deterministic::Context,
945 db: TestKeyless<F, V, C, H, S>,
946 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
947 ) where
948 V: ValueEncoding<Value: TestValue>,
949 C: Mutable<Item = Operation<F, V>>,
950 H: Hasher,
951 Operation<F, V>: EncodeShared,
952 {
953 let root = db.root();
954 const ELEMENTS: u64 = 200;
955
956 let db = reopen(context.child("db").with_attribute("index", 2)).await;
958 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
960
961 {
963 let mut batch = db.new_batch();
964 for i in 0..ELEMENTS {
965 batch = batch.append(V::Value::make(i));
966 }
967 }
969 drop(db);
970 let db = reopen(context.child("db").with_attribute("index", 3)).await;
971 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
973
974 {
976 let mut batch = db.new_batch();
977 for i in 0..ELEMENTS {
978 batch = batch.append(V::Value::make(i + 500));
979 }
980 }
982 drop(db);
983 let db = reopen(context.child("db").with_attribute("index", 4)).await;
984 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
986
987 {
989 let mut batch = db.new_batch();
990 for i in 0..ELEMENTS * 3 {
991 batch = batch.append(V::Value::make(i + 1000));
992 }
993 }
995 drop(db);
996 let mut db = reopen(context.child("db").with_attribute("index", 5)).await;
997 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
999 assert_eq!(db.last_commit_loc(), Location::new(0));
1000
1001 {
1003 let mut batch = db.new_batch();
1004 for i in 0..ELEMENTS {
1005 batch = batch.append(V::Value::make(i + 2000));
1006 }
1007 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1008 .await
1009 .unwrap();
1010 }
1011 db.commit().await.unwrap();
1012 let db = reopen(context.child("db").with_attribute("index", 6)).await;
1013 assert!(db.bounds().end > 1);
1014 assert_ne!(db.root(), root);
1015
1016 db.destroy().await.unwrap();
1017 }
1018
1019 #[boxed]
1020 pub(crate) async fn test_keyless_db_replay_with_trailing_appends<
1021 F: Family,
1022 V,
1023 C,
1024 H,
1025 S: Strategy,
1026 >(
1027 context: deterministic::Context,
1028 mut db: TestKeyless<F, V, C, H, S>,
1029 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1030 ) where
1031 V: ValueEncoding<Value: TestValue>,
1032 C: Mutable<Item = Operation<F, V>>,
1033 H: Hasher,
1034 Operation<F, V>: EncodeShared,
1035 {
1036 {
1038 let mut batch = db.new_batch();
1039 for i in 0..10u64 {
1040 batch = batch.append(V::Value::make(i));
1041 }
1042 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1043 .await
1044 .unwrap();
1045 }
1046 db.commit().await.unwrap();
1047 let committed_root = db.root();
1048 let committed_size = db.bounds().end;
1049
1050 {
1052 db.new_batch().append(V::Value::make(99));
1053 }
1055 drop(db);
1056
1057 let mut db = reopen(context.child("db").with_attribute("index", 2)).await;
1059 assert_eq!(
1060 db.bounds().end,
1061 committed_size,
1062 "Should rewind to last commit"
1063 );
1064 assert_eq!(db.root(), committed_root, "Root should match last commit");
1065 assert_eq!(
1066 db.last_commit_loc(),
1067 committed_size - 1,
1068 "Last commit location should be correct"
1069 );
1070
1071 let new_value = V::Value::make(77);
1073 {
1074 let batch = db.new_batch();
1075 let loc = batch.size();
1076 let batch = batch.append(new_value.clone());
1077 assert_eq!(
1078 loc, committed_size,
1079 "New append should get the expected location"
1080 );
1081 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1082 .await
1083 .unwrap();
1084 }
1085 db.commit().await.unwrap();
1086
1087 assert_eq!(db.get(committed_size).await.unwrap(), Some(new_value));
1088
1089 let new_committed_root = db.root();
1090 let new_committed_size = db.bounds().end;
1091
1092 {
1094 let mut batch = db.new_batch();
1095 for i in 0..5u64 {
1096 batch = batch.append(V::Value::make(200 + i));
1097 }
1098 }
1100 drop(db);
1101
1102 let db = reopen(context.child("db").with_attribute("index", 3)).await;
1104 assert_eq!(
1105 db.bounds().end,
1106 new_committed_size,
1107 "Should rewind to last commit with multiple trailing appends"
1108 );
1109 assert_eq!(
1110 db.root(),
1111 new_committed_root,
1112 "Root should match last commit after multiple appends"
1113 );
1114 assert_eq!(
1115 db.last_commit_loc(),
1116 new_committed_size - 1,
1117 "Last commit location should be correct after multiple appends"
1118 );
1119
1120 db.destroy().await.unwrap();
1121 }
1122
1123 #[boxed]
1126 pub(crate) async fn test_keyless_get_many<F: Family, V, C, S: Strategy>(
1127 mut db: TestKeyless<F, V, C, Sha256, S>,
1128 ) where
1129 V: ValueEncoding<Value: TestValue>,
1130 C: Mutable<Item = Operation<F, V>>,
1131 Operation<F, V>: EncodeShared,
1132 {
1133 let v1 = V::Value::make(1);
1134 let v2 = V::Value::make(2);
1135 let v3 = V::Value::make(3);
1136
1137 let batch = db.new_batch();
1139 let loc1 = batch.size();
1140 let batch = batch.append(v1.clone());
1141 let loc2 = batch.size();
1142 let batch = batch.append(v2.clone());
1143 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1144 .await
1145 .unwrap();
1146 db.commit().await.unwrap();
1147
1148 let results = db.get_many(&[loc1, loc2]).await.unwrap();
1150 assert_eq!(results, vec![Some(v1.clone()), Some(v2.clone())]);
1151
1152 let results = db.get_many(&[]).await.unwrap();
1154 assert!(results.is_empty());
1155
1156 let batch = db.new_batch();
1158 let loc3 = batch.size();
1159 let batch = batch.append(v3.clone());
1160 let results = batch.get_many(&[loc1, loc3], &db).await.unwrap();
1161 assert_eq!(results, vec![Some(v1.clone()), Some(v3.clone())]);
1162
1163 let parent = db
1165 .new_batch()
1166 .append(v3.clone())
1167 .merkleize(&db, None, db.inactivity_floor_loc())
1168 .await;
1169 let child = parent.new_batch::<Sha256>().append(V::Value::make(4));
1170 let results = child.get_many(&[loc1, loc2], &db).await.unwrap();
1171 assert_eq!(results, vec![Some(v1.clone()), Some(v2.clone())]);
1172
1173 db.destroy().await.unwrap();
1174 }
1175
1176 #[boxed]
1177 pub(crate) async fn test_keyless_batch_chained<F: Family, V, C, S: Strategy>(
1178 mut db: TestKeyless<F, V, C, Sha256, S>,
1179 ) where
1180 V: ValueEncoding<Value: TestValue>,
1181 C: Mutable<Item = Operation<F, V>>,
1182 Operation<F, V>: EncodeShared,
1183 {
1184 let v1 = V::Value::make(10);
1185 let v2 = V::Value::make(20);
1186 let v3 = V::Value::make(30);
1187
1188 let parent = db.new_batch();
1189 let loc1 = parent.size();
1190 let parent = parent.append(v1.clone());
1191 let parent_m = parent.merkleize(&db, None, db.inactivity_floor_loc()).await;
1192
1193 let child = parent_m.new_batch::<Sha256>();
1194 let loc2 = child.size();
1195 let child = child.append(v2.clone());
1196 let loc3 = child.size();
1197 let child = child.append(v3.clone());
1198 let child_m = child.merkleize(&db, None, db.inactivity_floor_loc()).await;
1199 let child_root = child_m.root();
1200
1201 db.apply_batch(child_m).await.unwrap();
1202 db.commit().await.unwrap();
1203
1204 assert_eq!(db.root(), child_root);
1205 assert_eq!(db.get(loc1).await.unwrap(), Some(v1));
1206 assert_eq!(db.get(loc2).await.unwrap(), Some(v2));
1207 assert_eq!(db.get(loc3).await.unwrap(), Some(v3));
1208
1209 db.destroy().await.unwrap();
1210 }
1211
1212 #[boxed]
1213 pub(crate) async fn test_keyless_stale_batch<F: Family, V, C, H, S: Strategy>(
1214 mut db: TestKeyless<F, V, C, H, S>,
1215 ) where
1216 V: ValueEncoding<Value: TestValue>,
1217 C: Mutable<Item = Operation<F, V>>,
1218 H: Hasher,
1219 Operation<F, V>: EncodeShared,
1220 {
1221 let batch_a = db
1222 .new_batch()
1223 .append(V::Value::make(10))
1224 .merkleize(&db, None, db.inactivity_floor_loc())
1225 .await;
1226 let batch_b = db
1227 .new_batch()
1228 .append(V::Value::make(20))
1229 .merkleize(&db, None, db.inactivity_floor_loc())
1230 .await;
1231
1232 db.apply_batch(batch_a).await.unwrap();
1233
1234 let result = db.apply_batch(batch_b).await;
1235 assert!(matches!(result, Err(Error::StaleBatch { .. })));
1236
1237 db.destroy().await.unwrap();
1238 }
1239
1240 #[boxed]
1241 pub(crate) async fn test_keyless_partial_ancestor_commit<F: Family, V, C, H, S: Strategy>(
1242 mut db: TestKeyless<F, V, C, H, S>,
1243 ) where
1244 V: ValueEncoding<Value: TestValue>,
1245 C: Mutable<Item = Operation<F, V>>,
1246 H: Hasher,
1247 Operation<F, V>: EncodeShared,
1248 {
1249 let a = db
1251 .new_batch()
1252 .append(V::Value::make(10))
1253 .merkleize(&db, None, db.inactivity_floor_loc())
1254 .await;
1255 let b = a
1256 .new_batch::<H>()
1257 .append(V::Value::make(20))
1258 .merkleize(&db, None, db.inactivity_floor_loc())
1259 .await;
1260 let c = b
1261 .new_batch::<H>()
1262 .append(V::Value::make(30))
1263 .merkleize(&db, None, db.inactivity_floor_loc())
1264 .await;
1265
1266 let expected_root = c.root();
1267
1268 db.apply_batch(a).await.unwrap();
1270 db.apply_batch(c).await.unwrap();
1271
1272 assert_eq!(db.root(), expected_root);
1274
1275 db.destroy().await.unwrap();
1276 }
1277
1278 #[boxed]
1279 pub(crate) async fn test_keyless_to_batch<F: Family, V, C, S: Strategy>(
1280 mut db: TestKeyless<F, V, C, Sha256, S>,
1281 ) where
1282 V: ValueEncoding<Value: TestValue>,
1283 C: Mutable<Item = Operation<F, V>>,
1284 Operation<F, V>: EncodeShared,
1285 {
1286 let batch = db.new_batch();
1287 let loc1 = batch.size();
1288 let batch = batch.append(V::Value::make(10));
1289 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1290 .await
1291 .unwrap();
1292
1293 let snapshot = db.to_batch();
1294 assert_eq!(snapshot.root(), db.root());
1295
1296 let child_batch = snapshot.new_batch::<Sha256>();
1297 let loc2 = child_batch.size();
1298 let child_batch = child_batch.append(V::Value::make(20));
1299 db.apply_batch(
1300 child_batch
1301 .merkleize(&db, None, db.inactivity_floor_loc())
1302 .await,
1303 )
1304 .await
1305 .unwrap();
1306
1307 assert_eq!(db.get(loc1).await.unwrap(), Some(V::Value::make(10)));
1308 assert_eq!(db.get(loc2).await.unwrap(), Some(V::Value::make(20)));
1309
1310 db.destroy().await.unwrap();
1311 }
1312
1313 #[boxed]
1314 pub(crate) async fn test_keyless_db_non_empty_recovery<F: Family, V, C, H, S: Strategy>(
1315 context: deterministic::Context,
1316 mut db: TestKeyless<F, V, C, H, S>,
1317 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1318 ) where
1319 V: ValueEncoding<Value: TestValue>,
1320 C: Mutable<Item = Operation<F, V>>,
1321 H: Hasher,
1322 Operation<F, V>: EncodeShared,
1323 {
1324 const ELEMENTS: u64 = 200;
1327 {
1328 let mut batch = db.new_batch();
1329 for i in 0..ELEMENTS {
1330 batch = batch.append(V::Value::make(i));
1331 }
1332 let new_commit = Location::new(*db.last_commit_loc() + 1 + ELEMENTS);
1333 db.apply_batch(batch.merkleize(&db, None, new_commit).await)
1334 .await
1335 .unwrap();
1336 }
1337 db.commit().await.unwrap();
1338 let root = db.root();
1339 let op_count = db.bounds().end;
1340
1341 let db = reopen(context.child("db").with_attribute("index", 2)).await;
1343 assert_eq!(db.bounds().end, op_count);
1344 assert_eq!(db.root(), root);
1345 assert_eq!(db.last_commit_loc(), op_count - 1);
1346 drop(db);
1347
1348 let db = reopen(context.child("recovery_a")).await;
1350 {
1351 let mut batch = db.new_batch();
1352 for i in 0..ELEMENTS {
1353 batch = batch.append(V::Value::make(i + 1000));
1354 }
1355 }
1357 drop(db);
1358 let db = reopen(context.child("recovery_b")).await;
1359 assert_eq!(db.bounds().end, op_count);
1360 assert_eq!(db.root(), root);
1361 drop(db);
1362
1363 let mut db = reopen(context.child("db").with_attribute("index", 3)).await;
1365 db.prune(db.last_commit_loc()).await.unwrap();
1366 assert_eq!(db.bounds().end, op_count);
1367 assert_eq!(db.root(), root);
1368 db.sync().await.unwrap();
1369 drop(db);
1370
1371 let db = reopen(context.child("recovery_c")).await;
1372 {
1373 let mut batch = db.new_batch();
1374 for i in 0..ELEMENTS {
1375 batch = batch.append(V::Value::make(i + 2000));
1376 }
1377 }
1378 drop(db);
1379 let db = reopen(context.child("recovery_d")).await;
1380 assert_eq!(db.bounds().end, op_count);
1381 assert_eq!(db.root(), root);
1382 drop(db);
1383
1384 let mut db = reopen(context.child("db").with_attribute("index", 4)).await;
1386 {
1387 let mut batch = db.new_batch();
1388 for i in 0..ELEMENTS {
1389 batch = batch.append(V::Value::make(i + 3000));
1390 }
1391 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1392 .await
1393 .unwrap();
1394 }
1395 db.commit().await.unwrap();
1396 let db = reopen(context.child("db").with_attribute("index", 5)).await;
1397 let bounds = db.bounds();
1398 assert!(bounds.end > op_count);
1399 assert_ne!(db.root(), root);
1400 assert_eq!(db.last_commit_loc(), bounds.end - 1);
1401
1402 db.destroy().await.unwrap();
1403 }
1404
1405 #[boxed]
1406 pub(crate) async fn test_keyless_db_proof_comprehensive<F: Family, V, C, S: Strategy>(
1407 mut db: TestKeyless<F, V, C, Sha256, S>,
1408 ) where
1409 V: ValueEncoding<Value: TestValue>,
1410 C: Mutable<Item = Operation<F, V>>,
1411 Operation<F, V>: EncodeShared + std::fmt::Debug,
1412 {
1413 const ELEMENTS: u64 = 100;
1415 {
1416 let mut batch = db.new_batch();
1417 for i in 0u64..ELEMENTS {
1418 batch = batch.append(V::Value::make(i));
1419 }
1420 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1421 .await
1422 .unwrap();
1423 }
1424
1425 assert!(matches!(
1427 db.historical_proof(db.bounds().end + 1, Location::new(5), NZU64!(10))
1428 .await,
1429 Err(Error::<F>::Merkle(crate::merkle::Error::RangeOutOfBounds(
1430 _
1431 )))
1432 ));
1433
1434 let root = db.root();
1435
1436 for (start_loc, max_ops) in [
1437 (0, 10),
1438 (10, 5),
1439 (50, 20),
1440 (90, 15),
1441 (0, 1),
1442 (ELEMENTS - 1, 1),
1443 (ELEMENTS, 1),
1444 ] {
1445 let (proof, ops) = db
1446 .proof(Location::new(start_loc), NZU64!(max_ops))
1447 .await
1448 .unwrap();
1449 assert!(
1450 verify_proof::<Sha256, _, _>(&proof, Location::new(start_loc), &ops, &root,),
1451 "Failed to verify proof for range starting at {start_loc} with max {max_ops} ops",
1452 );
1453 let expected_ops = std::cmp::min(max_ops, *db.bounds().end - start_loc);
1454 assert_eq!(ops.len() as u64, expected_ops);
1455
1456 let wrong_root = Sha256::hash(&[0xFF; 32]);
1457 assert!(!verify_proof::<Sha256, _, _>(
1458 &proof,
1459 Location::new(start_loc),
1460 &ops,
1461 &wrong_root,
1462 ));
1463 if start_loc > 0 {
1464 assert!(!verify_proof::<Sha256, _, _>(
1465 &proof,
1466 Location::new(start_loc - 1),
1467 &ops,
1468 &root,
1469 ));
1470 }
1471 }
1472
1473 db.destroy().await.unwrap();
1474 }
1475
1476 #[boxed]
1477 pub(crate) async fn test_keyless_db_proof_with_pruning<F: Family, V, C, S: Strategy>(
1478 context: deterministic::Context,
1479 mut db: TestKeyless<F, V, C, Sha256, S>,
1480 reopen: Reopen<TestKeyless<F, V, C, Sha256, S>>,
1481 ) where
1482 V: ValueEncoding<Value: TestValue>,
1483 C: Mutable<Item = Operation<F, V>>,
1484 Operation<F, V>: EncodeShared + std::fmt::Debug,
1485 {
1486 const ELEMENTS: u64 = 100;
1487 {
1488 let mut batch = db.new_batch();
1489 for i in 0u64..ELEMENTS {
1490 batch = batch.append(V::Value::make(i));
1491 }
1492 let new_commit = Location::new(*db.last_commit_loc() + 1 + ELEMENTS);
1493 db.apply_batch(batch.merkleize(&db, None, new_commit).await)
1494 .await
1495 .unwrap();
1496 }
1497
1498 {
1499 let mut batch = db.new_batch();
1500 for i in ELEMENTS..ELEMENTS * 2 {
1501 batch = batch.append(V::Value::make(i));
1502 }
1503 let new_commit = Location::new(*db.last_commit_loc() + 1 + ELEMENTS);
1504 db.apply_batch(batch.merkleize(&db, None, new_commit).await)
1505 .await
1506 .unwrap();
1507 }
1508 let root = db.root();
1509
1510 const PRUNE_LOC: u64 = 30;
1511 db.prune(Location::new(PRUNE_LOC)).await.unwrap();
1512 let oldest_retained = db.bounds().start;
1513 assert_eq!(db.root(), root);
1514
1515 db.sync().await.unwrap();
1516 drop(db);
1517 let mut db = reopen(context).await;
1518 assert_eq!(db.root(), root);
1519
1520 for (start_loc, max_ops) in [
1521 (oldest_retained, 10),
1522 (Location::new(50), 20),
1523 (Location::new(150), 10),
1524 (Location::new(190), 15),
1525 ] {
1526 if start_loc < oldest_retained {
1527 continue;
1528 }
1529 let (proof, ops) = db.proof(start_loc, NZU64!(max_ops)).await.unwrap();
1530 assert!(verify_proof::<Sha256, _, _>(&proof, start_loc, &ops, &root,));
1531 }
1532
1533 let aggressive_prune: Location<F> = Location::new(150);
1534 db.prune(aggressive_prune).await.unwrap();
1535
1536 let new_oldest = db.bounds().start;
1537 let (proof, ops) = db.proof(new_oldest, NZU64!(20)).await.unwrap();
1538 assert!(verify_proof::<Sha256, _, _>(
1539 &proof, new_oldest, &ops, &root,
1540 ));
1541
1542 let almost_all = db.bounds().end - 5;
1543 db.prune(almost_all).await.unwrap();
1544 let final_oldest = db.bounds().start;
1545 if final_oldest < db.bounds().end {
1546 let (final_proof, final_ops) = db.proof(final_oldest, NZU64!(10)).await.unwrap();
1547 assert!(verify_proof::<Sha256, _, _>(
1548 &final_proof,
1549 final_oldest,
1550 &final_ops,
1551 &root,
1552 ));
1553 }
1554
1555 db.destroy().await.unwrap();
1556 }
1557
1558 #[boxed]
1559 pub(crate) async fn test_keyless_db_get_out_of_bounds<F: Family, V, C, H, S: Strategy>(
1560 mut db: TestKeyless<F, V, C, H, S>,
1561 ) where
1562 V: ValueEncoding<Value: TestValue>,
1563 C: Mutable<Item = Operation<F, V>>,
1564 H: Hasher,
1565 Operation<F, V>: EncodeShared,
1566 {
1567 assert!(db.get(Location::new(0)).await.unwrap().is_none());
1568
1569 let merkleized = db
1570 .new_batch()
1571 .append(V::Value::make(1))
1572 .append(V::Value::make(2))
1573 .merkleize(&db, None, db.inactivity_floor_loc())
1574 .await;
1575 db.apply_batch(merkleized).await.unwrap();
1576
1577 assert_eq!(
1578 db.get(Location::new(1)).await.unwrap(),
1579 Some(V::Value::make(1))
1580 );
1581 assert!(db.get(Location::new(3)).await.unwrap().is_none());
1582 assert!(matches!(
1583 db.get(Location::new(4)).await,
1584 Err(Error::LocationOutOfBounds(loc, size))
1585 if loc == Location::new(4) && size == Location::new(4)
1586 ));
1587
1588 db.destroy().await.unwrap();
1589 }
1590
1591 #[boxed]
1592 pub(crate) async fn test_keyless_batch_get<F: Family, V, C, H, S: Strategy>(
1593 mut db: TestKeyless<F, V, C, H, S>,
1594 ) where
1595 V: ValueEncoding<Value: TestValue>,
1596 C: Mutable<Item = Operation<F, V>>,
1597 H: Hasher,
1598 Operation<F, V>: EncodeShared,
1599 {
1600 let base_vals: Vec<V::Value> = (0..3).map(|i| V::Value::make(10 + i)).collect();
1601 let mut base_locs = Vec::new();
1602 {
1603 let mut batch = db.new_batch();
1604 for v in &base_vals {
1605 let loc = batch.size();
1606 batch = batch.append(v.clone());
1607 base_locs.push(loc);
1608 }
1609 db.apply_batch(batch.merkleize(&db, None, db.inactivity_floor_loc()).await)
1610 .await
1611 .unwrap();
1612 }
1613
1614 let batch = db.new_batch();
1615 for (i, loc) in base_locs.iter().enumerate() {
1616 assert_eq!(
1617 batch.get(*loc, &db).await.unwrap(),
1618 Some(base_vals[i].clone()),
1619 );
1620 }
1621
1622 let new_val = V::Value::make(99);
1623 let new_loc = batch.size();
1624 let batch = batch.append(new_val.clone());
1625 assert_eq!(batch.get(new_loc, &db).await.unwrap(), Some(new_val));
1626 assert_eq!(
1627 batch.get(Location::new(*new_loc + 1), &db).await.unwrap(),
1628 None
1629 );
1630
1631 db.destroy().await.unwrap();
1632 }
1633
1634 #[boxed]
1635 pub(crate) async fn test_keyless_batch_stacked_get<F: Family, V, C, S: Strategy>(
1636 db: TestKeyless<F, V, C, Sha256, S>,
1637 ) where
1638 V: ValueEncoding<Value: TestValue>,
1639 C: Mutable<Item = Operation<F, V>>,
1640 Operation<F, V>: EncodeShared,
1641 {
1642 let v1 = V::Value::make(1);
1643 let v2 = V::Value::make(2);
1644
1645 let parent = db.new_batch();
1646 let loc1 = parent.size();
1647 let parent = parent.append(v1.clone());
1648 let parent_m = parent.merkleize(&db, None, db.inactivity_floor_loc()).await;
1649
1650 let child = parent_m.new_batch::<Sha256>();
1651 assert_eq!(child.get(loc1, &db).await.unwrap(), Some(v1));
1652
1653 let loc2 = child.size();
1654 let child = child.append(v2.clone());
1655 assert_eq!(child.get(loc2, &db).await.unwrap(), Some(v2));
1656 assert_eq!(child.get(Location::new(9999), &db).await.unwrap(), None);
1657
1658 db.destroy().await.unwrap();
1659 }
1660
1661 #[boxed]
1662 pub(crate) async fn test_keyless_batch_speculative_root<F: Family, V, C, H, S: Strategy>(
1663 mut db: TestKeyless<F, V, C, H, S>,
1664 ) where
1665 V: ValueEncoding<Value: TestValue>,
1666 C: Mutable<Item = Operation<F, V>>,
1667 H: Hasher,
1668 Operation<F, V>: EncodeShared,
1669 {
1670 let mut batch = db.new_batch();
1671 for i in 0u64..10 {
1672 batch = batch.append(V::Value::make(i));
1673 }
1674 let merkleized = batch.merkleize(&db, None, db.inactivity_floor_loc()).await;
1675 let speculative = merkleized.root();
1676 db.apply_batch(merkleized).await.unwrap();
1677 assert_eq!(db.root(), speculative);
1678
1679 let merkleized = db
1680 .new_batch()
1681 .append(V::Value::make(100))
1682 .merkleize(&db, Some(V::Value::make(55)), db.inactivity_floor_loc())
1683 .await;
1684 let speculative = merkleized.root();
1685 db.apply_batch(merkleized).await.unwrap();
1686 assert_eq!(db.root(), speculative);
1687
1688 db.destroy().await.unwrap();
1689 }
1690
1691 #[boxed]
1692 pub(crate) async fn test_keyless_merkleized_batch_get<F: Family, V, C, S: Strategy>(
1693 mut db: TestKeyless<F, V, C, Sha256, S>,
1694 ) where
1695 V: ValueEncoding<Value: TestValue>,
1696 C: Mutable<Item = Operation<F, V>>,
1697 Operation<F, V>: EncodeShared,
1698 {
1699 let base_val = V::Value::make(10);
1700 let merkleized = db
1701 .new_batch()
1702 .append(base_val.clone())
1703 .merkleize(&db, None, db.inactivity_floor_loc())
1704 .await;
1705 db.apply_batch(merkleized).await.unwrap();
1706
1707 let new_val = V::Value::make(20);
1708 let merkleized = db
1709 .new_batch()
1710 .append(new_val.clone())
1711 .merkleize(&db, None, db.inactivity_floor_loc())
1712 .await;
1713
1714 assert_eq!(
1715 merkleized.get(Location::new(1), &db).await.unwrap(),
1716 Some(base_val),
1717 );
1718 assert_eq!(
1719 merkleized.get(Location::new(3), &db).await.unwrap(),
1720 Some(new_val),
1721 );
1722 assert_eq!(merkleized.get(Location::new(4), &db).await.unwrap(), None);
1723
1724 db.destroy().await.unwrap();
1725 }
1726
1727 #[boxed]
1728 pub(crate) async fn test_keyless_batch_chained_apply_sequential<
1729 F: Family,
1730 V,
1731 C,
1732 H,
1733 S: Strategy,
1734 >(
1735 mut db: TestKeyless<F, V, C, H, S>,
1736 ) where
1737 V: ValueEncoding<Value: TestValue>,
1738 C: Mutable<Item = Operation<F, V>>,
1739 H: Hasher,
1740 Operation<F, V>: EncodeShared,
1741 {
1742 let v1 = V::Value::make(1);
1743 let v2 = V::Value::make(2);
1744
1745 let parent = db.new_batch();
1746 let loc1 = parent.size();
1747 let parent = parent.append(v1.clone());
1748 let parent_m = parent.merkleize(&db, None, db.inactivity_floor_loc()).await;
1749 let parent_root = parent_m.root();
1750
1751 db.apply_batch(parent_m).await.unwrap();
1752 assert_eq!(db.root(), parent_root);
1753 assert_eq!(db.get(loc1).await.unwrap(), Some(v1));
1754
1755 let batch2 = db.new_batch();
1756 let loc2 = batch2.size();
1757 let batch2 = batch2.append(v2.clone());
1758 let batch2_m = batch2.merkleize(&db, None, db.inactivity_floor_loc()).await;
1759 let batch2_root = batch2_m.root();
1760 db.apply_batch(batch2_m).await.unwrap();
1761 assert_eq!(db.root(), batch2_root);
1762 assert_eq!(db.get(loc2).await.unwrap(), Some(v2));
1763
1764 db.destroy().await.unwrap();
1765 }
1766
1767 #[boxed]
1768 pub(crate) async fn test_keyless_batch_many_sequential<F: Family, V, C, S: Strategy>(
1769 mut db: TestKeyless<F, V, C, Sha256, S>,
1770 ) where
1771 V: ValueEncoding<Value: TestValue>,
1772 C: Mutable<Item = Operation<F, V>>,
1773 Operation<F, V>: EncodeShared + std::fmt::Debug,
1774 {
1775 const BATCHES: u64 = 20;
1776 const APPENDS_PER_BATCH: u64 = 5;
1777 let mut all_values: Vec<V::Value> = Vec::new();
1778 let mut all_locs: Vec<Location<F>> = Vec::new();
1779
1780 for batch_idx in 0..BATCHES {
1781 let mut batch = db.new_batch();
1782 for j in 0..APPENDS_PER_BATCH {
1783 let v = V::Value::make(batch_idx * 10 + j);
1784 let loc = batch.size();
1785 batch = batch.append(v.clone());
1786 all_values.push(v);
1787 all_locs.push(loc);
1788 }
1789 let merkleized = batch.merkleize(&db, None, db.inactivity_floor_loc()).await;
1790 db.apply_batch(merkleized).await.unwrap();
1791 }
1792
1793 for (i, loc) in all_locs.iter().enumerate() {
1794 assert_eq!(db.get(*loc).await.unwrap(), Some(all_values[i].clone()));
1795 }
1796
1797 let root = db.root();
1798 let (proof, ops) = db.proof(Location::new(0), NZU64!(1000)).await.unwrap();
1799 assert!(verify_proof::<Sha256, _, _>(
1800 &proof,
1801 Location::new(0),
1802 &ops,
1803 &root,
1804 ));
1805 assert_eq!(db.bounds().end, 1 + BATCHES * (APPENDS_PER_BATCH + 1));
1806
1807 db.destroy().await.unwrap();
1808 }
1809
1810 #[boxed]
1811 pub(crate) async fn test_keyless_batch_empty<F: Family, V, C, H, S: Strategy>(
1812 mut db: TestKeyless<F, V, C, H, S>,
1813 ) where
1814 V: ValueEncoding<Value: TestValue>,
1815 C: Mutable<Item = Operation<F, V>>,
1816 H: Hasher,
1817 Operation<F, V>: EncodeShared,
1818 {
1819 let merkleized = db
1820 .new_batch()
1821 .append(V::Value::make(1))
1822 .merkleize(&db, None, db.inactivity_floor_loc())
1823 .await;
1824 db.apply_batch(merkleized).await.unwrap();
1825 let root_before = db.root();
1826 let size_before = db.bounds().end;
1827
1828 let merkleized = db
1829 .new_batch()
1830 .merkleize(&db, None, db.inactivity_floor_loc())
1831 .await;
1832 let speculative = merkleized.root();
1833 db.apply_batch(merkleized).await.unwrap();
1834
1835 assert_ne!(db.root(), root_before);
1836 assert_eq!(db.root(), speculative);
1837 assert_eq!(db.bounds().end, size_before + 1);
1838
1839 db.destroy().await.unwrap();
1840 }
1841
1842 #[boxed]
1843 pub(crate) async fn test_keyless_batch_chained_merkleized_get<F: Family, V, C, S: Strategy>(
1844 mut db: TestKeyless<F, V, C, Sha256, S>,
1845 ) where
1846 V: ValueEncoding<Value: TestValue>,
1847 C: Mutable<Item = Operation<F, V>>,
1848 Operation<F, V>: EncodeShared,
1849 {
1850 let base_val = V::Value::make(10);
1851 let floor = db.inactivity_floor_loc();
1852 db.apply_batch(
1853 db.new_batch()
1854 .append(base_val.clone())
1855 .merkleize(&db, None, floor)
1856 .await,
1857 )
1858 .await
1859 .unwrap();
1860
1861 let v1 = V::Value::make(1);
1862 let parent = db.new_batch();
1863 let loc1 = parent.size();
1864 let parent_m = parent
1865 .append(v1.clone())
1866 .merkleize(&db, None, db.inactivity_floor_loc())
1867 .await;
1868
1869 let v2 = V::Value::make(2);
1870 let child = parent_m.new_batch::<Sha256>();
1871 let loc2 = child.size();
1872 let child_m = child
1873 .append(v2.clone())
1874 .merkleize(&db, None, db.inactivity_floor_loc())
1875 .await;
1876
1877 assert_eq!(
1878 child_m.get(Location::new(1), &db).await.unwrap(),
1879 Some(base_val),
1880 );
1881 assert_eq!(child_m.get(loc1, &db).await.unwrap(), Some(v1));
1882 assert_eq!(child_m.get(loc2, &db).await.unwrap(), Some(v2));
1883
1884 db.destroy().await.unwrap();
1885 }
1886
1887 #[boxed]
1888 pub(crate) async fn test_keyless_batch_large<F: Family, V, C, S: Strategy>(
1889 mut db: TestKeyless<F, V, C, Sha256, S>,
1890 ) where
1891 V: ValueEncoding<Value: TestValue>,
1892 C: Mutable<Item = Operation<F, V>>,
1893 Operation<F, V>: EncodeShared + std::fmt::Debug,
1894 {
1895 const N: u64 = 500;
1896 let mut values = Vec::new();
1897 let mut locs = Vec::new();
1898
1899 let mut batch = db.new_batch();
1900 for i in 0..N {
1901 let v = V::Value::make(i);
1902 locs.push(batch.size());
1903 batch = batch.append(v.clone());
1904 values.push(v);
1905 }
1906 let merkleized = batch.merkleize(&db, None, db.inactivity_floor_loc()).await;
1907 db.apply_batch(merkleized).await.unwrap();
1908
1909 for (i, loc) in locs.iter().enumerate() {
1910 assert_eq!(db.get(*loc).await.unwrap(), Some(values[i].clone()));
1911 }
1912
1913 let root = db.root();
1914 let (proof, ops) = db.proof(Location::new(0), NZU64!(1000)).await.unwrap();
1915 assert!(verify_proof::<Sha256, _, _>(
1916 &proof,
1917 Location::new(0),
1918 &ops,
1919 &root,
1920 ));
1921 assert_eq!(db.bounds().end, 1 + N + 1);
1922
1923 db.destroy().await.unwrap();
1924 }
1925
1926 #[boxed]
1927 pub(crate) async fn test_keyless_stale_batch_chained<F: Family, V, C, S: Strategy>(
1928 mut db: TestKeyless<F, V, C, Sha256, S>,
1929 ) where
1930 V: ValueEncoding<Value: TestValue>,
1931 C: Mutable<Item = Operation<F, V>>,
1932 Operation<F, V>: EncodeShared,
1933 {
1934 let parent = db
1935 .new_batch()
1936 .append(V::Value::make(1))
1937 .merkleize(&db, None, db.inactivity_floor_loc())
1938 .await;
1939 let child_a = parent
1940 .new_batch::<Sha256>()
1941 .append(V::Value::make(2))
1942 .merkleize(&db, None, db.inactivity_floor_loc())
1943 .await;
1944 let child_b = parent
1945 .new_batch::<Sha256>()
1946 .append(V::Value::make(3))
1947 .merkleize(&db, None, db.inactivity_floor_loc())
1948 .await;
1949
1950 db.apply_batch(child_a).await.unwrap();
1951 assert!(matches!(
1952 db.apply_batch(child_b).await,
1953 Err(Error::StaleBatch { .. })
1954 ));
1955
1956 db.destroy().await.unwrap();
1957 }
1958
1959 #[boxed]
1960 pub(crate) async fn test_keyless_sequential_commit_parent_then_child<
1961 F: Family,
1962 V,
1963 C,
1964 S: Strategy,
1965 >(
1966 mut db: TestKeyless<F, V, C, Sha256, S>,
1967 ) where
1968 V: ValueEncoding<Value: TestValue>,
1969 C: Mutable<Item = Operation<F, V>>,
1970 Operation<F, V>: EncodeShared,
1971 {
1972 let parent = db
1973 .new_batch()
1974 .append(V::Value::make(1))
1975 .merkleize(&db, None, db.inactivity_floor_loc())
1976 .await;
1977 let child = parent
1978 .new_batch::<Sha256>()
1979 .append(V::Value::make(2))
1980 .merkleize(&db, None, db.inactivity_floor_loc())
1981 .await;
1982
1983 db.apply_batch(parent).await.unwrap();
1984 db.apply_batch(child).await.unwrap();
1985
1986 db.destroy().await.unwrap();
1987 }
1988
1989 #[boxed]
1990 pub(crate) async fn test_keyless_stale_batch_child_before_parent<F: Family, V, C, S: Strategy>(
1991 mut db: TestKeyless<F, V, C, Sha256, S>,
1992 ) where
1993 V: ValueEncoding<Value: TestValue>,
1994 C: Mutable<Item = Operation<F, V>>,
1995 Operation<F, V>: EncodeShared,
1996 {
1997 let parent = db
1998 .new_batch()
1999 .append(V::Value::make(1))
2000 .merkleize(&db, None, db.inactivity_floor_loc())
2001 .await;
2002 let child = parent
2003 .new_batch::<Sha256>()
2004 .append(V::Value::make(2))
2005 .merkleize(&db, None, db.inactivity_floor_loc())
2006 .await;
2007
2008 db.apply_batch(child).await.unwrap();
2009 assert!(matches!(
2010 db.apply_batch(parent).await,
2011 Err(Error::StaleBatch { .. })
2012 ));
2013
2014 db.destroy().await.unwrap();
2015 }
2016
2017 #[boxed]
2018 pub(crate) async fn test_keyless_child_root_matches_pending_and_committed<
2019 F: Family,
2020 V,
2021 C,
2022 S: Strategy,
2023 >(
2024 mut db: TestKeyless<F, V, C, Sha256, S>,
2025 ) where
2026 V: ValueEncoding<Value: TestValue>,
2027 C: Mutable<Item = Operation<F, V>>,
2028 Operation<F, V>: EncodeShared,
2029 {
2030 let parent = db
2032 .new_batch()
2033 .append(V::Value::make(1))
2034 .merkleize(&db, None, db.inactivity_floor_loc())
2035 .await;
2036 let pending_child = parent
2037 .new_batch::<Sha256>()
2038 .append(V::Value::make(2))
2039 .merkleize(&db, None, db.inactivity_floor_loc())
2040 .await;
2041
2042 db.apply_batch(parent).await.unwrap();
2045 db.commit().await.unwrap();
2046
2047 let committed_child = db
2048 .new_batch()
2049 .append(V::Value::make(2))
2050 .merkleize(&db, None, db.inactivity_floor_loc())
2051 .await;
2052
2053 assert_eq!(pending_child.root(), committed_child.root());
2054
2055 db.destroy().await.unwrap();
2056 }
2057
2058 async fn commit_appends<F: Family, V, C, H, S: Strategy>(
2059 db: &mut TestKeyless<F, V, C, H, S>,
2060 values: impl IntoIterator<Item = V::Value>,
2061 metadata: Option<V::Value>,
2062 ) -> core::ops::Range<Location<F>>
2063 where
2064 V: ValueEncoding<Value: TestValue>,
2065 C: Mutable<Item = Operation<F, V>>,
2066 H: Hasher,
2067 Operation<F, V>: EncodeShared,
2068 {
2069 let base_size = *db.last_commit_loc() + 1;
2073 let appends_iter: Vec<_> = values.into_iter().collect();
2074 let new_commit_loc = Location::new(base_size + appends_iter.len() as u64);
2075 let mut batch = db.new_batch();
2076 for value in appends_iter {
2077 batch = batch.append(value);
2078 }
2079 let range = db
2080 .apply_batch(batch.merkleize(db, metadata, new_commit_loc).await)
2081 .await
2082 .unwrap();
2083 db.commit().await.unwrap();
2084 range
2085 }
2086
2087 #[boxed]
2088 pub(crate) async fn test_keyless_db_rewind_recovery<F: Family, V, C, H, S: Strategy>(
2089 context: deterministic::Context,
2090 mut db: TestKeyless<F, V, C, H, S>,
2091 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
2092 ) where
2093 V: ValueEncoding<Value: TestValue>,
2094 C: Mutable<Item = Operation<F, V>>,
2095 H: Hasher,
2096 Operation<F, V>: EncodeShared,
2097 {
2098 let initial_root = db.root();
2099 let initial_size = db.bounds().end;
2100
2101 let value_a = V::Value::make(1);
2102 let value_b = V::Value::make(2);
2103 let metadata_a = V::Value::make(3);
2104 let first_range = commit_appends(
2105 &mut db,
2106 [value_a.clone(), value_b.clone()],
2107 Some(metadata_a.clone()),
2108 )
2109 .await;
2110
2111 let root_before = db.root();
2112 let size_before = db.bounds().end;
2113 let commit_before = db.last_commit_loc();
2114 assert_eq!(size_before, first_range.end);
2115
2116 let value_c = V::Value::make(4);
2117 let metadata_b = V::Value::make(5);
2118 let second_range =
2119 commit_appends(&mut db, [value_c.clone()], Some(metadata_b.clone())).await;
2120 assert_eq!(second_range.start, size_before);
2121 assert_ne!(db.root(), root_before);
2122 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_b));
2123
2124 db.rewind(size_before).await.unwrap();
2125 assert_eq!(db.root(), root_before);
2126 assert_eq!(db.bounds().end, size_before);
2127 assert_eq!(db.last_commit_loc(), commit_before);
2128 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a.clone()));
2129 assert_eq!(
2130 db.get(Location::new(1)).await.unwrap(),
2131 Some(value_a.clone())
2132 );
2133 assert_eq!(
2134 db.get(Location::new(2)).await.unwrap(),
2135 Some(value_b.clone())
2136 );
2137 assert!(
2138 matches!(
2139 db.get(Location::new(4)).await,
2140 Err(Error::LocationOutOfBounds(_, size)) if size == size_before
2141 ),
2142 "rewound append should be out of bounds",
2143 );
2144
2145 db.commit().await.unwrap();
2146 drop(db);
2147 let mut db = reopen(context.child("reopen")).await;
2148 assert_eq!(db.root(), root_before);
2149 assert_eq!(db.bounds().end, size_before);
2150 assert_eq!(db.last_commit_loc(), commit_before);
2151 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a));
2152 assert_eq!(
2153 db.get(Location::new(1)).await.unwrap(),
2154 Some(value_a.clone())
2155 );
2156 assert_eq!(
2157 db.get(Location::new(2)).await.unwrap(),
2158 Some(value_b.clone())
2159 );
2160 assert!(matches!(
2161 db.get(Location::new(4)).await,
2162 Err(Error::LocationOutOfBounds(_, size)) if size == size_before
2163 ));
2164
2165 db.rewind(initial_size).await.unwrap();
2166 assert_eq!(db.root(), initial_root);
2167 assert_eq!(db.bounds().end, initial_size);
2168 assert_eq!(db.get_metadata().await.unwrap(), None);
2169 assert!(matches!(
2170 db.get(Location::new(1)).await,
2171 Err(Error::LocationOutOfBounds(_, size)) if size == initial_size
2172 ));
2173
2174 db.commit().await.unwrap();
2175 drop(db);
2176 let db = reopen(context.child("reopen_initial_boundary")).await;
2177 assert_eq!(db.root(), initial_root);
2178 assert_eq!(db.bounds().end, initial_size);
2179 assert_eq!(db.get_metadata().await.unwrap(), None);
2180 assert!(matches!(
2181 db.get(Location::new(1)).await,
2182 Err(Error::LocationOutOfBounds(_, size)) if size == initial_size
2183 ));
2184
2185 db.destroy().await.unwrap();
2186 }
2187
2188 #[boxed]
2189 pub(crate) async fn test_keyless_db_rewind_pruned_target_errors<
2190 F: Family,
2191 V,
2192 C,
2193 H,
2194 S: Strategy,
2195 >(
2196 mut db: TestKeyless<F, V, C, H, S>,
2197 ) where
2198 V: ValueEncoding<Value: TestValue>,
2199 C: Mutable<Item = Operation<F, V>>,
2200 H: Hasher,
2201 Operation<F, V>: EncodeShared,
2202 {
2203 let first_range = commit_appends(&mut db, (0..16).map(V::Value::make), None).await;
2204
2205 let mut round = 0u64;
2206 loop {
2207 round += 1;
2208 assert!(
2209 round <= 64,
2210 "failed to prune enough history for rewind test"
2211 );
2212
2213 commit_appends(
2214 &mut db,
2215 (0..16).map(|i| V::Value::make(round * 100 + i)),
2216 None,
2217 )
2218 .await;
2219 db.prune(db.last_commit_loc()).await.unwrap();
2220
2221 if db.bounds().start > first_range.start {
2222 break;
2223 }
2224 }
2225
2226 let oldest_retained = db.bounds().start;
2227 let boundary_err = db.rewind(oldest_retained).await.unwrap_err();
2228 assert!(
2229 matches!(
2230 boundary_err,
2231 Error::Journal(crate::journal::Error::ItemPruned(_))
2232 ),
2233 "unexpected rewind error at retained boundary: {boundary_err:?}"
2234 );
2235
2236 let err = db.rewind(first_range.start).await.unwrap_err();
2237 assert!(
2238 matches!(err, Error::Journal(crate::journal::Error::ItemPruned(_))),
2239 "unexpected rewind error: {err:?}"
2240 );
2241
2242 db.destroy().await.unwrap();
2243 }
2244
2245 #[boxed]
2246 pub(crate) async fn test_keyless_db_floor_tracking<F: Family, V, C, H, S: Strategy>(
2247 context: deterministic::Context,
2248 mut db: TestKeyless<F, V, C, H, S>,
2249 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
2250 ) where
2251 V: ValueEncoding<Value: TestValue>,
2252 C: Mutable<Item = Operation<F, V>>,
2253 H: Hasher,
2254 Operation<F, V>: EncodeShared,
2255 {
2256 assert_eq!(db.inactivity_floor_loc(), Location::new(0));
2258
2259 let floor_a = Location::<F>::new(2);
2261 let merkleized = db
2262 .new_batch()
2263 .append(V::Value::make(1))
2264 .append(V::Value::make(2))
2265 .merkleize(&db, None, floor_a)
2266 .await;
2267 db.apply_batch(merkleized).await.unwrap();
2268 db.commit().await.unwrap();
2269 assert_eq!(db.inactivity_floor_loc(), floor_a);
2270
2271 drop(db);
2273 let mut db = reopen(context.child("reopen")).await;
2274 assert_eq!(db.inactivity_floor_loc(), floor_a);
2275
2276 let merkleized = db
2278 .new_batch()
2279 .append(V::Value::make(3))
2280 .merkleize(&db, None, floor_a)
2281 .await;
2282 db.apply_batch(merkleized).await.unwrap();
2283 assert_eq!(db.inactivity_floor_loc(), floor_a);
2284
2285 let floor_b = Location::<F>::new(5);
2287 let merkleized = db
2288 .new_batch()
2289 .append(V::Value::make(4))
2290 .merkleize(&db, None, floor_b)
2291 .await;
2292 db.apply_batch(merkleized).await.unwrap();
2293 assert_eq!(db.inactivity_floor_loc(), floor_b);
2294
2295 db.destroy().await.unwrap();
2296 }
2297
2298 #[boxed]
2299 pub(crate) async fn test_keyless_db_floor_regression_rejected<F: Family, V, C, H, S: Strategy>(
2300 mut db: TestKeyless<F, V, C, H, S>,
2301 ) where
2302 V: ValueEncoding<Value: TestValue>,
2303 C: Mutable<Item = Operation<F, V>>,
2304 H: Hasher,
2305 Operation<F, V>: EncodeShared,
2306 {
2307 let merkleized = db
2309 .new_batch()
2310 .append(V::Value::make(1))
2311 .append(V::Value::make(2))
2312 .merkleize(&db, None, Location::new(3))
2313 .await;
2314 db.apply_batch(merkleized).await.unwrap();
2315 assert_eq!(db.inactivity_floor_loc(), Location::new(3));
2316 let root_before = db.root();
2317 let last_commit_before = db.last_commit_loc();
2318
2319 let merkleized = db
2321 .new_batch()
2322 .append(V::Value::make(3))
2323 .merkleize(&db, None, Location::new(1))
2324 .await;
2325 let err = db.apply_batch(merkleized).await.unwrap_err();
2326 assert!(
2327 matches!(err, Error::FloorRegressed(new, current) if *new == 1 && *current == 3),
2328 "unexpected error: {err:?}"
2329 );
2330
2331 assert_eq!(db.inactivity_floor_loc(), Location::new(3));
2333 assert_eq!(db.last_commit_loc(), last_commit_before);
2334 assert_eq!(db.root(), root_before);
2335
2336 db.destroy().await.unwrap();
2337 }
2338
2339 #[boxed]
2340 pub(crate) async fn test_keyless_db_floor_beyond_commit_loc_rejected<
2341 F: Family,
2342 V,
2343 C,
2344 H,
2345 S: Strategy,
2346 >(
2347 mut db: TestKeyless<F, V, C, H, S>,
2348 ) where
2349 V: ValueEncoding<Value: TestValue>,
2350 C: Mutable<Item = Operation<F, V>>,
2351 H: Hasher,
2352 Operation<F, V>: EncodeShared,
2353 {
2354 let merkleized = db
2358 .new_batch()
2359 .append(V::Value::make(1))
2360 .append(V::Value::make(2))
2361 .merkleize(&db, None, Location::new(999))
2362 .await;
2363 let err = db.apply_batch(merkleized).await.unwrap_err();
2364 assert!(
2365 matches!(err, Error::FloorBeyondSize(floor, commit) if *floor == 999 && *commit == 3),
2366 "unexpected error: {err:?}"
2367 );
2368
2369 let merkleized = db
2371 .new_batch()
2372 .append(V::Value::make(3))
2373 .append(V::Value::make(4))
2374 .merkleize(&db, None, Location::new(4))
2375 .await;
2376 let err = db.apply_batch(merkleized).await.unwrap_err();
2377 assert!(
2378 matches!(err, Error::FloorBeyondSize(floor, commit) if *floor == 4 && *commit == 3),
2379 "unexpected error: {err:?}"
2380 );
2381
2382 db.destroy().await.unwrap();
2383 }
2384
2385 #[boxed]
2386 pub(crate) async fn test_keyless_db_rewind_restores_floor<F: Family, V, C, H, S: Strategy>(
2387 mut db: TestKeyless<F, V, C, H, S>,
2388 ) where
2389 V: ValueEncoding<Value: TestValue>,
2390 C: Mutable<Item = Operation<F, V>>,
2391 H: Hasher,
2392 Operation<F, V>: EncodeShared,
2393 {
2394 let floor_a = Location::<F>::new(3);
2396 let merkleized = db
2397 .new_batch()
2398 .append(V::Value::make(1))
2399 .append(V::Value::make(2))
2400 .merkleize(&db, None, floor_a)
2401 .await;
2402 db.apply_batch(merkleized).await.unwrap();
2403 db.commit().await.unwrap();
2404 let rewind_target = Location::new(*db.last_commit_loc() + 1);
2405
2406 let floor_b = Location::<F>::new(6);
2408 let merkleized = db
2409 .new_batch()
2410 .append(V::Value::make(3))
2411 .append(V::Value::make(4))
2412 .merkleize(&db, None, floor_b)
2413 .await;
2414 db.apply_batch(merkleized).await.unwrap();
2415 db.commit().await.unwrap();
2416 assert_eq!(db.inactivity_floor_loc(), floor_b);
2417
2418 db.rewind(rewind_target).await.unwrap();
2420 assert_eq!(db.inactivity_floor_loc(), floor_a);
2421
2422 let beyond = Location::new(*floor_a + 1);
2424 let err = db.prune(beyond).await.unwrap_err();
2425 assert!(matches!(err, Error::PruneBeyondMinRequired(_, _)));
2426
2427 db.prune(floor_a).await.unwrap();
2429
2430 db.destroy().await.unwrap();
2431 }
2432
2433 #[boxed]
2436 pub(crate) async fn test_keyless_db_floor_changes_root<F: Family, V, C, H, S: Strategy>(
2437 mut db_a: TestKeyless<F, V, C, H, S>,
2438 mut db_b: TestKeyless<F, V, C, H, S>,
2439 ) where
2440 V: ValueEncoding<Value: TestValue>,
2441 C: Mutable<Item = Operation<F, V>>,
2442 H: Hasher,
2443 Operation<F, V>: EncodeShared,
2444 {
2445 let appends = [V::Value::make(1), V::Value::make(2)];
2446
2447 let mut batch_a = db_a.new_batch();
2449 for v in appends.iter() {
2450 batch_a = batch_a.append(v.clone());
2451 }
2452 db_a.apply_batch(batch_a.merkleize(&db_a, None, Location::new(0)).await)
2453 .await
2454 .unwrap();
2455
2456 let mut batch_b = db_b.new_batch();
2458 for v in appends.iter() {
2459 batch_b = batch_b.append(v.clone());
2460 }
2461 db_b.apply_batch(batch_b.merkleize(&db_b, None, Location::new(3)).await)
2462 .await
2463 .unwrap();
2464
2465 assert_ne!(db_a.root(), db_b.root());
2466
2467 db_a.destroy().await.unwrap();
2468 db_b.destroy().await.unwrap();
2469 }
2470
2471 #[boxed]
2473 pub(crate) async fn test_keyless_db_floor_at_commit_loc_accepted<
2474 F: Family,
2475 V,
2476 C,
2477 H,
2478 S: Strategy,
2479 >(
2480 mut db: TestKeyless<F, V, C, H, S>,
2481 ) where
2482 V: ValueEncoding<Value: TestValue>,
2483 C: Mutable<Item = Operation<F, V>>,
2484 H: Hasher,
2485 Operation<F, V>: EncodeShared,
2486 {
2487 let commit_loc = Location::<F>::new(3);
2490 db.apply_batch(
2491 db.new_batch()
2492 .append(V::Value::make(1))
2493 .append(V::Value::make(2))
2494 .merkleize(&db, None, commit_loc)
2495 .await,
2496 )
2497 .await
2498 .unwrap();
2499 assert_eq!(db.inactivity_floor_loc(), commit_loc);
2500
2501 db.destroy().await.unwrap();
2502 }
2503
2504 #[boxed]
2506 pub(crate) async fn test_keyless_db_rewind_after_reopen_with_floor<
2507 F: Family,
2508 V,
2509 C,
2510 H,
2511 S: Strategy,
2512 >(
2513 context: deterministic::Context,
2514 mut db: TestKeyless<F, V, C, H, S>,
2515 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
2516 ) where
2517 V: ValueEncoding<Value: TestValue>,
2518 C: Mutable<Item = Operation<F, V>>,
2519 H: Hasher,
2520 Operation<F, V>: EncodeShared,
2521 {
2522 let floor_a = Location::<F>::new(3);
2524 db.apply_batch(
2525 db.new_batch()
2526 .append(V::Value::make(1))
2527 .append(V::Value::make(2))
2528 .merkleize(&db, None, floor_a)
2529 .await,
2530 )
2531 .await
2532 .unwrap();
2533 db.commit().await.unwrap();
2534 let rewind_target = Location::new(*db.last_commit_loc() + 1);
2535
2536 let floor_b = Location::<F>::new(6);
2538 db.apply_batch(
2539 db.new_batch()
2540 .append(V::Value::make(3))
2541 .append(V::Value::make(4))
2542 .merkleize(&db, None, floor_b)
2543 .await,
2544 )
2545 .await
2546 .unwrap();
2547 db.commit().await.unwrap();
2548
2549 drop(db);
2551 let mut db = reopen(context.child("reopen")).await;
2552 assert_eq!(db.inactivity_floor_loc(), floor_b);
2553
2554 db.rewind(rewind_target).await.unwrap();
2556 assert_eq!(db.inactivity_floor_loc(), floor_a);
2557 assert_eq!(db.last_commit_loc(), Location::new(3));
2558
2559 db.commit().await.unwrap();
2561 drop(db);
2562 let db = reopen(context.child("reopen").with_attribute("index", 2)).await;
2563 assert_eq!(db.inactivity_floor_loc(), floor_a);
2564
2565 db.destroy().await.unwrap();
2566 }
2567
2568 #[boxed]
2573 pub(crate) async fn test_keyless_db_ancestor_floor_regression_rejected<
2574 F,
2575 V,
2576 C,
2577 H,
2578 S: Strategy,
2579 >(
2580 mut db: TestKeyless<F, V, C, H, S>,
2581 ) where
2582 F: Family,
2583 V: ValueEncoding<Value: TestValue>,
2584 C: Mutable<Item = Operation<F, V>>,
2585 H: Hasher,
2586 Operation<F, V>: EncodeShared,
2587 {
2588 let parent = db
2590 .new_batch()
2591 .append(V::Value::make(1))
2592 .merkleize(&db, None, Location::new(2))
2593 .await;
2594 let child = parent
2596 .new_batch::<H>()
2597 .append(V::Value::make(2))
2598 .merkleize(&db, None, Location::new(1))
2599 .await;
2600
2601 let root_before = db.root();
2602 let last_commit_before = db.last_commit_loc();
2603 let floor_before = db.inactivity_floor_loc();
2604
2605 let err = db.apply_batch(child).await.unwrap_err();
2606 assert!(
2607 matches!(err, Error::FloorRegressed(new, prev) if *new == 1 && *prev == 2),
2608 "unexpected error: {err:?}"
2609 );
2610
2611 assert_eq!(db.root(), root_before);
2613 assert_eq!(db.last_commit_loc(), last_commit_before);
2614 assert_eq!(db.inactivity_floor_loc(), floor_before);
2615
2616 db.destroy().await.unwrap();
2617 }
2618
2619 #[boxed]
2622 pub(crate) async fn test_keyless_db_ancestor_floor_beyond_commit_loc_rejected<
2623 F,
2624 V,
2625 C,
2626 H,
2627 S: Strategy,
2628 >(
2629 mut db: TestKeyless<F, V, C, H, S>,
2630 ) where
2631 F: Family,
2632 V: ValueEncoding<Value: TestValue>,
2633 C: Mutable<Item = Operation<F, V>>,
2634 H: Hasher,
2635 Operation<F, V>: EncodeShared,
2636 {
2637 let parent = db
2639 .new_batch()
2640 .append(V::Value::make(1))
2641 .merkleize(&db, None, Location::new(3))
2642 .await;
2643 let child = parent
2645 .new_batch::<H>()
2646 .append(V::Value::make(2))
2647 .merkleize(&db, None, Location::new(0))
2648 .await;
2649
2650 let err = db.apply_batch(child).await.unwrap_err();
2651 assert!(
2653 matches!(err, Error::FloorBeyondSize(floor, commit) if *floor == 3 && *commit == 2),
2654 "unexpected error: {err:?}"
2655 );
2656
2657 db.destroy().await.unwrap();
2658 }
2659
2660 #[boxed]
2666 pub(crate) async fn test_keyless_db_single_commit_live_set<F, V, C, H, S: Strategy>(
2667 context: deterministic::Context,
2668 mut db: TestKeyless<F, V, C, H, S>,
2669 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
2670 ) where
2671 F: Family,
2672 V: ValueEncoding<Value: TestValue>,
2673 C: Mutable<Item = Operation<F, V>>,
2674 H: Hasher,
2675 Operation<F, V>: EncodeShared,
2676 {
2677 let metadata = V::Value::make(42);
2680 let commit_loc = Location::<F>::new(4);
2681 db.apply_batch(
2682 db.new_batch()
2683 .append(V::Value::make(1))
2684 .append(V::Value::make(2))
2685 .append(V::Value::make(3))
2686 .merkleize(&db, Some(metadata.clone()), commit_loc)
2687 .await,
2688 )
2689 .await
2690 .unwrap();
2691 db.commit().await.unwrap();
2692 assert_eq!(db.last_commit_loc(), commit_loc);
2693 assert_eq!(db.inactivity_floor_loc(), commit_loc);
2694 let root_after_commit = db.root();
2695
2696 db.prune(commit_loc).await.unwrap();
2701 let bounds = db.bounds();
2702 assert!(
2703 bounds.start <= commit_loc,
2704 "prune must not advance bounds.start past the floor"
2705 );
2706 assert_eq!(bounds.end, Location::new(*commit_loc + 1));
2707
2708 let err = db.prune(Location::new(*commit_loc + 1)).await.unwrap_err();
2710 assert!(matches!(err, Error::PruneBeyondMinRequired(p, f)
2711 if *p == *commit_loc + 1 && *f == *commit_loc));
2712
2713 assert_eq!(db.get(commit_loc).await.unwrap(), Some(metadata.clone()));
2715 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata.clone()));
2716 assert_eq!(db.last_commit_loc(), commit_loc);
2717 assert_eq!(db.inactivity_floor_loc(), commit_loc);
2718 assert_eq!(db.root(), root_after_commit);
2720
2721 db.sync().await.unwrap();
2724 drop(db);
2725 let mut db = reopen(context.child("reopened")).await;
2726 let reopened_bounds = db.bounds();
2727 assert_eq!(reopened_bounds.end, Location::new(*commit_loc + 1));
2728 assert_eq!(db.last_commit_loc(), commit_loc);
2729 assert_eq!(db.inactivity_floor_loc(), commit_loc);
2730 assert_eq!(db.root(), root_after_commit);
2731 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata.clone()));
2732
2733 let next_commit_loc = Location::<F>::new(7);
2737 let v5 = V::Value::make(5);
2738 let v6 = V::Value::make(6);
2739 db.apply_batch(
2740 db.new_batch()
2741 .append(v5.clone())
2742 .append(v6.clone())
2743 .merkleize(&db, None, next_commit_loc)
2744 .await,
2745 )
2746 .await
2747 .unwrap();
2748 db.commit().await.unwrap();
2749 assert_eq!(db.last_commit_loc(), next_commit_loc);
2750 assert_eq!(db.inactivity_floor_loc(), next_commit_loc);
2751
2752 assert_eq!(db.get(Location::new(5)).await.unwrap(), Some(v5));
2755 assert_eq!(db.get(Location::new(6)).await.unwrap(), Some(v6));
2756 assert_eq!(db.get(commit_loc).await.unwrap(), Some(metadata));
2757
2758 db.destroy().await.unwrap();
2759 }
2760
2761 #[boxed]
2763 pub(crate) async fn test_keyless_db_chained_apply_with_valid_floors_succeeds<
2764 F,
2765 V,
2766 C,
2767 H,
2768 S: Strategy,
2769 >(
2770 mut db: TestKeyless<F, V, C, H, S>,
2771 ) where
2772 F: Family,
2773 V: ValueEncoding<Value: TestValue>,
2774 C: Mutable<Item = Operation<F, V>>,
2775 H: Hasher,
2776 Operation<F, V>: EncodeShared,
2777 {
2778 let parent = db
2782 .new_batch()
2783 .append(V::Value::make(1))
2784 .merkleize(&db, None, Location::new(2))
2785 .await;
2786 let child = parent
2787 .new_batch::<H>()
2788 .append(V::Value::make(2))
2789 .merkleize(&db, None, Location::new(3))
2790 .await;
2791 let grandchild = child
2792 .new_batch::<H>()
2793 .append(V::Value::make(3))
2794 .merkleize(&db, None, Location::new(5))
2795 .await;
2796
2797 db.apply_batch(grandchild).await.unwrap();
2798
2799 assert_eq!(db.last_commit_loc(), Location::new(6));
2801 assert_eq!(db.inactivity_floor_loc(), Location::new(5));
2802
2803 db.destroy().await.unwrap();
2804 }
2805}