1use crate::{
16 Context,
17 journal::{
18 Error as JournalError,
19 contiguous::{Contiguous, Many, Mutable},
20 },
21 merkle::{
22 self, Bagging, Family, Location, Position, Proof, Readable, batch, full::Merkle,
23 hasher::Standard as StandardHasher, mem::Mem,
24 },
25};
26use alloc::{
27 sync::{Arc, Weak},
28 vec::Vec,
29};
30use commonware_codec::{Encode, EncodeShared};
31use commonware_cryptography::{Digest, Hasher};
32use commonware_macros::boxed;
33use commonware_parallel::Strategy;
34use commonware_runtime::{Handle, ReadOptions};
35use core::{
36 num::{NonZeroU64, NonZeroUsize},
37 ops::Range,
38};
39use futures::{Stream, TryFutureExt as _, try_join};
40use thiserror::Error;
41use tracing::{debug, warn};
42
43#[derive(Error, Debug)]
45pub enum Error<F: Family> {
46 #[error("merkle error: {0}")]
47 Merkle(#[from] merkle::Error<F>),
48
49 #[error("journal error: {0}")]
50 Journal(#[from] super::Error),
51}
52
53type MerkleizedParent<F, H, Item, S> = Arc<MerkleizedBatch<F, <H as Hasher>::Digest, Item, S>>;
55
56pub struct UnmerkleizedBatch<F: Family, H: Hasher, Item: Send + Sync, S: Strategy> {
59 inner: batch::UnmerkleizedBatch<F, H::Digest, S>,
61 hasher: StandardHasher<H>,
63 items: Vec<Item>,
65 parent: Option<MerkleizedParent<F, H, Item, S>>,
67}
68
69type MerkleizedBatchArc<F, H, Item, S> = Arc<MerkleizedBatch<F, <H as Hasher>::Digest, Item, S>>;
70
71impl<F: Family, H: Hasher, Item: Encode + Send + Sync, S: Strategy>
72 UnmerkleizedBatch<F, H, Item, S>
73{
74 #[allow(clippy::should_implement_trait)]
76 pub fn add(mut self, item: Item) -> Self {
77 let encoded = item.encode();
78 self.inner = self.inner.add(&self.hasher, &encoded);
79 self.items.push(item);
80 self
81 }
82
83 fn collect_ancestor_items(
85 parent: &MerkleizedParent<F, H, Item, S>,
86 ) -> (u64, Vec<Arc<Vec<Item>>>) {
87 let mut items = Vec::new();
88 let mut base_leaves = parent.as_ref().size() - parent.items.len() as u64;
89 if !parent.items.is_empty() {
90 items.push(Arc::clone(&parent.items));
91 }
92 let mut current = parent.parent.as_ref().and_then(Weak::upgrade);
93 while let Some(batch) = current {
94 base_leaves = batch.as_ref().size() - batch.items.len() as u64;
95 if !batch.items.is_empty() {
96 items.push(Arc::clone(&batch.items));
97 }
98 current = batch.parent.as_ref().and_then(Weak::upgrade);
99 }
100 items.reverse();
101 (base_leaves, items)
102 }
103
104 pub fn merkleize(self, base: &Mem<F, H::Digest>) -> MerkleizedBatchArc<F, H, Item, S> {
107 let Self {
108 inner,
109 hasher,
110 items,
111 parent,
112 } = self;
113
114 let (ancestor_base_leaves, ancestor_items) = parent.as_ref().map_or_else(
115 || (*inner.leaves() - items.len() as u64, Vec::new()),
116 Self::collect_ancestor_items,
117 );
118 let items = Arc::new(items);
119 let merkle = inner.merkleize(base, &hasher);
120 Arc::new(MerkleizedBatch {
121 inner: merkle,
122 bagging: hasher.root_bagging(),
123 items,
124 parent: parent.as_ref().map(Arc::downgrade),
125 ancestor_base_leaves,
126 ancestor_items,
127 })
128 }
129
130 pub(crate) fn add_many(mut self, items: Vec<Item>) -> Self {
136 assert!(
137 self.items.is_empty(),
138 "add_many expects no items added via add"
139 );
140
141 self.inner = self.inner.add_many(&self.hasher, &items);
142 self.items = items;
143 self
144 }
145}
146
147#[derive(Clone, Debug)]
149pub struct MerkleizedBatch<F: Family, D: Digest, Item: Send + Sync, S: Strategy> {
150 pub(crate) inner: Arc<batch::MerkleizedBatch<F, D, S>>,
152 bagging: Bagging,
154 items: Arc<Vec<Item>>,
156 parent: Option<Weak<Self>>,
158 pub(crate) ancestor_base_leaves: u64,
160 pub(crate) ancestor_items: Vec<Arc<Vec<Item>>>,
162}
163
164impl<F: Family, D: Digest, Item: Send + Sync, S: Strategy> MerkleizedBatch<F, D, Item, S> {
165 pub(crate) fn size(&self) -> u64 {
167 *self.inner.leaves()
168 }
169
170 pub fn root(
175 &self,
176 base: &Mem<F, D>,
177 hasher: &impl merkle::hasher::Hasher<F, Digest = D>,
178 inactive_peaks: usize,
179 ) -> Result<D, merkle::Error<F>> {
180 self.inner.root(base, hasher, inactive_peaks)
181 }
182
183 pub fn proof(
185 &self,
186 base: &Mem<F, D>,
187 hasher: &impl merkle::hasher::Hasher<F, Digest = D>,
188 loc: Location<F>,
189 inactive_peaks: usize,
190 ) -> Result<Proof<F, D>, merkle::Error<F>> {
191 self.inner.proof(base, hasher, loc, inactive_peaks)
192 }
193
194 pub fn range_proof(
196 &self,
197 base: &Mem<F, D>,
198 hasher: &impl merkle::hasher::Hasher<F, Digest = D>,
199 range: core::ops::Range<Location<F>>,
200 inactive_peaks: usize,
201 ) -> Result<Proof<F, D>, merkle::Error<F>> {
202 self.inner.range_proof(base, hasher, range, inactive_peaks)
203 }
204
205 pub(crate) const fn items(&self) -> &Arc<Vec<Item>> {
207 &self.items
208 }
209
210 pub fn new_batch<H: Hasher<Digest = D>>(self: &Arc<Self>) -> UnmerkleizedBatch<F, H, Item, S>
215 where
216 Item: Encode,
217 {
218 UnmerkleizedBatch {
219 inner: self.inner.new_batch(),
220 hasher: StandardHasher::new(self.bagging),
221 items: Vec::new(),
222 parent: Some(Arc::clone(self)),
223 }
224 }
225}
226
227impl<F: Family, D: Digest, Item: Send + Sync, S: Strategy> Readable
228 for MerkleizedBatch<F, D, Item, S>
229{
230 type Family = F;
231 type Digest = D;
232
233 fn size(&self) -> Position<F> {
234 self.inner.size()
235 }
236
237 fn get_node(&self, pos: Position<F>) -> Option<D> {
238 self.inner.get_node(pos)
239 }
240}
241
242pub struct Journal<F, E, C, H, S>
247where
248 F: Family,
249 E: Context,
250 C: Contiguous<Item: EncodeShared>,
251 H: Hasher,
252 S: Strategy,
253{
254 pub(crate) merkle: Merkle<F, E, H::Digest, S>,
257
258 pub(crate) journal: C,
261
262 pub(crate) hasher: StandardHasher<H>,
263}
264
265impl<F, E, C, H, S> core::fmt::Debug for Journal<F, E, C, H, S>
266where
267 F: Family,
268 E: Context,
269 C: Contiguous<Item: EncodeShared>,
270 H: Hasher,
271 S: Strategy,
272{
273 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
274 f.debug_struct("Journal")
275 .field("size", &self.size())
276 .finish_non_exhaustive()
277 }
278}
279
280impl<F, E, C, H, S> Journal<F, E, C, H, S>
281where
282 F: Family,
283 E: Context,
284 C: Contiguous<Item: EncodeShared>,
285 H: Hasher,
286 S: Strategy,
287{
288 pub fn size(&self) -> Location<F> {
290 Location::new(self.journal.bounds().end)
291 }
292
293 pub fn root(&self, inactive_peaks: usize) -> Result<H::Digest, Error<F>> {
296 self.merkle
297 .root(&self.hasher, inactive_peaks)
298 .map_err(Into::into)
299 }
300
301 fn map_error(error: Error<F>) -> JournalError {
303 match error {
304 Error::Journal(inner) => inner,
305 Error::Merkle(inner) => JournalError::Merkle(anyhow::Error::from(inner)),
306 }
307 }
308
309 pub const fn strategy(&self) -> &S {
311 self.merkle.strategy()
312 }
313
314 pub fn new_batch(&self) -> UnmerkleizedBatch<F, H, C::Item, S>
316 where
317 C::Item: Encode,
318 {
319 let root = self.merkle.to_batch();
320 UnmerkleizedBatch {
321 inner: root.new_batch(),
322 hasher: StandardHasher::new(self.hasher.root_bagging()),
323 items: Vec::new(),
324 parent: None,
325 }
326 }
327
328 pub(crate) async fn merkleize(
336 &self,
337 batch: UnmerkleizedBatch<F, H, C::Item, S>,
338 items: Vec<C::Item>,
339 inactive_peaks: usize,
340 ) -> Result<(MerkleizedBatchArc<F, H, C::Item, S>, H::Digest), merkle::Error<F>>
341 where
342 C::Item: 'static,
343 {
344 let ancestors = batch.inner.retain_ancestors();
345 let mem = self.merkle.snapshot();
346 let hasher = self.hasher.clone();
347 let strategy = self.strategy().clone();
348 strategy
349 .spawn(items.len(), move |_| {
350 let merkleized = batch.add_many(items).merkleize(&mem);
351 let root = merkleized.root(&mem, &hasher, inactive_peaks)?;
352 drop(ancestors);
353 Ok((merkleized, root))
354 })
355 .await
356 }
357
358 pub(crate) fn to_merkleized_batch(&self) -> Arc<MerkleizedBatch<F, H::Digest, C::Item, S>> {
363 Arc::new(MerkleizedBatch {
364 inner: self.merkle.to_batch(),
365 bagging: self.hasher.root_bagging(),
366 items: Arc::new(Vec::new()),
367 parent: None,
368 ancestor_base_leaves: *self.size(),
369 ancestor_items: Vec::new(),
370 })
371 }
372}
373
374impl<F, E, C, H, S> Journal<F, E, C, H, S>
375where
376 F: Family,
377 E: Context,
378 C: Mutable<Item: EncodeShared>,
379 H: Hasher,
380 S: Strategy,
381{
382 pub async fn start_sync(mut self) -> Result<(Self, Handle<()>), Error<F>> {
388 let (journal_handle, merkle_handle);
389 ((self.journal, journal_handle), (self.merkle, merkle_handle)) = try_join!(
390 self.journal.start_sync().map_err(Error::Journal),
391 self.merkle.start_sync().map_err(Error::Merkle)
392 )?;
393
394 let handle =
395 Handle::from_future(
396 async move { try_join!(journal_handle, merkle_handle).map(|_| ()) },
397 );
398 Ok((self, handle))
399 }
400
401 pub async fn commit(mut self) -> Result<Self, Error<F>> {
405 (self.journal, self.merkle) = try_join!(
408 self.journal.commit().map_err(Error::Journal),
409 self.merkle.flush().map_err(Error::Merkle)
410 )?;
411
412 Ok(self)
413 }
414}
415
416impl<F, E, C, H, S> Journal<F, E, C, H, S>
417where
418 F: Family,
419 E: Context,
420 C: Mutable<Item: EncodeShared>,
421 H: Hasher,
422 S: Strategy,
423{
424 #[boxed]
427 pub async fn from_components(
428 merkle: Merkle<F, E, H::Digest, S>,
429 journal: C,
430 hasher: StandardHasher<H>,
431 apply_batch_size: u64,
432 ) -> Result<Self, Error<F>> {
433 let merkle = Self::align(merkle, &journal, &hasher, apply_batch_size).await?;
434
435 let merkle = merkle.sync().await?;
438
439 Ok(Self {
440 merkle,
441 journal,
442 hasher,
443 })
444 }
445
446 async fn align(
452 mut merkle: Merkle<F, E, H::Digest, S>,
453 journal: &C,
454 hasher: &StandardHasher<H>,
455 apply_batch_size: u64,
456 ) -> Result<Merkle<F, E, H::Digest, S>, Error<F>> {
457 let journal_size = journal.bounds().end;
459 let mut merkle_leaves = merkle.leaves();
460 if merkle_leaves > journal_size {
461 let rewind_count = merkle_leaves - journal_size;
462 warn!(
463 journal_size,
464 ?rewind_count,
465 "rewinding Merkle structure to match journal"
466 );
467 merkle = merkle.rewind(*rewind_count as usize).await?;
468 merkle_leaves = Location::new(journal_size);
469 }
470
471 if merkle_leaves < journal_size {
473 let replay_count = journal_size - *merkle_leaves;
474 warn!(
475 ?journal_size,
476 replay_count, "Merkle structure lags behind journal, replaying journal to catch up"
477 );
478
479 while merkle_leaves < journal_size {
480 let count = apply_batch_size.min(journal_size - *merkle_leaves);
481 let mut items = Vec::with_capacity(count as usize);
482 for _ in 0..count {
483 items.push(journal.read(*merkle_leaves).await?);
484 merkle_leaves += 1;
485 }
486
487 let batch = merkle.new_batch().add_many(hasher, &items);
488 let batch = merkle.with_mem(|mem| batch.merkleize(mem, hasher));
489 merkle = merkle.apply_batch(&batch)?;
490 }
491 return Ok(merkle);
492 }
493
494 assert_eq!(journal.bounds().end, *merkle.leaves());
496
497 Ok(merkle)
498 }
499
500 pub async fn append(mut self, item: &C::Item) -> Result<(Self, Location<F>), Error<F>> {
502 let encoded_item = item.encode();
503
504 let loc;
506 (self.journal, loc) = self.journal.append(item).await?;
507 let unmerkleized_batch = self.merkle.new_batch().add(&self.hasher, &encoded_item);
508 let batch = self
509 .merkle
510 .with_mem(|mem| unmerkleized_batch.merkleize(mem, &self.hasher));
511 self.merkle = self.merkle.apply_batch(&batch)?;
512
513 Ok((self, Location::new(loc)))
514 }
515
516 pub async fn apply_batch(
523 mut self,
524 batch: &MerkleizedBatch<F, H::Digest, C::Item, S>,
525 ) -> Result<Self, Error<F>> {
526 let merkle_size = self.merkle.size();
527 let base_size = batch.inner.base_size();
528
529 let skip_ancestors = if merkle_size == base_size {
535 false
536 } else if merkle_size > base_size && merkle_size < batch.inner.size() {
537 true
538 } else {
539 return Err(merkle::Error::StaleBatch {
542 expected: base_size,
543 actual: merkle_size,
544 }
545 .into());
546 };
547
548 let committed_leaves = self.journal.bounds().end;
553 if committed_leaves < batch.ancestor_base_leaves {
554 return Err(merkle::Error::AncestorDropped {
555 expected: batch.inner.size(),
556 actual: merkle_size,
557 }
558 .into());
559 }
560
561 let mut batch_leaf_end = batch.ancestor_base_leaves;
562 let mut batches: Vec<&[C::Item]> = Vec::with_capacity(batch.ancestor_items.len() + 1);
563 for ancestor in &batch.ancestor_items {
564 batch_leaf_end += ancestor.len() as u64;
565 if skip_ancestors && batch_leaf_end <= committed_leaves {
566 continue;
567 }
568 batches.push(ancestor);
569 }
570 if !batch.items.is_empty() {
571 batches.push(&batch.items);
572 }
573 if !batches.is_empty() {
574 (self.journal, _) = self.journal.append_many(Many::Nested(&batches)).await?;
575 }
576
577 self.merkle = self.merkle.apply_batch(&batch.inner)?;
578 assert_eq!(*self.merkle.leaves(), self.journal.bounds().end);
579 Ok(self)
580 }
581
582 #[boxed]
584 pub async fn rewind(mut self, size: u64) -> Result<Self, Error<F>> {
585 self.journal = self.journal.rewind(size).await?;
586
587 let leaves = *self.merkle.leaves();
588 if leaves > size {
589 self.merkle = self.merkle.rewind((leaves - size) as usize).await?;
590 }
591
592 Ok(self)
593 }
594
595 #[boxed]
600 pub async fn prune(self, prune_loc: Location<F>) -> Result<(Self, Location<F>), Error<F>> {
601 let (journal, boundary, _) = self.prune_inner(prune_loc).await?;
602 Ok((journal, boundary))
603 }
604
605 async fn prune_inner(
606 mut self,
607 prune_loc: Location<F>,
608 ) -> Result<(Self, Location<F>, bool), Error<F>> {
609 if self.merkle.size() == 0 {
610 let boundary = Location::new(self.journal.bounds().start);
612 return Ok((self, boundary, false));
613 }
614
615 (self.journal, self.merkle) = try_join!(
621 self.journal.commit().map_err(Error::Journal),
622 self.merkle.sync().map_err(Error::Merkle)
623 )?;
624
625 let journal_pruned;
626 (self.journal, journal_pruned) = self.journal.prune(*prune_loc).await?;
627 let bounds = self.journal.bounds();
628 let boundary = Location::new(bounds.start);
629 let merkle_boundary = self.merkle.bounds().start;
630
631 if boundary > merkle_boundary {
632 debug!(size = ?bounds.end, ?prune_loc, boundary = ?bounds.start, "pruned inactive ops");
633 self.merkle = self.merkle.prune(boundary).await?;
634 }
635
636 Ok((self, boundary, journal_pruned || boundary > merkle_boundary))
637 }
638}
639
640impl<F, E, C, H, S> Journal<F, E, C, H, S>
641where
642 F: Family,
643 E: Context,
644 C: Contiguous<Item: EncodeShared>,
645 H: Hasher,
646 S: Strategy,
647{
648 pub async fn proof(
662 &self,
663 start_loc: Location<F>,
664 max_ops: NonZeroU64,
665 inactive_peaks: usize,
666 ) -> Result<(Proof<F, H::Digest>, Vec<C::Item>), Error<F>> {
667 self.historical_proof(self.size(), start_loc, max_ops, inactive_peaks)
668 .await
669 }
670
671 pub fn speculative_proof(
677 &self,
678 batch: &MerkleizedBatch<F, H::Digest, C::Item, S>,
679 inactive_peaks: usize,
680 ) -> Result<Proof<F, H::Digest>, Error<F>> {
681 let end = batch.size();
682 let start = Location::new(end - batch.items().len() as u64);
683 self.merkle
684 .with_mem(|mem| {
685 batch.range_proof(mem, &self.hasher, start..Location::new(end), inactive_peaks)
686 })
687 .map_err(Error::Merkle)
688 }
689
690 pub fn speculative_pinned_nodes(
696 &self,
697 batch: &MerkleizedBatch<F, H::Digest, C::Item, S>,
698 ) -> Result<Vec<H::Digest>, Error<F>> {
699 let start = Location::new(batch.size() - batch.items().len() as u64);
700 self.merkle
701 .with_mem(|mem| {
702 F::nodes_to_pin(start)
703 .map(|pos| {
704 batch
705 .get_node(pos)
706 .or_else(|| mem.get_node(pos))
707 .ok_or(merkle::Error::ElementPruned(pos))
708 })
709 .collect::<Result<Vec<_>, _>>()
710 })
711 .map_err(Error::Merkle)
712 }
713
714 pub async fn historical_proof(
727 &self,
728 historical_leaves: Location<F>,
729 start_loc: Location<F>,
730 max_ops: NonZeroU64,
731 inactive_peaks: usize,
732 ) -> Result<(Proof<F, H::Digest>, Vec<C::Item>), Error<F>> {
733 let bounds = self.journal.bounds();
734
735 if *historical_leaves > bounds.end {
736 return Err(merkle::Error::RangeOutOfBounds(Location::new(bounds.end)).into());
737 }
738 if start_loc >= historical_leaves {
739 return Err(merkle::Error::RangeOutOfBounds(start_loc).into());
740 }
741
742 let end_loc = std::cmp::min(historical_leaves, start_loc.saturating_add(max_ops.get()));
743
744 let hasher = self.hasher.clone();
745 let proof = self
746 .merkle
747 .historical_range_proof(
748 &hasher,
749 historical_leaves,
750 start_loc..end_loc,
751 inactive_peaks,
752 )
753 .await?;
754
755 let positions: Vec<u64> = (*start_loc..*end_loc).collect();
756 let ops = self.journal.read_many(&positions).await?;
757
758 Ok((proof, ops))
759 }
760}
761
762impl<F, E, C, H, S> Journal<F, E, C, H, S>
763where
764 F: Family,
765 E: Context,
766 C: Mutable<Item: EncodeShared>,
767 H: Hasher,
768 S: Strategy,
769{
770 #[boxed]
772 pub async fn destroy(self) -> Result<(), Error<F>> {
773 let Self {
776 journal, merkle, ..
777 } = self;
778 try_join!(
779 journal.destroy().map_err(Error::Journal),
780 merkle.destroy().map_err(Error::Merkle),
781 )?;
782
783 Ok(())
784 }
785
786 pub async fn sync(mut self) -> Result<Self, Error<F>> {
788 (self.journal, self.merkle) = try_join!(
789 self.journal.sync().map_err(Error::Journal),
790 self.merkle.sync().map_err(Error::Merkle)
791 )?;
792
793 Ok(self)
794 }
795}
796
797const APPLY_BATCH_SIZE: u64 = 1 << 16;
799
800impl<F, E, C, H, S> Journal<F, E, C, H, S>
801where
802 F: Family,
803 E: Context,
804 C: Backing<E, Item: EncodeShared>,
805 H: Hasher,
806 S: Strategy,
807{
808 #[boxed]
813 pub async fn new(
814 context: E,
815 merkle_cfg: merkle::full::Config<S>,
816 journal_cfg: C::Config,
817 rewind_predicate: fn(&C::Item) -> bool,
818 bagging: merkle::Bagging,
819 ) -> Result<Self, Error<F>> {
820 let journal = C::init(context.child("journal"), journal_cfg).await?;
821 let (journal, _) = journal.rewind_to(rewind_predicate).await?;
822
823 let hasher = StandardHasher::<H>::new(bagging);
824 let merkle = Merkle::init(context.child("merkle"), &hasher, merkle_cfg).await?;
825 let merkle = Self::align(merkle, &journal, &hasher, APPLY_BATCH_SIZE).await?;
826
827 let journal = journal.sync().await?;
828 let merkle = merkle.sync().await?;
829
830 Ok(Self {
831 merkle,
832 journal,
833 hasher,
834 })
835 }
836}
837
838impl<F, E, C, H, S> Journal<F, E, C, H, S>
839where
840 F: Family,
841 E: Context,
842 C: Contiguous<Item: EncodeShared>,
843 H: Hasher,
844 S: Strategy,
845{
846 pub(crate) async fn read_many_sharded(
854 &self,
855 positions: &[u64],
856 ) -> Result<Vec<Vec<C::Item>>, JournalError> {
857 if positions.is_empty() {
860 return Ok(Vec::new());
861 }
862
863 assert!(
870 positions.is_sorted_by(|a, b| a < b),
871 "positions must be strictly increasing"
872 );
873 let strategy = self.strategy();
874 let journal = &self.journal;
875
876 let probe = |positions: &[u64]| -> (Vec<C::Item>, Vec<usize>) {
878 let probed = journal.try_read_many_sync(positions);
879 let mut hits = Vec::with_capacity(probed.len());
880 let mut missed = Vec::new();
881 for (idx, item) in probed.into_iter().enumerate() {
882 match item {
883 Some(item) => hits.push(item),
884 None => missed.push(idx),
885 }
886 }
887 (hits, missed)
888 };
889 let shards: Vec<(Vec<C::Item>, Vec<usize>)> = strategy.run(
890 positions.len(),
891 || vec![probe(positions)],
892 || {
893 let manual = strategy.manual();
894 let shard_len = positions.len().div_ceil(manual.parallelism());
895 manual.map_collect_vec(positions.chunks(shard_len).collect::<Vec<_>>(), &probe)
896 },
897 );
898
899 let mut misses: Vec<u64> = Vec::new();
903 let mut offset = 0;
904 for (hits, missed) in &shards {
905 misses.extend(missed.iter().map(|idx| positions[offset + idx]));
906 offset += hits.len() + missed.len();
907 }
908 if misses.is_empty() {
909 return Ok(shards.into_iter().map(|(hits, _)| hits).collect());
910 }
911 let mut fetched = journal.read_many(&misses).await?.into_iter();
912
913 let mut result = Vec::with_capacity(shards.len());
915 for (hits, missed) in shards {
916 if missed.is_empty() {
917 result.push(hits);
918 continue;
919 }
920 let total = hits.len() + missed.len();
921 let mut woven = Vec::with_capacity(total);
922 let mut hits = hits.into_iter();
923 let mut missed = missed.into_iter().peekable();
924 for idx in 0..total {
925 if missed.next_if_eq(&idx).is_some() {
926 woven.push(fetched.next().expect("one fetched item per miss"));
927 } else {
928 woven.push(hits.next().expect("one probed item per hit"));
929 }
930 }
931 result.push(woven);
932 }
933 Ok(result)
934 }
935}
936
937impl<F, E, C, H, S> Contiguous for Journal<F, E, C, H, S>
938where
939 F: Family,
940 E: Context,
941 C: Contiguous<Item: EncodeShared>,
942 H: Hasher,
943 S: Strategy,
944{
945 type Item = C::Item;
946
947 fn bounds(&self) -> Range<u64> {
948 self.journal.bounds()
949 }
950
951 async fn read(&self, position: u64) -> Result<C::Item, JournalError> {
952 self.journal.read(position).await
953 }
954
955 async fn read_many(&self, positions: &[u64]) -> Result<Vec<C::Item>, JournalError> {
956 let mut shards = self.read_many_sharded(positions).await?;
957 if shards.len() == 1 {
958 return Ok(shards.pop().expect("length checked"));
959 }
960 let mut items = Vec::with_capacity(positions.len());
961 for shard in shards {
962 items.extend(shard);
963 }
964 Ok(items)
965 }
966
967 fn try_read_sync(&self, position: u64) -> Option<C::Item> {
968 self.journal.try_read_sync(position)
969 }
970
971 fn try_read_many_sync(&self, positions: &[u64]) -> Vec<Option<C::Item>> {
972 self.journal.try_read_many_sync(positions)
973 }
974
975 async fn replay(
976 &self,
977 start_pos: u64,
978 buffer: NonZeroUsize,
979 read_options: ReadOptions,
980 ) -> Result<impl Stream<Item = Result<(u64, C::Item), JournalError>> + Send, JournalError> {
981 self.journal.replay(start_pos, buffer, read_options).await
982 }
983}
984
985impl<F, E, C, H, S> Mutable for Journal<F, E, C, H, S>
986where
987 F: Family,
988 E: Context,
989 C: Mutable<Item: EncodeShared>,
990 H: Hasher,
991 S: Strategy,
992{
993 async fn append(self, item: &Self::Item) -> Result<(Self, u64), JournalError> {
994 let (journal, loc) = Self::append(self, item).await.map_err(Self::map_error)?;
995
996 Ok((journal, *loc))
997 }
998
999 async fn append_many(
1000 mut self,
1001 items: Many<'_, Self::Item>,
1002 ) -> Result<(Self, u64), JournalError> {
1003 if items.is_empty() {
1006 return Err(JournalError::EmptyAppend);
1007 }
1008
1009 let mut last_pos = self.journal.bounds().end;
1013 match items {
1014 Many::Flat(items) => {
1015 for item in items {
1016 let (journal, loc) = Self::append(self, item).await.map_err(Self::map_error)?;
1017 self = journal;
1018 last_pos = *loc;
1019 }
1020 }
1021 Many::Nested(nested_items) => {
1022 for items in nested_items {
1023 for item in *items {
1024 let (journal, loc) =
1025 Self::append(self, item).await.map_err(Self::map_error)?;
1026 self = journal;
1027 last_pos = *loc;
1028 }
1029 }
1030 }
1031 }
1032 Ok((self, last_pos))
1033 }
1034
1035 async fn prune(self, min_position: u64) -> Result<(Self, bool), JournalError> {
1036 let prune_to = {
1037 let bounds = self.journal.bounds();
1038 min_position.min(bounds.end)
1039 };
1040
1041 let (journal, _, pruned) = self
1042 .prune_inner(Location::new(prune_to))
1043 .await
1044 .map_err(Self::map_error)?;
1045 Ok((journal, pruned))
1046 }
1047
1048 async fn rewind(self, size: u64) -> Result<Self, JournalError> {
1049 Self::rewind(self, size).await.map_err(Self::map_error)
1050 }
1051
1052 async fn start_sync(self) -> Result<(Self, Handle<()>), JournalError> {
1053 Self::start_sync(self).await.map_err(Self::map_error)
1054 }
1055
1056 async fn commit(self) -> Result<Self, JournalError> {
1057 Self::commit(self).await.map_err(Self::map_error)
1058 }
1059
1060 async fn sync(self) -> Result<Self, JournalError> {
1061 Self::sync(self).await.map_err(Self::map_error)
1062 }
1063
1064 async fn destroy(self) -> Result<(), JournalError> {
1065 Self::destroy(self).await.map_err(Self::map_error)
1066 }
1067}
1068
1069pub trait Backing<E: Context>: Mutable {
1071 type Config: Clone + Send;
1073
1074 fn init(
1076 context: E,
1077 cfg: Self::Config,
1078 ) -> impl core::future::Future<Output = Result<Self, JournalError>> + Send
1079 where
1080 Self: Sized;
1081}
1082
1083#[cfg(test)]
1084mod tests {
1085 use super::*;
1086 use crate::{
1087 journal::contiguous::fixed::{Config as JConfig, Journal as ContiguousJournal},
1088 merkle::{
1089 Bagging::{BackwardFold, ForwardFold},
1090 full::{Config as MerkleConfig, Merkle},
1091 mmb, mmr,
1092 },
1093 qmdb::{
1094 any::{
1095 operation::{Unordered as Op, update::Unordered as Update},
1096 value::FixedEncoding,
1097 },
1098 operation::Committable,
1099 },
1100 utils::detached::{DropMonitor, block_strategy},
1101 };
1102 use commonware_codec::Encode;
1103 use commonware_cryptography::{Sha256, sha256::Digest};
1104 use commonware_macros::test_traced;
1105 use commonware_parallel::{Manual, Rayon, Sequential};
1106 use commonware_runtime::{
1107 BufferPooler, Runner as _, Spawner as _, Strategizer as _, Supervisor as _,
1108 buffer::paged::CacheRef,
1109 deterministic::{self, Context},
1110 mocks::{
1111 DelayedSyncContext, PendingSyncs, RecordingContext, drive_pending_syncs,
1112 fail_pending_syncs, next_pending_sync,
1113 },
1114 reschedule,
1115 };
1116 use commonware_utils::{NZU16, NZU64, NZUsize};
1117 use futures::StreamExt as _;
1118 use std::{
1119 future::Future,
1120 num::{NonZeroU16, NonZeroUsize},
1121 time::Duration,
1122 };
1123
1124 const PAGE_SIZE: NonZeroU16 = NZU16!(101);
1125 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11);
1126
1127 type TestOp<F> = Op<F, Digest, FixedEncoding<Digest>>;
1129
1130 type TestJournal<F> = Journal<
1132 F,
1133 deterministic::Context,
1134 ContiguousJournal<deterministic::Context, TestOp<F>>,
1135 Sha256,
1136 Sequential,
1137 >;
1138
1139 type RecordingTestJournal<F> = Journal<
1140 F,
1141 RecordingContext<deterministic::Context>,
1142 ContiguousJournal<RecordingContext<deterministic::Context>, TestOp<F>>,
1143 Sha256,
1144 Sequential,
1145 >;
1146
1147 fn journal_root<F: Family>(journal: &TestJournal<F>) -> Digest {
1148 journal.root(0).unwrap()
1149 }
1150
1151 fn batch_root<F: Family>(
1152 journal: &TestJournal<F>,
1153 batch: &MerkleizedBatch<F, Digest, TestOp<F>, Sequential>,
1154 ) -> Digest {
1155 journal
1156 .merkle
1157 .with_mem(|mem| batch.root(mem, &journal.hasher, 0))
1158 .unwrap()
1159 }
1160
1161 fn merkleize_with<F: Family + PartialEq>(
1162 batch: UnmerkleizedBatch<F, Sha256, TestOp<F>, Sequential>,
1163 base: &Mem<F, Digest>,
1164 items: Vec<TestOp<F>>,
1165 ) -> MerkleizedBatchArc<F, Sha256, TestOp<F>, Sequential> {
1166 batch.add_many(items).merkleize(base)
1167 }
1168
1169 fn merkle_config_with<S: Strategy>(
1171 suffix: &str,
1172 pooler: &impl BufferPooler,
1173 strategy: S,
1174 ) -> MerkleConfig<S> {
1175 MerkleConfig {
1176 journal_partition: format!("mmr-journal-{suffix}"),
1177 metadata_partition: format!("mmr-metadata-{suffix}"),
1178 items_per_blob: NZU64!(11),
1179 write_buffer: NZUsize!(1024),
1180 replay_buffer: NZUsize!(1024),
1181 strategy,
1182 page_cache: CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE),
1183 }
1184 }
1185
1186 fn merkle_config(suffix: &str, pooler: &impl BufferPooler) -> MerkleConfig<Sequential> {
1188 merkle_config_with(suffix, pooler, Sequential)
1189 }
1190
1191 fn journal_config(suffix: &str, pooler: &impl BufferPooler) -> JConfig {
1193 JConfig {
1194 partition: format!("journal-{suffix}"),
1195 items_per_blob: NZU64!(7),
1196 write_buffer: NZUsize!(1024),
1197 replay_buffer: NZUsize!(1024),
1198 page_cache: CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE),
1199 }
1200 }
1201
1202 async fn create_empty_journal<F: Family + PartialEq>(
1204 context: Context,
1205 suffix: &str,
1206 ) -> TestJournal<F> {
1207 let merkle_cfg = merkle_config(suffix, &context);
1208 let journal_cfg = journal_config(suffix, &context);
1209 TestJournal::<F>::new(
1210 context,
1211 merkle_cfg,
1212 journal_cfg,
1213 |op: &TestOp<F>| op.is_commit(),
1214 ForwardFold,
1215 )
1216 .await
1217 .unwrap()
1218 }
1219
1220 #[test]
1221 fn test_batches_inherit_journal_bagging() {
1222 deterministic::Runner::default().start(|context| async move {
1223 let merkle_cfg = merkle_config("batch-bagging", &context);
1224 let journal_cfg = journal_config("batch-bagging", &context);
1225 let journal = TestJournal::<mmr::Family>::new(
1226 context,
1227 merkle_cfg,
1228 journal_cfg,
1229 |op: &TestOp<mmr::Family>| op.is_commit(),
1230 BackwardFold,
1231 )
1232 .await
1233 .unwrap();
1234
1235 let batch = journal.new_batch();
1236 assert_eq!(batch.hasher.root_bagging(), BackwardFold);
1237
1238 let merkleized = journal.merkle.with_mem(|mem| batch.merkleize(mem));
1239 let child: UnmerkleizedBatch<mmr::Family, Sha256, TestOp<mmr::Family>, Sequential> =
1240 merkleized.new_batch();
1241 assert_eq!(child.hasher.root_bagging(), BackwardFold);
1242 });
1243 }
1244
1245 #[test]
1247 fn test_read_many_shards_across_strategy_pool() {
1248 deterministic::Runner::default().start(|context| async move {
1249 let strategy = context.strategy(NZUsize!(2));
1254 let merkle_cfg = merkle_config_with("shard", &context, strategy);
1255 let journal_cfg = journal_config("shard", &context);
1256 type RayonJournal = Journal<
1257 mmr::Family,
1258 Context,
1259 ContiguousJournal<Context, TestOp<mmr::Family>>,
1260 Sha256,
1261 Rayon,
1262 >;
1263 let mut journal = RayonJournal::new(
1264 context,
1265 merkle_cfg,
1266 journal_cfg,
1267 |op: &TestOp<mmr::Family>| op.is_commit(),
1268 ForwardFold,
1269 )
1270 .await
1271 .unwrap();
1272
1273 let count = 4200u64;
1274 for i in 0..count {
1275 let op = create_operation::<mmr::Family>((i % 251) as u8);
1276 (journal, _) = journal.append(&op).await.unwrap();
1277 }
1278 let journal = journal.sync().await.unwrap();
1279
1280 let positions: Vec<u64> = (0..count).collect();
1281 let batch = Contiguous::read_many(&journal, &positions).await.unwrap();
1282 assert_eq!(batch.len(), positions.len());
1283 for &pos in &positions {
1284 let single = Contiguous::read(&journal, pos).await.unwrap();
1285 assert_eq!(batch[pos as usize], single);
1286 }
1287
1288 assert!(
1290 Contiguous::read_many(&journal, &[])
1291 .await
1292 .unwrap()
1293 .is_empty()
1294 );
1295 });
1296 }
1297
1298 #[test]
1300 #[should_panic(expected = "positions must be strictly increasing")]
1301 fn test_read_many_rejects_unsorted_positions() {
1302 deterministic::Runner::default().start(|context| async move {
1303 let mut journal = create_empty_journal::<mmr::Family>(context, "unsorted").await;
1304 for i in 0..2u8 {
1305 let op = create_operation::<mmr::Family>(i);
1306 (journal, _) = journal.append(&op).await.unwrap();
1307 }
1308 let journal = journal.sync().await.unwrap();
1309
1310 let _ = Contiguous::read_many(&journal, &[1, 0]).await;
1311 });
1312 }
1313
1314 fn create_operation<F: Family + PartialEq>(index: u8) -> TestOp<F> {
1316 TestOp::<F>::Update(Update(
1317 Sha256::fill(index),
1318 Sha256::fill(index.wrapping_add(1)),
1319 ))
1320 }
1321
1322 async fn create_journal_with_ops<F: Family + PartialEq>(
1326 context: Context,
1327 suffix: &str,
1328 count: usize,
1329 ) -> TestJournal<F> {
1330 let mut journal = create_empty_journal::<F>(context, suffix).await;
1331
1332 for i in 0..count {
1333 let op = create_operation::<F>(i as u8);
1334 let loc;
1335 (journal, loc) = journal.append(&op).await.unwrap();
1336 assert_eq!(loc, Location::<F>::new(i as u64));
1337 }
1338
1339 journal = journal.sync().await.unwrap();
1340 journal
1341 }
1342
1343 async fn create_components<F: Family + PartialEq>(
1349 context: Context,
1350 suffix: &str,
1351 ) -> (
1352 Merkle<F, deterministic::Context, Digest, Sequential>,
1353 ContiguousJournal<deterministic::Context, TestOp<F>>,
1354 StandardHasher<Sha256>,
1355 ) {
1356 let hasher = StandardHasher::new(ForwardFold);
1357 let merkle = Merkle::<F, _, Digest, Sequential>::init(
1358 context.child("mmr"),
1359 &hasher,
1360 merkle_config(suffix, &context),
1361 )
1362 .await
1363 .unwrap();
1364 let journal =
1365 ContiguousJournal::init(context.child("journal"), journal_config(suffix, &context))
1366 .await
1367 .unwrap();
1368 (merkle, journal, hasher)
1369 }
1370
1371 fn verify_proof<F: Family + PartialEq>(
1374 proof: &Proof<F, <Sha256 as commonware_cryptography::Hasher>::Digest>,
1375 operations: &[TestOp<F>],
1376 start_loc: Location<F>,
1377 root: &<Sha256 as commonware_cryptography::Hasher>::Digest,
1378 hasher: &StandardHasher<Sha256>,
1379 ) -> bool {
1380 let encoded_ops: Vec<_> = operations.iter().map(|op| op.encode()).collect();
1381 proof.verify_range_inclusion(hasher, &encoded_ops, start_loc, root)
1382 }
1383
1384 async fn test_new_creates_empty_journal_inner<F: Family + PartialEq>(context: Context) {
1386 let journal = create_empty_journal::<F>(context, "new-empty").await;
1387
1388 let bounds = journal.bounds();
1389 assert_eq!(bounds.end, 0);
1390 assert_eq!(bounds.start, 0);
1391 assert!(bounds.is_empty());
1392 }
1393
1394 #[test_traced("INFO")]
1395 fn test_new_creates_empty_journal_mmr() {
1396 let executor = deterministic::Runner::default();
1397 executor.start(test_new_creates_empty_journal_inner::<mmr::Family>);
1398 }
1399
1400 #[test_traced("INFO")]
1401 fn test_new_creates_empty_journal_mmb() {
1402 let executor = deterministic::Runner::default();
1403 executor.start(test_new_creates_empty_journal_inner::<mmb::Family>);
1404 }
1405
1406 async fn test_align_with_empty_mmr_and_journal_inner<F: Family + PartialEq>(context: Context) {
1408 let (merkle, journal, hasher) = create_components::<F>(context, "align-empty").await;
1409
1410 let merkle = TestJournal::<F>::align(merkle, &journal, &hasher, APPLY_BATCH_SIZE)
1411 .await
1412 .unwrap();
1413
1414 assert_eq!(merkle.leaves(), Location::<F>::new(0));
1415 assert_eq!(journal.size(), 0);
1416 }
1417
1418 #[test_traced("INFO")]
1419 fn test_align_with_empty_mmr_and_journal_mmr() {
1420 let executor = deterministic::Runner::default();
1421 executor.start(test_align_with_empty_mmr_and_journal_inner::<mmr::Family>);
1422 }
1423
1424 #[test_traced("INFO")]
1425 fn test_align_with_empty_mmr_and_journal_mmb() {
1426 let executor = deterministic::Runner::default();
1427 executor.start(test_align_with_empty_mmr_and_journal_inner::<mmb::Family>);
1428 }
1429
1430 async fn test_align_when_mmr_ahead_inner<F: Family + PartialEq>(context: Context) {
1432 let (mut merkle, mut journal, hasher) = create_components::<F>(context, "mmr-ahead").await;
1433
1434 {
1436 let batch = {
1437 let mut batch = merkle.new_batch();
1438 for i in 0..20 {
1439 let op = create_operation::<F>(i as u8);
1440 let encoded = op.encode();
1441 batch = batch.add(&hasher, &encoded);
1442 (journal, _) = journal.append(&op).await.unwrap();
1443 }
1444 batch
1445 };
1446 let batch = merkle.with_mem(|mem| batch.merkleize(mem, &hasher));
1447 merkle = merkle.apply_batch(&batch).unwrap();
1448 }
1449
1450 let commit_op = TestOp::<F>::CommitFloor(None, Location::<F>::new(0));
1452 let (journal, _) = journal.append(&commit_op).await.unwrap();
1453 let journal = journal.sync().await.unwrap();
1454
1455 let merkle = TestJournal::<F>::align(merkle, &journal, &hasher, APPLY_BATCH_SIZE)
1457 .await
1458 .unwrap();
1459
1460 assert_eq!(merkle.leaves(), Location::<F>::new(21));
1462 assert_eq!(journal.size(), 21);
1463 }
1464
1465 #[test_traced("WARN")]
1466 fn test_align_when_mmr_ahead_mmr() {
1467 let executor = deterministic::Runner::default();
1468 executor.start(test_align_when_mmr_ahead_inner::<mmr::Family>);
1469 }
1470
1471 #[test_traced("WARN")]
1472 fn test_align_when_mmr_ahead_mmb() {
1473 let executor = deterministic::Runner::default();
1474 executor.start(test_align_when_mmr_ahead_inner::<mmb::Family>);
1475 }
1476
1477 async fn test_align_when_journal_ahead_inner<F: Family + PartialEq>(context: Context) {
1479 let (merkle, mut journal, hasher) = create_components::<F>(context, "journal-ahead").await;
1480
1481 for i in 0..20 {
1483 let op = create_operation::<F>(i as u8);
1484 (journal, _) = journal.append(&op).await.unwrap();
1485 }
1486
1487 let commit_op = TestOp::<F>::CommitFloor(None, Location::<F>::new(0));
1489 let (journal, _) = journal.append(&commit_op).await.unwrap();
1490 let journal = journal.sync().await.unwrap();
1491
1492 let merkle = TestJournal::<F>::align(merkle, &journal, &hasher, APPLY_BATCH_SIZE)
1494 .await
1495 .unwrap();
1496
1497 assert_eq!(merkle.leaves(), Location::<F>::new(21));
1499 assert_eq!(journal.size(), 21);
1500 }
1501
1502 #[test_traced("WARN")]
1503 fn test_align_when_journal_ahead_mmr() {
1504 let executor = deterministic::Runner::default();
1505 executor.start(test_align_when_journal_ahead_inner::<mmr::Family>);
1506 }
1507
1508 #[test_traced("WARN")]
1509 fn test_align_when_journal_ahead_mmb() {
1510 let executor = deterministic::Runner::default();
1511 executor.start(test_align_when_journal_ahead_inner::<mmb::Family>);
1512 }
1513
1514 async fn test_align_replay_parallel_matches_serial_inner<F: Family + PartialEq>(
1516 context: Context,
1517 ) {
1518 type ParallelJournal<F> = Journal<
1519 F,
1520 deterministic::Context,
1521 ContiguousJournal<deterministic::Context, TestOp<F>>,
1522 Sha256,
1523 Manual<Rayon>,
1524 >;
1525
1526 let mut journal = ContiguousJournal::init(
1528 context.child("journal"),
1529 journal_config("replay-strategies", &context),
1530 )
1531 .await
1532 .unwrap();
1533 for i in 0..20 {
1534 (journal, _) = journal
1535 .append(&create_operation::<F>(i as u8))
1536 .await
1537 .unwrap();
1538 }
1539 let commit_op = TestOp::<F>::CommitFloor(None, Location::<F>::new(0));
1540 let (journal, _) = journal.append(&commit_op).await.unwrap();
1541 let journal = journal.sync().await.unwrap();
1542
1543 let hasher = StandardHasher::<Sha256>::new(ForwardFold);
1548 let serial = Merkle::<F, _, Digest, Sequential>::init(
1549 context.child("mmr_serial"),
1550 &hasher,
1551 merkle_config("replay-serial", &context),
1552 )
1553 .await
1554 .unwrap();
1555 let serial = TestJournal::<F>::align(serial, &journal, &hasher, 7)
1556 .await
1557 .unwrap();
1558
1559 let parallel = Merkle::<F, _, Digest, Manual<Rayon>>::init(
1560 context.child("mmr_parallel"),
1561 &hasher,
1562 merkle_config_with(
1563 "replay-parallel",
1564 &context,
1565 Rayon::new(NZUsize!(2)).unwrap().manual(),
1566 ),
1567 )
1568 .await
1569 .unwrap();
1570 let parallel = ParallelJournal::<F>::align(parallel, &journal, &hasher, 7)
1571 .await
1572 .unwrap();
1573
1574 assert_eq!(serial.leaves(), Location::<F>::new(21));
1575 assert_eq!(parallel.leaves(), Location::<F>::new(21));
1576 assert_eq!(
1577 serial.root(&hasher, 0).unwrap(),
1578 parallel.root(&hasher, 0).unwrap()
1579 );
1580 }
1581
1582 #[test_traced("WARN")]
1583 fn test_align_replay_parallel_matches_serial_mmr() {
1584 let executor = deterministic::Runner::default();
1585 executor.start(test_align_replay_parallel_matches_serial_inner::<mmr::Family>);
1586 }
1587
1588 #[test_traced("WARN")]
1589 fn test_align_replay_parallel_matches_serial_mmb() {
1590 let executor = deterministic::Runner::default();
1591 executor.start(test_align_replay_parallel_matches_serial_inner::<mmb::Family>);
1592 }
1593
1594 async fn test_align_with_mismatched_committed_ops_inner<F: Family + PartialEq>(
1596 context: Context,
1597 ) {
1598 let mut journal = create_empty_journal::<F>(context.child("first"), "mismatched").await;
1599
1600 for i in 0..20 {
1602 let loc;
1603 (journal, loc) = journal
1604 .append(&create_operation::<F>(i as u8))
1605 .await
1606 .unwrap();
1607 assert_eq!(loc, Location::<F>::new(i as u64));
1608 }
1609
1610 let size_before = journal.size();
1613 assert_eq!(size_before, 20);
1614
1615 journal.sync().await.unwrap();
1617 let journal = create_empty_journal::<F>(context.child("second"), "mismatched").await;
1618
1619 assert_eq!(journal.size(), 0);
1621 }
1622
1623 #[test_traced("INFO")]
1624 fn test_align_with_mismatched_committed_ops_mmr() {
1625 let executor = deterministic::Runner::default();
1626 executor.start(|context| {
1627 test_align_with_mismatched_committed_ops_inner::<mmr::Family>(context)
1628 });
1629 }
1630
1631 #[test_traced("INFO")]
1632 fn test_align_with_mismatched_committed_ops_mmb() {
1633 let executor = deterministic::Runner::default();
1634 executor.start(|context| {
1635 test_align_with_mismatched_committed_ops_inner::<mmb::Family>(context)
1636 });
1637 }
1638
1639 async fn test_rewind_inner<F: Family + PartialEq>(context: Context) {
1640 {
1642 let mut journal = ContiguousJournal::init(
1643 context.child("rewind_match"),
1644 journal_config("rewind-match", &context),
1645 )
1646 .await
1647 .unwrap();
1648
1649 for i in 0..3 {
1651 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1652 }
1653 (journal, _) = journal
1654 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
1655 .await
1656 .unwrap();
1657 for i in 4..7 {
1658 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1659 }
1660
1661 let final_size;
1663 (journal, final_size) = journal.rewind_to(|op| op.is_commit()).await.unwrap();
1664 assert_eq!(final_size, 4);
1665 assert_eq!(journal.size(), 4);
1666
1667 let op = journal.read(3).await.unwrap();
1669 assert!(op.is_commit());
1670 }
1671
1672 {
1674 let mut journal = ContiguousJournal::init(
1675 context.child("rewind_multiple"),
1676 journal_config("rewind-multiple", &context),
1677 )
1678 .await
1679 .unwrap();
1680
1681 (journal, _) = journal.append(&create_operation::<F>(0)).await.unwrap();
1683 (journal, _) = journal
1684 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
1685 .await
1686 .unwrap(); (journal, _) = journal.append(&create_operation::<F>(2)).await.unwrap();
1688 (journal, _) = journal
1689 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(1)))
1690 .await
1691 .unwrap(); (journal, _) = journal.append(&create_operation::<F>(4)).await.unwrap();
1693
1694 let final_size;
1696 (journal, final_size) = journal.rewind_to(|op| op.is_commit()).await.unwrap();
1697 assert_eq!(final_size, 4);
1698
1699 let op = journal.read(3).await.unwrap();
1701 assert!(op.is_commit());
1702
1703 assert!(journal.read(4).await.is_err());
1705 }
1706
1707 {
1709 let mut journal = ContiguousJournal::init(
1710 context.child("rewind_no_match"),
1711 journal_config("rewind-no-match", &context),
1712 )
1713 .await
1714 .unwrap();
1715
1716 for i in 0..10 {
1718 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1719 }
1720
1721 let final_size;
1723 (journal, final_size) = journal.rewind_to(|op| op.is_commit()).await.unwrap();
1724 assert_eq!(final_size, 0, "Should rewind to pruning boundary (0)");
1725 assert_eq!(journal.size(), 0);
1726 }
1727
1728 {
1730 let mut journal = ContiguousJournal::init(
1731 context.child("rewind_with_pruning"),
1732 journal_config("rewind-with-pruning", &context),
1733 )
1734 .await
1735 .unwrap();
1736
1737 for i in 0..10 {
1739 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1740 }
1741 (journal, _) = journal
1742 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
1743 .await
1744 .unwrap(); for i in 11..15 {
1746 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1747 }
1748 journal = journal.sync().await.unwrap();
1749
1750 (journal, _) = journal.prune(8).await.unwrap();
1752 assert_eq!(journal.bounds().start, 7);
1753
1754 for i in 15..20 {
1756 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1757 }
1758
1759 let final_size;
1761 (journal, final_size) = journal.rewind_to(|op| op.is_commit()).await.unwrap();
1762 assert_eq!(final_size, 11);
1763
1764 let op = journal.read(10).await.unwrap();
1766 assert!(op.is_commit());
1767 }
1768
1769 {
1771 let mut journal = ContiguousJournal::init(
1772 context.child("rewind_no_match_pruned"),
1773 journal_config("rewind-no-match-pruned", &context),
1774 )
1775 .await
1776 .unwrap();
1777
1778 for i in 0..5 {
1780 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1781 }
1782 (journal, _) = journal
1783 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
1784 .await
1785 .unwrap(); for i in 6..10 {
1787 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1788 }
1789 journal = journal.sync().await.unwrap();
1790
1791 (journal, _) = journal.prune(8).await.unwrap();
1794 assert_eq!(journal.bounds().start, 7);
1795
1796 for i in 10..14 {
1798 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1799 }
1800
1801 let (_, final_size) = journal.rewind_to(|op| op.is_commit()).await.unwrap();
1804 assert_eq!(final_size, 7);
1805 }
1806
1807 {
1809 let mut journal = ContiguousJournal::init(
1810 context.child("rewind_empty"),
1811 journal_config("rewind-empty", &context),
1812 )
1813 .await
1814 .unwrap();
1815
1816 let final_size;
1818 (journal, final_size) = journal
1819 .rewind_to(|op: &TestOp<F>| op.is_commit())
1820 .await
1821 .unwrap();
1822 assert_eq!(final_size, 0);
1823 assert_eq!(journal.size(), 0);
1824 }
1825
1826 {
1828 let merkle_cfg = merkle_config("rewind", &context);
1829 let journal_cfg = journal_config("rewind", &context);
1830 let mut journal = TestJournal::<F>::new(
1831 context.child("rewind"),
1832 merkle_cfg,
1833 journal_cfg,
1834 |op| op.is_commit(),
1835 ForwardFold,
1836 )
1837 .await
1838 .unwrap();
1839
1840 for i in 0..5 {
1842 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1843 }
1844 (journal, _) = journal
1845 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
1846 .await
1847 .unwrap(); for i in 6..10 {
1849 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1850 }
1851 assert_eq!(journal.size(), 10);
1852
1853 journal = journal.rewind(2).await.unwrap();
1854 assert_eq!(journal.size(), 2);
1855 assert_eq!(journal.merkle.leaves(), 2);
1856 assert_eq!(journal.merkle.size(), 3);
1857 let bounds = journal.bounds();
1858 assert_eq!(bounds.start, 0);
1859 assert!(!bounds.is_empty());
1860
1861 journal = journal.rewind(0).await.unwrap();
1862 assert_eq!(journal.size(), 0);
1863 assert_eq!(journal.merkle.leaves(), 0);
1864 assert_eq!(journal.merkle.size(), 0);
1865 let bounds = journal.bounds();
1866 assert_eq!(bounds.start, 0);
1867 assert!(bounds.is_empty());
1868
1869 for i in 0..255 {
1871 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1872 }
1873 (journal, _) = journal.prune(Location::<F>::new(100)).await.unwrap();
1874 assert_eq!(journal.bounds().start, 98);
1875 journal = journal.rewind(98).await.unwrap();
1876 let bounds = journal.bounds();
1877 assert_eq!(bounds.end, 98);
1878 assert_eq!(journal.merkle.leaves(), 98);
1879 assert_eq!(bounds.start, 98);
1880 assert!(bounds.is_empty());
1881
1882 let res = journal.rewind(97).await;
1884 assert!(matches!(
1885 res,
1886 Err(Error::Journal(JournalError::ItemPruned(97)))
1887 ));
1888 }
1889
1890 {
1892 let merkle_cfg = merkle_config("rewind-invalid", &context);
1893 let journal_cfg = journal_config("rewind-invalid", &context);
1894 let mut journal = TestJournal::<F>::new(
1895 context,
1896 merkle_cfg,
1897 journal_cfg,
1898 |op| op.is_commit(),
1899 ForwardFold,
1900 )
1901 .await
1902 .unwrap();
1903
1904 for i in 0..2 {
1905 (journal, _) = journal.append(&create_operation::<F>(i)).await.unwrap();
1906 }
1907 assert!(matches!(
1908 journal.rewind(3).await,
1909 Err(Error::Journal(JournalError::InvalidRewind(_)))
1910 ));
1911 }
1912 }
1913
1914 #[test_traced("INFO")]
1915 fn test_rewind_mmr() {
1916 let executor = deterministic::Runner::default();
1917 executor.start(test_rewind_inner::<mmr::Family>);
1918 }
1919
1920 #[test_traced("INFO")]
1921 fn test_rewind_mmb() {
1922 let executor = deterministic::Runner::default();
1923 executor.start(test_rewind_inner::<mmb::Family>);
1924 }
1925
1926 async fn test_apply_op_and_read_operations_inner<F: Family + PartialEq>(context: Context) {
1929 let mut journal = create_empty_journal::<F>(context, "apply_op").await;
1930
1931 assert_eq!(journal.size(), 0);
1932
1933 let expected_ops: Vec<_> = (0..50).map(|i| create_operation::<F>(i as u8)).collect();
1935 for (i, op) in expected_ops.iter().enumerate() {
1936 let loc;
1937 (journal, loc) = journal.append(op).await.unwrap();
1938 assert_eq!(loc, Location::<F>::new(i as u64));
1939 assert_eq!(journal.size(), (i + 1) as u64);
1940 }
1941
1942 assert_eq!(journal.size(), 50);
1943
1944 journal = journal.sync().await.unwrap();
1946 for (i, expected_op) in expected_ops.iter().enumerate() {
1947 let read_op = journal.read(*Location::<F>::new(i as u64)).await.unwrap();
1948 assert_eq!(read_op, *expected_op);
1949 }
1950 }
1951
1952 #[test_traced("INFO")]
1953 fn test_apply_op_and_read_operations_mmr() {
1954 let executor = deterministic::Runner::default();
1955 executor.start(test_apply_op_and_read_operations_inner::<mmr::Family>);
1956 }
1957
1958 #[test_traced("INFO")]
1959 fn test_apply_op_and_read_operations_mmb() {
1960 let executor = deterministic::Runner::default();
1961 executor.start(test_apply_op_and_read_operations_inner::<mmb::Family>);
1962 }
1963
1964 async fn test_read_operations_at_various_positions_inner<F: Family + PartialEq>(
1966 context: Context,
1967 ) {
1968 let journal = create_journal_with_ops::<F>(context, "read", 50).await;
1969
1970 let first_op = journal.read(*Location::<F>::new(0)).await.unwrap();
1972 assert_eq!(first_op, create_operation::<F>(0));
1973
1974 let middle_op = journal.read(*Location::<F>::new(25)).await.unwrap();
1976 assert_eq!(middle_op, create_operation::<F>(25));
1977
1978 let last_op = journal.read(*Location::<F>::new(49)).await.unwrap();
1980 assert_eq!(last_op, create_operation::<F>(49));
1981
1982 for i in 0..50 {
1984 let op = journal.read(*Location::<F>::new(i)).await.unwrap();
1985 assert_eq!(op, create_operation::<F>(i as u8));
1986 }
1987 }
1988
1989 #[test_traced("INFO")]
1990 fn test_read_operations_at_various_positions_mmr() {
1991 let executor = deterministic::Runner::default();
1992 executor.start(|context| {
1993 test_read_operations_at_various_positions_inner::<mmr::Family>(context)
1994 });
1995 }
1996
1997 #[test_traced("INFO")]
1998 fn test_read_operations_at_various_positions_mmb() {
1999 let executor = deterministic::Runner::default();
2000 executor.start(|context| {
2001 test_read_operations_at_various_positions_inner::<mmb::Family>(context)
2002 });
2003 }
2004
2005 async fn test_read_pruned_operation_returns_error_inner<F: Family + PartialEq>(
2007 context: Context,
2008 ) {
2009 let mut journal = create_journal_with_ops::<F>(context, "read_pruned", 100).await;
2010
2011 (journal, _) = journal
2013 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2014 .await
2015 .unwrap();
2016 journal = journal.sync().await.unwrap();
2017 let pruned_boundary;
2018 (journal, pruned_boundary) = journal.prune(Location::<F>::new(50)).await.unwrap();
2019
2020 let read_loc = Location::<F>::new(0);
2022 if read_loc < pruned_boundary {
2023 let result = journal.read(*read_loc).await;
2024 assert!(matches!(result, Err(crate::journal::Error::ItemPruned(_))));
2025 }
2026 }
2027
2028 #[test_traced("INFO")]
2029 fn test_read_pruned_operation_returns_error_mmr() {
2030 let executor = deterministic::Runner::default();
2031 executor.start(|context| {
2032 test_read_pruned_operation_returns_error_inner::<mmr::Family>(context)
2033 });
2034 }
2035
2036 #[test_traced("INFO")]
2037 fn test_read_pruned_operation_returns_error_mmb() {
2038 let executor = deterministic::Runner::default();
2039 executor.start(|context| {
2040 test_read_pruned_operation_returns_error_inner::<mmb::Family>(context)
2041 });
2042 }
2043
2044 async fn test_read_out_of_range_returns_error_inner<F: Family + PartialEq>(context: Context) {
2046 let journal = create_journal_with_ops::<F>(context, "read_oob", 3).await;
2047
2048 let result = journal.read(*Location::<F>::new(10)).await;
2050 assert!(matches!(
2051 result,
2052 Err(crate::journal::Error::ItemOutOfRange(_))
2053 ));
2054 }
2055
2056 #[test_traced("INFO")]
2057 fn test_read_out_of_range_returns_error_mmr() {
2058 let executor = deterministic::Runner::default();
2059 executor.start(test_read_out_of_range_returns_error_inner::<mmr::Family>);
2060 }
2061
2062 #[test_traced("INFO")]
2063 fn test_read_out_of_range_returns_error_mmb() {
2064 let executor = deterministic::Runner::default();
2065 executor.start(test_read_out_of_range_returns_error_inner::<mmb::Family>);
2066 }
2067
2068 async fn test_read_all_operations_back_correctly_inner<F: Family + PartialEq>(
2070 context: Context,
2071 ) {
2072 let journal = create_journal_with_ops::<F>(context, "read_all", 50).await;
2073
2074 assert_eq!(journal.size(), 50);
2075
2076 for i in 0..50 {
2078 let op = journal.read(*Location::<F>::new(i)).await.unwrap();
2079 assert_eq!(op, create_operation::<F>(i as u8));
2080 }
2081 }
2082
2083 #[test_traced("INFO")]
2084 fn test_read_all_operations_back_correctly_mmr() {
2085 let executor = deterministic::Runner::default();
2086 executor.start(test_read_all_operations_back_correctly_inner::<mmr::Family>);
2087 }
2088
2089 #[test_traced("INFO")]
2090 fn test_read_all_operations_back_correctly_mmb() {
2091 let executor = deterministic::Runner::default();
2092 executor.start(test_read_all_operations_back_correctly_inner::<mmb::Family>);
2093 }
2094
2095 async fn test_sync_inner<F: Family + PartialEq>(context: Context) {
2097 let mut journal = create_empty_journal::<F>(context.child("first"), "close_pending").await;
2098
2099 let expected_ops: Vec<_> = (0..20).map(|i| create_operation::<F>(i as u8)).collect();
2101 for (i, op) in expected_ops.iter().enumerate() {
2102 let loc;
2103 (journal, loc) = journal.append(op).await.unwrap();
2104 assert_eq!(loc, Location::<F>::new(i as u64),);
2105 }
2106
2107 let commit_loc;
2109 (journal, commit_loc) = journal
2110 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
2111 .await
2112 .unwrap();
2113 assert_eq!(
2114 commit_loc,
2115 Location::<F>::new(20),
2116 "commit should be at location 20"
2117 );
2118 journal.sync().await.unwrap();
2119
2120 let journal = create_empty_journal::<F>(context.child("second"), "close_pending").await;
2122 assert_eq!(journal.size(), 21);
2123
2124 for (i, expected_op) in expected_ops.iter().enumerate() {
2126 let read_op = journal.read(*Location::<F>::new(i as u64)).await.unwrap();
2127 assert_eq!(read_op, *expected_op);
2128 }
2129 }
2130
2131 #[test_traced("INFO")]
2132 fn test_sync_mmr() {
2133 let executor = deterministic::Runner::default();
2134 executor.start(test_sync_inner::<mmr::Family>);
2135 }
2136
2137 #[test_traced("INFO")]
2138 fn test_sync_mmb() {
2139 let executor = deterministic::Runner::default();
2140 executor.start(test_sync_inner::<mmb::Family>);
2141 }
2142
2143 async fn test_start_sync_durability_inner<F: Family + PartialEq>(context: Context) {
2146 let mut journal = create_empty_journal::<F>(context.child("first"), "start_sync").await;
2147 let expected_ops: Vec<_> = (0..5).map(|i| create_operation::<F>(i as u8)).collect();
2148 for op in expected_ops.iter() {
2149 (journal, _) = journal.append(op).await.unwrap();
2150 }
2151 (journal, _) = journal
2152 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(0)))
2153 .await
2154 .unwrap();
2155
2156 let handle;
2157 (journal, handle) = journal.start_sync().await.unwrap();
2158 handle.await.unwrap();
2159 let root = journal_root(&journal);
2160 drop(journal);
2161
2162 let journal = create_empty_journal::<F>(context.child("second"), "start_sync").await;
2163 assert_eq!(journal.size(), 6);
2164 assert_eq!(journal_root(&journal), root);
2165 for (i, expected_op) in expected_ops.iter().enumerate() {
2166 let read_op = journal.read(*Location::<F>::new(i as u64)).await.unwrap();
2167 assert_eq!(read_op, *expected_op);
2168 }
2169 }
2170
2171 #[test_traced("INFO")]
2172 fn test_start_sync_durability_mmr() {
2173 let executor = deterministic::Runner::default();
2174 executor.start(test_start_sync_durability_inner::<mmr::Family>);
2175 }
2176
2177 #[test_traced("INFO")]
2178 fn test_start_sync_durability_mmb() {
2179 let executor = deterministic::Runner::default();
2180 executor.start(test_start_sync_durability_inner::<mmb::Family>);
2181 }
2182
2183 type DelayedCtx = DelayedSyncContext<deterministic::Context>;
2185
2186 type DelayedTestJournal<F> =
2188 Journal<F, DelayedCtx, ContiguousJournal<DelayedCtx, TestOp<F>>, Sha256, Sequential>;
2189
2190 fn open_delayed_journal(
2195 context: &Context,
2196 label: &'static str,
2197 suffix: &str,
2198 pending: &PendingSyncs,
2199 ) -> impl Future<Output = Result<DelayedTestJournal<mmr::Family>, Error<mmr::Family>>> {
2200 DelayedTestJournal::<mmr::Family>::new(
2201 DelayedCtx {
2202 inner: context.child(label),
2203 pending: pending.clone(),
2204 },
2205 merkle_config(suffix, context),
2206 journal_config(suffix, context),
2207 |op: &TestOp<mmr::Family>| op.is_commit(),
2208 ForwardFold,
2209 )
2210 }
2211
2212 #[test_traced("INFO")]
2214 fn test_start_sync_overlaps_work() {
2215 let executor = deterministic::Runner::default();
2216 executor.start(|context| async move {
2217 let pending = PendingSyncs::default();
2218 let open = open_delayed_journal(&context, "first", "start_sync_overlap", &pending);
2219 let mut journal = drive_pending_syncs(&pending, open).await.unwrap();
2220 for i in 0..4 {
2221 (journal, _) = journal
2222 .append(&create_operation::<mmr::Family>(i))
2223 .await
2224 .unwrap();
2225 }
2226
2227 let starts_before = pending.starts();
2228 let entered_before = pending.entered();
2229 let completions_before = pending.completions();
2230 let handle;
2231 (journal, handle) = journal.start_sync().await.unwrap();
2232 assert!(pending.starts() > starts_before);
2233 assert_eq!(pending.completions(), completions_before);
2234
2235 let waiter = context
2237 .child("await_sync")
2238 .spawn(|_| async move { handle.await.unwrap() });
2239 while pending.entered() == entered_before {
2240 reschedule().await;
2241 }
2242
2243 (journal, _) = journal
2245 .append(&create_operation::<mmr::Family>(4))
2246 .await
2247 .unwrap();
2248 let read_op = journal.read(0).await.unwrap();
2249 assert_eq!(read_op, create_operation::<mmr::Family>(0));
2250 assert_eq!(
2251 pending.completions(),
2252 completions_before,
2253 "the journal made progress while the sync was still in flight"
2254 );
2255
2256 pending.unblock();
2257 waiter.await.unwrap();
2258
2259 (journal, _) = journal
2261 .append(&TestOp::<mmr::Family>::CommitFloor(None, Location::new(0)))
2262 .await
2263 .unwrap();
2264 let handle;
2265 (journal, handle) = journal.start_sync().await.unwrap();
2266 handle.await.unwrap();
2267 let root = journal.root(0).unwrap();
2268 drop(journal);
2269
2270 let journal = open_delayed_journal(&context, "second", "start_sync_overlap", &pending)
2271 .await
2272 .unwrap();
2273 assert_eq!(journal.size(), 6);
2274 assert_eq!(journal.root(0).unwrap(), root);
2275 });
2276 }
2277
2278 #[test_traced("INFO")]
2281 fn test_start_sync_failure_propagates() {
2282 let executor = deterministic::Runner::default();
2283 executor.start(|context| async move {
2284 let pending = PendingSyncs::default();
2286 pending.unblock();
2287 let mut journal = open_delayed_journal(&context, "first", "start_sync_fail", &pending)
2288 .await
2289 .unwrap();
2290 for i in 0..4 {
2291 (journal, _) = journal
2292 .append(&create_operation::<mmr::Family>(i))
2293 .await
2294 .unwrap();
2295 }
2296
2297 pending.arm_fail();
2299
2300 let handle;
2301 (journal, handle) = journal.start_sync().await.unwrap();
2302 assert!(
2303 handle.await.is_err(),
2304 "the sync handle surfaces the failure"
2305 );
2306 let starts_before = pending.starts();
2307 assert!(
2309 matches!(
2310 journal.commit().await,
2311 Err(Error::Journal(JournalError::Runtime(_)))
2312 ),
2313 "the next durability op surfaces the failed in-flight sync"
2314 );
2315 assert_eq!(
2316 pending.starts(),
2317 starts_before,
2318 "the surfaced error is the retained failure, not a fresh sync's"
2319 );
2320 });
2321 }
2322
2323 #[test_traced("INFO")]
2326 fn test_start_sync_merkle_failure_fails_handle() {
2327 let executor = deterministic::Runner::default();
2328 executor.start(|context| async move {
2329 let pending = PendingSyncs::default();
2330 let open = open_delayed_journal(&context, "first", "merkle_fail", &pending);
2331 let mut journal = drive_pending_syncs(&pending, open).await.unwrap();
2332 for i in 0..4 {
2333 (journal, _) = journal
2334 .append(&create_operation::<mmr::Family>(i))
2335 .await
2336 .unwrap();
2337 }
2338
2339 let handle;
2342 (journal, handle) = journal.start_sync().await.unwrap();
2343 drive_pending_syncs(&pending, handle).await.unwrap();
2344 for i in 4..6 {
2345 (journal, _) = journal
2346 .append(&create_operation::<mmr::Family>(i))
2347 .await
2348 .unwrap();
2349 }
2350 journal = drive_pending_syncs(&pending, journal.commit())
2351 .await
2352 .unwrap();
2353
2354 let handle;
2357 (journal, handle) = journal.start_sync().await.unwrap();
2358 let ops_watermark = next_pending_sync(&pending);
2359 ops_watermark.release.send(Ok(())).unwrap();
2360 fail_pending_syncs(&pending);
2361 assert!(
2362 handle.await.is_err(),
2363 "a merkle-only failure surfaces on the joined handle"
2364 );
2365
2366 assert!(drive_pending_syncs(&pending, journal.sync()).await.is_err());
2368 });
2369 }
2370
2371 async fn test_prune_empty_journal_inner<F: Family + PartialEq>(context: Context) {
2373 let journal = create_empty_journal::<F>(context, "prune_empty").await;
2374
2375 let (_, boundary) = journal.prune(Location::<F>::new(0)).await.unwrap();
2376
2377 assert_eq!(boundary, Location::<F>::new(0));
2378 }
2379
2380 #[test_traced("INFO")]
2381 fn test_prune_empty_journal_mmr() {
2382 let executor = deterministic::Runner::default();
2383 executor.start(test_prune_empty_journal_inner::<mmr::Family>);
2384 }
2385
2386 #[test_traced("INFO")]
2387 fn test_prune_empty_journal_mmb() {
2388 let executor = deterministic::Runner::default();
2389 executor.start(test_prune_empty_journal_inner::<mmb::Family>);
2390 }
2391
2392 async fn test_prune_to_location_inner<F: Family + PartialEq>(context: Context) {
2394 let mut journal = create_journal_with_ops::<F>(context, "prune_to", 100).await;
2395
2396 (journal, _) = journal
2398 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2399 .await
2400 .unwrap();
2401 journal = journal.sync().await.unwrap();
2402
2403 let (_, boundary) = journal.prune(Location::<F>::new(50)).await.unwrap();
2404
2405 assert!(boundary <= Location::<F>::new(50));
2407 }
2408
2409 #[test_traced("INFO")]
2410 fn test_prune_to_location_mmr() {
2411 let executor = deterministic::Runner::default();
2412 executor.start(test_prune_to_location_inner::<mmr::Family>);
2413 }
2414
2415 #[test_traced("INFO")]
2416 fn test_prune_to_location_mmb() {
2417 let executor = deterministic::Runner::default();
2418 executor.start(test_prune_to_location_inner::<mmb::Family>);
2419 }
2420
2421 async fn test_prune_returns_actual_boundary_inner<F: Family + PartialEq>(context: Context) {
2423 let mut journal = create_journal_with_ops::<F>(context, "prune_boundary", 100).await;
2424
2425 (journal, _) = journal
2426 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2427 .await
2428 .unwrap();
2429 journal = journal.sync().await.unwrap();
2430
2431 let requested = Location::<F>::new(50);
2432 let (journal, actual) = journal.prune(requested).await.unwrap();
2433
2434 let bounds = journal.bounds();
2436 assert!(!bounds.is_empty());
2437 assert_eq!(actual, bounds.start);
2438
2439 assert!(actual <= requested);
2441 }
2442
2443 #[test_traced("INFO")]
2444 fn test_prune_returns_actual_boundary_mmr() {
2445 let executor = deterministic::Runner::default();
2446 executor.start(test_prune_returns_actual_boundary_inner::<mmr::Family>);
2447 }
2448
2449 #[test_traced("INFO")]
2450 fn test_prune_returns_actual_boundary_mmb() {
2451 let executor = deterministic::Runner::default();
2452 executor.start(test_prune_returns_actual_boundary_inner::<mmb::Family>);
2453 }
2454
2455 async fn test_mutable_prune_updates_merkle_boundary_inner<F: Family + PartialEq>(
2457 context: Context,
2458 ) {
2459 let mut journal = create_journal_with_ops::<F>(context, "trait_prune", 100).await;
2460
2461 (journal, _) = journal
2462 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2463 .await
2464 .unwrap();
2465 journal = journal.sync().await.unwrap();
2466
2467 let (journal, pruned) = <TestJournal<F> as Mutable>::prune(journal, 50)
2468 .await
2469 .unwrap();
2470 assert!(pruned);
2471
2472 let item_boundary = journal.bounds().start;
2473 let merkle_boundary = journal.merkle.bounds().start;
2474 assert_eq!(Location::<F>::new(item_boundary), merkle_boundary);
2475 assert!(merkle_boundary > Location::<F>::new(0));
2476
2477 let (journal, pruned) = <TestJournal<F> as Mutable>::prune(journal, 50)
2478 .await
2479 .unwrap();
2480 assert!(!pruned);
2481 assert_eq!(journal.bounds().start, item_boundary);
2482 assert_eq!(journal.merkle.bounds().start, merkle_boundary);
2483 }
2484
2485 #[test_traced("INFO")]
2486 fn test_mutable_prune_updates_merkle_boundary_mmr() {
2487 let executor = deterministic::Runner::default();
2488 executor.start(test_mutable_prune_updates_merkle_boundary_inner::<mmr::Family>);
2489 }
2490
2491 #[test_traced("INFO")]
2492 fn test_mutable_prune_updates_merkle_boundary_mmb() {
2493 let executor = deterministic::Runner::default();
2494 executor.start(test_mutable_prune_updates_merkle_boundary_inner::<mmb::Family>);
2495 }
2496
2497 async fn test_prune_preserves_operation_count_inner<F: Family + PartialEq>(context: Context) {
2499 let mut journal = create_journal_with_ops::<F>(context, "prune_count", 100).await;
2500
2501 (journal, _) = journal
2502 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2503 .await
2504 .unwrap();
2505 journal = journal.sync().await.unwrap();
2506
2507 let count_before = journal.size();
2508 let (journal, _) = journal.prune(Location::<F>::new(50)).await.unwrap();
2509 let count_after = journal.size();
2510
2511 assert_eq!(count_before, count_after);
2512 }
2513
2514 #[test_traced("INFO")]
2515 fn test_prune_preserves_operation_count_mmr() {
2516 let executor = deterministic::Runner::default();
2517 executor.start(test_prune_preserves_operation_count_inner::<mmr::Family>);
2518 }
2519
2520 #[test_traced("INFO")]
2521 fn test_prune_preserves_operation_count_mmb() {
2522 let executor = deterministic::Runner::default();
2523 executor.start(test_prune_preserves_operation_count_inner::<mmb::Family>);
2524 }
2525
2526 async fn test_bounds_empty_and_pruned_inner<F: Family + PartialEq>(context: Context) {
2528 let journal = create_empty_journal::<F>(context.child("empty"), "oldest").await;
2530 assert!(journal.bounds().is_empty());
2531 journal.destroy().await.unwrap();
2532
2533 let journal = create_journal_with_ops::<F>(context.child("no_prune"), "oldest", 100).await;
2535 let bounds = journal.bounds();
2536 assert!(!bounds.is_empty());
2537 assert_eq!(bounds.start, 0);
2538 journal.destroy().await.unwrap();
2539
2540 let mut journal =
2542 create_journal_with_ops::<F>(context.child("pruned"), "oldest", 100).await;
2543 (journal, _) = journal
2544 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2545 .await
2546 .unwrap();
2547 journal = journal.sync().await.unwrap();
2548
2549 let (journal, pruned_boundary) = journal.prune(Location::<F>::new(50)).await.unwrap();
2550
2551 let bounds = journal.bounds();
2553 assert!(!bounds.is_empty());
2554 assert_eq!(bounds.start, pruned_boundary);
2555 assert!(pruned_boundary <= 50);
2557 journal.destroy().await.unwrap();
2558 }
2559
2560 #[test_traced("INFO")]
2561 fn test_bounds_empty_and_pruned_mmr() {
2562 let executor = deterministic::Runner::default();
2563 executor.start(test_bounds_empty_and_pruned_inner::<mmr::Family>);
2564 }
2565
2566 #[test_traced("INFO")]
2567 fn test_bounds_empty_and_pruned_mmb() {
2568 let executor = deterministic::Runner::default();
2569 executor.start(test_bounds_empty_and_pruned_inner::<mmb::Family>);
2570 }
2571
2572 async fn test_bounds_start_after_prune_inner<F: Family + PartialEq>(context: Context) {
2574 let journal = create_empty_journal::<F>(context.child("empty"), "boundary").await;
2576 assert_eq!(journal.bounds().start, 0);
2577
2578 let journal =
2580 create_journal_with_ops::<F>(context.child("no_prune"), "boundary", 100).await;
2581 assert_eq!(journal.bounds().start, 0);
2582
2583 let mut journal =
2585 create_journal_with_ops::<F>(context.child("pruned"), "boundary", 100).await;
2586 (journal, _) = journal
2587 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(50)))
2588 .await
2589 .unwrap();
2590 journal = journal.sync().await.unwrap();
2591
2592 let (journal, pruned_boundary) = journal.prune(Location::<F>::new(50)).await.unwrap();
2593
2594 assert_eq!(journal.bounds().start, pruned_boundary);
2595 }
2596
2597 #[test_traced("INFO")]
2598 fn test_bounds_start_after_prune_mmr() {
2599 let executor = deterministic::Runner::default();
2600 executor.start(test_bounds_start_after_prune_inner::<mmr::Family>);
2601 }
2602
2603 #[test_traced("INFO")]
2604 fn test_bounds_start_after_prune_mmb() {
2605 let executor = deterministic::Runner::default();
2606 executor.start(test_bounds_start_after_prune_inner::<mmb::Family>);
2607 }
2608
2609 async fn test_mmr_prunes_to_journal_boundary_inner<F: Family + PartialEq>(context: Context) {
2611 let mut journal = create_journal_with_ops::<F>(context, "mmr_boundary", 50).await;
2612
2613 (journal, _) = journal
2614 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(25)))
2615 .await
2616 .unwrap();
2617 journal = journal.sync().await.unwrap();
2618
2619 let (journal, pruned_boundary) = journal.prune(Location::<F>::new(25)).await.unwrap();
2620
2621 let bounds = journal.bounds();
2623 assert!(!bounds.is_empty());
2624 assert_eq!(pruned_boundary, bounds.start);
2625
2626 assert!(pruned_boundary <= Location::<F>::new(25));
2628
2629 assert_eq!(journal.size(), 51);
2631 }
2632
2633 #[test_traced("INFO")]
2634 fn test_mmr_prunes_to_journal_boundary_mmr() {
2635 let executor = deterministic::Runner::default();
2636 executor.start(test_mmr_prunes_to_journal_boundary_inner::<mmr::Family>);
2637 }
2638
2639 #[test_traced("INFO")]
2640 fn test_mmr_prunes_to_journal_boundary_mmb() {
2641 let executor = deterministic::Runner::default();
2642 executor.start(test_mmr_prunes_to_journal_boundary_inner::<mmb::Family>);
2643 }
2644
2645 async fn test_proof_multiple_operations_inner<F: Family + PartialEq>(context: Context) {
2647 let journal = create_journal_with_ops::<F>(context, "proof_multi", 50).await;
2648
2649 let (proof, ops) = journal
2650 .proof(Location::<F>::new(0), NZU64!(50), 0)
2651 .await
2652 .unwrap();
2653
2654 assert_eq!(ops.len(), 50);
2655 for (i, op) in ops.iter().enumerate() {
2656 assert_eq!(*op, create_operation::<F>(i as u8));
2657 }
2658
2659 let hasher = StandardHasher::new(ForwardFold);
2661 let root = journal_root(&journal);
2662 assert!(verify_proof(
2663 &proof,
2664 &ops,
2665 Location::<F>::new(0),
2666 &root,
2667 &hasher
2668 ));
2669 }
2670
2671 #[test_traced("INFO")]
2672 fn test_proof_multiple_operations_mmr() {
2673 let executor = deterministic::Runner::default();
2674 executor.start(test_proof_multiple_operations_inner::<mmr::Family>);
2675 }
2676
2677 #[test_traced("INFO")]
2678 fn test_proof_multiple_operations_mmb() {
2679 let executor = deterministic::Runner::default();
2680 executor.start(test_proof_multiple_operations_inner::<mmb::Family>);
2681 }
2682
2683 async fn test_historical_proof_limited_by_max_ops_inner<F: Family + PartialEq>(
2685 context: Context,
2686 ) {
2687 let journal = create_journal_with_ops::<F>(context, "proof_limit", 50).await;
2688
2689 let size = journal.size();
2690 let (proof, ops) = journal
2691 .historical_proof(size, Location::<F>::new(0), NZU64!(20), 0)
2692 .await
2693 .unwrap();
2694
2695 assert_eq!(ops.len(), 20);
2697 for (i, op) in ops.iter().enumerate() {
2698 assert_eq!(*op, create_operation::<F>(i as u8));
2699 }
2700
2701 let hasher = StandardHasher::new(ForwardFold);
2703 let root = journal_root(&journal);
2704 assert!(verify_proof(
2705 &proof,
2706 &ops,
2707 Location::<F>::new(0),
2708 &root,
2709 &hasher
2710 ));
2711 }
2712
2713 #[test_traced("INFO")]
2714 fn test_historical_proof_limited_by_max_ops_mmr() {
2715 let executor = deterministic::Runner::default();
2716 executor.start(|context| {
2717 test_historical_proof_limited_by_max_ops_inner::<mmr::Family>(context)
2718 });
2719 }
2720
2721 #[test_traced("INFO")]
2722 fn test_historical_proof_limited_by_max_ops_mmb() {
2723 let executor = deterministic::Runner::default();
2724 executor.start(|context| {
2725 test_historical_proof_limited_by_max_ops_inner::<mmb::Family>(context)
2726 });
2727 }
2728
2729 async fn test_historical_proof_at_end_of_journal_inner<F: Family + PartialEq>(
2731 context: Context,
2732 ) {
2733 let journal = create_journal_with_ops::<F>(context, "proof_end", 50).await;
2734
2735 let size = journal.size();
2736 let (proof, ops) = journal
2738 .historical_proof(size, Location::<F>::new(40), NZU64!(20), 0)
2739 .await
2740 .unwrap();
2741
2742 assert_eq!(ops.len(), 10);
2744 for (i, op) in ops.iter().enumerate() {
2745 assert_eq!(*op, create_operation::<F>((40 + i) as u8));
2746 }
2747
2748 let hasher = StandardHasher::new(ForwardFold);
2750 let root = journal_root(&journal);
2751 assert!(verify_proof(
2752 &proof,
2753 &ops,
2754 Location::<F>::new(40),
2755 &root,
2756 &hasher
2757 ));
2758 }
2759
2760 #[test_traced("INFO")]
2761 fn test_historical_proof_at_end_of_journal_mmr() {
2762 let executor = deterministic::Runner::default();
2763 executor.start(test_historical_proof_at_end_of_journal_inner::<mmr::Family>);
2764 }
2765
2766 #[test_traced("INFO")]
2767 fn test_historical_proof_at_end_of_journal_mmb() {
2768 let executor = deterministic::Runner::default();
2769 executor.start(test_historical_proof_at_end_of_journal_inner::<mmb::Family>);
2770 }
2771
2772 async fn test_historical_proof_out_of_range_returns_error_inner<F: Family + PartialEq>(
2774 context: Context,
2775 ) {
2776 let journal = create_journal_with_ops::<F>(context, "proof_oob", 5).await;
2777
2778 let result = journal
2780 .historical_proof(Location::<F>::new(10), Location::<F>::new(0), NZU64!(1), 0)
2781 .await;
2782
2783 assert!(matches!(
2784 result,
2785 Err(Error::Merkle(merkle::Error::RangeOutOfBounds(_)))
2786 ));
2787 }
2788
2789 #[test_traced("INFO")]
2790 fn test_historical_proof_out_of_range_returns_error_mmr() {
2791 let executor = deterministic::Runner::default();
2792 executor.start(|context| {
2793 test_historical_proof_out_of_range_returns_error_inner::<mmr::Family>(context)
2794 });
2795 }
2796
2797 #[test_traced("INFO")]
2798 fn test_historical_proof_out_of_range_returns_error_mmb() {
2799 let executor = deterministic::Runner::default();
2800 executor.start(|context| {
2801 test_historical_proof_out_of_range_returns_error_inner::<mmb::Family>(context)
2802 });
2803 }
2804
2805 async fn test_historical_proof_start_too_large_returns_error_inner<F: Family + PartialEq>(
2807 context: Context,
2808 ) {
2809 let journal = create_journal_with_ops::<F>(context, "proof_start_oob", 5).await;
2810
2811 let size = journal.size();
2812 let result = journal.historical_proof(size, size, NZU64!(1), 0).await;
2814
2815 assert!(matches!(
2816 result,
2817 Err(Error::Merkle(merkle::Error::RangeOutOfBounds(_)))
2818 ));
2819 }
2820
2821 #[test_traced("INFO")]
2822 fn test_historical_proof_start_too_large_returns_error_mmr() {
2823 let executor = deterministic::Runner::default();
2824 executor.start(|context| {
2825 test_historical_proof_start_too_large_returns_error_inner::<mmr::Family>(context)
2826 });
2827 }
2828
2829 #[test_traced("INFO")]
2830 fn test_historical_proof_start_too_large_returns_error_mmb() {
2831 let executor = deterministic::Runner::default();
2832 executor.start(|context| {
2833 test_historical_proof_start_too_large_returns_error_inner::<mmb::Family>(context)
2834 });
2835 }
2836
2837 async fn test_historical_proof_truly_historical_inner<F: Family + PartialEq>(context: Context) {
2839 let mut journal = create_journal_with_ops::<F>(context, "proof_historical", 50).await;
2841
2842 let hasher = StandardHasher::new(ForwardFold);
2844 let historical_root = journal_root(&journal);
2845 let historical_size = journal.size();
2846
2847 for i in 50..100 {
2849 (journal, _) = journal
2850 .append(&create_operation::<F>(i as u8))
2851 .await
2852 .unwrap();
2853 }
2854 let journal = journal.sync().await.unwrap();
2855
2856 let (proof, ops) = journal
2858 .historical_proof(historical_size, Location::<F>::new(0), NZU64!(50), 0)
2859 .await
2860 .unwrap();
2861
2862 assert_eq!(ops.len(), 50);
2864 for (i, op) in ops.iter().enumerate() {
2865 assert_eq!(*op, create_operation::<F>(i as u8));
2866 }
2867
2868 assert!(verify_proof(
2870 &proof,
2871 &ops,
2872 Location::<F>::new(0),
2873 &historical_root,
2874 &hasher
2875 ));
2876 }
2877
2878 #[test_traced("INFO")]
2879 fn test_historical_proof_truly_historical_mmr() {
2880 let executor = deterministic::Runner::default();
2881 executor.start(test_historical_proof_truly_historical_inner::<mmr::Family>);
2882 }
2883
2884 #[test_traced("INFO")]
2885 fn test_historical_proof_truly_historical_mmb() {
2886 let executor = deterministic::Runner::default();
2887 executor.start(test_historical_proof_truly_historical_inner::<mmb::Family>);
2888 }
2889
2890 async fn test_historical_proof_pruned_location_returns_error_inner<F: Family + PartialEq>(
2892 context: Context,
2893 ) {
2894 let mut journal = create_journal_with_ops::<F>(context, "proof_pruned", 50).await;
2895
2896 (journal, _) = journal
2897 .append(&TestOp::<F>::CommitFloor(None, Location::<F>::new(25)))
2898 .await
2899 .unwrap();
2900 journal = journal.sync().await.unwrap();
2901 let pruned_boundary;
2902 (journal, pruned_boundary) = journal.prune(Location::<F>::new(25)).await.unwrap();
2903
2904 let size = journal.size();
2906 let start_loc = Location::<F>::new(0);
2907 if start_loc < pruned_boundary {
2908 let result = journal
2909 .historical_proof(size, start_loc, NZU64!(1), 0)
2910 .await;
2911
2912 assert!(result.is_err());
2914 }
2915 }
2916
2917 #[test_traced("INFO")]
2918 fn test_historical_proof_pruned_location_returns_error_mmr() {
2919 let executor = deterministic::Runner::default();
2920 executor.start(|context| {
2921 test_historical_proof_pruned_location_returns_error_inner::<mmr::Family>(context)
2922 });
2923 }
2924
2925 #[test_traced("INFO")]
2926 fn test_historical_proof_pruned_location_returns_error_mmb() {
2927 let executor = deterministic::Runner::default();
2928 executor.start(|context| {
2929 test_historical_proof_pruned_location_returns_error_inner::<mmb::Family>(context)
2930 });
2931 }
2932
2933 async fn test_replay_operations_inner<F: Family + PartialEq>(context: Context) {
2935 let journal = create_empty_journal::<F>(context.child("empty"), "replay").await;
2937 let stream = journal
2938 .replay(0, NZUsize!(10), ReadOptions::default())
2939 .await
2940 .unwrap();
2941 futures::pin_mut!(stream);
2942 assert!(stream.next().await.is_none());
2943
2944 let journal = create_journal_with_ops::<F>(context.child("with_ops"), "replay", 50).await;
2946 let stream = journal
2947 .replay(0, NZUsize!(100), ReadOptions::default())
2948 .await
2949 .unwrap();
2950 futures::pin_mut!(stream);
2951
2952 for i in 0..50 {
2953 let (pos, op) = stream.next().await.unwrap().unwrap();
2954 assert_eq!(pos, i);
2955 assert_eq!(op, create_operation::<F>(i as u8));
2956 }
2957
2958 assert!(stream.next().await.is_none());
2959 }
2960
2961 #[test_traced("INFO")]
2962 fn test_replay_operations_mmr() {
2963 let executor = deterministic::Runner::default();
2964 executor.start(test_replay_operations_inner::<mmr::Family>);
2965 }
2966
2967 #[test_traced("INFO")]
2968 fn test_replay_operations_mmb() {
2969 let executor = deterministic::Runner::default();
2970 executor.start(test_replay_operations_inner::<mmb::Family>);
2971 }
2972
2973 #[test_traced("INFO")]
2974 fn test_replay_propagates_read_options_to_backing_journal() {
2975 let executor = deterministic::Runner::default();
2976 executor.start(|context| async move {
2977 let (context, recordings) = RecordingContext::new(context);
2978 let merkle_cfg = merkle_config("replay-options", &context);
2979 let journal_cfg = journal_config("replay-options", &context);
2980 let page_cache = journal_cfg.page_cache.clone();
2981 let mut journal = RecordingTestJournal::<mmr::Family>::new(
2982 context,
2983 merkle_cfg,
2984 journal_cfg,
2985 |op| op.is_commit(),
2986 ForwardFold,
2987 )
2988 .await
2989 .unwrap();
2990
2991 for i in 0..8 {
2992 let operation = create_operation::<mmr::Family>(i);
2993 (journal, _) = journal.append(&operation).await.unwrap();
2994 }
2995 journal = journal.sync().await.unwrap();
2996
2997 page_cache.clear();
3000 let stream = journal
3001 .replay(0, NZUsize!(100), ReadOptions::DONT_CACHE)
3002 .await
3003 .unwrap();
3004 recordings.clear();
3005 futures::pin_mut!(stream);
3006 let (position, operation) = stream.next().await.unwrap().unwrap();
3007 assert_eq!(position, 0);
3008 assert_eq!(operation, create_operation::<mmr::Family>(0));
3009
3010 let reads = recordings.snapshot().reads;
3012 assert!(!reads.is_empty());
3013 assert!(
3014 reads
3015 .iter()
3016 .all(|options| *options == ReadOptions::DONT_CACHE)
3017 );
3018 });
3019 }
3020
3021 async fn test_replay_from_middle_inner<F: Family + PartialEq>(context: Context) {
3023 let journal = create_journal_with_ops::<F>(context, "replay_middle", 50).await;
3024 let stream = journal
3025 .replay(25, NZUsize!(100), ReadOptions::default())
3026 .await
3027 .unwrap();
3028 futures::pin_mut!(stream);
3029
3030 let mut count = 0;
3031 while let Some(result) = stream.next().await {
3032 let (pos, op) = result.unwrap();
3033 assert_eq!(pos, 25 + count);
3034 assert_eq!(op, create_operation::<F>((25 + count) as u8));
3035 count += 1;
3036 }
3037
3038 assert_eq!(count, 25);
3040 }
3041
3042 #[test_traced("INFO")]
3043 fn test_replay_from_middle_mmr() {
3044 let executor = deterministic::Runner::default();
3045 executor.start(test_replay_from_middle_inner::<mmr::Family>);
3046 }
3047
3048 #[test_traced("INFO")]
3049 fn test_replay_from_middle_mmb() {
3050 let executor = deterministic::Runner::default();
3051 executor.start(test_replay_from_middle_inner::<mmb::Family>);
3052 }
3053
3054 async fn test_speculative_batch_inner<F: Family + PartialEq>(context: Context) {
3056 let mut journal = create_journal_with_ops::<F>(context, "speculative_batch", 10).await;
3057 let original_root = journal_root(&journal);
3058
3059 let b1 = journal.new_batch();
3061 let b2 = journal.new_batch();
3062
3063 let op_a = create_operation::<F>(100);
3065 let op_b = create_operation::<F>(200);
3066 let b1 = b1.add(op_a.clone());
3067 let b2 = b2.add(op_b);
3068
3069 let m1 = journal.merkle.with_mem(|mem| b1.merkleize(mem));
3071 let m2 = journal.merkle.with_mem(|mem| b2.merkleize(mem));
3072 assert_ne!(batch_root(&journal, &m1), batch_root(&journal, &m2));
3073 assert_ne!(batch_root(&journal, &m1), original_root);
3074 assert_ne!(batch_root(&journal, &m2), original_root);
3075
3076 assert_eq!(journal_root(&journal), original_root);
3078
3079 let expected_root = batch_root(&journal, &m1);
3081 journal = journal.apply_batch(&m1).await.unwrap();
3082
3083 assert_eq!(journal_root(&journal), expected_root);
3085 assert_eq!(*journal.size(), 11);
3086 }
3087
3088 #[test_traced("INFO")]
3089 fn test_speculative_batch_mmr() {
3090 let executor = deterministic::Runner::default();
3091 executor.start(test_speculative_batch_inner::<mmr::Family>);
3092 }
3093
3094 #[test_traced("INFO")]
3095 fn test_speculative_batch_mmb() {
3096 let executor = deterministic::Runner::default();
3097 executor.start(test_speculative_batch_inner::<mmb::Family>);
3098 }
3099
3100 async fn test_speculative_batch_stacking_inner<F: Family + PartialEq>(context: Context) {
3103 let mut journal = create_journal_with_ops::<F>(context, "batch_stacking", 10).await;
3104
3105 let op_a = create_operation::<F>(100);
3106 let op_b = create_operation::<F>(200);
3107
3108 let (merkleized_a, merkleized_b) = {
3109 let batch_a = journal.new_batch().add(op_a.clone());
3110 let merkleized_a = journal.merkle.with_mem(|mem| batch_a.merkleize(mem));
3111
3112 let batch_b = merkleized_a.new_batch::<Sha256>().add(op_b.clone());
3113 let merkleized_b = journal.merkle.with_mem(|mem| batch_b.merkleize(mem));
3114 (merkleized_a, merkleized_b)
3115 };
3116
3117 let expected_root = batch_root(&journal, &merkleized_b);
3118 journal = journal.apply_batch(&merkleized_b).await.unwrap();
3119 drop(merkleized_a);
3120
3121 assert_eq!(journal_root(&journal), expected_root);
3122 assert_eq!(*journal.size(), 12);
3123
3124 let read_a = journal.read(*Location::<F>::new(10)).await.unwrap();
3126 assert_eq!(read_a, op_a);
3127 let read_b = journal.read(*Location::<F>::new(11)).await.unwrap();
3128 assert_eq!(read_b, op_b);
3129 }
3130
3131 #[test_traced("INFO")]
3132 fn test_speculative_batch_stacking_mmr() {
3133 let executor = deterministic::Runner::default();
3134 executor.start(test_speculative_batch_stacking_inner::<mmr::Family>);
3135 }
3136
3137 #[test_traced("INFO")]
3138 fn test_speculative_batch_stacking_mmb() {
3139 let executor = deterministic::Runner::default();
3140 executor.start(test_speculative_batch_stacking_inner::<mmb::Family>);
3141 }
3142
3143 async fn test_speculative_batch_sequential_inner<F: Family + PartialEq>(context: Context) {
3146 let mut journal = create_journal_with_ops::<F>(context, "batch_sequential", 10).await;
3147
3148 let op_a = create_operation::<F>(100);
3149 let op_b = create_operation::<F>(200);
3150
3151 let batch_a = journal.new_batch().add(op_a.clone());
3153 let merkleized_a = journal.merkle.with_mem(|mem| batch_a.merkleize(mem));
3154 journal = journal.apply_batch(&merkleized_a).await.unwrap();
3155 assert_eq!(*journal.size(), 11);
3156
3157 let batch_b = journal.new_batch().add(op_b.clone());
3159 let merkleized_b = journal.merkle.with_mem(|mem| batch_b.merkleize(mem));
3160 let expected_root = batch_root(&journal, &merkleized_b);
3161 journal = journal.apply_batch(&merkleized_b).await.unwrap();
3162
3163 assert_eq!(journal_root(&journal), expected_root);
3164 assert_eq!(*journal.size(), 12);
3165
3166 let read_a = journal.read(*Location::<F>::new(10)).await.unwrap();
3168 assert_eq!(read_a, op_a);
3169 let read_b = journal.read(*Location::<F>::new(11)).await.unwrap();
3170 assert_eq!(read_b, op_b);
3171 }
3172
3173 #[test_traced("INFO")]
3174 fn test_speculative_batch_sequential_mmr() {
3175 let executor = deterministic::Runner::default();
3176 executor.start(test_speculative_batch_sequential_inner::<mmr::Family>);
3177 }
3178
3179 #[test_traced("INFO")]
3180 fn test_speculative_batch_sequential_mmb() {
3181 let executor = deterministic::Runner::default();
3182 executor.start(test_speculative_batch_sequential_inner::<mmb::Family>);
3183 }
3184
3185 async fn test_stale_batch_sibling_inner<F: Family + PartialEq>(context: Context) {
3186 let mut journal = create_empty_journal::<F>(context.child("open"), "stale-sibling").await;
3187 let op_a = create_operation::<F>(1);
3188 let op_b = create_operation::<F>(2);
3189
3190 let batch_a = journal.new_batch().add(op_a.clone());
3192 let merkleized_a = journal.merkle.with_mem(|mem| batch_a.merkleize(mem));
3193 let batch_b = journal.new_batch().add(op_b);
3194 let merkleized_b = journal.merkle.with_mem(|mem| batch_b.merkleize(mem));
3195
3196 journal = journal.apply_batch(&merkleized_a).await.unwrap();
3199 let commit_op = TestOp::<F>::CommitFloor(None, Location::<F>::new(0));
3200 (journal, _) = journal.append(&commit_op).await.unwrap();
3201 journal = journal.sync().await.unwrap();
3202 let root_a = journal_root(&journal);
3203 let size_a = journal.size();
3204 let (_, ops) = journal
3205 .proof(Location::<F>::new(0), NZU64!(1), 0)
3206 .await
3207 .unwrap();
3208 assert_eq!(ops, vec![op_a.clone()]);
3209
3210 let result = journal.apply_batch(&merkleized_b).await;
3212 assert!(
3213 matches!(
3214 result,
3215 Err(super::Error::Merkle(merkle::Error::StaleBatch { .. }))
3216 ),
3217 "expected StaleBatch, got {result:?}"
3218 );
3219
3220 let journal = create_empty_journal::<F>(context.child("reopen"), "stale-sibling").await;
3222 assert_eq!(journal_root(&journal), root_a);
3223 assert_eq!(journal.size(), size_a);
3224 let (_, ops) = journal
3225 .proof(Location::<F>::new(0), NZU64!(1), 0)
3226 .await
3227 .unwrap();
3228 assert_eq!(ops, vec![op_a]);
3229 journal.destroy().await.unwrap();
3230 }
3231
3232 #[test_traced("INFO")]
3233 fn test_stale_batch_sibling_mmr() {
3234 let executor = deterministic::Runner::default();
3235 executor.start(test_stale_batch_sibling_inner::<mmr::Family>);
3236 }
3237
3238 #[test_traced("INFO")]
3239 fn test_stale_batch_sibling_mmb() {
3240 let executor = deterministic::Runner::default();
3241 executor.start(test_stale_batch_sibling_inner::<mmb::Family>);
3242 }
3243
3244 async fn test_stale_batch_chained_inner<F: Family + PartialEq>(context: Context) {
3245 let mut journal = create_journal_with_ops::<F>(context, "stale-chained", 5).await;
3246
3247 let parent_batch = journal.new_batch().add(create_operation::<F>(10));
3249 let parent = journal.merkle.with_mem(|mem| parent_batch.merkleize(mem));
3250 let batch_a = parent.new_batch::<Sha256>().add(create_operation::<F>(20));
3251 let child_a = journal.merkle.with_mem(|mem| batch_a.merkleize(mem));
3252 let batch_b = parent.new_batch::<Sha256>().add(create_operation::<F>(30));
3253 let child_b = journal.merkle.with_mem(|mem| batch_b.merkleize(mem));
3254
3255 journal = journal.apply_batch(&child_a).await.unwrap();
3257 let result = journal.apply_batch(&child_b).await;
3258 drop(parent);
3259 assert!(
3260 matches!(
3261 result,
3262 Err(super::Error::Merkle(merkle::Error::StaleBatch { .. }))
3263 ),
3264 "expected StaleBatch for sibling, got {result:?}"
3265 );
3266 }
3267
3268 #[test_traced("INFO")]
3269 fn test_stale_batch_chained_mmr() {
3270 let executor = deterministic::Runner::default();
3271 executor.start(test_stale_batch_chained_inner::<mmr::Family>);
3272 }
3273
3274 #[test_traced("INFO")]
3275 fn test_stale_batch_chained_mmb() {
3276 let executor = deterministic::Runner::default();
3277 executor.start(test_stale_batch_chained_inner::<mmb::Family>);
3278 }
3279
3280 async fn test_stale_batch_parent_before_child_inner<F: Family + PartialEq>(context: Context) {
3281 let mut journal = create_empty_journal::<F>(context, "stale-parent-first").await;
3282
3283 let parent_batch = journal.new_batch().add(create_operation::<F>(1));
3285 let parent = journal.merkle.with_mem(|mem| parent_batch.merkleize(mem));
3286 let child_batch = parent.new_batch::<Sha256>().add(create_operation::<F>(2));
3287 let child = journal.merkle.with_mem(|mem| child_batch.merkleize(mem));
3288
3289 let expected_root = batch_root(&journal, &child);
3290
3291 journal = journal.apply_batch(&parent).await.unwrap();
3293 journal = journal.apply_batch(&child).await.unwrap();
3294
3295 assert_eq!(journal_root(&journal), expected_root);
3296 assert_eq!(*journal.size(), 2);
3297 }
3298
3299 #[test_traced("INFO")]
3300 fn test_stale_batch_parent_before_child_mmr() {
3301 let executor = deterministic::Runner::default();
3302 executor.start(test_stale_batch_parent_before_child_inner::<mmr::Family>);
3303 }
3304
3305 #[test_traced("INFO")]
3306 fn test_stale_batch_parent_before_child_mmb() {
3307 let executor = deterministic::Runner::default();
3308 executor.start(test_stale_batch_parent_before_child_inner::<mmb::Family>);
3309 }
3310
3311 async fn test_stale_batch_child_before_parent_inner<F: Family + PartialEq>(context: Context) {
3312 let mut journal = create_empty_journal::<F>(context, "stale-child-first").await;
3313
3314 let parent_batch = journal.new_batch().add(create_operation::<F>(1));
3316 let parent = journal.merkle.with_mem(|mem| parent_batch.merkleize(mem));
3317 let child_batch = parent.new_batch::<Sha256>().add(create_operation::<F>(2));
3318 let child = journal.merkle.with_mem(|mem| child_batch.merkleize(mem));
3319
3320 journal = journal.apply_batch(&child).await.unwrap();
3322 let result = journal.apply_batch(&parent).await;
3323 assert!(
3324 matches!(
3325 result,
3326 Err(super::Error::Merkle(merkle::Error::StaleBatch { .. }))
3327 ),
3328 "expected StaleBatch for parent after child applied, got {result:?}"
3329 );
3330 }
3331
3332 #[test_traced("INFO")]
3333 fn test_stale_batch_child_before_parent_mmr() {
3334 let executor = deterministic::Runner::default();
3335 executor.start(test_stale_batch_child_before_parent_inner::<mmr::Family>);
3336 }
3337
3338 #[test_traced("INFO")]
3339 fn test_stale_batch_child_before_parent_mmb() {
3340 let executor = deterministic::Runner::default();
3341 executor.start(test_stale_batch_child_before_parent_inner::<mmb::Family>);
3342 }
3343
3344 async fn test_apply_batch_skip_ancestor_items_inner<F: Family + PartialEq>(context: Context) {
3346 let mut journal = create_journal_with_ops::<F>(context, "rp-skip", 3).await;
3347
3348 let parent_batch = journal
3350 .new_batch()
3351 .add(create_operation::<F>(10))
3352 .add(create_operation::<F>(11));
3353 let parent = journal.merkle.with_mem(|mem| parent_batch.merkleize(mem));
3354
3355 let child_batch = parent
3357 .new_batch::<Sha256>()
3358 .add(create_operation::<F>(20))
3359 .add(create_operation::<F>(21))
3360 .add(create_operation::<F>(22));
3361 let child = journal.merkle.with_mem(|mem| child_batch.merkleize(mem));
3362
3363 journal = journal.apply_batch(&parent).await.unwrap();
3365
3366 journal = journal.apply_batch(&child).await.unwrap();
3368
3369 let (_, ops) = journal
3371 .proof(Location::<F>::new(3), NZU64!(5), 0)
3372 .await
3373 .unwrap();
3374 assert_eq!(ops.len(), 5);
3375 }
3376
3377 #[test_traced("INFO")]
3378 fn test_apply_batch_skip_ancestor_items_mmr() {
3379 let executor = deterministic::Runner::default();
3380 executor.start(test_apply_batch_skip_ancestor_items_inner::<mmr::Family>);
3381 }
3382
3383 #[test_traced("INFO")]
3384 fn test_apply_batch_skip_ancestor_items_mmb() {
3385 let executor = deterministic::Runner::default();
3386 executor.start(test_apply_batch_skip_ancestor_items_inner::<mmb::Family>);
3387 }
3388
3389 async fn test_apply_batch_cross_batch_inner<F: Family + PartialEq>(context: Context) {
3391 let mut journal = create_journal_with_ops::<F>(context, "rp-cross", 2).await;
3392
3393 let grandparent_batch = journal
3395 .new_batch()
3396 .add(create_operation::<F>(3))
3397 .add(create_operation::<F>(4))
3398 .add(create_operation::<F>(5));
3399 let grandparent = journal
3400 .merkle
3401 .with_mem(|mem| grandparent_batch.merkleize(mem));
3402
3403 let parent_batch = grandparent
3405 .new_batch::<Sha256>()
3406 .add(create_operation::<F>(6))
3407 .add(create_operation::<F>(7));
3408 let parent = journal.merkle.with_mem(|mem| parent_batch.merkleize(mem));
3409
3410 let child_batch = parent.new_batch::<Sha256>().add(create_operation::<F>(8));
3412 let child = journal.merkle.with_mem(|mem| child_batch.merkleize(mem));
3413
3414 journal = journal.apply_batch(&grandparent).await.unwrap();
3416
3417 journal = journal.apply_batch(&parent).await.unwrap();
3419
3420 journal = journal.apply_batch(&child).await.unwrap();
3422
3423 assert_eq!(*journal.size(), 8);
3425
3426 let (_, ops) = journal
3428 .proof(Location::<F>::new(2), NZU64!(6), 0)
3429 .await
3430 .unwrap();
3431 for (i, op) in ops.iter().enumerate() {
3432 assert_eq!(*op, create_operation::<F>((i + 3) as u8));
3433 }
3434 }
3435
3436 #[test_traced("INFO")]
3437 fn test_apply_batch_cross_batch_mmr() {
3438 let executor = deterministic::Runner::default();
3439 executor.start(test_apply_batch_cross_batch_inner::<mmr::Family>);
3440 }
3441
3442 #[test_traced("INFO")]
3443 fn test_apply_batch_cross_batch_mmb() {
3444 let executor = deterministic::Runner::default();
3445 executor.start(test_apply_batch_cross_batch_inner::<mmb::Family>);
3446 }
3447
3448 async fn test_merkleize_with_matches_add_inner<F: Family + PartialEq>(context: Context) {
3450 let journal = create_journal_with_ops::<F>(context, "mw-matches", 5).await;
3451
3452 let ops = vec![
3453 create_operation::<F>(10),
3454 create_operation::<F>(11),
3455 create_operation::<F>(12),
3456 ];
3457
3458 let mut batch = journal.new_batch();
3460 for op in &ops {
3461 batch = batch.add(op.clone());
3462 }
3463 let expected = journal.merkle.with_mem(|mem| batch.merkleize(mem));
3464
3465 let batch = journal.new_batch();
3467 let actual = journal
3468 .merkle
3469 .with_mem(|mem| merkleize_with(batch, mem, ops));
3470
3471 assert_eq!(
3472 batch_root(&journal, &actual),
3473 batch_root(&journal, &expected)
3474 );
3475 }
3476
3477 #[test_traced("INFO")]
3478 fn test_merkleize_with_matches_add_mmr() {
3479 let executor = deterministic::Runner::default();
3480 executor.start(test_merkleize_with_matches_add_inner::<mmr::Family>);
3481 }
3482
3483 #[test_traced("INFO")]
3484 fn test_merkleize_with_matches_add_mmb() {
3485 let executor = deterministic::Runner::default();
3486 executor.start(test_merkleize_with_matches_add_inner::<mmb::Family>);
3487 }
3488
3489 async fn test_merkleize_with_apply_inner<F: Family + PartialEq>(context: Context) {
3491 let mut journal = create_journal_with_ops::<F>(context, "mw-apply", 5).await;
3492
3493 let ops = vec![create_operation::<F>(10), create_operation::<F>(11)];
3494 let batch = journal.new_batch();
3495 let merkleized = journal
3496 .merkle
3497 .with_mem(|mem| merkleize_with(batch, mem, ops.clone()));
3498
3499 let expected_root = batch_root(&journal, &merkleized);
3500 journal = journal.apply_batch(&merkleized).await.unwrap();
3501
3502 assert_eq!(journal_root(&journal), expected_root);
3503 assert_eq!(*journal.size(), 7);
3504
3505 assert_eq!(journal.read(5).await.unwrap(), ops[0]);
3506 assert_eq!(journal.read(6).await.unwrap(), ops[1]);
3507 }
3508
3509 #[test_traced("INFO")]
3510 fn test_merkleize_with_apply_mmr() {
3511 let executor = deterministic::Runner::default();
3512 executor.start(test_merkleize_with_apply_inner::<mmr::Family>);
3513 }
3514
3515 #[test_traced("INFO")]
3516 fn test_merkleize_with_apply_mmb() {
3517 let executor = deterministic::Runner::default();
3518 executor.start(test_merkleize_with_apply_inner::<mmb::Family>);
3519 }
3520
3521 async fn test_apply_batch_skips_only_committed_ancestor_items_inner<F: Family + PartialEq>(
3524 context: Context,
3525 ) {
3526 let mut journal = create_empty_journal::<F>(context.child("storage"), "skip-partial").await;
3527
3528 let a_batch = journal.new_batch().add(create_operation::<F>(1));
3530 let a = journal.merkle.with_mem(|mem| a_batch.merkleize(mem));
3531 let b_batch = a.new_batch::<Sha256>().add(create_operation::<F>(2));
3532 let b = journal.merkle.with_mem(|mem| b_batch.merkleize(mem));
3533 let c_batch = b.new_batch::<Sha256>().add(create_operation::<F>(3));
3534 let c = journal.merkle.with_mem(|mem| c_batch.merkleize(mem));
3535
3536 journal = journal.apply_batch(&a).await.unwrap();
3538 journal = journal.apply_batch(&c).await.unwrap();
3539
3540 assert_eq!(*journal.size(), 3);
3542
3543 let mut reference =
3545 create_empty_journal::<F>(context.child("ref"), "skip-partial-ref").await;
3546 for i in 1..=3u8 {
3547 (reference, _) = reference.append(&create_operation::<F>(i)).await.unwrap();
3548 }
3549 assert_eq!(journal_root(&journal), journal_root(&reference));
3550 }
3551
3552 #[test_traced("INFO")]
3553 fn test_apply_batch_skips_only_committed_ancestor_items_mmr() {
3554 let executor = deterministic::Runner::default();
3555 executor.start(test_apply_batch_skips_only_committed_ancestor_items_inner::<mmr::Family>);
3556 }
3557
3558 #[test_traced("INFO")]
3559 fn test_apply_batch_skips_only_committed_ancestor_items_mmb() {
3560 let executor = deterministic::Runner::default();
3561 executor.start(test_apply_batch_skips_only_committed_ancestor_items_inner::<mmb::Family>);
3562 }
3563
3564 async fn test_apply_batch_detects_dropped_uncommitted_ancestor_inner<F: Family + PartialEq>(
3567 context: Context,
3568 ) {
3569 let journal =
3570 create_empty_journal::<F>(context.child("storage"), "dropped-uncommitted").await;
3571
3572 let a_batch = journal.new_batch().add(create_operation::<F>(1));
3573 let a = journal.merkle.with_mem(|mem| a_batch.merkleize(mem));
3574 let b_batch = a.new_batch::<Sha256>().add(create_operation::<F>(2));
3575 let b = journal.merkle.with_mem(|mem| b_batch.merkleize(mem));
3576
3577 drop(a);
3578 let c_batch = b.new_batch::<Sha256>().add(create_operation::<F>(3));
3579 let c = journal.merkle.with_mem(|mem| c_batch.merkleize(mem));
3580 drop(b);
3581
3582 assert_eq!(c.ancestor_base_leaves, 1);
3583 assert_eq!(c.ancestor_items.len(), 1);
3584
3585 let result = journal.apply_batch(&c).await;
3586 assert!(
3587 matches!(
3588 result,
3589 Err(super::Error::Merkle(merkle::Error::AncestorDropped { expected, .. }))
3590 if expected == c.inner.size()
3591 ),
3592 "expected AncestorDropped, got {result:?}"
3593 );
3594 }
3595
3596 #[test_traced("INFO")]
3597 fn test_apply_batch_detects_dropped_uncommitted_ancestor_mmb() {
3598 let executor = deterministic::Runner::default();
3599 executor.start(test_apply_batch_detects_dropped_uncommitted_ancestor_inner::<mmb::Family>);
3600 }
3601
3602 async fn test_apply_batch_after_committed_ancestor_dropped_inner<F: Family + PartialEq>(
3605 context: Context,
3606 ) {
3607 let mut journal =
3608 create_empty_journal::<F>(context.child("storage"), "dropped-committed").await;
3609
3610 let mut a_batch = journal.new_batch();
3611 for i in 0..8u8 {
3612 a_batch = a_batch.add(create_operation::<F>(i));
3613 }
3614 let a = journal.merkle.with_mem(|mem| a_batch.merkleize(mem));
3615 let b_batch = a.new_batch::<Sha256>().add(create_operation::<F>(8));
3616 let b = journal.merkle.with_mem(|mem| b_batch.merkleize(mem));
3617
3618 journal = journal.apply_batch(&a).await.unwrap();
3619 drop(a);
3620
3621 let c_batch = b.new_batch::<Sha256>().add(create_operation::<F>(9));
3622 let c = journal.merkle.with_mem(|mem| c_batch.merkleize(mem));
3623
3624 assert_eq!(c.ancestor_items.len(), 1);
3626 assert_eq!(c.ancestor_base_leaves, *journal.size());
3627 assert_eq!(c.inner.ancestor_base_size, journal.merkle.size());
3628
3629 drop(b);
3630 journal = journal.apply_batch(&c).await.unwrap();
3631 assert_eq!(*journal.size(), 10);
3632
3633 let mut reference =
3634 create_empty_journal::<F>(context.child("reference"), "dropped-committed-ref").await;
3635 for i in 0..10u8 {
3636 (reference, _) = reference.append(&create_operation::<F>(i)).await.unwrap();
3637 }
3638 assert_eq!(journal_root(&journal), journal_root(&reference));
3639 }
3640
3641 #[test_traced("INFO")]
3642 fn test_apply_batch_after_committed_ancestor_dropped_mmb() {
3643 let executor = deterministic::Runner::default();
3644 executor.start(test_apply_batch_after_committed_ancestor_dropped_inner::<mmb::Family>);
3645 }
3646
3647 async fn test_merkleize_after_committed_prefix_dropped_inner<F: Family + PartialEq>(
3649 context: Context,
3650 ) {
3651 let mut journal =
3652 create_empty_journal::<F>(context.child("storage"), "committed-prefix").await;
3653
3654 let prefix_items = (0..8u8).map(create_operation::<F>).collect();
3656 let (prefix, _) = journal
3657 .merkleize(journal.new_batch(), prefix_items, 0)
3658 .await
3659 .unwrap();
3660 let pending_items = (8..10u8).map(create_operation::<F>).collect();
3661 let (pending, _) = journal
3662 .merkleize(prefix.new_batch::<Sha256>(), pending_items, 0)
3663 .await
3664 .unwrap();
3665
3666 journal = journal.apply_batch(&prefix).await.unwrap();
3668 drop(prefix);
3669
3670 let child_batch = pending.new_batch::<Sha256>();
3673 drop(pending);
3674 let (child, expected_root) = journal
3675 .merkleize(child_batch, vec![create_operation::<F>(10)], 0)
3676 .await
3677 .unwrap();
3678 journal = journal.apply_batch(&child).await.unwrap();
3679
3680 assert_eq!(journal_root(&journal), expected_root);
3681 assert_eq!(*journal.size(), 11);
3682 }
3683
3684 #[test_traced("INFO")]
3685 fn test_merkleize_after_committed_prefix_dropped_mmr() {
3686 let executor = deterministic::Runner::default();
3687 executor.start(test_merkleize_after_committed_prefix_dropped_inner::<mmr::Family>);
3688 }
3689
3690 #[test_traced("INFO")]
3691 fn test_merkleize_after_committed_prefix_dropped_mmb() {
3692 let executor = deterministic::Runner::default();
3693 executor.start(test_merkleize_after_committed_prefix_dropped_inner::<mmb::Family>);
3694 }
3695
3696 #[test_traced("INFO")]
3698 fn test_merkleize_retains_ancestors_after_cancellation() {
3699 deterministic::Runner::default().start(|context| async move {
3700 let strategy = Rayon::new(NZUsize!(2)).unwrap();
3701 let merkle_cfg = merkle_config_with("cancelled-merkleize", &context, strategy);
3702 let journal_cfg = journal_config("cancelled-merkleize", &context);
3703 type RayonJournal = Journal<
3704 mmr::Family,
3705 Context,
3706 ContiguousJournal<Context, DropMonitor<TestOp<mmr::Family>>>,
3707 Sha256,
3708 Rayon,
3709 >;
3710 let journal = RayonJournal::new(
3711 context,
3712 merkle_cfg,
3713 journal_cfg,
3714 |_: &DropMonitor<TestOp<mmr::Family>>| false,
3715 ForwardFold,
3716 )
3717 .await
3718 .unwrap();
3719
3720 let a_items = (0..8u8)
3721 .map(create_operation::<mmr::Family>)
3722 .map(DropMonitor::untracked)
3723 .collect();
3724 let a_batch = journal.new_batch().add_many(a_items);
3725 let a = journal.merkle.with_mem(|mem| a_batch.merkleize(mem));
3726 let b_items = (8..10u8)
3727 .map(create_operation::<mmr::Family>)
3728 .map(DropMonitor::untracked)
3729 .collect();
3730 let b_batch = a.new_batch::<Sha256>().add_many(b_items);
3731 let b = journal.merkle.with_mem(|mem| b_batch.merkleize(mem));
3732
3733 let ancestor = Arc::downgrade(&a.inner);
3734 let c_batch = b.new_batch::<Sha256>();
3735 drop(b);
3736
3737 let release = block_strategy(journal.strategy(), 2);
3738 let (item, clean_drop) = DropMonitor::tracked(create_operation::<mmr::Family>(10));
3739 let mut merkleize = Box::pin(journal.merkleize(c_batch, vec![item], 0));
3740 assert!(futures::poll!(merkleize.as_mut()).is_pending());
3741 drop(merkleize);
3742 drop(a);
3743
3744 assert!(ancestor.upgrade().is_some());
3745 drop(release);
3746 assert!(
3747 clean_drop
3748 .recv_timeout(Duration::from_secs(10))
3749 .expect("detached merkleization did not finish"),
3750 "detached merkleization panicked"
3751 );
3752 assert!(ancestor.upgrade().is_none());
3753 });
3754 }
3755}