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,
16    },
17};
18use commonware_codec::Read;
19use commonware_cryptography::Hasher;
20use commonware_runtime::{Clock, Metrics, Storage};
21
22/// Keyless operation for variable-length values.
23pub type Operation<V> = BaseOperation<VariableEncoding<V>>;
24
25/// A keyless authenticated database for variable-length data.
26pub type Db<F, E, V, H> =
27    super::Keyless<F, E, VariableEncoding<V>, variable::Journal<E, Operation<V>>, H>;
28
29type Journal<F, E, V, H> = authenticated::Journal<F, E, variable::Journal<E, Operation<V>>, H>;
30
31/// Configuration for a variable-size [keyless](super) authenticated db.
32pub type Config<C> = super::Config<JournalConfig<C>>;
33
34impl<F: Family, E: Storage + Clock + Metrics, V: VariableValue, H: Hasher> Db<F, E, V, H> {
35    /// Returns a [Db] initialized from `cfg`. Any uncommitted operations will be
36    /// discarded and the state of the db will be as of the last committed operation.
37    pub async fn init(
38        context: E,
39        cfg: Config<<Operation<V> as Read>::Cfg>,
40    ) -> Result<Self, Error<F>> {
41        let journal: Journal<F, E, V, H> =
42            Journal::new(context, cfg.merkle, cfg.log, Operation::<V>::is_commit).await?;
43        Self::init_from_journal(journal).await
44    }
45}
46
47#[cfg(test)]
48mod test {
49    use super::*;
50    use crate::{
51        merkle::{mmb, mmr},
52        qmdb::keyless::tests,
53    };
54    use commonware_cryptography::Sha256;
55    use commonware_macros::test_traced;
56    use commonware_runtime::{
57        buffer::paged::CacheRef, deterministic, BufferPooler, Metrics, Runner as _,
58    };
59    use commonware_utils::{NZUsize, NZU16, NZU64};
60    use std::num::{NonZeroU16, NonZeroUsize};
61
62    // Use some weird sizes here to test boundary conditions.
63    const PAGE_SIZE: NonZeroU16 = NZU16!(101);
64    const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11);
65
66    fn db_config(
67        suffix: &str,
68        pooler: &impl BufferPooler,
69    ) -> Config<(commonware_codec::RangeCfg<usize>, ())> {
70        let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
71        Config {
72            merkle: crate::merkle::journaled::Config {
73                journal_partition: format!("journal-{suffix}"),
74                metadata_partition: format!("metadata-{suffix}"),
75                items_per_blob: NZU64!(11),
76                write_buffer: NZUsize!(1024),
77                thread_pool: None,
78                page_cache: page_cache.clone(),
79            },
80            log: JournalConfig {
81                partition: format!("log-journal-{suffix}"),
82                items_per_section: NZU64!(7),
83                compression: None,
84                codec_config: ((0..=10000).into(), ()),
85                page_cache,
86                write_buffer: NZUsize!(1024),
87            },
88        }
89    }
90
91    type TestDb<F> = Db<F, deterministic::Context, Vec<u8>, Sha256>;
92
93    /// Return a [Db] database initialized with a fixed config.
94    async fn open_db<F: crate::merkle::Family>(context: deterministic::Context) -> TestDb<F> {
95        let cfg = db_config("partition", &context);
96        TestDb::init(context, cfg).await.unwrap()
97    }
98
99    fn reopen<F: crate::merkle::Family>() -> tests::Reopen<TestDb<F>> {
100        Box::new(|ctx| Box::pin(open_db(ctx)))
101    }
102
103    #[test_traced("INFO")]
104    fn test_keyless_db_empty() {
105        deterministic::Runner::default().start(|ctx| async move {
106            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
107            tests::test_keyless_db_empty(ctx, db, reopen::<mmr::Family>()).await;
108        });
109    }
110
111    #[test_traced("WARN")]
112    fn test_keyless_db_build_basic() {
113        deterministic::Runner::default().start(|ctx| async move {
114            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
115            tests::test_keyless_db_build_basic(ctx, db, reopen::<mmr::Family>()).await;
116        });
117    }
118
119    #[test_traced("WARN")]
120    fn test_keyless_db_recovery() {
121        deterministic::Runner::default().start(|ctx| async move {
122            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
123            tests::test_keyless_db_recovery(ctx, db, reopen::<mmr::Family>()).await;
124        });
125    }
126
127    #[test_traced("WARN")]
128    fn test_keyless_db_non_empty_recovery() {
129        deterministic::Runner::default().start(|ctx| async move {
130            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
131            tests::test_keyless_db_non_empty_recovery(ctx, db, reopen::<mmr::Family>()).await;
132        });
133    }
134
135    #[test_traced("INFO")]
136    fn test_keyless_db_proof() {
137        deterministic::Runner::default().start(|ctx| async move {
138            let db = open_db::<mmr::Family>(ctx.clone()).await;
139            tests::test_keyless_db_proof(db).await;
140        });
141    }
142
143    #[test_traced("INFO")]
144    fn test_keyless_db_proof_comprehensive() {
145        deterministic::Runner::default().start(|ctx| async move {
146            let db = open_db::<mmr::Family>(ctx.clone()).await;
147            tests::test_keyless_db_proof_comprehensive(db).await;
148        });
149    }
150
151    #[test_traced("INFO")]
152    fn test_keyless_db_proof_with_pruning() {
153        deterministic::Runner::default().start(|ctx| async move {
154            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
155            tests::test_keyless_db_proof_with_pruning(ctx, db, reopen::<mmr::Family>()).await;
156        });
157    }
158
159    #[test_traced("WARN")]
160    fn test_keyless_db_empty_db_recovery() {
161        deterministic::Runner::default().start(|ctx| async move {
162            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
163            tests::test_keyless_db_empty_db_recovery(ctx, db, reopen::<mmr::Family>()).await;
164        });
165    }
166
167    #[test_traced("WARN")]
168    fn test_keyless_db_replay_with_trailing_appends() {
169        deterministic::Runner::default().start(|ctx| async move {
170            let db = open_db::<mmr::Family>(ctx.with_label("db1")).await;
171            tests::test_keyless_db_replay_with_trailing_appends(ctx, db, reopen::<mmr::Family>())
172                .await;
173        });
174    }
175
176    #[test_traced("INFO")]
177    fn test_keyless_db_get_out_of_bounds() {
178        deterministic::Runner::default().start(|ctx| async move {
179            let db = open_db::<mmr::Family>(ctx.clone()).await;
180            tests::test_keyless_db_get_out_of_bounds(db).await;
181        });
182    }
183
184    #[test_traced("INFO")]
185    fn test_keyless_db_metadata() {
186        deterministic::Runner::default().start(|ctx| async move {
187            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
188            tests::test_keyless_db_metadata(db).await;
189        });
190    }
191
192    #[test_traced("INFO")]
193    fn test_keyless_db_pruning() {
194        deterministic::Runner::default().start(|ctx| async move {
195            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
196            tests::test_keyless_db_pruning(db).await;
197        });
198    }
199
200    #[test_traced("INFO")]
201    fn test_keyless_batch_get() {
202        deterministic::Runner::default().start(|ctx| async move {
203            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
204            tests::test_keyless_batch_get(db).await;
205        });
206    }
207
208    #[test_traced("INFO")]
209    fn test_keyless_batch_stacked_get() {
210        deterministic::Runner::default().start(|ctx| async move {
211            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
212            tests::test_keyless_batch_stacked_get(db).await;
213        });
214    }
215
216    #[test_traced("INFO")]
217    fn test_keyless_batch_speculative_root() {
218        deterministic::Runner::default().start(|ctx| async move {
219            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
220            tests::test_keyless_batch_speculative_root(db).await;
221        });
222    }
223
224    #[test_traced("INFO")]
225    fn test_keyless_merkleized_batch_get() {
226        deterministic::Runner::default().start(|ctx| async move {
227            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
228            tests::test_keyless_merkleized_batch_get(db).await;
229        });
230    }
231
232    #[test_traced("INFO")]
233    fn test_keyless_batch_chained() {
234        deterministic::Runner::default().start(|ctx| async move {
235            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
236            tests::test_keyless_batch_chained(db).await;
237        });
238    }
239
240    #[test_traced("INFO")]
241    fn test_keyless_batch_chained_apply_sequential() {
242        deterministic::Runner::default().start(|ctx| async move {
243            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
244            tests::test_keyless_batch_chained_apply_sequential(db).await;
245        });
246    }
247
248    #[test_traced("INFO")]
249    fn test_keyless_batch_many_sequential() {
250        deterministic::Runner::default().start(|ctx| async move {
251            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
252            tests::test_keyless_batch_many_sequential(db).await;
253        });
254    }
255
256    #[test_traced("INFO")]
257    fn test_keyless_batch_empty() {
258        deterministic::Runner::default().start(|ctx| async move {
259            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
260            tests::test_keyless_batch_empty(db).await;
261        });
262    }
263
264    #[test_traced("INFO")]
265    fn test_keyless_batch_chained_merkleized_get() {
266        deterministic::Runner::default().start(|ctx| async move {
267            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
268            tests::test_keyless_batch_chained_merkleized_get(db).await;
269        });
270    }
271
272    #[test_traced("INFO")]
273    fn test_keyless_batch_large() {
274        deterministic::Runner::default().start(|ctx| async move {
275            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
276            tests::test_keyless_batch_large(db).await;
277        });
278    }
279
280    #[test_traced]
281    fn test_keyless_stale_batch() {
282        deterministic::Runner::default().start(|ctx| async move {
283            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
284            tests::test_keyless_stale_batch(db).await;
285        });
286    }
287
288    #[test_traced]
289    fn test_stale_batch_chained() {
290        deterministic::Runner::default().start(|ctx| async move {
291            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
292            tests::test_keyless_stale_batch_chained(db).await;
293        });
294    }
295
296    #[test_traced]
297    fn test_sequential_commit_parent_then_child() {
298        deterministic::Runner::default().start(|ctx| async move {
299            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
300            tests::test_keyless_sequential_commit_parent_then_child(db).await;
301        });
302    }
303
304    #[test_traced]
305    fn test_stale_batch_child_applied_before_parent() {
306        deterministic::Runner::default().start(|ctx| async move {
307            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
308            tests::test_keyless_stale_batch_child_before_parent(db).await;
309        });
310    }
311
312    #[test_traced]
313    fn test_partial_ancestor_commit() {
314        deterministic::Runner::default().start(|ctx| async move {
315            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
316            tests::test_keyless_partial_ancestor_commit(db).await;
317        });
318    }
319
320    #[test_traced]
321    fn test_keyless_to_batch() {
322        deterministic::Runner::default().start(|ctx| async move {
323            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
324            tests::test_keyless_to_batch(db).await;
325        });
326    }
327
328    #[test_traced]
329    fn test_keyless_child_root_matches_pending_and_committed() {
330        deterministic::Runner::default().start(|ctx| async move {
331            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
332            tests::test_keyless_child_root_matches_pending_and_committed(db).await;
333        });
334    }
335
336    #[test_traced("INFO")]
337    fn test_keyless_rewind_recovery() {
338        deterministic::Runner::default().start(|ctx| async move {
339            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
340            tests::test_keyless_db_rewind_recovery(ctx, db, reopen::<mmr::Family>()).await;
341        });
342    }
343
344    #[test_traced("INFO")]
345    fn test_keyless_rewind_pruned_target_errors() {
346        deterministic::Runner::default().start(|ctx| async move {
347            let db = open_db::<mmr::Family>(ctx.with_label("db")).await;
348            tests::test_keyless_db_rewind_pruned_target_errors(db).await;
349        });
350    }
351
352    // mmb::Family variants
353
354    #[test_traced("INFO")]
355    fn test_keyless_db_empty_mmb() {
356        deterministic::Runner::default().start(|ctx| async move {
357            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
358            tests::test_keyless_db_empty(ctx, db, reopen::<mmb::Family>()).await;
359        });
360    }
361
362    #[test_traced("WARN")]
363    fn test_keyless_db_build_basic_mmb() {
364        deterministic::Runner::default().start(|ctx| async move {
365            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
366            tests::test_keyless_db_build_basic(ctx, db, reopen::<mmb::Family>()).await;
367        });
368    }
369
370    #[test_traced("WARN")]
371    fn test_keyless_db_recovery_mmb() {
372        deterministic::Runner::default().start(|ctx| async move {
373            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
374            tests::test_keyless_db_recovery(ctx, db, reopen::<mmb::Family>()).await;
375        });
376    }
377
378    #[test_traced("WARN")]
379    fn test_keyless_db_non_empty_recovery_mmb() {
380        deterministic::Runner::default().start(|ctx| async move {
381            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
382            tests::test_keyless_db_non_empty_recovery(ctx, db, reopen::<mmb::Family>()).await;
383        });
384    }
385
386    #[test_traced("INFO")]
387    fn test_keyless_db_proof_mmb() {
388        deterministic::Runner::default().start(|ctx| async move {
389            let db = open_db::<mmb::Family>(ctx.clone()).await;
390            tests::test_keyless_db_proof(db).await;
391        });
392    }
393
394    #[test_traced("INFO")]
395    fn test_keyless_db_proof_comprehensive_mmb() {
396        deterministic::Runner::default().start(|ctx| async move {
397            let db = open_db::<mmb::Family>(ctx.clone()).await;
398            tests::test_keyless_db_proof_comprehensive(db).await;
399        });
400    }
401
402    #[test_traced("INFO")]
403    fn test_keyless_db_proof_with_pruning_mmb() {
404        deterministic::Runner::default().start(|ctx| async move {
405            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
406            tests::test_keyless_db_proof_with_pruning(ctx, db, reopen::<mmb::Family>()).await;
407        });
408    }
409
410    #[test_traced("WARN")]
411    fn test_keyless_db_empty_db_recovery_mmb() {
412        deterministic::Runner::default().start(|ctx| async move {
413            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
414            tests::test_keyless_db_empty_db_recovery(ctx, db, reopen::<mmb::Family>()).await;
415        });
416    }
417
418    #[test_traced("WARN")]
419    fn test_keyless_db_replay_with_trailing_appends_mmb() {
420        deterministic::Runner::default().start(|ctx| async move {
421            let db = open_db::<mmb::Family>(ctx.with_label("db1")).await;
422            tests::test_keyless_db_replay_with_trailing_appends(ctx, db, reopen::<mmb::Family>())
423                .await;
424        });
425    }
426
427    #[test_traced("INFO")]
428    fn test_keyless_db_get_out_of_bounds_mmb() {
429        deterministic::Runner::default().start(|ctx| async move {
430            let db = open_db::<mmb::Family>(ctx.clone()).await;
431            tests::test_keyless_db_get_out_of_bounds(db).await;
432        });
433    }
434
435    #[test_traced("INFO")]
436    fn test_keyless_db_metadata_mmb() {
437        deterministic::Runner::default().start(|ctx| async move {
438            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
439            tests::test_keyless_db_metadata(db).await;
440        });
441    }
442
443    #[test_traced("INFO")]
444    fn test_keyless_db_pruning_mmb() {
445        deterministic::Runner::default().start(|ctx| async move {
446            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
447            tests::test_keyless_db_pruning(db).await;
448        });
449    }
450
451    #[test_traced("INFO")]
452    fn test_keyless_batch_get_mmb() {
453        deterministic::Runner::default().start(|ctx| async move {
454            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
455            tests::test_keyless_batch_get(db).await;
456        });
457    }
458
459    #[test_traced("INFO")]
460    fn test_keyless_batch_stacked_get_mmb() {
461        deterministic::Runner::default().start(|ctx| async move {
462            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
463            tests::test_keyless_batch_stacked_get(db).await;
464        });
465    }
466
467    #[test_traced("INFO")]
468    fn test_keyless_batch_speculative_root_mmb() {
469        deterministic::Runner::default().start(|ctx| async move {
470            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
471            tests::test_keyless_batch_speculative_root(db).await;
472        });
473    }
474
475    #[test_traced("INFO")]
476    fn test_keyless_merkleized_batch_get_mmb() {
477        deterministic::Runner::default().start(|ctx| async move {
478            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
479            tests::test_keyless_merkleized_batch_get(db).await;
480        });
481    }
482
483    #[test_traced("INFO")]
484    fn test_keyless_batch_chained_mmb() {
485        deterministic::Runner::default().start(|ctx| async move {
486            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
487            tests::test_keyless_batch_chained(db).await;
488        });
489    }
490
491    #[test_traced("INFO")]
492    fn test_keyless_batch_chained_apply_sequential_mmb() {
493        deterministic::Runner::default().start(|ctx| async move {
494            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
495            tests::test_keyless_batch_chained_apply_sequential(db).await;
496        });
497    }
498
499    #[test_traced("INFO")]
500    fn test_keyless_batch_many_sequential_mmb() {
501        deterministic::Runner::default().start(|ctx| async move {
502            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
503            tests::test_keyless_batch_many_sequential(db).await;
504        });
505    }
506
507    #[test_traced("INFO")]
508    fn test_keyless_batch_empty_mmb() {
509        deterministic::Runner::default().start(|ctx| async move {
510            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
511            tests::test_keyless_batch_empty(db).await;
512        });
513    }
514
515    #[test_traced("INFO")]
516    fn test_keyless_batch_chained_merkleized_get_mmb() {
517        deterministic::Runner::default().start(|ctx| async move {
518            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
519            tests::test_keyless_batch_chained_merkleized_get(db).await;
520        });
521    }
522
523    #[test_traced("INFO")]
524    fn test_keyless_batch_large_mmb() {
525        deterministic::Runner::default().start(|ctx| async move {
526            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
527            tests::test_keyless_batch_large(db).await;
528        });
529    }
530
531    #[test_traced]
532    fn test_keyless_stale_batch_mmb() {
533        deterministic::Runner::default().start(|ctx| async move {
534            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
535            tests::test_keyless_stale_batch(db).await;
536        });
537    }
538
539    #[test_traced]
540    fn test_stale_batch_chained_mmb() {
541        deterministic::Runner::default().start(|ctx| async move {
542            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
543            tests::test_keyless_stale_batch_chained(db).await;
544        });
545    }
546
547    #[test_traced]
548    fn test_sequential_commit_parent_then_child_mmb() {
549        deterministic::Runner::default().start(|ctx| async move {
550            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
551            tests::test_keyless_sequential_commit_parent_then_child(db).await;
552        });
553    }
554
555    #[test_traced]
556    fn test_stale_batch_child_applied_before_parent_mmb() {
557        deterministic::Runner::default().start(|ctx| async move {
558            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
559            tests::test_keyless_stale_batch_child_before_parent(db).await;
560        });
561    }
562
563    #[test_traced]
564    fn test_keyless_to_batch_mmb() {
565        deterministic::Runner::default().start(|ctx| async move {
566            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
567            tests::test_keyless_to_batch(db).await;
568        });
569    }
570
571    #[test_traced]
572    fn test_keyless_child_root_matches_pending_and_committed_mmb() {
573        deterministic::Runner::default().start(|ctx| async move {
574            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
575            tests::test_keyless_child_root_matches_pending_and_committed(db).await;
576        });
577    }
578
579    #[test_traced("INFO")]
580    fn test_keyless_rewind_recovery_mmb() {
581        deterministic::Runner::default().start(|ctx| async move {
582            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
583            tests::test_keyless_db_rewind_recovery(ctx, db, reopen::<mmb::Family>()).await;
584        });
585    }
586
587    #[test_traced("INFO")]
588    fn test_keyless_rewind_pruned_target_errors_mmb() {
589        deterministic::Runner::default().start(|ctx| async move {
590            let db = open_db::<mmb::Family>(ctx.with_label("db")).await;
591            tests::test_keyless_db_rewind_pruned_target_errors(db).await;
592        });
593    }
594
595    fn is_send<T: Send>(_: T) {}
596
597    #[allow(dead_code)]
598    fn assert_db_futures_are_send(
599        db: &mut TestDb<mmr::Family>,
600        loc: crate::merkle::Location<mmr::Family>,
601    ) {
602        is_send(db.get_metadata());
603        is_send(db.proof(loc, NZU64!(1)));
604        is_send(db.sync());
605        is_send(db.get(loc));
606        is_send(db.rewind(loc));
607    }
608}