1use crate::{
44 Context,
45 journal::{
46 authenticated,
47 contiguous::{Contiguous, Mutable},
48 },
49 merkle::{Family, Location, Proof, full::Config as MerkleConfig},
50 qmdb::{
51 Error, any::value::ValueEncoding, chain, metrics::Metrics, single_operation_root,
52 sync::source,
53 },
54};
55use commonware_codec::EncodeShared;
56use commonware_cryptography::Hasher;
57use commonware_macros::boxed;
58use commonware_parallel::Strategy;
59use commonware_runtime::Handle;
60use std::{num::NonZeroU64, sync::Arc};
61use tracing::{debug, warn};
62
63pub mod batch;
64mod compact;
65pub mod fixed;
66mod operation;
67pub(crate) mod sync;
68pub mod variable;
69pub use compact::{
70 Config as CompactConfig, Db as CompactDb, MerkleizedBatch as CompactMerkleizedBatch,
71 UnmerkleizedBatch as CompactUnmerkleizedBatch,
72};
73pub use operation::{APPEND_CONTEXT, COMMIT_CONTEXT, Operation};
74
75pub fn initial_root<F, V, H>() -> H::Digest
79where
80 F: Family,
81 V: ValueEncoding,
82 H: Hasher,
83 Operation<F, V>: EncodeShared,
84{
85 single_operation_root::<F, H>(&Operation::<F, V>::Commit(None, Location::new(0)))
86}
87
88fn check_locations<F: Family>(locs: &[Location<F>], end: Location<F>) -> Result<(), Error<F>> {
90 if let Some(pair) = locs.windows(2).find(|pair| pair[0] >= pair[1]) {
91 return Err(Error::LocationOutOfOrder(pair[0], pair[1]));
92 }
93 if let Some(&last) = locs.last()
95 && last >= end
96 {
97 return Err(Error::LocationOutOfBounds(last, end));
98 }
99 Ok(())
100}
101
102#[derive(Clone)]
104pub struct Config<J, S: Strategy> {
105 pub merkle: MerkleConfig<S>,
107
108 pub log: J,
110}
111
112pub struct Keyless<F, E, V, C, H, S>
114where
115 F: Family,
116 E: Context,
117 V: ValueEncoding,
118 C: Contiguous<Item = Operation<F, V>>,
119 H: Hasher,
120 S: Strategy,
121 Operation<F, V>: EncodeShared,
122{
123 journal: authenticated::Journal<F, E, C, H, S>,
125
126 root: H::Digest,
128
129 inactivity_floor_loc: Location<F>,
132
133 metrics: Metrics<E>,
135}
136
137impl<F, E, V, C, H, S> std::fmt::Debug for Keyless<F, E, V, C, H, S>
138where
139 F: Family,
140 E: Context,
141 V: ValueEncoding,
142 C: Mutable<Item = Operation<F, V>>,
143 H: Hasher,
144 S: Strategy,
145 Operation<F, V>: EncodeShared,
146{
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 f.debug_struct("Keyless")
149 .field("bounds", &self.bounds())
150 .field("inactivity_floor_loc", &self.inactivity_floor_loc())
151 .finish_non_exhaustive()
152 }
153}
154
155impl<F, E, V, C, H, S> Keyless<F, E, V, C, H, S>
156where
157 F: Family,
158 E: Context,
159 V: ValueEncoding,
160 C: Mutable<Item = Operation<F, V>>,
161 H: Hasher,
162 S: Strategy,
163 Operation<F, V>: EncodeShared,
164{
165 #[boxed]
176 pub async fn init(
177 context: E,
178 cfg: Config<C::Config, S>,
179 max_size: Option<Location<F>>,
180 ) -> Result<Self, Error<F>>
181 where
182 C: authenticated::Backing<E>,
183 {
184 let mut journal = crate::qmdb::init_journal::<F, E, C, H, S>(
187 context.child("journal"),
188 cfg.merkle,
189 cfg.log,
190 max_size,
191 false,
192 )
193 .await?;
194 let metrics = Metrics::new(context);
195 if journal.size() == 0 {
196 warn!("no operations found in log, creating initial commit");
197 (journal, _) = journal
198 .append(&Operation::Commit(None, Location::new(0)))
199 .await?;
200 journal = journal.sync().await?;
201 }
202
203 let (last_commit_loc, inactivity_floor_loc) = {
204 let bounds = journal.bounds();
205 let last_commit_loc = Location::new(
206 bounds
207 .end
208 .checked_sub(1)
209 .expect("at least one commit should exist"),
210 );
211 let op = journal.read(*last_commit_loc).await?;
212 let inactivity_floor_loc = op
213 .has_floor()
214 .expect("last operation should be a commit with floor");
215 (last_commit_loc, inactivity_floor_loc)
216 };
217 let inactive_peaks = F::inactive_peaks(last_commit_loc + 1, inactivity_floor_loc);
218 let root = journal.root(inactive_peaks)?;
219
220 let db = Self {
221 journal,
222 root,
223 inactivity_floor_loc,
224 metrics,
225 };
226 db.update_metrics();
227 Ok(db)
228 }
229
230 pub async fn get(&self, loc: Location<F>) -> Result<Option<V::Value>, Error<F>> {
238 let _timer = self.metrics.get_timer();
239 self.metrics.get_calls.inc();
240 self.metrics.lookups_requested.inc();
241 let op_count = self.journal.bounds().end;
242 if loc >= op_count {
243 return Err(Error::LocationOutOfBounds(loc, Location::new(op_count)));
244 }
245 let op = self.journal.read(*loc).await?;
246
247 let result = op.into_value();
248 Ok(result)
249 }
250
251 pub async fn get_many(&self, locs: &[Location<F>]) -> Result<Vec<Option<V::Value>>, Error<F>> {
263 if locs.is_empty() {
264 return Ok(Vec::new());
265 }
266
267 let _timer = self.metrics.get_many_timer();
268 self.metrics.get_many_calls.inc();
269 self.metrics.lookups_requested.inc_by(locs.len() as u64);
270 check_locations(locs, Location::new(self.journal.bounds().end))?;
271 let positions: Vec<u64> = locs.iter().map(|loc| **loc).collect();
272 let ops = self.journal.read_many(&positions).await?;
273 let result = ops.into_iter().map(|op| op.into_value()).collect();
274 Ok(result)
275 }
276
277 pub const fn inactivity_floor_loc(&self) -> Location<F> {
279 self.inactivity_floor_loc
280 }
281
282 pub fn bounds(&self) -> std::ops::Range<Location<F>> {
286 let bounds = self.journal.bounds();
287 Location::new(bounds.start)..Location::new(bounds.end)
288 }
289
290 fn update_metrics(&self) {
292 let bounds = self.journal.bounds();
293 self.metrics.update(
294 bounds.end,
295 bounds.start,
296 *self.inactivity_floor_loc,
297 bounds.end - 1,
298 );
299 }
300
301 pub const fn sync_boundary(&self) -> Location<F> {
306 self.inactivity_floor_loc
307 }
308
309 pub async fn get_metadata(&self) -> Result<Option<V::Value>, Error<F>> {
311 let op = self.journal.read(*self.journal.size() - 1).await?;
313 let Operation::Commit(metadata, _floor) = op else {
314 return Ok(None);
315 };
316
317 Ok(metadata)
318 }
319
320 pub const fn root(&self) -> H::Digest {
322 self.root
323 }
324
325 pub const fn strategy(&self) -> &S {
327 self.journal.strategy()
328 }
329
330 pub async fn proof(
345 &self,
346 start_loc: Location<F>,
347 max_ops: NonZeroU64,
348 ) -> Result<(Proof<F, H::Digest>, Vec<Operation<F, V>>), Error<F>> {
349 self.historical_proof(self.bounds().end, start_loc, max_ops)
350 .await
351 }
352
353 #[allow(clippy::type_complexity)]
368 #[tracing::instrument(
369 name = "qmdb.keyless.db.historical_proof",
370 level = "info",
371 skip_all,
372 fields(
373 op_count = *op_count,
374 start_loc = *start_loc,
375 max_ops = max_ops.get(),
376 ),
377 )]
378 pub async fn historical_proof(
379 &self,
380 op_count: Location<F>,
381 start_loc: Location<F>,
382 max_ops: NonZeroU64,
383 ) -> Result<(Proof<F, H::Digest>, Vec<Operation<F, V>>), Error<F>> {
384 if op_count > self.journal.size() {
385 return Err(crate::merkle::Error::RangeOutOfBounds(op_count).into());
386 }
387
388 let inactive_peaks =
389 crate::qmdb::inactive_peaks_at::<F, _>(&self.journal, op_count).await?;
390
391 Ok(self
392 .journal
393 .historical_proof(op_count, start_loc, max_ops, inactive_peaks)
394 .await?)
395 }
396
397 pub async fn pinned_nodes_at(&self, loc: Location<F>) -> Result<Vec<H::Digest>, Error<F>> {
399 self.journal
400 .merkle
401 .pinned_nodes_at(loc)
402 .await
403 .map_err(Into::into)
404 }
405
406 #[tracing::instrument(name = "qmdb.keyless.db.prune", level = "info", skip_all)]
417 #[boxed]
418 pub async fn prune(mut self, loc: Location<F>) -> Result<Self, Error<F>> {
419 let _timer = self.metrics.prune_timer();
420 self.metrics.prune_calls.inc();
421 if loc > self.inactivity_floor_loc {
422 return Err(Error::PruneBeyondMinRequired(
423 loc,
424 self.inactivity_floor_loc,
425 ));
426 }
427 (self.journal, _) = self.journal.prune(loc).await?;
428 self.update_metrics();
429 Ok(self)
430 }
431
432 #[tracing::instrument(name = "qmdb.keyless.db.sync", level = "info", skip_all)]
436 pub async fn sync(mut self) -> Result<Self, Error<F>> {
437 let _timer = self.metrics.sync_timer();
438 self.metrics.sync_calls.inc();
439 self.journal = self.journal.sync().await?;
440 Ok(self)
441 }
442
443 #[tracing::instrument(name = "qmdb.keyless.db.start_sync", level = "info", skip_all)]
454 pub async fn start_sync(mut self) -> Result<(Self, Handle<()>), Error<F>> {
455 self.metrics.start_sync_calls.inc();
456 let handle;
457 (self.journal, handle) = self.journal.start_sync().await?;
458 Ok((self, handle))
459 }
460
461 #[tracing::instrument(name = "qmdb.keyless.db.commit", level = "info", skip_all)]
463 pub async fn commit(mut self) -> Result<Self, Error<F>> {
464 let _timer = self.metrics.commit_timer();
465 self.metrics.commit_calls.inc();
466 self.journal = self.journal.commit().await?;
467 Ok(self)
468 }
469
470 #[boxed]
472 pub async fn destroy(self) -> Result<(), Error<F>> {
473 Ok(self.journal.destroy().await?)
474 }
475
476 pub(crate) fn commitment(&self) -> chain::Commitment<F, H::Digest> {
478 chain::Commitment::new(self.journal.size(), self.root)
479 }
480
481 pub fn new_batch(&self) -> batch::UnmerkleizedBatch<F, H, V, S> {
483 batch::UnmerkleizedBatch::new(self, self.commitment())
484 }
485
486 pub fn to_batch(&self) -> Arc<batch::MerkleizedBatch<F, H::Digest, V, S>> {
488 Arc::new(batch::MerkleizedBatch {
489 journal_batch: self.journal.to_merkleized_batch(),
490 parent: None,
491 bounds: chain::Bounds::from_db(self.commitment(), self.inactivity_floor_loc),
492 })
493 }
494
495 pub fn validate_batch(
501 &self,
502 batch: &batch::MerkleizedBatch<F, H::Digest, V, S>,
503 ) -> Result<(), Error<F>> {
504 batch.bounds.validate_apply_to(self.commitment())
505 }
506
507 #[tracing::instrument(name = "qmdb.keyless.db.apply_batch", level = "info", skip_all)]
518 pub async fn apply_batch(
519 mut self,
520 batch: Arc<batch::MerkleizedBatch<F, H::Digest, V, S>>,
521 ) -> Result<Self, Error<F>> {
522 let _timer = self.metrics.apply_batch_timer();
523 self.metrics.apply_batch_calls.inc();
524 self.validate_batch(&batch)?;
525 let start_loc = self.journal.size();
526
527 self.journal = self.journal.apply_batch(&batch.journal_batch).await?;
528
529 self.inactivity_floor_loc = batch.bounds.inactivity_floor;
530 self.root = batch.root();
531 let end_loc = batch.bounds.tip.size;
532 debug!(size = ?end_loc, "applied batch");
533 self.update_metrics();
534 self.metrics
535 .operations_applied
536 .inc_by(*end_loc - *start_loc);
537 Ok(self)
538 }
539}
540
541impl<F, E, V, C, H, S> crate::qmdb::sync::Source for Keyless<F, E, V, C, H, S>
542where
543 F: Family,
544 E: Context,
545 V: ValueEncoding,
546 C: Mutable<Item = Operation<F, V>>,
547 H: Hasher,
548 S: Strategy,
549 Operation<F, V>: EncodeShared,
550{
551 type Family = F;
552 type Digest = H::Digest;
553 type Op = Operation<F, V>;
554 type Error = Error<F>;
555
556 async fn serve(&self, request: crate::qmdb::sync::Request<F>) -> source::Result<Self> {
557 self.journal.serve(request).await
558 }
559}
560
561#[cfg(test)]
562pub(crate) mod tests {
563 use super::*;
564 use crate::qmdb::{verify_proof, verify_proof_and_pinned_nodes};
565 use commonware_cryptography::Sha256;
566 use commonware_parallel::Strategy;
567 use commonware_runtime::{Supervisor as _, deterministic};
568 use commonware_utils::NZU64;
569 use std::{future::Future, pin::Pin};
570
571 pub(crate) type Reopen<D> =
572 Box<dyn Fn(deterministic::Context) -> Pin<Box<dyn Future<Output = D> + Send>>>;
573
574 pub(crate) type BoundedOpen<D, F> = Box<
575 dyn Fn(
576 deterministic::Context,
577 Location<F>,
578 ) -> Pin<Box<dyn Future<Output = Result<D, Error<F>>> + Send>>,
579 >;
580
581 type TestKeyless<F, V, C, H, S> = Keyless<F, deterministic::Context, V, C, H, S>;
582
583 pub(crate) trait TestValue: Clone + PartialEq + std::fmt::Debug + Send + Sync {
585 fn make(i: u64) -> Self;
586 }
587
588 impl TestValue for Vec<u8> {
589 fn make(i: u64) -> Self {
590 vec![(i % 255) as u8; ((i % 13) + 7) as usize]
591 }
592 }
593
594 impl TestValue for commonware_utils::sequence::U64 {
595 fn make(i: u64) -> Self {
596 Self::new(i * 10 + 1)
597 }
598 }
599
600 macro_rules! keyless_tests {
602 ($($name:ident => $scenario:ident, $fixture:ident;)*) => {
603 $(
604 #[test_traced]
605 fn $name() {
606 deterministic::Runner::default().start(|ctx| async move {
607 keyless_tests!(@fixture $fixture, $scenario, mmr, ctx);
608 });
609 }
610 )*
611 paste::paste! {
612 $(
613 #[test_traced]
614 fn [<$name _mmb>]() {
615 deterministic::Runner::default().start(|ctx| async move {
616 keyless_tests!(@fixture $fixture, $scenario, mmb, ctx);
617 });
618 }
619 )*
620 }
621 };
622 (@fixture bounded, $scenario:ident, $family:ident, $ctx:ident) => {
623 let db = open_db::<$family::Family>($ctx.child("db")).await;
624 tests::$scenario($ctx, db, reopen::<$family::Family>(), bounded_open::<$family::Family>()).await;
625 };
626 (@fixture bounded_floor, $scenario:ident, $family:ident, $ctx:ident) => {
627 let db = open_db::<$family::Family>($ctx.child("db")).await;
628 tests::$scenario($ctx, db, bounded_open::<$family::Family>()).await;
629 };
630 (@fixture bounded_indexed, $scenario:ident, $family:ident, $ctx:ident) => {
631 let db = open_db::<$family::Family>($ctx.child("db").with_attribute("index", 1)).await;
632 tests::$scenario($ctx, db, reopen::<$family::Family>(), bounded_open::<$family::Family>()).await;
633 };
634 (@fixture db, $scenario:ident, $family:ident, $ctx:ident) => {
635 let db = open_db::<$family::Family>($ctx.child("db")).await;
636 tests::$scenario(db).await;
637 };
638 (@fixture pair, $scenario:ident, $family:ident, $ctx:ident) => {
639 let db = open_db::<$family::Family>($ctx.child("db")).await;
640 let foreign = open_db_with_suffix::<$family::Family>("foreign", $ctx.child("foreign")).await;
641 tests::$scenario(db, foreign).await;
642 };
643 (@fixture reopen, $scenario:ident, $family:ident, $ctx:ident) => {
644 let db = open_db::<$family::Family>($ctx.child("db")).await;
645 tests::$scenario($ctx, db, reopen::<$family::Family>()).await;
646 };
647 (@fixture reopen_indexed, $scenario:ident, $family:ident, $ctx:ident) => {
648 let db =
649 open_db::<$family::Family>($ctx.child("db").with_attribute("index", 1)).await;
650 tests::$scenario($ctx, db, reopen::<$family::Family>()).await;
651 };
652 }
653
654 pub(super) use keyless_tests;
655
656 #[boxed]
659 pub(crate) async fn run_dropped_ancestor_reads<F: Family, V, C, S: Strategy>(
660 mut db: TestKeyless<F, V, C, Sha256, S>,
661 ) where
662 V: ValueEncoding<Value: TestValue>,
663 C: Mutable<Item = Operation<F, V>>,
664 Operation<F, V>: EncodeShared + std::fmt::Debug,
665 {
666 let seed = db
668 .new_batch()
669 .append(V::Value::make(0))
670 .merkleize(&db, None, db.inactivity_floor_loc())
671 .await
672 .unwrap();
673 db = db.apply_batch(seed).await.unwrap();
674 db = db.commit().await.unwrap();
675 let floor = db.inactivity_floor_loc();
676
677 let base_a = db.bounds().end;
680 let a = db
681 .new_batch()
682 .append(V::Value::make(1))
683 .append(V::Value::make(2))
684 .merkleize(&db, None, floor)
685 .await
686 .unwrap();
687 let base_b = a.bounds().tip.size;
688 let b = a
689 .new_batch::<Sha256>()
690 .append(V::Value::make(3))
691 .append(V::Value::make(4))
692 .merkleize(&db, None, floor)
693 .await
694 .unwrap();
695 let base_c = b.bounds().tip.size;
696 let c = b
697 .new_batch::<Sha256>()
698 .append(V::Value::make(5))
699 .merkleize(&db, None, floor)
700 .await
701 .unwrap();
702 let child = c.new_batch::<Sha256>();
703 drop(a);
704 drop(b);
705
706 let locs = [base_a, base_a + 1, base_b, base_b + 1, base_c];
708 let expected: Vec<_> = (1..=5u64).map(|i| Some(V::Value::make(i))).collect();
709 for (loc, expected) in locs.iter().zip(&expected) {
710 assert_eq!(&c.get(*loc, &db).await.unwrap(), expected);
711 assert_eq!(&child.get(*loc, &db).await.unwrap(), expected);
712 }
713 assert_eq!(c.get_many(&locs, &db).await.unwrap(), expected);
714 assert_eq!(child.get_many(&locs, &db).await.unwrap(), expected);
715
716 assert!(matches!(
718 child.merkleize(&db, None, floor).await,
719 Err(Error::StaleBatch)
720 ));
721
722 let db = db.apply_batch(c).await.unwrap();
724 for (loc, expected) in locs.iter().zip(&expected) {
725 assert_eq!(&db.get(*loc).await.unwrap(), expected);
726 }
727 db.destroy().await.unwrap();
728 }
729
730 #[boxed]
734 pub(crate) async fn run_reads_below_floor_refused<F: Family, V, C, S: Strategy>(
735 mut db: TestKeyless<F, V, C, Sha256, S>,
736 ) where
737 V: ValueEncoding<Value: TestValue>,
738 C: Mutable<Item = Operation<F, V>>,
739 Operation<F, V>: EncodeShared + std::fmt::Debug,
740 {
741 let seed = db
743 .new_batch()
744 .append(V::Value::make(1))
745 .append(V::Value::make(2))
746 .merkleize(&db, None, Location::new(2))
747 .await
748 .unwrap();
749 db = db.apply_batch(seed).await.unwrap();
750 db = db.commit().await.unwrap();
751
752 let batch = db.new_batch();
754 assert!(matches!(
755 batch.get(Location::new(1), &db).await,
756 Err(Error::BelowInactivityFloor(loc)) if loc == Location::new(1)
757 ));
758 assert_eq!(
759 batch.get(Location::new(2), &db).await.unwrap(),
760 Some(V::Value::make(2))
761 );
762 assert!(matches!(
763 batch
764 .get_many(&[Location::new(1), Location::new(2)], &db)
765 .await,
766 Err(Error::BelowInactivityFloor(loc)) if loc == Location::new(1)
767 ));
768
769 assert!(matches!(
771 batch
772 .get_many(&[Location::new(1), Location::new(0)], &db)
773 .await,
774 Err(Error::LocationOutOfOrder(first, second))
775 if first == Location::new(1) && second == Location::new(0)
776 ));
777
778 let parent = db
781 .new_batch()
782 .append(V::Value::make(3))
783 .merkleize(&db, None, Location::new(4))
784 .await
785 .unwrap();
786 assert!(matches!(
787 parent.get(Location::new(2), &db).await,
788 Err(Error::BelowInactivityFloor(loc)) if loc == Location::new(2)
789 ));
790 assert_eq!(
791 parent.get(Location::new(4), &db).await.unwrap(),
792 Some(V::Value::make(3))
793 );
794 assert!(matches!(
795 parent
796 .get_many(&[Location::new(2), Location::new(4)], &db)
797 .await,
798 Err(Error::BelowInactivityFloor(loc)) if loc == Location::new(2)
799 ));
800 assert!(matches!(
801 parent
802 .get_many(&[Location::new(3), Location::new(2)], &db)
803 .await,
804 Err(Error::LocationOutOfOrder(first, second))
805 if first == Location::new(3) && second == Location::new(2)
806 ));
807
808 let tip = parent.bounds().tip.size;
810 assert!(matches!(
811 parent.get(tip, &db).await,
812 Err(Error::LocationOutOfBounds(loc, end)) if loc == tip && end == tip
813 ));
814 assert!(matches!(
815 parent.get_many(&[Location::new(4), tip], &db).await,
816 Err(Error::LocationOutOfBounds(loc, end)) if loc == tip && end == tip
817 ));
818
819 let child = parent.new_batch::<Sha256>();
821 assert!(matches!(
822 child.get(Location::new(3), &db).await,
823 Err(Error::BelowInactivityFloor(loc)) if loc == Location::new(3)
824 ));
825 assert_eq!(
826 child.get(Location::new(4), &db).await.unwrap(),
827 Some(V::Value::make(3))
828 );
829 drop(child);
830
831 db = db.apply_batch(parent).await.unwrap();
833 let mut batch = db.new_batch();
834 for i in 10..22 {
835 batch = batch.append(V::Value::make(i));
836 }
837 let floor = db.bounds().end + 12;
838 let batch = batch.merkleize(&db, None, floor).await.unwrap();
839 db = db.apply_batch(batch).await.unwrap();
840 db = db.commit().await.unwrap();
841 db = db.prune(floor).await.unwrap();
842 assert!(db.bounds().start > Location::new(2));
843 assert!(matches!(
844 db.new_batch().get(Location::new(2), &db).await,
845 Err(Error::BelowInactivityFloor(loc)) if loc == Location::new(2)
846 ));
847 db.destroy().await.unwrap();
848 }
849
850 #[boxed]
853 pub(crate) async fn run_merkleize_across_prune<F: Family, V, C, S: Strategy>(
854 mut db: TestKeyless<F, V, C, Sha256, S>,
855 ) where
856 V: ValueEncoding<Value: TestValue>,
857 C: Mutable<Item = Operation<F, V>>,
858 Operation<F, V>: EncodeShared + std::fmt::Debug,
859 {
860 let mut seed = db.new_batch();
862 for i in 0..80 {
863 seed = seed.append(V::Value::make(i));
864 }
865 let seed = seed
866 .merkleize(&db, None, db.inactivity_floor_loc())
867 .await
868 .unwrap();
869 db = db.apply_batch(seed).await.unwrap();
870 db = db.commit().await.unwrap();
871
872 let parent_floor = db.bounds().end + 1;
874 let parent = db
875 .new_batch()
876 .append(V::Value::make(100))
877 .merkleize(&db, None, parent_floor)
878 .await
879 .unwrap();
880
881 let build = |parent: &Arc<batch::MerkleizedBatch<F, _, V, S>>| {
884 let mut child = parent.new_batch::<Sha256>();
885 for i in 200..280 {
886 child = child.append(V::Value::make(i));
887 }
888 child
889 };
890 let child = build(&parent);
891 let expected = build(&parent)
892 .merkleize(&db, None, parent_floor)
893 .await
894 .unwrap()
895 .root();
896
897 let db = db.apply_batch(parent).await.unwrap();
899 let db = db.commit().await.unwrap();
900 let floor = db.inactivity_floor_loc();
901 let db = db.prune(floor).await.unwrap();
902 assert!(*db.bounds().start > 0, "the prune must remove history");
903
904 let merkleized = child.merkleize(&db, None, parent_floor).await.unwrap();
906 assert_eq!(merkleized.root(), expected);
907 db.destroy().await.unwrap();
908 }
909
910 #[boxed]
913 pub(crate) async fn run_stale_fork_refuses<F: Family, V, C, S: Strategy>(
914 mut db: TestKeyless<F, V, C, Sha256, S>,
915 ) where
916 V: ValueEncoding<Value: TestValue>,
917 C: Mutable<Item = Operation<F, V>>,
918 Operation<F, V>: EncodeShared + std::fmt::Debug,
919 {
920 let merkleized = db
922 .new_batch()
923 .append(V::Value::make(0))
924 .merkleize(&db, None, db.inactivity_floor_loc())
925 .await
926 .unwrap();
927 db = db.apply_batch(merkleized).await.unwrap();
928 db = db.commit().await.unwrap();
929
930 let floor = db.inactivity_floor_loc();
931 let winner = db
932 .new_batch()
933 .append(V::Value::make(1))
934 .merkleize(&db, None, floor)
935 .await
936 .unwrap();
937 let loser = db
938 .new_batch()
939 .append(V::Value::make(2))
940 .merkleize(&db, None, floor)
941 .await
942 .unwrap();
943 assert_eq!(winner.bounds().tip.size, loser.bounds().tip.size);
944 assert_ne!(winner.root(), loser.root());
945
946 let child = loser.new_batch::<Sha256>().append(V::Value::make(3));
947 let direct = db.new_batch().append(V::Value::make(4));
948 let committed = Location::new(0);
949 let db = db.apply_batch(winner).await.unwrap();
950
951 assert!(matches!(
953 direct.get(committed, &db).await,
954 Err(Error::StaleRead)
955 ));
956 assert!(matches!(
957 direct.get_many(&[committed], &db).await,
958 Err(Error::StaleRead)
959 ));
960
961 assert!(matches!(
962 child.get(committed, &db).await,
963 Err(Error::StaleRead)
964 ));
965 assert!(matches!(
966 child.get_many(&[committed], &db).await,
967 Err(Error::StaleRead)
968 ));
969 assert!(matches!(
970 loser.get(committed, &db).await,
971 Err(Error::StaleRead)
972 ));
973
974 assert!(matches!(
978 child.get_many(&[], &db).await,
979 Err(Error::StaleRead)
980 ));
981 assert!(matches!(
982 loser.get_many(&[], &db).await,
983 Err(Error::StaleRead)
984 ));
985 assert!(matches!(
986 child.get(child.size(), &db).await,
987 Err(Error::StaleRead)
988 ));
989 assert!(matches!(
990 loser.get(loser.size(), &db).await,
991 Err(Error::StaleRead)
992 ));
993 assert!(matches!(
994 child
995 .get_many(&[Location::new(1), Location::new(0)], &db)
996 .await,
997 Err(Error::StaleRead)
998 ));
999 assert!(matches!(
1000 child.merkleize(&db, None, floor).await,
1001 Err(Error::StaleBatch)
1002 ));
1003 assert!(matches!(db.validate_batch(&loser), Err(Error::StaleBatch)));
1004 db.destroy().await.unwrap();
1005 }
1006
1007 #[boxed]
1010 pub(crate) async fn run_descendant_apply_makes_parent_reads_stale<
1011 F: Family,
1012 V,
1013 C,
1014 S: Strategy,
1015 >(
1016 db: TestKeyless<F, V, C, Sha256, S>,
1017 ) where
1018 V: ValueEncoding<Value: TestValue>,
1019 C: Mutable<Item = Operation<F, V>>,
1020 Operation<F, V>: EncodeShared + std::fmt::Debug,
1021 {
1022 let floor = db.inactivity_floor_loc();
1023 let parent = db
1024 .new_batch()
1025 .append(V::Value::make(1))
1026 .merkleize(&db, None, floor)
1027 .await
1028 .unwrap();
1029 let loc = Location::new(1);
1030 assert_eq!(parent.get(loc, &db).await.unwrap(), Some(V::Value::make(1)));
1031 let child = parent
1032 .new_batch::<Sha256>()
1033 .append(V::Value::make(2))
1034 .merkleize(&db, None, floor)
1035 .await
1036 .unwrap();
1037
1038 let db = db.apply_batch(child.clone()).await.unwrap();
1039 assert_eq!(child.get(loc, &db).await.unwrap(), Some(V::Value::make(1)));
1040 assert!(matches!(parent.get(loc, &db).await, Err(Error::StaleRead)));
1041 assert!(matches!(
1042 parent.get_many(&[loc], &db).await,
1043 Err(Error::StaleRead)
1044 ));
1045 db.destroy().await.unwrap();
1046 }
1047
1048 #[boxed]
1049 pub(crate) async fn run_merkleize_foreign_db<F: Family, V, C, S: Strategy>(
1050 db: TestKeyless<F, V, C, Sha256, S>,
1051 foreign: TestKeyless<F, V, C, Sha256, S>,
1052 ) where
1053 V: ValueEncoding<Value: TestValue>,
1054 C: Mutable<Item = Operation<F, V>>,
1055 Operation<F, V>: EncodeShared,
1056 {
1057 let batch = db
1058 .new_batch()
1059 .append(V::Value::make(11))
1060 .merkleize(&db, None, Location::new(0))
1061 .await
1062 .unwrap();
1063 let db = db.apply_batch(batch).await.unwrap();
1064 let batch = foreign
1065 .new_batch()
1066 .append(V::Value::make(99))
1067 .merkleize(&foreign, None, Location::new(0))
1068 .await
1069 .unwrap();
1070 let foreign = foreign.apply_batch(batch).await.unwrap();
1071 assert_eq!(db.bounds().end, foreign.bounds().end);
1072 assert_ne!(db.root(), foreign.root());
1073
1074 let batch = db.new_batch().append(V::Value::make(22));
1075 assert!(matches!(
1076 batch.merkleize(&foreign, None, Location::new(0)).await,
1077 Err(Error::StaleBatch)
1078 ));
1079
1080 let parent = db
1082 .new_batch()
1083 .append(V::Value::make(33))
1084 .merkleize(&db, None, Location::new(0))
1085 .await
1086 .unwrap();
1087 let child = parent.new_batch::<Sha256>().append(V::Value::make(44));
1088 assert!(matches!(
1089 child.merkleize(&foreign, None, Location::new(0)).await,
1090 Err(Error::StaleBatch)
1091 ));
1092 db.destroy().await.unwrap();
1093 foreign.destroy().await.unwrap();
1094 }
1095
1096 #[boxed]
1097 pub(crate) async fn run_merkleize_stale_sibling<F: Family, V, C, S: Strategy>(
1098 db: TestKeyless<F, V, C, Sha256, S>,
1099 ) where
1100 V: ValueEncoding<Value: TestValue>,
1101 C: Mutable<Item = Operation<F, V>>,
1102 Operation<F, V>: EncodeShared,
1103 {
1104 let direct = db.new_batch().append(V::Value::make(11));
1105 let sibling = db
1106 .new_batch()
1107 .append(V::Value::make(22))
1108 .merkleize(&db, None, Location::new(0))
1109 .await
1110 .unwrap();
1111 let db = db.apply_batch(sibling).await.unwrap();
1112 let direct_result = direct.merkleize(&db, None, Location::new(0)).await;
1113
1114 let parent = db
1115 .new_batch()
1116 .append(V::Value::make(33))
1117 .merkleize(&db, None, Location::new(0))
1118 .await
1119 .unwrap();
1120 let child = parent.new_batch::<Sha256>().append(V::Value::make(44));
1121 let sibling = db
1122 .new_batch()
1123 .append(V::Value::make(55))
1124 .merkleize(&db, None, Location::new(0))
1125 .await
1126 .unwrap();
1127 let db = db.apply_batch(sibling).await.unwrap();
1128 let child_result = child.merkleize(&db, None, Location::new(0)).await;
1129
1130 db.destroy().await.unwrap();
1131 assert!(matches!(direct_result, Err(Error::StaleBatch)));
1132 assert!(matches!(child_result, Err(Error::StaleBatch)));
1133 }
1134
1135 #[boxed]
1136 pub(crate) async fn run_merkleize_ancestor_states<F: Family, V, C, S: Strategy>(
1137 db: TestKeyless<F, V, C, Sha256, S>,
1138 ) where
1139 V: ValueEncoding<Value: TestValue>,
1140 C: Mutable<Item = Operation<F, V>>,
1141 Operation<F, V>: EncodeShared,
1142 {
1143 let grandparent = db
1144 .new_batch()
1145 .append(V::Value::make(1))
1146 .merkleize(&db, None, Location::new(0))
1147 .await
1148 .unwrap();
1149 let parent = grandparent
1150 .new_batch::<Sha256>()
1151 .append(V::Value::make(2))
1152 .merkleize(&db, None, Location::new(0))
1153 .await
1154 .unwrap();
1155 let pending = parent
1156 .new_batch::<Sha256>()
1157 .append(V::Value::make(3))
1158 .merkleize(&db, None, Location::new(0))
1159 .await
1160 .unwrap();
1161
1162 let db = db.apply_batch(Arc::clone(&grandparent)).await.unwrap();
1164 let applied = parent
1165 .new_batch::<Sha256>()
1166 .append(V::Value::make(3))
1167 .merkleize(&db, None, Location::new(0))
1168 .await
1169 .unwrap();
1170 assert_eq!(pending.root(), applied.root());
1171
1172 drop(grandparent);
1174 let retired = parent
1175 .new_batch::<Sha256>()
1176 .append(V::Value::make(3))
1177 .merkleize(&db, None, Location::new(0))
1178 .await
1179 .unwrap();
1180 assert_eq!(retired.bounds().db, db.commitment());
1181 assert_eq!(pending.root(), retired.root());
1182
1183 let child = parent.new_batch::<Sha256>().append(V::Value::make(3));
1186 let db = db.apply_batch(parent).await.unwrap();
1187 let child = child.merkleize(&db, None, Location::new(0)).await.unwrap();
1188 assert_eq!(pending.root(), child.root());
1189 let db = db.apply_batch(child).await.unwrap();
1190 let (proof, ops) = db.proof(Location::new(0), NZU64!(100)).await.unwrap();
1191 assert!(verify_proof::<Sha256, _, _>(
1192 &proof,
1193 Location::new(0),
1194 &ops,
1195 &db.root()
1196 ));
1197 db.destroy().await.unwrap();
1198 }
1199
1200 #[boxed]
1201 pub(crate) async fn run_empty<F: Family, V, C, H, S: Strategy>(
1202 context: deterministic::Context,
1203 db: TestKeyless<F, V, C, H, S>,
1204 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1205 ) where
1206 V: ValueEncoding<Value: TestValue>,
1207 C: Mutable<Item = Operation<F, V>>,
1208 H: Hasher,
1209 Operation<F, V>: EncodeShared,
1210 {
1211 let bounds = db.bounds();
1212 assert_eq!(bounds.end, 1); assert_eq!(bounds.start, Location::new(0));
1214 assert_eq!(db.get_metadata().await.unwrap(), None);
1215 assert_eq!(db.bounds().end - 1, Location::new(0));
1216
1217 let root = db.root();
1219 {
1220 db.new_batch().append(V::Value::make(1));
1221
1222 }
1224 drop(db);
1225
1226 let db = reopen(context.child("db").with_attribute("index", 2)).await;
1227 assert_eq!(db.root(), root);
1228 assert_eq!(db.bounds().end, 1);
1229 assert_eq!(db.get_metadata().await.unwrap(), None);
1230
1231 let metadata = V::Value::make(99);
1233 let merkleized = db
1234 .new_batch()
1235 .merkleize(&db, Some(metadata.clone()), db.inactivity_floor_loc())
1236 .await
1237 .unwrap();
1238 let db = db.apply_batch(merkleized).await.unwrap();
1239 let db = db.commit().await.unwrap();
1240 assert_eq!(db.bounds().end, 2); assert_eq!(db.get_metadata().await.unwrap(), Some(metadata.clone()));
1242 assert_eq!(
1243 db.get(Location::new(1)).await.unwrap(),
1244 Some(metadata.clone())
1245 ); let root = db.root();
1247
1248 drop(db);
1250 let db = reopen(context.child("db").with_attribute("index", 3)).await;
1251 assert_eq!(db.bounds().end, 2); assert_eq!(db.get_metadata().await.unwrap(), Some(metadata));
1253 assert_eq!(db.root(), root);
1254 assert_eq!(db.bounds().end - 1, Location::new(1));
1255
1256 db.destroy().await.unwrap();
1257 }
1258
1259 #[boxed]
1263 pub(crate) async fn run_operations_match_applied_log<F: Family, V, C, H, S: Strategy>(
1264 db: TestKeyless<F, V, C, H, S>,
1265 ) where
1266 V: ValueEncoding<Value: TestValue>,
1267 C: Mutable<Item = Operation<F, V>>,
1268 H: Hasher,
1269 Operation<F, V>: EncodeShared + PartialEq + core::fmt::Debug,
1270 {
1271 let seed = db
1272 .new_batch()
1273 .append(V::Value::make(1))
1274 .append(V::Value::make(2))
1275 .merkleize(&db, None, db.inactivity_floor_loc())
1276 .await
1277 .unwrap();
1278 let (seed_start, seed_ops) = seed.operations();
1279 let seed_root = seed.root();
1280 let seed_proof = seed.proof(&db).unwrap();
1281 let seed_pins = seed.pinned_nodes(&db).unwrap();
1282 let applied_start = db.bounds().end;
1283 let db = db.apply_batch(seed).await.unwrap();
1284 let seed_range = applied_start..db.bounds().end;
1285 assert_eq!(seed_start, seed_range.start);
1286 assert_eq!(*seed_start + seed_ops.len() as u64, *seed_range.end);
1287
1288 let parent = db
1290 .new_batch()
1291 .append(V::Value::make(3))
1292 .merkleize(&db, None, db.inactivity_floor_loc())
1293 .await
1294 .unwrap();
1295 let child = parent
1296 .new_batch::<H>()
1297 .append(V::Value::make(4))
1298 .merkleize(&db, None, db.inactivity_floor_loc())
1299 .await
1300 .unwrap();
1301 let (parent_start, parent_ops) = parent.operations();
1302 let (child_start, child_ops) = child.operations();
1303 let (parent_root, child_root) = (parent.root(), child.root());
1304 let (parent_pins, child_pins) = (
1305 parent.pinned_nodes(&db).unwrap(),
1306 child.pinned_nodes(&db).unwrap(),
1307 );
1308 let (parent_proof, child_proof) = (parent.proof(&db).unwrap(), child.proof(&db).unwrap());
1309 let applied_start = db.bounds().end;
1310 let db = db.apply_batch(parent).await.unwrap();
1311 let parent_range = applied_start..db.bounds().end;
1312
1313 assert_eq!(child.proof(&db).unwrap(), child_proof);
1316 assert_eq!(child.pinned_nodes(&db).unwrap(), child_pins);
1317 let applied_start = db.bounds().end;
1318 let db = db.apply_batch(child).await.unwrap();
1319 let child_range = applied_start..db.bounds().end;
1320 assert_eq!(parent_start, parent_range.start);
1321 assert_eq!(*parent_start + parent_ops.len() as u64, *parent_range.end);
1322 assert_eq!(child_start, child_range.start);
1323 assert_eq!(*child_start + child_ops.len() as u64, *child_range.end);
1324
1325 let empty = db
1327 .new_batch()
1328 .merkleize(&db, None, db.inactivity_floor_loc())
1329 .await
1330 .unwrap();
1331 let (empty_start, empty_ops) = empty.operations();
1332 let (empty_root, empty_proof) = (empty.root(), empty.proof(&db).unwrap());
1333 let empty_pins = empty.pinned_nodes(&db).unwrap();
1334 let applied_start = db.bounds().end;
1335 let db = db.apply_batch(empty).await.unwrap();
1336 let empty_range = applied_start..db.bounds().end;
1337 assert_eq!(empty_start, empty_range.start);
1338 assert_eq!(*empty_start + empty_ops.len() as u64, *empty_range.end);
1339
1340 for (start, ops, proof, pins, root) in [
1343 (seed_start, seed_ops, seed_proof, seed_pins, seed_root),
1344 (
1345 parent_start,
1346 parent_ops,
1347 parent_proof,
1348 parent_pins,
1349 parent_root,
1350 ),
1351 (child_start, child_ops, child_proof, child_pins, child_root),
1352 (empty_start, empty_ops, empty_proof, empty_pins, empty_root),
1353 ] {
1354 let len = core::num::NonZeroU64::new(ops.len() as u64).unwrap();
1355 let end = Location::new(*start + ops.len() as u64);
1356 let (log_proof, log_ops) = db.historical_proof(end, start, len).await.unwrap();
1357 assert_eq!(log_ops, *ops);
1358 assert_eq!(log_proof, proof);
1359 assert!(verify_proof::<H, _, _>(&proof, start, &ops, &root));
1360 assert!(verify_proof_and_pinned_nodes::<H, _, _>(
1361 &proof, start, &ops, &pins, &root
1362 ));
1363 }
1364
1365 let late = db
1368 .new_batch()
1369 .append(V::Value::make(5))
1370 .merkleize(&db, None, db.inactivity_floor_loc())
1371 .await
1372 .unwrap();
1373 let db = db.apply_batch(Arc::clone(&late)).await.unwrap();
1374 let db = db.commit().await.unwrap();
1375 assert!(matches!(
1376 late.proof(&db),
1377 Err(crate::qmdb::Error::Merkle(
1378 crate::merkle::Error::ElementPruned(_)
1379 ))
1380 ));
1381 assert!(matches!(
1382 late.pinned_nodes(&db),
1383 Err(crate::qmdb::Error::Merkle(
1384 crate::merkle::Error::ElementPruned(_)
1385 ))
1386 ));
1387
1388 let flushed = db
1390 .new_batch()
1391 .append(V::Value::make(6))
1392 .merkleize(&db, None, db.inactivity_floor_loc())
1393 .await
1394 .unwrap();
1395 let (flushed_start, flushed_ops) = flushed.operations();
1396 let flushed_root = flushed.root();
1397 let flushed_proof = flushed.proof(&db).unwrap();
1398 let flushed_pins = flushed.pinned_nodes(&db).unwrap();
1399 assert!(verify_proof_and_pinned_nodes::<H, _, _>(
1400 &flushed_proof,
1401 flushed_start,
1402 &flushed_ops,
1403 &flushed_pins,
1404 &flushed_root
1405 ));
1406 let applied_start = db.bounds().end;
1407 let db = db.apply_batch(flushed).await.unwrap();
1408 let flushed_range = applied_start..db.bounds().end;
1409 assert_eq!(flushed_start, flushed_range.start);
1410 assert_eq!(
1411 *flushed_start + flushed_ops.len() as u64,
1412 *flushed_range.end
1413 );
1414
1415 db.destroy().await.unwrap();
1416 }
1417
1418 #[boxed]
1419 pub(crate) async fn run_commit_after_sync_recovery<F: Family, V, C, H, S: Strategy>(
1420 context: deterministic::Context,
1421 db: TestKeyless<F, V, C, H, S>,
1422 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1423 ) where
1424 V: ValueEncoding<Value: TestValue>,
1425 C: Mutable<Item = Operation<F, V>>,
1426 H: Hasher,
1427 Operation<F, V>: EncodeShared,
1428 {
1429 let value0 = V::Value::make(10);
1430 let value1 = V::Value::make(20);
1431
1432 let first_loc = Location::new(1);
1434 let merkleized = db
1435 .new_batch()
1436 .append(value0.clone())
1437 .merkleize(&db, None, db.inactivity_floor_loc())
1438 .await
1439 .unwrap();
1440 let db = db.apply_batch(merkleized).await.unwrap();
1441 let db = db.commit().await.unwrap();
1442 let db = db.sync().await.unwrap();
1443
1444 let second_loc = db.bounds().end;
1446 let merkleized = db
1447 .new_batch()
1448 .append(value1.clone())
1449 .merkleize(&db, None, db.inactivity_floor_loc())
1450 .await
1451 .unwrap();
1452 let db = db.apply_batch(merkleized).await.unwrap();
1453 let db = db.commit().await.unwrap();
1454 let committed_bounds = db.bounds();
1455 let committed_root = db.root();
1456 drop(db);
1457
1458 let db = reopen(context.child("db").with_attribute("index", 2)).await;
1459 assert_eq!(db.bounds(), committed_bounds);
1460 assert_eq!(db.root(), committed_root);
1461 assert_eq!(db.get(first_loc).await.unwrap(), Some(value0));
1462 assert_eq!(db.get(second_loc).await.unwrap(), Some(value1));
1463
1464 db.destroy().await.unwrap();
1465 }
1466
1467 #[boxed]
1468 pub(crate) async fn run_build_basic<F: Family, V, C, H, S: Strategy>(
1469 context: deterministic::Context,
1470 mut db: TestKeyless<F, V, C, H, S>,
1471 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1472 ) where
1473 V: ValueEncoding<Value: TestValue>,
1474 C: Mutable<Item = Operation<F, V>>,
1475 H: Hasher,
1476 Operation<F, V>: EncodeShared,
1477 {
1478 let v1 = V::Value::make(1);
1480 let v2 = V::Value::make(2);
1481
1482 {
1483 let batch = db.new_batch();
1484 let loc1 = batch.size();
1485 let batch = batch.append(v1.clone());
1486 let loc2 = batch.size();
1487 let batch = batch.append(v2.clone());
1488 assert_eq!(loc1, Location::new(1));
1489 assert_eq!(loc2, Location::new(2));
1490 let merkleized = batch
1491 .merkleize(&db, None, db.inactivity_floor_loc())
1492 .await
1493 .unwrap();
1494 db = db.apply_batch(merkleized).await.unwrap();
1495 }
1496
1497 assert_eq!(db.bounds().end, 4); assert_eq!(db.get_metadata().await.unwrap(), None);
1500 assert_eq!(db.get(Location::new(3)).await.unwrap(), None); let root = db.root();
1502 db.sync().await.unwrap();
1503
1504 let db = reopen(context.child("db").with_attribute("index", 2)).await;
1505 assert_eq!(db.bounds().end, 4);
1506 assert_eq!(db.root(), root);
1507 assert_eq!(db.get(Location::new(1)).await.unwrap().unwrap(), v1);
1508 assert_eq!(db.get(Location::new(2)).await.unwrap().unwrap(), v2);
1509
1510 drop(db);
1512 let db = reopen(context.child("db").with_attribute("index", 3)).await;
1513 assert_eq!(db.bounds().end, 4);
1514 assert_eq!(db.root(), root);
1515
1516 db.destroy().await.unwrap();
1517 }
1518
1519 #[boxed]
1520 pub(crate) async fn run_recovery<F: Family, V, C, H, S: Strategy>(
1521 context: deterministic::Context,
1522 db: TestKeyless<F, V, C, H, S>,
1523 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1524 ) where
1525 V: ValueEncoding<Value: TestValue>,
1526 C: Mutable<Item = Operation<F, V>>,
1527 H: Hasher,
1528 Operation<F, V>: EncodeShared,
1529 {
1530 let root = db.root();
1531 const ELEMENTS: u64 = 100;
1532
1533 {
1535 let mut batch = db.new_batch();
1536 for i in 0..ELEMENTS {
1537 batch = batch.append(V::Value::make(i));
1538 }
1539
1540 }
1542 drop(db);
1543 let mut db = reopen(context.child("db").with_attribute("index", 2)).await;
1545 assert_eq!(root, db.root());
1546
1547 {
1549 let mut batch = db.new_batch();
1550 for i in 0..ELEMENTS {
1551 batch = batch.append(V::Value::make(i + 100));
1552 }
1553 let merkleized = batch
1554 .merkleize(&db, None, db.inactivity_floor_loc())
1555 .await
1556 .unwrap();
1557 db = db.apply_batch(merkleized).await.unwrap();
1558 }
1559 let db = db.commit().await.unwrap();
1560 let root = db.root();
1561
1562 {
1564 let mut batch = db.new_batch();
1565 for i in 0..ELEMENTS {
1566 batch = batch.append(V::Value::make(i + 200));
1567 }
1568
1569 }
1571 drop(db);
1572 let mut db = reopen(context.child("db").with_attribute("index", 3)).await;
1574 assert_eq!(root, db.root());
1575
1576 {
1578 let mut batch = db.new_batch();
1579 for i in 0..ELEMENTS {
1580 batch = batch.append(V::Value::make(i + 300));
1581 }
1582 let merkleized = batch
1583 .merkleize(&db, None, db.inactivity_floor_loc())
1584 .await
1585 .unwrap();
1586 db = db.apply_batch(merkleized).await.unwrap();
1587 }
1588 let db = db.commit().await.unwrap();
1589 let root = db.root();
1590
1591 drop(db);
1593 let db = reopen(context.child("db").with_attribute("index", 4)).await;
1594 assert_eq!(db.bounds().end, 2 * ELEMENTS + 3);
1595 assert_eq!(db.root(), root);
1596
1597 db.destroy().await.unwrap();
1598 }
1599
1600 #[boxed]
1601 pub(crate) async fn run_proof<F: Family, V, C, S: Strategy>(
1602 mut db: TestKeyless<F, V, C, Sha256, S>,
1603 ) where
1604 V: ValueEncoding<Value: TestValue>,
1605 C: Mutable<Item = Operation<F, V>>,
1606 Operation<F, V>: EncodeShared + std::fmt::Debug,
1607 {
1608 const ELEMENTS: u64 = 50;
1609
1610 {
1611 let mut batch = db.new_batch();
1612 for i in 0..ELEMENTS {
1613 batch = batch.append(V::Value::make(i));
1614 }
1615 let merkleized = batch
1616 .merkleize(&db, None, db.inactivity_floor_loc())
1617 .await
1618 .unwrap();
1619 db = db.apply_batch(merkleized).await.unwrap();
1620 }
1621 let root = db.root();
1622
1623 let (proof, ops) = db.proof(Location::new(0), NZU64!(100)).await.unwrap();
1624 assert!(verify_proof::<Sha256, _, _>(
1625 &proof,
1626 Location::new(0),
1627 &ops,
1628 &root,
1629 ));
1630 assert_eq!(ops.len() as u64, 1 + ELEMENTS + 1);
1631
1632 let (proof, ops) = db.proof(Location::new(10), NZU64!(5)).await.unwrap();
1633 assert!(verify_proof::<Sha256, _, _>(
1634 &proof,
1635 Location::new(10),
1636 &ops,
1637 &root,
1638 ));
1639 assert_eq!(ops.len(), 5);
1640
1641 db.destroy().await.unwrap();
1642 }
1643
1644 #[boxed]
1645 pub(crate) async fn run_metadata<F: Family, V, C, H, S: Strategy>(
1646 db: TestKeyless<F, V, C, H, S>,
1647 ) where
1648 V: ValueEncoding<Value: TestValue>,
1649 C: Mutable<Item = Operation<F, V>>,
1650 H: Hasher,
1651 Operation<F, V>: EncodeShared,
1652 {
1653 let metadata = V::Value::make(99);
1654 let merkleized = db
1655 .new_batch()
1656 .append(V::Value::make(1))
1657 .merkleize(&db, Some(metadata.clone()), db.inactivity_floor_loc())
1658 .await
1659 .unwrap();
1660 let db = db.apply_batch(merkleized).await.unwrap();
1661 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata));
1662
1663 let merkleized = db
1664 .new_batch()
1665 .merkleize(&db, None, db.inactivity_floor_loc())
1666 .await
1667 .unwrap();
1668 let db = db.apply_batch(merkleized).await.unwrap();
1669 assert_eq!(db.get_metadata().await.unwrap(), None);
1670
1671 db.destroy().await.unwrap();
1672 }
1673
1674 #[boxed]
1675 pub(crate) async fn run_pruning<F: Family, V, C, H, S: Strategy>(
1676 context: deterministic::Context,
1677 db: TestKeyless<F, V, C, H, S>,
1678 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1679 ) where
1680 V: ValueEncoding<Value: TestValue>,
1681 C: Mutable<Item = Operation<F, V>>,
1682 H: Hasher,
1683 Operation<F, V>: EncodeShared,
1684 {
1685 assert_eq!(db.inactivity_floor_loc(), Location::new(0));
1687 let result = db.prune(Location::new(1)).await;
1688 assert!(
1689 matches!(result, Err(Error::PruneBeyondMinRequired(prune_loc, floor))
1690 if prune_loc == Location::new(1) && floor == Location::new(0))
1691 );
1692
1693 let db = reopen(context.child("reopen_empty")).await;
1694
1695 let first_commit_loc = Location::<F>::new(3);
1697 let merkleized = db
1698 .new_batch()
1699 .append(V::Value::make(1))
1700 .append(V::Value::make(2))
1701 .merkleize(&db, None, first_commit_loc)
1702 .await
1703 .unwrap();
1704 let db = db.apply_batch(merkleized).await.unwrap();
1705 assert_eq!(db.bounds().end - 1, first_commit_loc);
1706 assert_eq!(db.inactivity_floor_loc(), first_commit_loc);
1707
1708 let second_commit_loc = Location::<F>::new(5);
1710 let merkleized = db
1711 .new_batch()
1712 .append(V::Value::make(3))
1713 .merkleize(&db, None, second_commit_loc)
1714 .await
1715 .unwrap();
1716 let db = db.apply_batch(merkleized).await.unwrap();
1717
1718 let root = db.root();
1720 let db = db.prune(first_commit_loc).await.unwrap();
1721 assert_eq!(db.root(), root);
1722
1723 let new_floor = db.inactivity_floor_loc();
1725 let beyond = new_floor + 1;
1726 let result = db.prune(beyond).await;
1727 assert!(
1728 matches!(result, Err(Error::PruneBeyondMinRequired(prune_loc, floor))
1729 if prune_loc == beyond && floor == new_floor)
1730 );
1731 }
1732
1733 #[boxed]
1734 pub(crate) async fn run_empty_db_recovery<F: Family, V, C, H, S: Strategy>(
1735 context: deterministic::Context,
1736 db: TestKeyless<F, V, C, H, S>,
1737 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1738 ) where
1739 V: ValueEncoding<Value: TestValue>,
1740 C: Mutable<Item = Operation<F, V>>,
1741 H: Hasher,
1742 Operation<F, V>: EncodeShared,
1743 {
1744 let root = db.root();
1745 const ELEMENTS: u64 = 200;
1746
1747 drop(db);
1749 let db = reopen(context.child("db").with_attribute("index", 2)).await;
1750 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
1752
1753 {
1755 let mut batch = db.new_batch();
1756 for i in 0..ELEMENTS {
1757 batch = batch.append(V::Value::make(i));
1758 }
1759
1760 }
1762 drop(db);
1763 let db = reopen(context.child("db").with_attribute("index", 3)).await;
1764 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
1766
1767 {
1769 let mut batch = db.new_batch();
1770 for i in 0..ELEMENTS {
1771 batch = batch.append(V::Value::make(i + 500));
1772 }
1773
1774 }
1776 drop(db);
1777 let db = reopen(context.child("db").with_attribute("index", 4)).await;
1778 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
1780
1781 {
1783 let mut batch = db.new_batch();
1784 for i in 0..ELEMENTS * 3 {
1785 batch = batch.append(V::Value::make(i + 1000));
1786 }
1787
1788 }
1790 drop(db);
1791 let mut db = reopen(context.child("db").with_attribute("index", 5)).await;
1792 assert_eq!(db.bounds().end, 1); assert_eq!(db.root(), root);
1794 assert_eq!(db.bounds().end - 1, Location::new(0));
1795
1796 {
1798 let mut batch = db.new_batch();
1799 for i in 0..ELEMENTS {
1800 batch = batch.append(V::Value::make(i + 2000));
1801 }
1802 let merkleized = batch
1803 .merkleize(&db, None, db.inactivity_floor_loc())
1804 .await
1805 .unwrap();
1806 db = db.apply_batch(merkleized).await.unwrap();
1807 }
1808 db.commit().await.unwrap();
1809 let db = reopen(context.child("db").with_attribute("index", 6)).await;
1810 assert!(db.bounds().end > 1);
1811 assert_ne!(db.root(), root);
1812
1813 db.destroy().await.unwrap();
1814 }
1815
1816 #[boxed]
1817 pub(crate) async fn run_replay_with_trailing_appends<F: Family, V, C, H, S: Strategy>(
1818 context: deterministic::Context,
1819 mut db: TestKeyless<F, V, C, H, S>,
1820 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
1821 ) where
1822 V: ValueEncoding<Value: TestValue>,
1823 C: Mutable<Item = Operation<F, V>>,
1824 H: Hasher,
1825 Operation<F, V>: EncodeShared,
1826 {
1827 {
1829 let mut batch = db.new_batch();
1830 for i in 0..10u64 {
1831 batch = batch.append(V::Value::make(i));
1832 }
1833 let merkleized = batch
1834 .merkleize(&db, None, db.inactivity_floor_loc())
1835 .await
1836 .unwrap();
1837 db = db.apply_batch(merkleized).await.unwrap();
1838 }
1839 let db = db.commit().await.unwrap();
1840 let committed_root = db.root();
1841 let committed_size = db.bounds().end;
1842
1843 {
1845 db.new_batch().append(V::Value::make(99));
1846
1847 }
1849 drop(db);
1850
1851 let mut db = reopen(context.child("db").with_attribute("index", 2)).await;
1853 assert_eq!(
1854 db.bounds().end,
1855 committed_size,
1856 "Should recover to last commit"
1857 );
1858 assert_eq!(db.root(), committed_root, "Root should match last commit");
1859 assert_eq!(
1860 db.bounds().end - 1,
1861 committed_size - 1,
1862 "Last commit location should be correct"
1863 );
1864
1865 let new_value = V::Value::make(77);
1867 {
1868 let batch = db.new_batch();
1869 let loc = batch.size();
1870 let batch = batch.append(new_value.clone());
1871 assert_eq!(
1872 loc, committed_size,
1873 "New append should get the expected location"
1874 );
1875 let merkleized = batch
1876 .merkleize(&db, None, db.inactivity_floor_loc())
1877 .await
1878 .unwrap();
1879 db = db.apply_batch(merkleized).await.unwrap();
1880 }
1881 let db = db.commit().await.unwrap();
1882
1883 assert_eq!(db.get(committed_size).await.unwrap(), Some(new_value));
1884
1885 let new_committed_root = db.root();
1886 let new_committed_size = db.bounds().end;
1887
1888 {
1890 let mut batch = db.new_batch();
1891 for i in 0..5u64 {
1892 batch = batch.append(V::Value::make(200 + i));
1893 }
1894
1895 }
1897 drop(db);
1898
1899 let db = reopen(context.child("db").with_attribute("index", 3)).await;
1901 assert_eq!(
1902 db.bounds().end,
1903 new_committed_size,
1904 "Should recover to last commit with multiple trailing appends"
1905 );
1906 assert_eq!(
1907 db.root(),
1908 new_committed_root,
1909 "Root should match last commit after multiple appends"
1910 );
1911 assert_eq!(
1912 db.bounds().end - 1,
1913 new_committed_size - 1,
1914 "Last commit location should be correct after multiple appends"
1915 );
1916
1917 db.destroy().await.unwrap();
1918 }
1919
1920 #[boxed]
1924 pub(crate) async fn run_get_many<F: Family, V, C, S: Strategy>(
1925 db: TestKeyless<F, V, C, Sha256, S>,
1926 ) where
1927 V: ValueEncoding<Value: TestValue>,
1928 C: Mutable<Item = Operation<F, V>>,
1929 Operation<F, V>: EncodeShared,
1930 {
1931 let v1 = V::Value::make(1);
1932 let v2 = V::Value::make(2);
1933 let v3 = V::Value::make(3);
1934
1935 let batch = db.new_batch();
1937 let loc1 = batch.size();
1938 let batch = batch.append(v1.clone());
1939 let loc2 = batch.size();
1940 let batch = batch.append(v2.clone());
1941 let merkleized = batch
1942 .merkleize(&db, None, db.inactivity_floor_loc())
1943 .await
1944 .unwrap();
1945 let db = db.apply_batch(merkleized).await.unwrap();
1946 let db = db.commit().await.unwrap();
1947
1948 let results = db.get_many(&[loc1, loc2]).await.unwrap();
1950 assert_eq!(results, vec![Some(v1.clone()), Some(v2.clone())]);
1951
1952 let results = db.get_many(&[]).await.unwrap();
1954 assert!(results.is_empty());
1955
1956 assert!(matches!(
1958 db.get_many(&[loc2, loc1]).await,
1959 Err(Error::LocationOutOfOrder(first, second)) if first == loc2 && second == loc1
1960 ));
1961 assert!(matches!(
1962 db.get_many(&[loc1, loc1]).await,
1963 Err(Error::LocationOutOfOrder(..))
1964 ));
1965
1966 let end = db.bounds().end;
1968 assert!(matches!(
1969 db.get_many(&[loc1, end]).await,
1970 Err(Error::LocationOutOfBounds(loc, bound)) if loc == end && bound == end
1971 ));
1972
1973 let batch = db.new_batch();
1975 let loc3 = batch.size();
1976 let batch = batch.append(v3.clone());
1977 let results = batch.get_many(&[loc1, loc3], &db).await.unwrap();
1978 assert_eq!(results, vec![Some(v1.clone()), Some(v3.clone())]);
1979 assert!(matches!(
1980 batch.get_many(&[loc3, loc1], &db).await,
1981 Err(Error::LocationOutOfOrder(..))
1982 ));
1983 let end = batch.size();
1984 assert!(matches!(
1985 batch.get_many(&[loc1, end], &db).await,
1986 Err(Error::LocationOutOfBounds(loc, bound)) if loc == end && bound == end
1987 ));
1988
1989 let parent = db.new_batch();
1991 let parent_loc = parent.size();
1992 let parent = parent
1993 .append(v3.clone())
1994 .merkleize(&db, None, db.inactivity_floor_loc())
1995 .await
1996 .unwrap();
1997 let results = parent.get_many(&[loc1, parent_loc], &db).await.unwrap();
1998 assert_eq!(results, vec![Some(v1.clone()), Some(v3.clone())]);
1999 assert!(matches!(
2000 parent.get_many(&[parent_loc, loc1], &db).await,
2001 Err(Error::LocationOutOfOrder(..))
2002 ));
2003 let end = parent.size();
2004 assert!(matches!(
2005 parent.get_many(&[loc1, end], &db).await,
2006 Err(Error::LocationOutOfBounds(loc, bound)) if loc == end && bound == end
2007 ));
2008 let child = parent.new_batch::<Sha256>().append(V::Value::make(4));
2009 let results = child.get_many(&[loc1, loc2], &db).await.unwrap();
2010 assert_eq!(results, vec![Some(v1.clone()), Some(v2.clone())]);
2011 assert!(matches!(
2012 child.get_many(&[loc2, loc1], &db).await,
2013 Err(Error::LocationOutOfOrder(..))
2014 ));
2015
2016 db.destroy().await.unwrap();
2017 }
2018
2019 #[boxed]
2020 pub(crate) async fn run_batch_chained<F: Family, V, C, S: Strategy>(
2021 db: TestKeyless<F, V, C, Sha256, S>,
2022 ) where
2023 V: ValueEncoding<Value: TestValue>,
2024 C: Mutable<Item = Operation<F, V>>,
2025 Operation<F, V>: EncodeShared,
2026 {
2027 let v1 = V::Value::make(10);
2028 let v2 = V::Value::make(20);
2029 let v3 = V::Value::make(30);
2030
2031 let parent = db.new_batch();
2032 let loc1 = parent.size();
2033 let parent = parent.append(v1.clone());
2034 let parent_m = parent
2035 .merkleize(&db, None, db.inactivity_floor_loc())
2036 .await
2037 .unwrap();
2038
2039 let child = parent_m.new_batch::<Sha256>();
2040 let loc2 = child.size();
2041 let child = child.append(v2.clone());
2042 let loc3 = child.size();
2043 let child = child.append(v3.clone());
2044 let child_m = child
2045 .merkleize(&db, None, db.inactivity_floor_loc())
2046 .await
2047 .unwrap();
2048 let child_root = child_m.root();
2049
2050 let db = db.apply_batch(child_m).await.unwrap();
2051 let db = db.commit().await.unwrap();
2052
2053 assert_eq!(db.root(), child_root);
2054 assert_eq!(db.get(loc1).await.unwrap(), Some(v1));
2055 assert_eq!(db.get(loc2).await.unwrap(), Some(v2));
2056 assert_eq!(db.get(loc3).await.unwrap(), Some(v3));
2057
2058 db.destroy().await.unwrap();
2059 }
2060
2061 #[boxed]
2062 pub(crate) async fn run_stale_batch<F: Family, V, C, H, S: Strategy>(
2063 context: deterministic::Context,
2064 db: TestKeyless<F, V, C, H, S>,
2065 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
2066 ) where
2067 V: ValueEncoding<Value: TestValue>,
2068 C: Mutable<Item = Operation<F, V>>,
2069 H: Hasher,
2070 Operation<F, V>: EncodeShared,
2071 {
2072 let batch_a = db
2073 .new_batch()
2074 .append(V::Value::make(10))
2075 .merkleize(&db, None, db.inactivity_floor_loc())
2076 .await
2077 .unwrap();
2078 let batch_b = db
2079 .new_batch()
2080 .append(V::Value::make(20))
2081 .merkleize(&db, None, db.inactivity_floor_loc())
2082 .await
2083 .unwrap();
2084
2085 let db = db.apply_batch(batch_a).await.unwrap();
2086 let db = db.commit().await.unwrap();
2087 let root = db.root();
2088 let last_commit_loc = db.bounds().end - 1;
2089
2090 let result = db.apply_batch(batch_b).await;
2091 assert!(matches!(result, Err(Error::StaleBatch)));
2092
2093 let db = reopen(context.child("reopen")).await;
2095 assert_eq!(db.root(), root);
2096 assert_eq!(db.bounds().end - 1, last_commit_loc);
2097 db.destroy().await.unwrap();
2098 }
2099
2100 #[boxed]
2101 pub(crate) async fn run_partial_ancestor_commit<F: Family, V, C, H, S: Strategy>(
2102 db: TestKeyless<F, V, C, H, S>,
2103 ) where
2104 V: ValueEncoding<Value: TestValue>,
2105 C: Mutable<Item = Operation<F, V>>,
2106 H: Hasher,
2107 Operation<F, V>: EncodeShared,
2108 {
2109 let a = db
2111 .new_batch()
2112 .append(V::Value::make(10))
2113 .merkleize(&db, None, db.inactivity_floor_loc())
2114 .await
2115 .unwrap();
2116 let b = a
2117 .new_batch::<H>()
2118 .append(V::Value::make(20))
2119 .merkleize(&db, None, db.inactivity_floor_loc())
2120 .await
2121 .unwrap();
2122 let c = b
2123 .new_batch::<H>()
2124 .append(V::Value::make(30))
2125 .merkleize(&db, None, db.inactivity_floor_loc())
2126 .await
2127 .unwrap();
2128
2129 let expected_root = c.root();
2130
2131 let db = db.apply_batch(a).await.unwrap();
2133 let db = db.apply_batch(c).await.unwrap();
2134
2135 assert_eq!(db.root(), expected_root);
2137
2138 db.destroy().await.unwrap();
2139 }
2140
2141 #[boxed]
2142 pub(crate) async fn run_delayed_merkleize_after_ancestor_apply<
2143 F: Family,
2144 V,
2145 C,
2146 H,
2147 S: Strategy,
2148 >(
2149 db: TestKeyless<F, V, C, H, S>,
2150 ) where
2151 V: ValueEncoding<Value: TestValue>,
2152 C: Mutable<Item = Operation<F, V>>,
2153 H: Hasher,
2154 Operation<F, V>: EncodeShared,
2155 {
2156 let floor = db.inactivity_floor_loc();
2157 let a = db
2158 .new_batch()
2159 .append(V::Value::make(10))
2160 .merkleize(&db, None, floor)
2161 .await
2162 .unwrap();
2163 let b = a
2164 .new_batch::<H>()
2165 .append(V::Value::make(20))
2166 .merkleize(&db, None, floor)
2167 .await
2168 .unwrap();
2169 let c = b.new_batch::<H>().append(V::Value::make(30));
2170
2171 let db = db.apply_batch(a).await.unwrap();
2172 let c = c.merkleize(&db, None, floor).await.unwrap();
2173 let expected_root = c.root();
2174 let db = db.apply_batch(c).await.unwrap();
2175
2176 assert_eq!(db.root(), expected_root);
2177 db.destroy().await.unwrap();
2178 }
2179
2180 #[boxed]
2181 pub(crate) async fn run_to_batch<F: Family, V, C, S: Strategy>(
2182 db: TestKeyless<F, V, C, Sha256, S>,
2183 ) where
2184 V: ValueEncoding<Value: TestValue>,
2185 C: Mutable<Item = Operation<F, V>>,
2186 Operation<F, V>: EncodeShared,
2187 {
2188 let batch = db.new_batch();
2189 let loc1 = batch.size();
2190 let batch = batch.append(V::Value::make(10));
2191 let merkleized = batch
2192 .merkleize(&db, None, db.inactivity_floor_loc())
2193 .await
2194 .unwrap();
2195 let db = db.apply_batch(merkleized).await.unwrap();
2196
2197 let snapshot = db.to_batch();
2198 assert_eq!(snapshot.root(), db.root());
2199
2200 let child_batch = snapshot.new_batch::<Sha256>();
2201 let loc2 = child_batch.size();
2202 let child_batch = child_batch.append(V::Value::make(20));
2203 let merkleized = child_batch
2204 .merkleize(&db, None, db.inactivity_floor_loc())
2205 .await
2206 .unwrap();
2207 let db = db.apply_batch(merkleized).await.unwrap();
2208
2209 assert_eq!(db.get(loc1).await.unwrap(), Some(V::Value::make(10)));
2210 assert_eq!(db.get(loc2).await.unwrap(), Some(V::Value::make(20)));
2211
2212 db.destroy().await.unwrap();
2213 }
2214
2215 #[boxed]
2216 pub(crate) async fn run_non_empty_recovery<F: Family, V, C, H, S: Strategy>(
2217 context: deterministic::Context,
2218 mut db: TestKeyless<F, V, C, H, S>,
2219 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
2220 ) where
2221 V: ValueEncoding<Value: TestValue>,
2222 C: Mutable<Item = Operation<F, V>>,
2223 H: Hasher,
2224 Operation<F, V>: EncodeShared,
2225 {
2226 const ELEMENTS: u64 = 200;
2229 {
2230 let mut batch = db.new_batch();
2231 for i in 0..ELEMENTS {
2232 batch = batch.append(V::Value::make(i));
2233 }
2234 let new_commit = db.bounds().end + ELEMENTS;
2235 let merkleized = batch.merkleize(&db, None, new_commit).await.unwrap();
2236 db = db.apply_batch(merkleized).await.unwrap();
2237 }
2238 let db = db.commit().await.unwrap();
2239 let root = db.root();
2240 let op_count = db.bounds().end;
2241
2242 drop(db);
2244 let db = reopen(context.child("db").with_attribute("index", 2)).await;
2245 assert_eq!(db.bounds().end, op_count);
2246 assert_eq!(db.root(), root);
2247 assert_eq!(db.bounds().end - 1, op_count - 1);
2248 drop(db);
2249
2250 let db = reopen(context.child("recovery_a")).await;
2252 {
2253 let mut batch = db.new_batch();
2254 for i in 0..ELEMENTS {
2255 batch = batch.append(V::Value::make(i + 1000));
2256 }
2257
2258 }
2260 drop(db);
2261 let db = reopen(context.child("recovery_b")).await;
2262 assert_eq!(db.bounds().end, op_count);
2263 assert_eq!(db.root(), root);
2264 drop(db);
2265
2266 let db = reopen(context.child("db").with_attribute("index", 3)).await;
2268 let last_commit = db.bounds().end - 1;
2269 let db = db.prune(last_commit).await.unwrap();
2270 assert_eq!(db.bounds().end, op_count);
2271 assert_eq!(db.root(), root);
2272 db.sync().await.unwrap();
2273
2274 let db = reopen(context.child("recovery_c")).await;
2275 {
2276 let mut batch = db.new_batch();
2277 for i in 0..ELEMENTS {
2278 batch = batch.append(V::Value::make(i + 2000));
2279 }
2280 }
2281 drop(db);
2282 let db = reopen(context.child("recovery_d")).await;
2283 assert_eq!(db.bounds().end, op_count);
2284 assert_eq!(db.root(), root);
2285 drop(db);
2286
2287 let mut db = reopen(context.child("db").with_attribute("index", 4)).await;
2289 {
2290 let mut batch = db.new_batch();
2291 for i in 0..ELEMENTS {
2292 batch = batch.append(V::Value::make(i + 3000));
2293 }
2294 let merkleized = batch
2295 .merkleize(&db, None, db.inactivity_floor_loc())
2296 .await
2297 .unwrap();
2298 db = db.apply_batch(merkleized).await.unwrap();
2299 }
2300 db.commit().await.unwrap();
2301 let db = reopen(context.child("db").with_attribute("index", 5)).await;
2302 let bounds = db.bounds();
2303 assert!(bounds.end > op_count);
2304 assert_ne!(db.root(), root);
2305
2306 db.destroy().await.unwrap();
2307 }
2308
2309 #[boxed]
2310 pub(crate) async fn run_proof_comprehensive<F: Family, V, C, S: Strategy>(
2311 mut db: TestKeyless<F, V, C, Sha256, S>,
2312 ) where
2313 V: ValueEncoding<Value: TestValue>,
2314 C: Mutable<Item = Operation<F, V>>,
2315 Operation<F, V>: EncodeShared + std::fmt::Debug,
2316 {
2317 const ELEMENTS: u64 = 100;
2319 {
2320 let mut batch = db.new_batch();
2321 for i in 0u64..ELEMENTS {
2322 batch = batch.append(V::Value::make(i));
2323 }
2324 let merkleized = batch
2325 .merkleize(&db, None, db.inactivity_floor_loc())
2326 .await
2327 .unwrap();
2328 db = db.apply_batch(merkleized).await.unwrap();
2329 }
2330
2331 assert!(matches!(
2333 db.historical_proof(db.bounds().end + 1, Location::new(5), NZU64!(10))
2334 .await,
2335 Err(Error::<F>::Merkle(crate::merkle::Error::RangeOutOfBounds(
2336 _
2337 )))
2338 ));
2339
2340 let root = db.root();
2341
2342 for (start_loc, max_ops) in [
2343 (0, 10),
2344 (10, 5),
2345 (50, 20),
2346 (90, 15),
2347 (0, 1),
2348 (ELEMENTS - 1, 1),
2349 (ELEMENTS, 1),
2350 ] {
2351 let (proof, ops) = db
2352 .proof(Location::new(start_loc), NZU64!(max_ops))
2353 .await
2354 .unwrap();
2355 assert!(
2356 verify_proof::<Sha256, _, _>(&proof, Location::new(start_loc), &ops, &root,),
2357 "Failed to verify proof for range starting at {start_loc} with max {max_ops} ops",
2358 );
2359 let expected_ops = std::cmp::min(max_ops, *db.bounds().end - start_loc);
2360 assert_eq!(ops.len() as u64, expected_ops);
2361
2362 let wrong_root = Sha256::hash(&[&[0xFF; 32]]);
2363 assert!(!verify_proof::<Sha256, _, _>(
2364 &proof,
2365 Location::new(start_loc),
2366 &ops,
2367 &wrong_root,
2368 ));
2369 if start_loc > 0 {
2370 assert!(!verify_proof::<Sha256, _, _>(
2371 &proof,
2372 Location::new(start_loc - 1),
2373 &ops,
2374 &root,
2375 ));
2376 }
2377 }
2378
2379 db.destroy().await.unwrap();
2380 }
2381
2382 #[boxed]
2383 pub(crate) async fn run_proof_with_pruning<F: Family, V, C, S: Strategy>(
2384 context: deterministic::Context,
2385 mut db: TestKeyless<F, V, C, Sha256, S>,
2386 reopen: Reopen<TestKeyless<F, V, C, Sha256, S>>,
2387 ) where
2388 V: ValueEncoding<Value: TestValue>,
2389 C: Mutable<Item = Operation<F, V>>,
2390 Operation<F, V>: EncodeShared + std::fmt::Debug,
2391 {
2392 const ELEMENTS: u64 = 100;
2393 {
2394 let mut batch = db.new_batch();
2395 for i in 0u64..ELEMENTS {
2396 batch = batch.append(V::Value::make(i));
2397 }
2398 let new_commit = db.bounds().end + ELEMENTS;
2399 let merkleized = batch.merkleize(&db, None, new_commit).await.unwrap();
2400 db = db.apply_batch(merkleized).await.unwrap();
2401 }
2402
2403 {
2404 let mut batch = db.new_batch();
2405 for i in ELEMENTS..ELEMENTS * 2 {
2406 batch = batch.append(V::Value::make(i));
2407 }
2408 let new_commit = db.bounds().end + ELEMENTS;
2409 let merkleized = batch.merkleize(&db, None, new_commit).await.unwrap();
2410 db = db.apply_batch(merkleized).await.unwrap();
2411 }
2412 let root = db.root();
2413
2414 const PRUNE_LOC: u64 = 30;
2415 let db = db.prune(Location::new(PRUNE_LOC)).await.unwrap();
2416 let oldest_retained = db.bounds().start;
2417 assert_eq!(db.root(), root);
2418
2419 db.sync().await.unwrap();
2420 let db = reopen(context).await;
2421 assert_eq!(db.root(), root);
2422
2423 for (start_loc, max_ops) in [
2424 (oldest_retained, 10),
2425 (Location::new(50), 20),
2426 (Location::new(150), 10),
2427 (Location::new(190), 15),
2428 ] {
2429 if start_loc < oldest_retained {
2430 continue;
2431 }
2432 let (proof, ops) = db.proof(start_loc, NZU64!(max_ops)).await.unwrap();
2433 assert!(verify_proof::<Sha256, _, _>(&proof, start_loc, &ops, &root,));
2434 }
2435
2436 let aggressive_prune: Location<F> = Location::new(150);
2437 let db = db.prune(aggressive_prune).await.unwrap();
2438
2439 let new_oldest = db.bounds().start;
2440 let (proof, ops) = db.proof(new_oldest, NZU64!(20)).await.unwrap();
2441 assert!(verify_proof::<Sha256, _, _>(
2442 &proof, new_oldest, &ops, &root,
2443 ));
2444
2445 let almost_all = db.bounds().end - 5;
2446 let db = db.prune(almost_all).await.unwrap();
2447 let final_oldest = db.bounds().start;
2448 if final_oldest < db.bounds().end {
2449 let (final_proof, final_ops) = db.proof(final_oldest, NZU64!(10)).await.unwrap();
2450 assert!(verify_proof::<Sha256, _, _>(
2451 &final_proof,
2452 final_oldest,
2453 &final_ops,
2454 &root,
2455 ));
2456 }
2457
2458 db.destroy().await.unwrap();
2459 }
2460
2461 #[boxed]
2462 pub(crate) async fn run_get_out_of_bounds<F: Family, V, C, H, S: Strategy>(
2463 db: TestKeyless<F, V, C, H, S>,
2464 ) where
2465 V: ValueEncoding<Value: TestValue>,
2466 C: Mutable<Item = Operation<F, V>>,
2467 H: Hasher,
2468 Operation<F, V>: EncodeShared,
2469 {
2470 assert!(db.get(Location::new(0)).await.unwrap().is_none());
2471
2472 let merkleized = db
2473 .new_batch()
2474 .append(V::Value::make(1))
2475 .append(V::Value::make(2))
2476 .merkleize(&db, None, db.inactivity_floor_loc())
2477 .await
2478 .unwrap();
2479 let db = db.apply_batch(merkleized).await.unwrap();
2480
2481 assert_eq!(
2482 db.get(Location::new(1)).await.unwrap(),
2483 Some(V::Value::make(1))
2484 );
2485 assert!(db.get(Location::new(3)).await.unwrap().is_none());
2486 assert!(matches!(
2487 db.get(Location::new(4)).await,
2488 Err(Error::LocationOutOfBounds(loc, size))
2489 if loc == Location::new(4) && size == Location::new(4)
2490 ));
2491
2492 db.destroy().await.unwrap();
2493 }
2494
2495 #[boxed]
2496 pub(crate) async fn run_get_pruned<F: Family, V, C, H, S: Strategy>(
2497 mut db: TestKeyless<F, V, C, H, S>,
2498 ) where
2499 V: ValueEncoding<Value: TestValue>,
2500 C: Mutable<Item = Operation<F, V>>,
2501 H: Hasher,
2502 Operation<F, V>: EncodeShared,
2503 {
2504 let mut round = 0u64;
2505 while db.bounds().start == Location::new(0) {
2506 round += 1;
2507 assert!(round <= 64, "failed to prune any history");
2508 db = commit_appends(db, (0..16).map(|i| V::Value::make(round * 100 + i)), None).await;
2509 let last_commit = db.bounds().end - 1;
2510 db = db.prune(last_commit).await.unwrap();
2511 }
2512
2513 let start = db.bounds().start;
2514 let pruned = start - 1;
2515 assert!(matches!(
2516 db.get(pruned).await,
2517 Err(Error::Journal(crate::journal::Error::ItemPruned(loc))) if loc == *pruned
2518 ));
2519 assert!(matches!(
2520 db.get_many(&[pruned, start]).await,
2521 Err(Error::Journal(crate::journal::Error::ItemPruned(loc))) if loc == *pruned
2522 ));
2523 db.get(start).await.unwrap();
2524 db.get_many(&[start]).await.unwrap();
2525
2526 db.destroy().await.unwrap();
2527 }
2528
2529 #[boxed]
2530 pub(crate) async fn run_batch_get<F: Family, V, C, H, S: Strategy>(
2531 mut db: TestKeyless<F, V, C, H, S>,
2532 ) where
2533 V: ValueEncoding<Value: TestValue>,
2534 C: Mutable<Item = Operation<F, V>>,
2535 H: Hasher,
2536 Operation<F, V>: EncodeShared,
2537 {
2538 let base_vals: Vec<V::Value> = (0..3).map(|i| V::Value::make(10 + i)).collect();
2539 let mut base_locs = Vec::new();
2540 {
2541 let mut batch = db.new_batch();
2542 for v in &base_vals {
2543 let loc = batch.size();
2544 batch = batch.append(v.clone());
2545 base_locs.push(loc);
2546 }
2547 let merkleized = batch
2548 .merkleize(&db, None, db.inactivity_floor_loc())
2549 .await
2550 .unwrap();
2551 db = db.apply_batch(merkleized).await.unwrap();
2552 }
2553
2554 let batch = db.new_batch();
2555 for (i, loc) in base_locs.iter().enumerate() {
2556 assert_eq!(
2557 batch.get(*loc, &db).await.unwrap(),
2558 Some(base_vals[i].clone()),
2559 );
2560 }
2561
2562 let new_val = V::Value::make(99);
2563 let new_loc = batch.size();
2564 let batch = batch.append(new_val.clone());
2565 assert_eq!(batch.get(new_loc, &db).await.unwrap(), Some(new_val));
2566 let end = new_loc + 1;
2567 assert!(matches!(
2568 batch.get(end, &db).await,
2569 Err(Error::LocationOutOfBounds(loc, bound)) if loc == end && bound == end
2570 ));
2571 assert!(matches!(
2572 batch.get_many(&[base_locs[0], end], &db).await,
2573 Err(Error::LocationOutOfBounds(loc, bound)) if loc == end && bound == end
2574 ));
2575
2576 db.destroy().await.unwrap();
2577 }
2578
2579 #[boxed]
2580 pub(crate) async fn run_batch_stacked_get<F: Family, V, C, S: Strategy>(
2581 db: TestKeyless<F, V, C, Sha256, S>,
2582 ) where
2583 V: ValueEncoding<Value: TestValue>,
2584 C: Mutable<Item = Operation<F, V>>,
2585 Operation<F, V>: EncodeShared,
2586 {
2587 let v1 = V::Value::make(1);
2588 let v2 = V::Value::make(2);
2589
2590 let parent = db.new_batch();
2591 let loc1 = parent.size();
2592 let parent = parent.append(v1.clone());
2593 let parent_m = parent
2594 .merkleize(&db, None, db.inactivity_floor_loc())
2595 .await
2596 .unwrap();
2597
2598 let child = parent_m.new_batch::<Sha256>();
2599 assert_eq!(child.get(loc1, &db).await.unwrap(), Some(v1));
2600
2601 let loc2 = child.size();
2602 let child = child.append(v2.clone());
2603 assert_eq!(child.get(loc2, &db).await.unwrap(), Some(v2));
2604 assert!(matches!(
2605 child.get(Location::new(9999), &db).await,
2606 Err(Error::LocationOutOfBounds(loc, bound))
2607 if loc == Location::new(9999) && bound == loc2 + 1
2608 ));
2609
2610 db.destroy().await.unwrap();
2611 }
2612
2613 #[boxed]
2614 pub(crate) async fn run_batch_speculative_root<F: Family, V, C, H, S: Strategy>(
2615 db: TestKeyless<F, V, C, H, S>,
2616 ) where
2617 V: ValueEncoding<Value: TestValue>,
2618 C: Mutable<Item = Operation<F, V>>,
2619 H: Hasher,
2620 Operation<F, V>: EncodeShared,
2621 {
2622 let mut batch = db.new_batch();
2623 for i in 0u64..10 {
2624 batch = batch.append(V::Value::make(i));
2625 }
2626 let merkleized = batch
2627 .merkleize(&db, None, db.inactivity_floor_loc())
2628 .await
2629 .unwrap();
2630 let speculative = merkleized.root();
2631 let db = db.apply_batch(merkleized).await.unwrap();
2632 assert_eq!(db.root(), speculative);
2633
2634 let merkleized = db
2635 .new_batch()
2636 .append(V::Value::make(100))
2637 .merkleize(&db, Some(V::Value::make(55)), db.inactivity_floor_loc())
2638 .await
2639 .unwrap();
2640 let speculative = merkleized.root();
2641 let db = db.apply_batch(merkleized).await.unwrap();
2642 assert_eq!(db.root(), speculative);
2643
2644 db.destroy().await.unwrap();
2645 }
2646
2647 #[boxed]
2648 pub(crate) async fn run_merkleized_batch_get<F: Family, V, C, S: Strategy>(
2649 db: TestKeyless<F, V, C, Sha256, S>,
2650 ) where
2651 V: ValueEncoding<Value: TestValue>,
2652 C: Mutable<Item = Operation<F, V>>,
2653 Operation<F, V>: EncodeShared,
2654 {
2655 let base_val = V::Value::make(10);
2656 let merkleized = db
2657 .new_batch()
2658 .append(base_val.clone())
2659 .merkleize(&db, None, db.inactivity_floor_loc())
2660 .await
2661 .unwrap();
2662 let db = db.apply_batch(merkleized).await.unwrap();
2663
2664 let new_val = V::Value::make(20);
2665 let merkleized = db
2666 .new_batch()
2667 .append(new_val.clone())
2668 .merkleize(&db, None, db.inactivity_floor_loc())
2669 .await
2670 .unwrap();
2671
2672 assert_eq!(
2673 merkleized.get(Location::new(1), &db).await.unwrap(),
2674 Some(base_val),
2675 );
2676 assert_eq!(
2677 merkleized.get(Location::new(3), &db).await.unwrap(),
2678 Some(new_val),
2679 );
2680 assert_eq!(merkleized.get(Location::new(4), &db).await.unwrap(), None);
2681 assert!(matches!(
2682 merkleized.get(Location::new(5), &db).await,
2683 Err(Error::LocationOutOfBounds(loc, bound))
2684 if loc == Location::new(5) && bound == Location::new(5)
2685 ));
2686
2687 db.destroy().await.unwrap();
2688 }
2689
2690 #[boxed]
2691 pub(crate) async fn run_batch_chained_apply_sequential<F: Family, V, C, H, S: Strategy>(
2692 db: TestKeyless<F, V, C, H, S>,
2693 ) where
2694 V: ValueEncoding<Value: TestValue>,
2695 C: Mutable<Item = Operation<F, V>>,
2696 H: Hasher,
2697 Operation<F, V>: EncodeShared,
2698 {
2699 let v1 = V::Value::make(1);
2700 let v2 = V::Value::make(2);
2701
2702 let parent = db.new_batch();
2703 let loc1 = parent.size();
2704 let parent = parent.append(v1.clone());
2705 let parent_m = parent
2706 .merkleize(&db, None, db.inactivity_floor_loc())
2707 .await
2708 .unwrap();
2709 let parent_root = parent_m.root();
2710
2711 let db = db.apply_batch(parent_m).await.unwrap();
2712 assert_eq!(db.root(), parent_root);
2713 assert_eq!(db.get(loc1).await.unwrap(), Some(v1));
2714
2715 let batch2 = db.new_batch();
2716 let loc2 = batch2.size();
2717 let batch2 = batch2.append(v2.clone());
2718 let batch2_m = batch2
2719 .merkleize(&db, None, db.inactivity_floor_loc())
2720 .await
2721 .unwrap();
2722 let batch2_root = batch2_m.root();
2723 let db = db.apply_batch(batch2_m).await.unwrap();
2724 assert_eq!(db.root(), batch2_root);
2725 assert_eq!(db.get(loc2).await.unwrap(), Some(v2));
2726
2727 db.destroy().await.unwrap();
2728 }
2729
2730 #[boxed]
2731 pub(crate) async fn run_batch_many_sequential<F: Family, V, C, S: Strategy>(
2732 mut db: TestKeyless<F, V, C, Sha256, S>,
2733 ) where
2734 V: ValueEncoding<Value: TestValue>,
2735 C: Mutable<Item = Operation<F, V>>,
2736 Operation<F, V>: EncodeShared + std::fmt::Debug,
2737 {
2738 const BATCHES: u64 = 20;
2739 const APPENDS_PER_BATCH: u64 = 5;
2740 let mut all_values: Vec<V::Value> = Vec::new();
2741 let mut all_locs: Vec<Location<F>> = Vec::new();
2742
2743 for batch_idx in 0..BATCHES {
2744 let mut batch = db.new_batch();
2745 for j in 0..APPENDS_PER_BATCH {
2746 let v = V::Value::make(batch_idx * 10 + j);
2747 let loc = batch.size();
2748 batch = batch.append(v.clone());
2749 all_values.push(v);
2750 all_locs.push(loc);
2751 }
2752 let merkleized = batch
2753 .merkleize(&db, None, db.inactivity_floor_loc())
2754 .await
2755 .unwrap();
2756 db = db.apply_batch(merkleized).await.unwrap();
2757 }
2758
2759 for (i, loc) in all_locs.iter().enumerate() {
2760 assert_eq!(db.get(*loc).await.unwrap(), Some(all_values[i].clone()));
2761 }
2762
2763 let root = db.root();
2764 let (proof, ops) = db.proof(Location::new(0), NZU64!(1000)).await.unwrap();
2765 assert!(verify_proof::<Sha256, _, _>(
2766 &proof,
2767 Location::new(0),
2768 &ops,
2769 &root,
2770 ));
2771 assert_eq!(db.bounds().end, 1 + BATCHES * (APPENDS_PER_BATCH + 1));
2772
2773 db.destroy().await.unwrap();
2774 }
2775
2776 #[boxed]
2777 pub(crate) async fn run_batch_empty<F: Family, V, C, H, S: Strategy>(
2778 db: TestKeyless<F, V, C, H, S>,
2779 ) where
2780 V: ValueEncoding<Value: TestValue>,
2781 C: Mutable<Item = Operation<F, V>>,
2782 H: Hasher,
2783 Operation<F, V>: EncodeShared,
2784 {
2785 let merkleized = db
2786 .new_batch()
2787 .append(V::Value::make(1))
2788 .merkleize(&db, None, db.inactivity_floor_loc())
2789 .await
2790 .unwrap();
2791 let db = db.apply_batch(merkleized).await.unwrap();
2792 let root_before = db.root();
2793 let size_before = db.bounds().end;
2794
2795 let merkleized = db
2796 .new_batch()
2797 .merkleize(&db, None, db.inactivity_floor_loc())
2798 .await
2799 .unwrap();
2800 let speculative = merkleized.root();
2801 let db = db.apply_batch(merkleized).await.unwrap();
2802
2803 assert_ne!(db.root(), root_before);
2804 assert_eq!(db.root(), speculative);
2805 assert_eq!(db.bounds().end, size_before + 1);
2806
2807 db.destroy().await.unwrap();
2808 }
2809
2810 #[boxed]
2811 pub(crate) async fn run_batch_chained_merkleized_get<F: Family, V, C, S: Strategy>(
2812 db: TestKeyless<F, V, C, Sha256, S>,
2813 ) where
2814 V: ValueEncoding<Value: TestValue>,
2815 C: Mutable<Item = Operation<F, V>>,
2816 Operation<F, V>: EncodeShared,
2817 {
2818 let base_val = V::Value::make(10);
2819 let floor = db.inactivity_floor_loc();
2820 let merkleized = db
2821 .new_batch()
2822 .append(base_val.clone())
2823 .merkleize(&db, None, floor)
2824 .await
2825 .unwrap();
2826 let db = db.apply_batch(merkleized).await.unwrap();
2827
2828 let v1 = V::Value::make(1);
2829 let parent = db.new_batch();
2830 let loc1 = parent.size();
2831 let parent_m = parent
2832 .append(v1.clone())
2833 .merkleize(&db, None, db.inactivity_floor_loc())
2834 .await
2835 .unwrap();
2836
2837 let v2 = V::Value::make(2);
2838 let child = parent_m.new_batch::<Sha256>();
2839 let loc2 = child.size();
2840 let child_m = child
2841 .append(v2.clone())
2842 .merkleize(&db, None, db.inactivity_floor_loc())
2843 .await
2844 .unwrap();
2845
2846 assert_eq!(
2847 child_m.get(Location::new(1), &db).await.unwrap(),
2848 Some(base_val),
2849 );
2850 assert_eq!(child_m.get(loc1, &db).await.unwrap(), Some(v1.clone()));
2851 assert_eq!(child_m.get(loc2, &db).await.unwrap(), Some(v2.clone()));
2852 let child_end = child_m.size();
2853 assert!(matches!(
2854 child_m.get(child_end, &db).await,
2855 Err(Error::LocationOutOfBounds(loc, bound)) if loc == child_end && bound == child_end
2856 ));
2857
2858 let parent_end = parent_m.size();
2860 assert!(matches!(
2861 parent_m.get(loc2, &db).await,
2862 Err(Error::LocationOutOfBounds(loc, bound)) if loc == loc2 && bound == parent_end
2863 ));
2864
2865 let db = db.apply_batch(Arc::clone(&parent_m)).await.unwrap();
2867 let db = db.apply_batch(child_m).await.unwrap();
2868 assert_eq!(db.get(loc2).await.unwrap(), Some(v2));
2869 assert!(matches!(
2870 parent_m.get(loc1, &db).await,
2871 Err(Error::StaleRead)
2872 ));
2873
2874 db.destroy().await.unwrap();
2875 }
2876
2877 #[boxed]
2878 pub(crate) async fn run_batch_large<F: Family, V, C, S: Strategy>(
2879 db: TestKeyless<F, V, C, Sha256, S>,
2880 ) where
2881 V: ValueEncoding<Value: TestValue>,
2882 C: Mutable<Item = Operation<F, V>>,
2883 Operation<F, V>: EncodeShared + std::fmt::Debug,
2884 {
2885 const N: u64 = 500;
2886 let mut values = Vec::new();
2887 let mut locs = Vec::new();
2888
2889 let mut batch = db.new_batch();
2890 for i in 0..N {
2891 let v = V::Value::make(i);
2892 locs.push(batch.size());
2893 batch = batch.append(v.clone());
2894 values.push(v);
2895 }
2896 let merkleized = batch
2897 .merkleize(&db, None, db.inactivity_floor_loc())
2898 .await
2899 .unwrap();
2900 let db = db.apply_batch(merkleized).await.unwrap();
2901
2902 for (i, loc) in locs.iter().enumerate() {
2903 assert_eq!(db.get(*loc).await.unwrap(), Some(values[i].clone()));
2904 }
2905
2906 let root = db.root();
2907 let (proof, ops) = db.proof(Location::new(0), NZU64!(1000)).await.unwrap();
2908 assert!(verify_proof::<Sha256, _, _>(
2909 &proof,
2910 Location::new(0),
2911 &ops,
2912 &root,
2913 ));
2914 assert_eq!(db.bounds().end, 1 + N + 1);
2915
2916 db.destroy().await.unwrap();
2917 }
2918
2919 #[boxed]
2920 pub(crate) async fn run_stale_batch_chained<F: Family, V, C, S: Strategy>(
2921 db: TestKeyless<F, V, C, Sha256, S>,
2922 ) where
2923 V: ValueEncoding<Value: TestValue>,
2924 C: Mutable<Item = Operation<F, V>>,
2925 Operation<F, V>: EncodeShared,
2926 {
2927 let common_parent = db
2928 .new_batch()
2929 .append(V::Value::make(10))
2930 .merkleize(&db, None, db.inactivity_floor_loc())
2931 .await
2932 .unwrap();
2933 let sibling_a = common_parent
2934 .new_batch::<Sha256>()
2935 .append(V::Value::make(11))
2936 .merkleize(&db, None, db.inactivity_floor_loc())
2937 .await
2938 .unwrap();
2939 let sibling_b = common_parent
2940 .new_batch::<Sha256>()
2941 .append(V::Value::make(12))
2942 .merkleize(&db, None, db.inactivity_floor_loc())
2943 .await
2944 .unwrap();
2945 let db = db.apply_batch(sibling_a).await.unwrap();
2946 assert!(matches!(
2947 db.validate_batch(&sibling_b),
2948 Err(Error::StaleBatch)
2949 ));
2950
2951 let parent_a = db
2952 .new_batch()
2953 .append(V::Value::make(1))
2954 .merkleize(&db, None, db.inactivity_floor_loc())
2955 .await
2956 .unwrap();
2957 let parent_b = db
2958 .new_batch()
2959 .append(V::Value::make(2))
2960 .merkleize(&db, None, db.inactivity_floor_loc())
2961 .await
2962 .unwrap();
2963 let child_b = parent_b
2964 .new_batch::<Sha256>()
2965 .append(V::Value::make(3))
2966 .merkleize(&db, None, db.inactivity_floor_loc())
2967 .await
2968 .unwrap();
2969
2970 let db = db.apply_batch(parent_a).await.unwrap();
2971 assert!(matches!(
2972 db.validate_batch(&child_b),
2973 Err(Error::StaleBatch)
2974 ));
2975 db.destroy().await.unwrap();
2976 }
2977
2978 #[boxed]
2979 pub(crate) async fn run_sequential_commit_parent_then_child<F: Family, V, C, S: Strategy>(
2980 db: TestKeyless<F, V, C, Sha256, S>,
2981 ) where
2982 V: ValueEncoding<Value: TestValue>,
2983 C: Mutable<Item = Operation<F, V>>,
2984 Operation<F, V>: EncodeShared,
2985 {
2986 let parent = db
2987 .new_batch()
2988 .append(V::Value::make(1))
2989 .merkleize(&db, None, db.inactivity_floor_loc())
2990 .await
2991 .unwrap();
2992 let child = parent
2993 .new_batch::<Sha256>()
2994 .append(V::Value::make(2))
2995 .merkleize(&db, None, db.inactivity_floor_loc())
2996 .await
2997 .unwrap();
2998
2999 let db = db.apply_batch(parent).await.unwrap();
3000 let db = db.apply_batch(child).await.unwrap();
3001
3002 db.destroy().await.unwrap();
3003 }
3004
3005 #[boxed]
3006 pub(crate) async fn run_stale_batch_child_before_parent<F: Family, V, C, S: Strategy>(
3007 db: TestKeyless<F, V, C, Sha256, S>,
3008 ) where
3009 V: ValueEncoding<Value: TestValue>,
3010 C: Mutable<Item = Operation<F, V>>,
3011 Operation<F, V>: EncodeShared,
3012 {
3013 let parent = db
3014 .new_batch()
3015 .append(V::Value::make(1))
3016 .merkleize(&db, None, db.inactivity_floor_loc())
3017 .await
3018 .unwrap();
3019 let child = parent
3020 .new_batch::<Sha256>()
3021 .append(V::Value::make(2))
3022 .merkleize(&db, None, db.inactivity_floor_loc())
3023 .await
3024 .unwrap();
3025
3026 let db = db.apply_batch(child).await.unwrap();
3027 assert!(matches!(
3028 db.apply_batch(parent).await,
3029 Err(Error::StaleBatch)
3030 ));
3031 }
3032
3033 #[boxed]
3034 pub(crate) async fn run_child_root_matches_pending_and_committed<F: Family, V, C, S: Strategy>(
3035 db: TestKeyless<F, V, C, Sha256, S>,
3036 ) where
3037 V: ValueEncoding<Value: TestValue>,
3038 C: Mutable<Item = Operation<F, V>>,
3039 Operation<F, V>: EncodeShared,
3040 {
3041 let parent = db
3043 .new_batch()
3044 .append(V::Value::make(1))
3045 .merkleize(&db, None, db.inactivity_floor_loc())
3046 .await
3047 .unwrap();
3048 let pending_child = parent
3049 .new_batch::<Sha256>()
3050 .append(V::Value::make(2))
3051 .merkleize(&db, None, db.inactivity_floor_loc())
3052 .await
3053 .unwrap();
3054
3055 let db = db.apply_batch(parent).await.unwrap();
3058 let db = db.commit().await.unwrap();
3059
3060 let committed_child = db
3061 .new_batch()
3062 .append(V::Value::make(2))
3063 .merkleize(&db, None, db.inactivity_floor_loc())
3064 .await
3065 .unwrap();
3066
3067 assert_eq!(pending_child.root(), committed_child.root());
3068
3069 db.destroy().await.unwrap();
3070 }
3071
3072 async fn commit_appends<F: Family, V, C, H, S: Strategy>(
3073 db: TestKeyless<F, V, C, H, S>,
3074 values: impl IntoIterator<Item = V::Value>,
3075 metadata: Option<V::Value>,
3076 ) -> TestKeyless<F, V, C, H, S>
3077 where
3078 V: ValueEncoding<Value: TestValue>,
3079 C: Mutable<Item = Operation<F, V>>,
3080 H: Hasher,
3081 Operation<F, V>: EncodeShared,
3082 {
3083 let base_size = *db.bounds().end;
3087 let appends_iter: Vec<_> = values.into_iter().collect();
3088 let new_commit_loc = Location::new(base_size + appends_iter.len() as u64);
3089 let mut batch = db.new_batch();
3090 for value in appends_iter {
3091 batch = batch.append(value);
3092 }
3093 let merkleized = batch
3094 .merkleize(&db, metadata, new_commit_loc)
3095 .await
3096 .unwrap();
3097 let expected_end = merkleized.bounds().tip.size;
3098 let db = db.apply_batch(merkleized).await.unwrap();
3099 assert_eq!(db.bounds().end, expected_end);
3100 db.commit().await.unwrap()
3101 }
3102
3103 #[boxed]
3104 pub(crate) async fn run_bounded_initialization_recovery<F: Family, V, C, H, S: Strategy>(
3105 context: deterministic::Context,
3106 db: TestKeyless<F, V, C, H, S>,
3107 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
3108 bounded: BoundedOpen<TestKeyless<F, V, C, H, S>, F>,
3109 ) where
3110 V: ValueEncoding<Value: TestValue>,
3111 C: Mutable<Item = Operation<F, V>>,
3112 H: Hasher,
3113 Operation<F, V>: EncodeShared,
3114 {
3115 let initial_root = db.root();
3116 let initial_size = db.bounds().end;
3117
3118 let value_a = V::Value::make(1);
3119 let value_b = V::Value::make(2);
3120 let metadata_a = V::Value::make(3);
3121 let db = commit_appends(
3122 db,
3123 [value_a.clone(), value_b.clone()],
3124 Some(metadata_a.clone()),
3125 )
3126 .await;
3127
3128 let root_before = db.root();
3129 let size_before = db.bounds().end;
3130 let commit_before = db.bounds().end - 1;
3131
3132 let value_c = V::Value::make(4);
3133 let metadata_b = V::Value::make(5);
3134 let db = commit_appends(db, [value_c.clone()], Some(metadata_b.clone())).await;
3135 assert_ne!(db.root(), root_before);
3136 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_b));
3137
3138 drop(db);
3139 let db = bounded(context.child("cap"), size_before).await.unwrap();
3140 assert_eq!(db.root(), root_before);
3141 assert_eq!(db.bounds().end, size_before);
3142 assert_eq!(db.bounds().end - 1, commit_before);
3143 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a.clone()));
3144 assert_eq!(
3145 db.get(Location::new(1)).await.unwrap(),
3146 Some(value_a.clone())
3147 );
3148 assert_eq!(
3149 db.get(Location::new(2)).await.unwrap(),
3150 Some(value_b.clone())
3151 );
3152 assert!(
3153 matches!(
3154 db.get(Location::new(4)).await,
3155 Err(Error::LocationOutOfBounds(_, size)) if size == size_before
3156 ),
3157 "discarded append should be out of bounds",
3158 );
3159
3160 drop(db);
3161 let db = reopen(context.child("reopen")).await;
3162 assert_eq!(db.root(), root_before);
3163 assert_eq!(db.bounds().end, size_before);
3164 assert_eq!(db.bounds().end - 1, commit_before);
3165 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata_a));
3166 assert_eq!(
3167 db.get(Location::new(1)).await.unwrap(),
3168 Some(value_a.clone())
3169 );
3170 assert_eq!(
3171 db.get(Location::new(2)).await.unwrap(),
3172 Some(value_b.clone())
3173 );
3174 assert!(matches!(
3175 db.get(Location::new(4)).await,
3176 Err(Error::LocationOutOfBounds(_, size)) if size == size_before
3177 ));
3178
3179 drop(db);
3180 let db = bounded(context.child("cap"), initial_size).await.unwrap();
3181 assert_eq!(db.root(), initial_root);
3182 assert_eq!(db.bounds().end, initial_size);
3183 assert_eq!(db.get_metadata().await.unwrap(), None);
3184 assert!(matches!(
3185 db.get(Location::new(1)).await,
3186 Err(Error::LocationOutOfBounds(_, size)) if size == initial_size
3187 ));
3188
3189 drop(db);
3190 let db = reopen(context.child("reopen_initial_boundary")).await;
3191 assert_eq!(db.root(), initial_root);
3192 assert_eq!(db.bounds().end, initial_size);
3193 assert_eq!(db.get_metadata().await.unwrap(), None);
3194 assert!(matches!(
3195 db.get(Location::new(1)).await,
3196 Err(Error::LocationOutOfBounds(_, size)) if size == initial_size
3197 ));
3198
3199 db.destroy().await.unwrap();
3200 }
3201
3202 #[boxed]
3203 pub(crate) async fn run_bounded_initialization_pruned_target_errors<
3204 F: Family,
3205 V,
3206 C,
3207 H,
3208 S: Strategy,
3209 >(
3210 context: deterministic::Context,
3211 db: TestKeyless<F, V, C, H, S>,
3212 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
3213 bounded: BoundedOpen<TestKeyless<F, V, C, H, S>, F>,
3214 ) where
3215 V: ValueEncoding<Value: TestValue>,
3216 C: Mutable<Item = Operation<F, V>>,
3217 H: Hasher,
3218 Operation<F, V>: EncodeShared,
3219 {
3220 let first_start = db.bounds().end;
3221 let mut db = commit_appends(db, (0..16).map(V::Value::make), None).await;
3222
3223 let mut round = 0u64;
3224 loop {
3225 round += 1;
3226 assert!(
3227 round <= 64,
3228 "failed to prune enough history for the initialization test"
3229 );
3230
3231 db = commit_appends(db, (0..16).map(|i| V::Value::make(round * 100 + i)), None).await;
3232 let last_commit = db.bounds().end - 1;
3233 db = db.prune(last_commit).await.unwrap();
3234
3235 if db.bounds().start > first_start {
3236 break;
3237 }
3238 }
3239
3240 let oldest_retained = db.bounds().start;
3241 drop(db);
3242 let Err(boundary_err) = bounded(context.child("cap_error"), oldest_retained).await else {
3243 panic!("expected bounded initialization to fail");
3244 };
3245 assert!(
3246 matches!(
3247 boundary_err,
3248 Error::Journal(crate::journal::Error::ItemPruned(_))
3249 ),
3250 "unexpected bounded initialization error at retained boundary: {boundary_err:?}"
3251 );
3252
3253 let db = reopen(context.child("reopen_boundary")).await;
3254 drop(db);
3255 let Err(err) = bounded(context.child("cap_error"), first_start).await else {
3256 panic!("expected bounded initialization to fail");
3257 };
3258 assert!(
3259 matches!(err, Error::Journal(crate::journal::Error::ItemPruned(_))),
3260 "unexpected bounded initialization error: {err:?}"
3261 );
3262 }
3263
3264 #[boxed]
3265 pub(crate) async fn run_floor_tracking<F: Family, V, C, H, S: Strategy>(
3266 context: deterministic::Context,
3267 db: TestKeyless<F, V, C, H, S>,
3268 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
3269 ) where
3270 V: ValueEncoding<Value: TestValue>,
3271 C: Mutable<Item = Operation<F, V>>,
3272 H: Hasher,
3273 Operation<F, V>: EncodeShared,
3274 {
3275 assert_eq!(db.inactivity_floor_loc(), Location::new(0));
3277
3278 let floor_a = Location::<F>::new(2);
3280 let merkleized = db
3281 .new_batch()
3282 .append(V::Value::make(1))
3283 .append(V::Value::make(2))
3284 .merkleize(&db, None, floor_a)
3285 .await
3286 .unwrap();
3287 let db = db.apply_batch(merkleized).await.unwrap();
3288 let db = db.commit().await.unwrap();
3289 assert_eq!(db.inactivity_floor_loc(), floor_a);
3290
3291 drop(db);
3293 let db = reopen(context.child("reopen")).await;
3294 assert_eq!(db.inactivity_floor_loc(), floor_a);
3295
3296 let merkleized = db
3298 .new_batch()
3299 .append(V::Value::make(3))
3300 .merkleize(&db, None, floor_a)
3301 .await
3302 .unwrap();
3303 let db = db.apply_batch(merkleized).await.unwrap();
3304 assert_eq!(db.inactivity_floor_loc(), floor_a);
3305
3306 let floor_b = Location::<F>::new(5);
3308 let merkleized = db
3309 .new_batch()
3310 .append(V::Value::make(4))
3311 .merkleize(&db, None, floor_b)
3312 .await
3313 .unwrap();
3314 let db = db.apply_batch(merkleized).await.unwrap();
3315 assert_eq!(db.inactivity_floor_loc(), floor_b);
3316
3317 db.destroy().await.unwrap();
3318 }
3319
3320 #[boxed]
3321 pub(crate) async fn run_floor_regression_rejected<F: Family, V, C, H, S: Strategy>(
3322 db: TestKeyless<F, V, C, H, S>,
3323 ) where
3324 V: ValueEncoding<Value: TestValue>,
3325 C: Mutable<Item = Operation<F, V>>,
3326 H: Hasher,
3327 Operation<F, V>: EncodeShared,
3328 {
3329 let merkleized = db
3331 .new_batch()
3332 .append(V::Value::make(1))
3333 .append(V::Value::make(2))
3334 .merkleize(&db, None, Location::new(3))
3335 .await
3336 .unwrap();
3337 let db = db.apply_batch(merkleized).await.unwrap();
3338 let db = db.commit().await.unwrap();
3339 assert_eq!(db.inactivity_floor_loc(), Location::new(3));
3340
3341 let Err(err) = db
3343 .new_batch()
3344 .append(V::Value::make(3))
3345 .merkleize(&db, None, Location::new(1))
3346 .await
3347 else {
3348 panic!("merkleize must reject the invalid floor");
3349 };
3350 assert!(
3351 matches!(err, Error::FloorRegressed(new, current) if *new == 1 && *current == 3),
3352 "unexpected error: {err:?}"
3353 );
3354
3355 db.destroy().await.unwrap();
3356 }
3357
3358 #[boxed]
3359 pub(crate) async fn run_floor_beyond_commit_loc_rejected<F: Family, V, C, H, S: Strategy>(
3360 db: TestKeyless<F, V, C, H, S>,
3361 ) where
3362 V: ValueEncoding<Value: TestValue>,
3363 C: Mutable<Item = Operation<F, V>>,
3364 H: Hasher,
3365 Operation<F, V>: EncodeShared,
3366 {
3367 let Err(err) = db
3371 .new_batch()
3372 .append(V::Value::make(1))
3373 .append(V::Value::make(2))
3374 .merkleize(&db, None, Location::new(999))
3375 .await
3376 else {
3377 panic!("merkleize must reject the invalid floor");
3378 };
3379 assert!(
3380 matches!(err, Error::FloorBeyondSize(floor, commit) if *floor == 999 && *commit == 3),
3381 "unexpected error: {err:?}"
3382 );
3383
3384 let Err(err) = db
3386 .new_batch()
3387 .append(V::Value::make(3))
3388 .append(V::Value::make(4))
3389 .merkleize(&db, None, Location::new(4))
3390 .await
3391 else {
3392 panic!("merkleize must reject the invalid floor");
3393 };
3394 assert!(
3395 matches!(err, Error::FloorBeyondSize(floor, commit) if *floor == 4 && *commit == 3),
3396 "unexpected error: {err:?}"
3397 );
3398 }
3399
3400 #[boxed]
3401 pub(crate) async fn run_bounded_initialization_restores_floor<F: Family, V, C, H, S: Strategy>(
3402 context: deterministic::Context,
3403 db: TestKeyless<F, V, C, H, S>,
3404 bounded: BoundedOpen<TestKeyless<F, V, C, H, S>, F>,
3405 ) where
3406 V: ValueEncoding<Value: TestValue>,
3407 C: Mutable<Item = Operation<F, V>>,
3408 H: Hasher,
3409 Operation<F, V>: EncodeShared,
3410 {
3411 let floor_a = Location::<F>::new(3);
3413 let merkleized = db
3414 .new_batch()
3415 .append(V::Value::make(1))
3416 .append(V::Value::make(2))
3417 .merkleize(&db, None, floor_a)
3418 .await
3419 .unwrap();
3420 let db = db.apply_batch(merkleized).await.unwrap();
3421 let db = db.commit().await.unwrap();
3422 let initialization_bound = db.bounds().end;
3423
3424 let floor_b = Location::<F>::new(6);
3426 let merkleized = db
3427 .new_batch()
3428 .append(V::Value::make(3))
3429 .append(V::Value::make(4))
3430 .merkleize(&db, None, floor_b)
3431 .await
3432 .unwrap();
3433 let db = db.apply_batch(merkleized).await.unwrap();
3434 let db = db.commit().await.unwrap();
3435 assert_eq!(db.inactivity_floor_loc(), floor_b);
3436
3437 drop(db);
3439 let db = bounded(context.child("cap"), initialization_bound)
3440 .await
3441 .unwrap();
3442 assert_eq!(db.inactivity_floor_loc(), floor_a);
3443
3444 let db = db.prune(floor_a).await.unwrap();
3446
3447 let beyond = floor_a + 1;
3449 let Err(err) = db.prune(beyond).await else {
3450 panic!("expected prune to fail");
3451 };
3452 assert!(matches!(err, Error::PruneBeyondMinRequired(_, _)));
3453 }
3454
3455 #[boxed]
3458 pub(crate) async fn run_floor_changes_root<F: Family, V, C, H, S: Strategy>(
3459 db_a: TestKeyless<F, V, C, H, S>,
3460 db_b: TestKeyless<F, V, C, H, S>,
3461 ) where
3462 V: ValueEncoding<Value: TestValue>,
3463 C: Mutable<Item = Operation<F, V>>,
3464 H: Hasher,
3465 Operation<F, V>: EncodeShared,
3466 {
3467 let appends = [V::Value::make(1), V::Value::make(2)];
3468
3469 let mut batch_a = db_a.new_batch();
3471 for v in appends.iter() {
3472 batch_a = batch_a.append(v.clone());
3473 }
3474 let merkleized = batch_a
3475 .merkleize(&db_a, None, Location::new(0))
3476 .await
3477 .unwrap();
3478 let db_a = db_a.apply_batch(merkleized).await.unwrap();
3479
3480 let mut batch_b = db_b.new_batch();
3482 for v in appends.iter() {
3483 batch_b = batch_b.append(v.clone());
3484 }
3485 let merkleized = batch_b
3486 .merkleize(&db_b, None, Location::new(3))
3487 .await
3488 .unwrap();
3489 let db_b = db_b.apply_batch(merkleized).await.unwrap();
3490
3491 assert_ne!(db_a.root(), db_b.root());
3492
3493 db_a.destroy().await.unwrap();
3494 db_b.destroy().await.unwrap();
3495 }
3496
3497 #[boxed]
3499 pub(crate) async fn run_floor_at_commit_loc_accepted<F: Family, V, C, H, S: Strategy>(
3500 db: TestKeyless<F, V, C, H, S>,
3501 ) where
3502 V: ValueEncoding<Value: TestValue>,
3503 C: Mutable<Item = Operation<F, V>>,
3504 H: Hasher,
3505 Operation<F, V>: EncodeShared,
3506 {
3507 let commit_loc = Location::<F>::new(3);
3510 let merkleized = db
3511 .new_batch()
3512 .append(V::Value::make(1))
3513 .append(V::Value::make(2))
3514 .merkleize(&db, None, commit_loc)
3515 .await
3516 .unwrap();
3517 let db = db.apply_batch(merkleized).await.unwrap();
3518 assert_eq!(db.inactivity_floor_loc(), commit_loc);
3519
3520 db.destroy().await.unwrap();
3521 }
3522
3523 #[boxed]
3526 pub(crate) async fn run_bounded_initialization_after_reopen_with_floor<
3527 F: Family,
3528 V,
3529 C,
3530 H,
3531 S: Strategy,
3532 >(
3533 context: deterministic::Context,
3534 db: TestKeyless<F, V, C, H, S>,
3535 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
3536 bounded: BoundedOpen<TestKeyless<F, V, C, H, S>, F>,
3537 ) where
3538 V: ValueEncoding<Value: TestValue>,
3539 C: Mutable<Item = Operation<F, V>>,
3540 H: Hasher,
3541 Operation<F, V>: EncodeShared,
3542 {
3543 let floor_a = Location::<F>::new(3);
3545 let merkleized = db
3546 .new_batch()
3547 .append(V::Value::make(1))
3548 .append(V::Value::make(2))
3549 .merkleize(&db, None, floor_a)
3550 .await
3551 .unwrap();
3552 let db = db.apply_batch(merkleized).await.unwrap();
3553 let db = db.commit().await.unwrap();
3554 let initialization_bound = db.bounds().end;
3555
3556 let floor_b = Location::<F>::new(6);
3558 let merkleized = db
3559 .new_batch()
3560 .append(V::Value::make(3))
3561 .append(V::Value::make(4))
3562 .merkleize(&db, None, floor_b)
3563 .await
3564 .unwrap();
3565 let db = db.apply_batch(merkleized).await.unwrap();
3566 db.commit().await.unwrap();
3567
3568 let db = reopen(context.child("reopen")).await;
3570 assert_eq!(db.inactivity_floor_loc(), floor_b);
3571
3572 drop(db);
3574 let db = bounded(context.child("cap"), initialization_bound)
3575 .await
3576 .unwrap();
3577 assert_eq!(db.inactivity_floor_loc(), floor_a);
3578 assert_eq!(db.bounds().end - 1, Location::new(3));
3579
3580 drop(db);
3582 let db = reopen(context.child("reopen").with_attribute("index", 2)).await;
3583 assert_eq!(db.inactivity_floor_loc(), floor_a);
3584
3585 db.destroy().await.unwrap();
3586 }
3587
3588 #[boxed]
3591 pub(crate) async fn run_chained_floor_regression_rejected<F, V, C, H, S: Strategy>(
3592 db: TestKeyless<F, V, C, H, S>,
3593 ) where
3594 F: Family,
3595 V: ValueEncoding<Value: TestValue>,
3596 C: Mutable<Item = Operation<F, V>>,
3597 H: Hasher,
3598 Operation<F, V>: EncodeShared,
3599 {
3600 let parent = db
3602 .new_batch()
3603 .append(V::Value::make(1))
3604 .merkleize(&db, None, Location::new(2))
3605 .await
3606 .unwrap();
3607
3608 let Err(err) = parent
3610 .new_batch::<H>()
3611 .append(V::Value::make(2))
3612 .merkleize(&db, None, Location::new(1))
3613 .await
3614 else {
3615 panic!("merkleize must reject the invalid floor");
3616 };
3617 assert!(
3618 matches!(err, Error::FloorRegressed(new, prev) if *new == 1 && *prev == 2),
3619 "unexpected error: {err:?}"
3620 );
3621
3622 db.destroy().await.unwrap();
3623 }
3624
3625 #[boxed]
3628 pub(crate) async fn run_chained_floor_beyond_commit_rejected<F, V, C, H, S: Strategy>(
3629 db: TestKeyless<F, V, C, H, S>,
3630 ) where
3631 F: Family,
3632 V: ValueEncoding<Value: TestValue>,
3633 C: Mutable<Item = Operation<F, V>>,
3634 H: Hasher,
3635 Operation<F, V>: EncodeShared,
3636 {
3637 let parent = db
3639 .new_batch()
3640 .append(V::Value::make(1))
3641 .merkleize(&db, None, Location::new(2))
3642 .await
3643 .unwrap();
3644
3645 let Err(err) = parent
3648 .new_batch::<H>()
3649 .append(V::Value::make(2))
3650 .merkleize(&db, None, Location::new(5))
3651 .await
3652 else {
3653 panic!("merkleize must reject the invalid floor");
3654 };
3655 assert!(
3656 matches!(err, Error::FloorBeyondSize(floor, commit) if *floor == 5 && *commit == 4),
3657 "unexpected error: {err:?}"
3658 );
3659 }
3660
3661 #[boxed]
3667 pub(crate) async fn run_single_commit_live_set<F, V, C, H, S: Strategy>(
3668 context: deterministic::Context,
3669 db: TestKeyless<F, V, C, H, S>,
3670 reopen: Reopen<TestKeyless<F, V, C, H, S>>,
3671 ) where
3672 F: Family,
3673 V: ValueEncoding<Value: TestValue>,
3674 C: Mutable<Item = Operation<F, V>>,
3675 H: Hasher,
3676 Operation<F, V>: EncodeShared,
3677 {
3678 let metadata = V::Value::make(42);
3681 let commit_loc = Location::<F>::new(4);
3682 let merkleized = db
3683 .new_batch()
3684 .append(V::Value::make(1))
3685 .append(V::Value::make(2))
3686 .append(V::Value::make(3))
3687 .merkleize(&db, Some(metadata.clone()), commit_loc)
3688 .await
3689 .unwrap();
3690 let db = db.apply_batch(merkleized).await.unwrap();
3691 let db = db.commit().await.unwrap();
3692 assert_eq!(db.bounds().end - 1, commit_loc);
3693 assert_eq!(db.inactivity_floor_loc(), commit_loc);
3694 let root_after_commit = db.root();
3695
3696 let db = db.prune(commit_loc).await.unwrap();
3701 let bounds = db.bounds();
3702 assert!(
3703 bounds.start <= commit_loc,
3704 "prune must not advance bounds.start past the floor"
3705 );
3706 assert_eq!(bounds.end, commit_loc + 1);
3707
3708 assert_eq!(db.get(commit_loc).await.unwrap(), Some(metadata.clone()));
3710 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata.clone()));
3711 assert_eq!(db.bounds().end - 1, commit_loc);
3712 assert_eq!(db.inactivity_floor_loc(), commit_loc);
3713 assert_eq!(db.root(), root_after_commit);
3715
3716 let db = db.sync().await.unwrap();
3719 let Err(err) = db.prune(commit_loc + 1).await else {
3720 panic!("expected prune to fail");
3721 };
3722 assert!(matches!(err, Error::PruneBeyondMinRequired(p, f)
3723 if *p == *commit_loc + 1 && *f == *commit_loc));
3724
3725 let db = reopen(context.child("reopened")).await;
3727 let reopened_bounds = db.bounds();
3728 assert_eq!(reopened_bounds.end, commit_loc + 1);
3729 assert_eq!(db.bounds().end - 1, commit_loc);
3730 assert_eq!(db.inactivity_floor_loc(), commit_loc);
3731 assert_eq!(db.root(), root_after_commit);
3732 assert_eq!(db.get_metadata().await.unwrap(), Some(metadata.clone()));
3733
3734 let next_commit_loc = Location::<F>::new(7);
3738 let v5 = V::Value::make(5);
3739 let v6 = V::Value::make(6);
3740 let merkleized = db
3741 .new_batch()
3742 .append(v5.clone())
3743 .append(v6.clone())
3744 .merkleize(&db, None, next_commit_loc)
3745 .await
3746 .unwrap();
3747 let db = db.apply_batch(merkleized).await.unwrap();
3748 let db = db.commit().await.unwrap();
3749 assert_eq!(db.bounds().end - 1, next_commit_loc);
3750 assert_eq!(db.inactivity_floor_loc(), next_commit_loc);
3751
3752 assert_eq!(db.get(Location::new(5)).await.unwrap(), Some(v5));
3755 assert_eq!(db.get(Location::new(6)).await.unwrap(), Some(v6));
3756 assert_eq!(db.get(commit_loc).await.unwrap(), Some(metadata));
3757
3758 db.destroy().await.unwrap();
3759 }
3760
3761 #[boxed]
3763 pub(crate) async fn run_chained_apply_with_valid_floors_succeeds<F, V, C, H, S: Strategy>(
3764 db: TestKeyless<F, V, C, H, S>,
3765 ) where
3766 F: Family,
3767 V: ValueEncoding<Value: TestValue>,
3768 C: Mutable<Item = Operation<F, V>>,
3769 H: Hasher,
3770 Operation<F, V>: EncodeShared,
3771 {
3772 let parent = db
3776 .new_batch()
3777 .append(V::Value::make(1))
3778 .merkleize(&db, None, Location::new(2))
3779 .await
3780 .unwrap();
3781 let child = parent
3782 .new_batch::<H>()
3783 .append(V::Value::make(2))
3784 .merkleize(&db, None, Location::new(3))
3785 .await
3786 .unwrap();
3787 let grandchild = child
3788 .new_batch::<H>()
3789 .append(V::Value::make(3))
3790 .merkleize(&db, None, Location::new(5))
3791 .await
3792 .unwrap();
3793
3794 let db = db.apply_batch(grandchild).await.unwrap();
3795
3796 assert_eq!(db.bounds().end - 1, Location::new(6));
3798 assert_eq!(db.inactivity_floor_loc(), Location::new(5));
3799
3800 db.destroy().await.unwrap();
3801 }
3802}