Skip to main content

commonware_storage/qmdb/keyless/
variable.rs

1//! A keyless authenticated database for variable-length data.
2//!
3//! For fixed-size values, use [super::fixed].
4
5use crate::{
6    journal::{
7        authenticated,
8        contiguous::variable::{self, Config as JournalConfig},
9    },
10    merkle::Family,
11    qmdb::{
12        any::value::{VariableEncoding, VariableValue},
13        keyless::operation::Operation as BaseOperation,
14        operation::Committable,
15        Error, ROOT_BAGGING,
16    },
17    Context,
18};
19use commonware_codec::Read;
20use commonware_cryptography::Hasher;
21use commonware_parallel::Strategy;
22
23/// Keyless operation for variable-length values.
24pub type Operation<F, V> = BaseOperation<F, VariableEncoding<V>>;
25
26/// A keyless authenticated database for variable-length data.
27pub type Db<F, E, V, H, S> =
28    super::Keyless<F, E, VariableEncoding<V>, variable::Journal<E, Operation<F, V>>, H, S>;
29
30/// A compact keyless authenticated db for variable-length data.
31pub type CompactDb<F, E, V, H, C, S> = super::CompactDb<F, E, VariableEncoding<V>, H, C, S>;
32
33type Journal<F, E, V, H, S> =
34    authenticated::Journal<F, E, variable::Journal<E, Operation<F, V>>, H, S>;
35
36/// Configuration for a variable-size [keyless](super) authenticated db.
37pub type Config<C, S> = super::Config<JournalConfig<C>, S>;
38
39/// Configuration for a variable-size [keyless](super) compact db.
40pub type CompactConfig<C, S> = super::CompactConfig<C, S>;
41
42impl<F: Family, E: Context, V: VariableValue, H: Hasher, S: Strategy> Db<F, E, V, H, S> {
43    /// Returns a [Db] initialized from `cfg`. Any uncommitted operations will be
44    /// discarded and the state of the db will be as of the last committed operation.
45    pub async fn init(
46        context: E,
47        cfg: Config<<Operation<F, V> as Read>::Cfg, S>,
48    ) -> Result<Self, Error<F>> {
49        let journal: Journal<F, E, V, H, S> = Journal::new(
50            context.child("journal"),
51            cfg.merkle,
52            cfg.log,
53            Operation::<F, V>::is_commit,
54            ROOT_BAGGING,
55        )
56        .await?;
57        Self::init_from_journal(journal, context).await
58    }
59}
60
61impl<
62        F: Family,
63        E: Context,
64        V: VariableValue,
65        H: Hasher,
66        C: Clone + Send + Sync + 'static,
67        S: Strategy,
68    > CompactDb<F, E, V, H, C, S>
69where
70    Operation<F, V>: Read<Cfg = C>,
71{
72    /// Returns a [CompactDb] initialized from `cfg`.
73    pub async fn init(context: E, cfg: CompactConfig<C, S>) -> Result<Self, Error<F>> {
74        let merkle = crate::merkle::compact::Merkle::new(cfg.strategy);
75        Self::init_from_merkle(
76            merkle,
77            context.child("witness"),
78            cfg.witness,
79            cfg.commit_codec_config,
80        )
81        .await
82    }
83}
84
85#[cfg(test)]
86mod test {
87    use super::*;
88    use crate::{
89        merkle::{mmb, mmr},
90        qmdb::keyless::tests,
91    };
92    use commonware_cryptography::Sha256;
93    use commonware_macros::{boxed, test_traced};
94    use commonware_parallel::Sequential;
95    use commonware_runtime::{
96        buffer::paged::CacheRef, deterministic, BufferPooler, Runner as _, Supervisor as _,
97    };
98    use commonware_utils::{NZUsize, NZU16, NZU64};
99    use std::num::{NonZeroU16, NonZeroUsize};
100
101    // Use some weird sizes here to test boundary conditions.
102    const PAGE_SIZE: NonZeroU16 = NZU16!(101);
103    const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11);
104
105    fn db_config(
106        suffix: &str,
107        pooler: &impl BufferPooler,
108    ) -> Config<(commonware_codec::RangeCfg<usize>, ()), Sequential> {
109        let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
110        Config {
111            merkle: crate::merkle::full::Config {
112                journal_partition: format!("journal-{suffix}"),
113                metadata_partition: format!("metadata-{suffix}"),
114                items_per_blob: NZU64!(11),
115                write_buffer: NZUsize!(1024),
116                strategy: Sequential,
117                page_cache: page_cache.clone(),
118            },
119            log: JournalConfig {
120                partition: format!("log-journal-{suffix}"),
121                items_per_section: NZU64!(7),
122                compression: None,
123                codec_config: ((0..=10000).into(), ()),
124                page_cache,
125                write_buffer: NZUsize!(1024),
126            },
127        }
128    }
129
130    type TestDb<F> = Db<F, deterministic::Context, Vec<u8>, Sha256, Sequential>;
131    type TestCompactDb<F> = CompactDb<
132        F,
133        deterministic::Context,
134        Vec<u8>,
135        Sha256,
136        (commonware_codec::RangeCfg<usize>, ()),
137        Sequential,
138    >;
139
140    /// Return a [Db] database initialized with a fixed config.
141    async fn open_db<F: Family>(context: deterministic::Context) -> TestDb<F> {
142        open_db_with_suffix("partition", context).await
143    }
144
145    async fn open_db_with_suffix<F: Family>(
146        suffix: &str,
147        context: deterministic::Context,
148    ) -> TestDb<F> {
149        let cfg = db_config(suffix, &context);
150        TestDb::init(context, cfg).await.unwrap()
151    }
152
153    async fn open_compact<F: crate::merkle::Family>(
154        context: deterministic::Context,
155    ) -> TestCompactDb<F> {
156        let cfg = CompactConfig {
157            strategy: Sequential,
158            witness: crate::journal::contiguous::variable::Config {
159                partition: "compact-keyless-variable-witness".into(),
160                items_per_section: NZU64!(64),
161                compression: None,
162                codec_config: (),
163                page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
164                write_buffer: NZUsize!(1024),
165            },
166            commit_codec_config: ((0..=10000usize).into(), ()),
167        };
168        TestCompactDb::init(context, cfg).await.unwrap()
169    }
170
171    fn reopen<F: Family>() -> tests::Reopen<TestDb<F>> {
172        Box::new(|ctx| Box::pin(open_db(ctx)))
173    }
174
175    #[test_traced("INFO")]
176    fn test_keyless_db_empty() {
177        deterministic::Runner::default().start(|ctx| async move {
178            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
179            tests::test_keyless_db_empty(ctx, db, reopen::<mmr::Family>()).await;
180        });
181    }
182
183    #[test_traced("INFO")]
184    fn test_keyless_db_commit_after_sync_recovery() {
185        deterministic::Runner::default().start(|ctx| async move {
186            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
187            tests::test_keyless_db_commit_after_sync_recovery(ctx, db, reopen::<mmr::Family>())
188                .await;
189        });
190    }
191
192    #[test_traced("WARN")]
193    fn test_keyless_db_build_basic() {
194        deterministic::Runner::default().start(|ctx| async move {
195            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
196            tests::test_keyless_db_build_basic(ctx, db, reopen::<mmr::Family>()).await;
197        });
198    }
199
200    #[test_traced("WARN")]
201    fn test_keyless_db_recovery() {
202        deterministic::Runner::default().start(|ctx| async move {
203            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
204            tests::test_keyless_db_recovery(ctx, db, reopen::<mmr::Family>()).await;
205        });
206    }
207
208    #[test_traced("WARN")]
209    fn test_keyless_db_non_empty_recovery() {
210        deterministic::Runner::default().start(|ctx| async move {
211            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
212            tests::test_keyless_db_non_empty_recovery(ctx, db, reopen::<mmr::Family>()).await;
213        });
214    }
215
216    #[test_traced("INFO")]
217    fn test_keyless_db_proof() {
218        deterministic::Runner::default().start(|ctx| async move {
219            let db = open_db::<mmr::Family>(ctx.child("storage")).await;
220            tests::test_keyless_db_proof(db).await;
221        });
222    }
223
224    #[test_traced("INFO")]
225    fn test_keyless_db_proof_comprehensive() {
226        deterministic::Runner::default().start(|ctx| async move {
227            let db = open_db::<mmr::Family>(ctx.child("storage")).await;
228            tests::test_keyless_db_proof_comprehensive(db).await;
229        });
230    }
231
232    #[test_traced("INFO")]
233    fn test_keyless_db_proof_with_pruning() {
234        deterministic::Runner::default().start(|ctx| async move {
235            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
236            tests::test_keyless_db_proof_with_pruning(ctx, db, reopen::<mmr::Family>()).await;
237        });
238    }
239
240    /// Regression: when pruning leaves `bounds.start` mid-blob ahead of the first retained commit,
241    /// `historical_proof` for sizes in that leading interval must report `HistoricalFloorPruned`
242    /// (the floor metadata is gone) rather than the misleading `UnexpectedData` (which sounds like
243    /// data corruption).
244    ///
245    /// Items_per_section=7 with batches of 3 appends + 1 commit places commits at locations 0, 4,
246    /// 8, 12, .... Pruning to loc=8 removes blob 0 (end=7 <=
247    /// 8) and retains blob 1 ([7, 14)). `bounds.start = 7` is a non-commit op (an Append), and the
248    /// previous commit at location 4 was pruned. `historical_proof(op_count=8, ...)` asks for the
249    /// state just before the first retained commit, which has no retained governing floor.
250    #[test_traced("INFO")]
251    fn test_keyless_historical_proof_floor_pruned() {
252        use crate::merkle::Location;
253        deterministic::Runner::default().start(|ctx| async move {
254            let mut db = open_db::<mmr::Family>(ctx.child("db")).await;
255
256            // Build commits at 0, 4, 8, 12, ... (3 appends + 1 commit per batch).
257            for batch_idx in 0u64..15 {
258                let mut batch = db.new_batch();
259                for j in 0..3 {
260                    batch =
261                        batch.append(<Vec<u8> as crate::qmdb::keyless::tests::TestValue>::make(
262                            batch_idx * 10 + j,
263                        ));
264                }
265                let new_commit_loc = Location::new(*db.last_commit_loc() + 1 + 3);
266                db.apply_batch(batch.merkleize(&db, None, new_commit_loc).await)
267                    .await
268                    .unwrap();
269            }
270
271            // Prune to loc=8: blob 0 ([0,7)) end=7 <= 8 -> pruned. bounds.start = 7, first retained
272            // commit is at 8.
273            db.prune(Location::new(8)).await.unwrap();
274            let bounds = db.bounds();
275            assert_eq!(*bounds.start, 7);
276
277            // op_count = first retained commit (= state just before that commit). Expected:
278            // HistoricalFloorPruned, NOT UnexpectedData.
279            let result = db
280                .historical_proof(Location::new(8), bounds.start, NZU64!(5))
281                .await;
282            assert!(
283                !matches!(result, Err(Error::UnexpectedData(_))),
284                "must not surface as UnexpectedData; got {result:?}",
285            );
286            assert!(
287                matches!(result, Err(Error::HistoricalFloorPruned(loc)) if loc == Location::new(8)),
288                "expected HistoricalFloorPruned(8), got {result:?}",
289            );
290
291            // Sanity: a commit-boundary size whose floor is retained still works. First retained
292            // commit at 8 declares some floor; op_count=9 is the post-commit size whose governing
293            // floor is the one declared at op 8.
294            db.historical_proof(Location::new(9), Location::new(8), NZU64!(1))
295                .await
296                .expect("commit-boundary historical_proof should succeed");
297
298            db.destroy().await.unwrap();
299        });
300    }
301
302    #[test_traced("WARN")]
303    fn test_keyless_db_empty_db_recovery() {
304        deterministic::Runner::default().start(|ctx| async move {
305            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
306            tests::test_keyless_db_empty_db_recovery(ctx, db, reopen::<mmr::Family>()).await;
307        });
308    }
309
310    #[test_traced("WARN")]
311    fn test_keyless_db_replay_with_trailing_appends() {
312        deterministic::Runner::default().start(|ctx| async move {
313            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
314            tests::test_keyless_db_replay_with_trailing_appends(ctx, db, reopen::<mmr::Family>())
315                .await;
316        });
317    }
318
319    #[test_traced("INFO")]
320    fn test_keyless_db_get_out_of_bounds() {
321        deterministic::Runner::default().start(|ctx| async move {
322            let db = open_db::<mmr::Family>(ctx.child("storage")).await;
323            tests::test_keyless_db_get_out_of_bounds(db).await;
324        });
325    }
326
327    #[test_traced("INFO")]
328    fn test_keyless_db_metadata() {
329        deterministic::Runner::default().start(|ctx| async move {
330            let db = open_db::<mmr::Family>(ctx.child("db")).await;
331            tests::test_keyless_db_metadata(db).await;
332        });
333    }
334
335    #[boxed]
336    async fn assert_compact_root_compatibility<F: crate::merkle::Family>(
337        ctx: deterministic::Context,
338    ) {
339        let mut db = open_db::<F>(ctx.child("db")).await;
340        let mut compact = open_compact::<F>(ctx.child("compact")).await;
341        assert_eq!(db.root(), compact.root());
342
343        let v1 = b"hello".to_vec();
344        let v2 = b"world".to_vec();
345        let metadata = b"metadata".to_vec();
346
347        let floor = db.inactivity_floor_loc();
348        let retained = db
349            .new_batch()
350            .append(v1.clone())
351            .append(v2.clone())
352            .merkleize(&db, Some(metadata.clone()), floor)
353            .await;
354        let compact_batch = compact
355            .new_batch()
356            .append(v1)
357            .append(v2)
358            .merkleize(&compact, Some(metadata.clone()), floor)
359            .await;
360
361        assert_eq!(retained.root(), compact_batch.root());
362
363        db.apply_batch(retained).await.unwrap();
364        compact.apply_batch(compact_batch).unwrap();
365        db.commit().await.unwrap();
366        compact.sync().await.unwrap();
367
368        assert_eq!(db.root(), compact.root());
369        assert_eq!(compact.get_metadata(), Some(metadata.clone()));
370
371        drop(compact);
372        let reopened = open_compact::<F>(ctx.child("reopen")).await;
373        assert_eq!(db.root(), reopened.root());
374        assert_eq!(reopened.get_metadata(), Some(metadata));
375
376        reopened.destroy().await.unwrap();
377        db.destroy().await.unwrap();
378    }
379
380    #[test_traced("INFO")]
381    fn test_keyless_variable_compact_root_compatibility() {
382        deterministic::Runner::default().start(|ctx| async move {
383            assert_compact_root_compatibility::<mmr::Family>(ctx).await;
384        });
385    }
386
387    #[test_traced("INFO")]
388    fn test_keyless_variable_compact_root_compatibility_mmb() {
389        deterministic::Runner::default().start(|ctx| async move {
390            assert_compact_root_compatibility::<mmb::Family>(ctx).await;
391        });
392    }
393
394    #[test_traced("INFO")]
395    fn test_keyless_db_pruning() {
396        deterministic::Runner::default().start(|ctx| async move {
397            let db = open_db::<mmr::Family>(ctx.child("db")).await;
398            tests::test_keyless_db_pruning(db).await;
399        });
400    }
401
402    #[test_traced("INFO")]
403    fn test_keyless_batch_get() {
404        deterministic::Runner::default().start(|ctx| async move {
405            let db = open_db::<mmr::Family>(ctx.child("db")).await;
406            tests::test_keyless_batch_get(db).await;
407        });
408    }
409
410    #[test_traced("INFO")]
411    fn test_keyless_batch_stacked_get() {
412        deterministic::Runner::default().start(|ctx| async move {
413            let db = open_db::<mmr::Family>(ctx.child("db")).await;
414            tests::test_keyless_batch_stacked_get(db).await;
415        });
416    }
417
418    #[test_traced("INFO")]
419    fn test_keyless_batch_speculative_root() {
420        deterministic::Runner::default().start(|ctx| async move {
421            let db = open_db::<mmr::Family>(ctx.child("db")).await;
422            tests::test_keyless_batch_speculative_root(db).await;
423        });
424    }
425
426    #[test_traced("INFO")]
427    fn test_keyless_merkleized_batch_get() {
428        deterministic::Runner::default().start(|ctx| async move {
429            let db = open_db::<mmr::Family>(ctx.child("db")).await;
430            tests::test_keyless_merkleized_batch_get(db).await;
431        });
432    }
433
434    #[test_traced("INFO")]
435    fn test_keyless_get_many() {
436        deterministic::Runner::default().start(|ctx| async move {
437            let db = open_db::<mmr::Family>(ctx.child("db")).await;
438            tests::test_keyless_get_many(db).await;
439        });
440    }
441
442    #[test_traced("INFO")]
443    fn test_keyless_batch_chained() {
444        deterministic::Runner::default().start(|ctx| async move {
445            let db = open_db::<mmr::Family>(ctx.child("db")).await;
446            tests::test_keyless_batch_chained(db).await;
447        });
448    }
449
450    #[test_traced("INFO")]
451    fn test_keyless_batch_chained_apply_sequential() {
452        deterministic::Runner::default().start(|ctx| async move {
453            let db = open_db::<mmr::Family>(ctx.child("db")).await;
454            tests::test_keyless_batch_chained_apply_sequential(db).await;
455        });
456    }
457
458    #[test_traced("INFO")]
459    fn test_keyless_batch_many_sequential() {
460        deterministic::Runner::default().start(|ctx| async move {
461            let db = open_db::<mmr::Family>(ctx.child("db")).await;
462            tests::test_keyless_batch_many_sequential(db).await;
463        });
464    }
465
466    #[test_traced("INFO")]
467    fn test_keyless_batch_empty() {
468        deterministic::Runner::default().start(|ctx| async move {
469            let db = open_db::<mmr::Family>(ctx.child("db")).await;
470            tests::test_keyless_batch_empty(db).await;
471        });
472    }
473
474    #[test_traced("INFO")]
475    fn test_keyless_batch_chained_merkleized_get() {
476        deterministic::Runner::default().start(|ctx| async move {
477            let db = open_db::<mmr::Family>(ctx.child("db")).await;
478            tests::test_keyless_batch_chained_merkleized_get(db).await;
479        });
480    }
481
482    #[test_traced("INFO")]
483    fn test_keyless_batch_large() {
484        deterministic::Runner::default().start(|ctx| async move {
485            let db = open_db::<mmr::Family>(ctx.child("db")).await;
486            tests::test_keyless_batch_large(db).await;
487        });
488    }
489
490    #[test_traced]
491    fn test_keyless_stale_batch() {
492        deterministic::Runner::default().start(|ctx| async move {
493            let db = open_db::<mmr::Family>(ctx.child("db")).await;
494            tests::test_keyless_stale_batch(db).await;
495        });
496    }
497
498    #[test_traced]
499    fn test_stale_batch_chained() {
500        deterministic::Runner::default().start(|ctx| async move {
501            let db = open_db::<mmr::Family>(ctx.child("db")).await;
502            tests::test_keyless_stale_batch_chained(db).await;
503        });
504    }
505
506    #[test_traced]
507    fn test_sequential_commit_parent_then_child() {
508        deterministic::Runner::default().start(|ctx| async move {
509            let db = open_db::<mmr::Family>(ctx.child("db")).await;
510            tests::test_keyless_sequential_commit_parent_then_child(db).await;
511        });
512    }
513
514    #[test_traced]
515    fn test_stale_batch_child_applied_before_parent() {
516        deterministic::Runner::default().start(|ctx| async move {
517            let db = open_db::<mmr::Family>(ctx.child("db")).await;
518            tests::test_keyless_stale_batch_child_before_parent(db).await;
519        });
520    }
521
522    #[test_traced]
523    fn test_partial_ancestor_commit() {
524        deterministic::Runner::default().start(|ctx| async move {
525            let db = open_db::<mmr::Family>(ctx.child("db")).await;
526            tests::test_keyless_partial_ancestor_commit(db).await;
527        });
528    }
529
530    #[test_traced]
531    fn test_keyless_to_batch() {
532        deterministic::Runner::default().start(|ctx| async move {
533            let db = open_db::<mmr::Family>(ctx.child("db")).await;
534            tests::test_keyless_to_batch(db).await;
535        });
536    }
537
538    #[test_traced]
539    fn test_keyless_child_root_matches_pending_and_committed() {
540        deterministic::Runner::default().start(|ctx| async move {
541            let db = open_db::<mmr::Family>(ctx.child("db")).await;
542            tests::test_keyless_child_root_matches_pending_and_committed(db).await;
543        });
544    }
545
546    #[test_traced("INFO")]
547    fn test_keyless_rewind_recovery() {
548        deterministic::Runner::default().start(|ctx| async move {
549            let db = open_db::<mmr::Family>(ctx.child("db")).await;
550            tests::test_keyless_db_rewind_recovery(ctx, db, reopen::<mmr::Family>()).await;
551        });
552    }
553
554    #[test_traced("INFO")]
555    fn test_keyless_rewind_pruned_target_errors() {
556        deterministic::Runner::default().start(|ctx| async move {
557            let db = open_db::<mmr::Family>(ctx.child("db")).await;
558            tests::test_keyless_db_rewind_pruned_target_errors(db).await;
559        });
560    }
561
562    // mmb::Family variants
563
564    #[test_traced("INFO")]
565    fn test_keyless_db_empty_mmb() {
566        deterministic::Runner::default().start(|ctx| async move {
567            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
568            tests::test_keyless_db_empty(ctx, db, reopen::<mmb::Family>()).await;
569        });
570    }
571
572    #[test_traced("WARN")]
573    fn test_keyless_db_build_basic_mmb() {
574        deterministic::Runner::default().start(|ctx| async move {
575            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
576            tests::test_keyless_db_build_basic(ctx, db, reopen::<mmb::Family>()).await;
577        });
578    }
579
580    #[test_traced("WARN")]
581    fn test_keyless_db_recovery_mmb() {
582        deterministic::Runner::default().start(|ctx| async move {
583            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
584            tests::test_keyless_db_recovery(ctx, db, reopen::<mmb::Family>()).await;
585        });
586    }
587
588    #[test_traced("WARN")]
589    fn test_keyless_db_non_empty_recovery_mmb() {
590        deterministic::Runner::default().start(|ctx| async move {
591            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
592            tests::test_keyless_db_non_empty_recovery(ctx, db, reopen::<mmb::Family>()).await;
593        });
594    }
595
596    #[test_traced("INFO")]
597    fn test_keyless_db_proof_mmb() {
598        deterministic::Runner::default().start(|ctx| async move {
599            let db = open_db::<mmb::Family>(ctx.child("storage")).await;
600            tests::test_keyless_db_proof(db).await;
601        });
602    }
603
604    #[test_traced("INFO")]
605    fn test_keyless_db_proof_comprehensive_mmb() {
606        deterministic::Runner::default().start(|ctx| async move {
607            let db = open_db::<mmb::Family>(ctx.child("storage")).await;
608            tests::test_keyless_db_proof_comprehensive(db).await;
609        });
610    }
611
612    #[test_traced("INFO")]
613    fn test_keyless_db_proof_with_pruning_mmb() {
614        deterministic::Runner::default().start(|ctx| async move {
615            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
616            tests::test_keyless_db_proof_with_pruning(ctx, db, reopen::<mmb::Family>()).await;
617        });
618    }
619
620    #[test_traced("WARN")]
621    fn test_keyless_db_empty_db_recovery_mmb() {
622        deterministic::Runner::default().start(|ctx| async move {
623            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
624            tests::test_keyless_db_empty_db_recovery(ctx, db, reopen::<mmb::Family>()).await;
625        });
626    }
627
628    #[test_traced("WARN")]
629    fn test_keyless_db_replay_with_trailing_appends_mmb() {
630        deterministic::Runner::default().start(|ctx| async move {
631            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
632            tests::test_keyless_db_replay_with_trailing_appends(ctx, db, reopen::<mmb::Family>())
633                .await;
634        });
635    }
636
637    #[test_traced("INFO")]
638    fn test_keyless_db_get_out_of_bounds_mmb() {
639        deterministic::Runner::default().start(|ctx| async move {
640            let db = open_db::<mmb::Family>(ctx.child("storage")).await;
641            tests::test_keyless_db_get_out_of_bounds(db).await;
642        });
643    }
644
645    #[test_traced("INFO")]
646    fn test_keyless_db_metadata_mmb() {
647        deterministic::Runner::default().start(|ctx| async move {
648            let db = open_db::<mmb::Family>(ctx.child("db")).await;
649            tests::test_keyless_db_metadata(db).await;
650        });
651    }
652
653    #[test_traced("INFO")]
654    fn test_keyless_db_pruning_mmb() {
655        deterministic::Runner::default().start(|ctx| async move {
656            let db = open_db::<mmb::Family>(ctx.child("db")).await;
657            tests::test_keyless_db_pruning(db).await;
658        });
659    }
660
661    #[test_traced("INFO")]
662    fn test_keyless_batch_get_mmb() {
663        deterministic::Runner::default().start(|ctx| async move {
664            let db = open_db::<mmb::Family>(ctx.child("db")).await;
665            tests::test_keyless_batch_get(db).await;
666        });
667    }
668
669    #[test_traced("INFO")]
670    fn test_keyless_batch_stacked_get_mmb() {
671        deterministic::Runner::default().start(|ctx| async move {
672            let db = open_db::<mmb::Family>(ctx.child("db")).await;
673            tests::test_keyless_batch_stacked_get(db).await;
674        });
675    }
676
677    #[test_traced("INFO")]
678    fn test_keyless_batch_speculative_root_mmb() {
679        deterministic::Runner::default().start(|ctx| async move {
680            let db = open_db::<mmb::Family>(ctx.child("db")).await;
681            tests::test_keyless_batch_speculative_root(db).await;
682        });
683    }
684
685    #[test_traced("INFO")]
686    fn test_keyless_merkleized_batch_get_mmb() {
687        deterministic::Runner::default().start(|ctx| async move {
688            let db = open_db::<mmb::Family>(ctx.child("db")).await;
689            tests::test_keyless_merkleized_batch_get(db).await;
690        });
691    }
692
693    #[test_traced("INFO")]
694    fn test_keyless_batch_chained_mmb() {
695        deterministic::Runner::default().start(|ctx| async move {
696            let db = open_db::<mmb::Family>(ctx.child("db")).await;
697            tests::test_keyless_batch_chained(db).await;
698        });
699    }
700
701    #[test_traced("INFO")]
702    fn test_keyless_batch_chained_apply_sequential_mmb() {
703        deterministic::Runner::default().start(|ctx| async move {
704            let db = open_db::<mmb::Family>(ctx.child("db")).await;
705            tests::test_keyless_batch_chained_apply_sequential(db).await;
706        });
707    }
708
709    #[test_traced("INFO")]
710    fn test_keyless_batch_many_sequential_mmb() {
711        deterministic::Runner::default().start(|ctx| async move {
712            let db = open_db::<mmb::Family>(ctx.child("db")).await;
713            tests::test_keyless_batch_many_sequential(db).await;
714        });
715    }
716
717    #[test_traced("INFO")]
718    fn test_keyless_batch_empty_mmb() {
719        deterministic::Runner::default().start(|ctx| async move {
720            let db = open_db::<mmb::Family>(ctx.child("db")).await;
721            tests::test_keyless_batch_empty(db).await;
722        });
723    }
724
725    #[test_traced("INFO")]
726    fn test_keyless_batch_chained_merkleized_get_mmb() {
727        deterministic::Runner::default().start(|ctx| async move {
728            let db = open_db::<mmb::Family>(ctx.child("db")).await;
729            tests::test_keyless_batch_chained_merkleized_get(db).await;
730        });
731    }
732
733    #[test_traced("INFO")]
734    fn test_keyless_batch_large_mmb() {
735        deterministic::Runner::default().start(|ctx| async move {
736            let db = open_db::<mmb::Family>(ctx.child("db")).await;
737            tests::test_keyless_batch_large(db).await;
738        });
739    }
740
741    #[test_traced]
742    fn test_keyless_stale_batch_mmb() {
743        deterministic::Runner::default().start(|ctx| async move {
744            let db = open_db::<mmb::Family>(ctx.child("db")).await;
745            tests::test_keyless_stale_batch(db).await;
746        });
747    }
748
749    #[test_traced]
750    fn test_stale_batch_chained_mmb() {
751        deterministic::Runner::default().start(|ctx| async move {
752            let db = open_db::<mmb::Family>(ctx.child("db")).await;
753            tests::test_keyless_stale_batch_chained(db).await;
754        });
755    }
756
757    #[test_traced]
758    fn test_sequential_commit_parent_then_child_mmb() {
759        deterministic::Runner::default().start(|ctx| async move {
760            let db = open_db::<mmb::Family>(ctx.child("db")).await;
761            tests::test_keyless_sequential_commit_parent_then_child(db).await;
762        });
763    }
764
765    #[test_traced]
766    fn test_stale_batch_child_applied_before_parent_mmb() {
767        deterministic::Runner::default().start(|ctx| async move {
768            let db = open_db::<mmb::Family>(ctx.child("db")).await;
769            tests::test_keyless_stale_batch_child_before_parent(db).await;
770        });
771    }
772
773    #[test_traced]
774    fn test_keyless_to_batch_mmb() {
775        deterministic::Runner::default().start(|ctx| async move {
776            let db = open_db::<mmb::Family>(ctx.child("db")).await;
777            tests::test_keyless_to_batch(db).await;
778        });
779    }
780
781    #[test_traced]
782    fn test_keyless_child_root_matches_pending_and_committed_mmb() {
783        deterministic::Runner::default().start(|ctx| async move {
784            let db = open_db::<mmb::Family>(ctx.child("db")).await;
785            tests::test_keyless_child_root_matches_pending_and_committed(db).await;
786        });
787    }
788
789    #[test_traced("INFO")]
790    fn test_keyless_rewind_recovery_mmb() {
791        deterministic::Runner::default().start(|ctx| async move {
792            let db = open_db::<mmb::Family>(ctx.child("db")).await;
793            tests::test_keyless_db_rewind_recovery(ctx, db, reopen::<mmb::Family>()).await;
794        });
795    }
796
797    #[test_traced("INFO")]
798    fn test_keyless_rewind_pruned_target_errors_mmb() {
799        deterministic::Runner::default().start(|ctx| async move {
800            let db = open_db::<mmb::Family>(ctx.child("db")).await;
801            tests::test_keyless_db_rewind_pruned_target_errors(db).await;
802        });
803    }
804
805    #[test_traced("INFO")]
806    fn test_keyless_variable_floor_tracking_mmb() {
807        deterministic::Runner::default().start(|ctx| async move {
808            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
809            tests::test_keyless_db_floor_tracking(ctx, db, reopen::<mmb::Family>()).await;
810        });
811    }
812
813    #[test_traced("INFO")]
814    fn test_keyless_variable_floor_regression_rejected_mmb() {
815        deterministic::Runner::default().start(|ctx| async move {
816            let db = open_db::<mmb::Family>(ctx.child("db")).await;
817            tests::test_keyless_db_floor_regression_rejected(db).await;
818        });
819    }
820
821    #[test_traced("INFO")]
822    fn test_keyless_variable_floor_beyond_commit_loc_rejected_mmb() {
823        deterministic::Runner::default().start(|ctx| async move {
824            let db = open_db::<mmb::Family>(ctx.child("db")).await;
825            tests::test_keyless_db_floor_beyond_commit_loc_rejected(db).await;
826        });
827    }
828
829    #[test_traced("INFO")]
830    fn test_keyless_variable_rewind_restores_floor_mmb() {
831        deterministic::Runner::default().start(|ctx| async move {
832            let db = open_db::<mmb::Family>(ctx.child("db")).await;
833            tests::test_keyless_db_rewind_restores_floor(db).await;
834        });
835    }
836
837    #[test_traced("INFO")]
838    fn test_keyless_variable_floor_changes_root_mmb() {
839        deterministic::Runner::default().start(|ctx| async move {
840            let db_a = open_db_with_suffix::<mmb::Family>("root-a", ctx.child("a")).await;
841            let db_b = open_db_with_suffix::<mmb::Family>("root-b", ctx.child("b")).await;
842            tests::test_keyless_db_floor_changes_root(db_a, db_b).await;
843        });
844    }
845
846    #[test_traced("INFO")]
847    fn test_keyless_variable_floor_at_commit_loc_accepted_mmb() {
848        deterministic::Runner::default().start(|ctx| async move {
849            let db = open_db::<mmb::Family>(ctx.child("db")).await;
850            tests::test_keyless_db_floor_at_commit_loc_accepted(db).await;
851        });
852    }
853
854    #[test_traced("INFO")]
855    fn test_keyless_variable_rewind_after_reopen_with_floor_mmb() {
856        deterministic::Runner::default().start(|ctx| async move {
857            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
858            tests::test_keyless_db_rewind_after_reopen_with_floor(ctx, db, reopen::<mmb::Family>())
859                .await;
860        });
861    }
862
863    #[test_traced("INFO")]
864    fn test_keyless_variable_ancestor_floor_regression_rejected_mmb() {
865        deterministic::Runner::default().start(|ctx| async move {
866            let db = open_db::<mmb::Family>(ctx.child("db")).await;
867            tests::test_keyless_db_ancestor_floor_regression_rejected(db).await;
868        });
869    }
870
871    #[test_traced("INFO")]
872    fn test_keyless_variable_ancestor_floor_beyond_commit_loc_rejected_mmb() {
873        deterministic::Runner::default().start(|ctx| async move {
874            let db = open_db::<mmb::Family>(ctx.child("db")).await;
875            tests::test_keyless_db_ancestor_floor_beyond_commit_loc_rejected(db).await;
876        });
877    }
878
879    #[test_traced("INFO")]
880    fn test_keyless_variable_chained_apply_with_valid_floors_succeeds_mmb() {
881        deterministic::Runner::default().start(|ctx| async move {
882            let db = open_db::<mmb::Family>(ctx.child("db")).await;
883            tests::test_keyless_db_chained_apply_with_valid_floors_succeeds(db).await;
884        });
885    }
886
887    #[test_traced("INFO")]
888    fn test_keyless_variable_single_commit_live_set_mmb() {
889        deterministic::Runner::default().start(|ctx| async move {
890            let db = open_db::<mmb::Family>(ctx.child("db").with_attribute("index", 1)).await;
891            tests::test_keyless_db_single_commit_live_set(ctx, db, reopen::<mmb::Family>()).await;
892        });
893    }
894
895    #[test_traced("INFO")]
896    fn test_keyless_variable_floor_tracking() {
897        deterministic::Runner::default().start(|ctx| async move {
898            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
899            tests::test_keyless_db_floor_tracking(ctx, db, reopen::<mmr::Family>()).await;
900        });
901    }
902
903    #[test_traced("INFO")]
904    fn test_keyless_variable_floor_regression_rejected() {
905        deterministic::Runner::default().start(|ctx| async move {
906            let db = open_db::<mmr::Family>(ctx.child("db")).await;
907            tests::test_keyless_db_floor_regression_rejected(db).await;
908        });
909    }
910
911    #[test_traced("INFO")]
912    fn test_keyless_variable_floor_beyond_commit_loc_rejected() {
913        deterministic::Runner::default().start(|ctx| async move {
914            let db = open_db::<mmr::Family>(ctx.child("db")).await;
915            tests::test_keyless_db_floor_beyond_commit_loc_rejected(db).await;
916        });
917    }
918
919    #[test_traced("INFO")]
920    fn test_keyless_variable_rewind_restores_floor() {
921        deterministic::Runner::default().start(|ctx| async move {
922            let db = open_db::<mmr::Family>(ctx.child("db")).await;
923            tests::test_keyless_db_rewind_restores_floor(db).await;
924        });
925    }
926
927    #[test_traced("INFO")]
928    fn test_keyless_variable_floor_changes_root() {
929        deterministic::Runner::default().start(|ctx| async move {
930            let db_a = open_db_with_suffix::<mmr::Family>("root-a", ctx.child("a")).await;
931            let db_b = open_db_with_suffix::<mmr::Family>("root-b", ctx.child("b")).await;
932            tests::test_keyless_db_floor_changes_root(db_a, db_b).await;
933        });
934    }
935
936    #[test_traced("INFO")]
937    fn test_keyless_variable_floor_at_commit_loc_accepted() {
938        deterministic::Runner::default().start(|ctx| async move {
939            let db = open_db::<mmr::Family>(ctx.child("db")).await;
940            tests::test_keyless_db_floor_at_commit_loc_accepted(db).await;
941        });
942    }
943
944    #[test_traced("INFO")]
945    fn test_keyless_variable_rewind_after_reopen_with_floor() {
946        deterministic::Runner::default().start(|ctx| async move {
947            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
948            tests::test_keyless_db_rewind_after_reopen_with_floor(ctx, db, reopen::<mmr::Family>())
949                .await;
950        });
951    }
952
953    #[test_traced("INFO")]
954    fn test_keyless_variable_ancestor_floor_regression_rejected() {
955        deterministic::Runner::default().start(|ctx| async move {
956            let db = open_db::<mmr::Family>(ctx.child("db")).await;
957            tests::test_keyless_db_ancestor_floor_regression_rejected(db).await;
958        });
959    }
960
961    #[test_traced("INFO")]
962    fn test_keyless_variable_ancestor_floor_beyond_commit_loc_rejected() {
963        deterministic::Runner::default().start(|ctx| async move {
964            let db = open_db::<mmr::Family>(ctx.child("db")).await;
965            tests::test_keyless_db_ancestor_floor_beyond_commit_loc_rejected(db).await;
966        });
967    }
968
969    #[test_traced("INFO")]
970    fn test_keyless_variable_chained_apply_with_valid_floors_succeeds() {
971        deterministic::Runner::default().start(|ctx| async move {
972            let db = open_db::<mmr::Family>(ctx.child("db")).await;
973            tests::test_keyless_db_chained_apply_with_valid_floors_succeeds(db).await;
974        });
975    }
976
977    #[test_traced("INFO")]
978    fn test_keyless_variable_single_commit_live_set() {
979        deterministic::Runner::default().start(|ctx| async move {
980            let db = open_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
981            tests::test_keyless_db_single_commit_live_set(ctx, db, reopen::<mmr::Family>()).await;
982        });
983    }
984
985    fn is_send<T: Send>(_: T) {}
986
987    #[allow(dead_code)]
988    fn assert_db_futures_are_send(
989        db: &mut TestDb<mmr::Family>,
990        loc: crate::merkle::Location<mmr::Family>,
991    ) {
992        is_send(db.get_metadata());
993        is_send(db.proof(loc, NZU64!(1)));
994        is_send(db.sync());
995        is_send(db.get(loc));
996        is_send(db.rewind(loc));
997    }
998}