Skip to main content

commonware_storage/qmdb/immutable/
variable.rs

1//! An immutable authenticated database with variable-size values.
2//!
3//! For fixed-size values, use [super::fixed] instead.
4
5use super::{operation::Operation as BaseOperation, Config as BaseConfig, Immutable};
6use crate::{
7    journal::{
8        authenticated,
9        contiguous::variable::{self, Config as JournalConfig},
10    },
11    merkle::Family,
12    qmdb::{
13        any::{value::VariableEncoding, VariableValue},
14        operation::Key,
15        Error,
16    },
17    translator::Translator,
18};
19use commonware_codec::Read;
20use commonware_cryptography::Hasher;
21use commonware_runtime::{Clock, Metrics, Storage};
22
23/// Type alias for a variable-size operation.
24pub type Operation<K, V> = BaseOperation<K, VariableEncoding<V>>;
25
26/// Type alias for the variable-size immutable database.
27pub type Db<F, E, K, V, H, T> =
28    Immutable<F, E, K, VariableEncoding<V>, variable::Journal<E, Operation<K, V>>, H, T>;
29
30type Journal<F, E, K, V, H> =
31    authenticated::Journal<F, E, variable::Journal<E, Operation<K, V>>, H>;
32
33/// Configuration for a variable-size immutable authenticated db.
34pub type Config<T, C> = BaseConfig<T, JournalConfig<C>>;
35
36impl<
37        F: Family,
38        E: Storage + Clock + Metrics,
39        K: Key,
40        V: VariableValue,
41        H: Hasher,
42        T: Translator,
43    > Db<F, E, K, V, H, T>
44{
45    /// Returns a [Db] initialized from `cfg`. Any uncommitted log operations will be
46    /// discarded and the state of the db will be as of the last committed operation.
47    pub async fn init(
48        context: E,
49        cfg: Config<T, <Operation<K, V> as Read>::Cfg>,
50    ) -> Result<Self, Error<F>> {
51        let journal: Journal<F, E, K, V, H> = Journal::new(
52            context.clone(),
53            cfg.merkle_config,
54            cfg.log,
55            Operation::<K, V>::is_commit,
56        )
57        .await?;
58        Self::init_from_journal(journal, context, cfg.translator).await
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::{
66        journal::contiguous::variable::Config as JournalConfig,
67        merkle::{journaled::Config as MmrConfig, mmb, mmr},
68        qmdb::immutable::test,
69        translator::TwoCap,
70    };
71    use commonware_cryptography::{sha256::Digest, Sha256};
72    use commonware_macros::test_traced;
73    use commonware_runtime::{buffer::paged::CacheRef, deterministic, BufferPooler, Runner as _};
74    use commonware_utils::{NZUsize, NZU16, NZU64};
75    use core::{future::Future, pin::Pin};
76    use std::num::{NonZeroU16, NonZeroUsize};
77
78    const PAGE_SIZE: NonZeroU16 = NZU16!(77);
79    const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(9);
80
81    fn config(suffix: &str, pooler: &impl BufferPooler) -> Config<TwoCap, ((), ())> {
82        let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
83        super::BaseConfig {
84            merkle_config: MmrConfig {
85                journal_partition: format!("journal-{suffix}"),
86                metadata_partition: format!("metadata-{suffix}"),
87                items_per_blob: NZU64!(11),
88                write_buffer: NZUsize!(1024),
89                thread_pool: None,
90                page_cache: page_cache.clone(),
91            },
92            log: JournalConfig {
93                partition: format!("log-{suffix}"),
94                items_per_section: NZU64!(5),
95                compression: None,
96                codec_config: ((), ()),
97                page_cache,
98                write_buffer: NZUsize!(1024),
99            },
100            translator: TwoCap,
101        }
102    }
103
104    async fn open_db<F: Family>(
105        context: deterministic::Context,
106    ) -> Db<F, deterministic::Context, Digest, Digest, Sha256, TwoCap> {
107        let cfg = config("partition", &context);
108        Db::init(context, cfg).await.unwrap()
109    }
110
111    #[allow(clippy::type_complexity)]
112    fn open<F: Family>(
113        ctx: deterministic::Context,
114    ) -> Pin<
115        Box<
116            dyn Future<Output = Db<F, deterministic::Context, Digest, Digest, Sha256, TwoCap>>
117                + Send,
118        >,
119    > {
120        Box::pin(open_db::<F>(ctx))
121    }
122
123    fn is_send<T: Send>(_: T) {}
124
125    #[allow(dead_code)]
126    fn assert_db_futures_are_send(
127        db: &mut Db<mmr::Family, deterministic::Context, Digest, Digest, Sha256, TwoCap>,
128        key: Digest,
129        loc: crate::merkle::mmr::Location,
130    ) {
131        is_send(db.get(&key));
132        is_send(db.get_metadata());
133        is_send(db.proof(loc, NZU64!(1)));
134        is_send(db.sync());
135        is_send(db.rewind(loc));
136    }
137
138    fn small_sections_config(suffix: &str, pooler: &impl BufferPooler) -> Config<TwoCap, ((), ())> {
139        let mut cfg = config(suffix, pooler);
140        cfg.log.items_per_section = NZU64!(1);
141        cfg
142    }
143
144    async fn open_small_sections_db<F: Family>(
145        context: deterministic::Context,
146    ) -> Db<F, deterministic::Context, Digest, Digest, Sha256, TwoCap> {
147        let cfg = small_sections_config("partition", &context);
148        Db::init(context, cfg).await.unwrap()
149    }
150
151    #[allow(clippy::type_complexity)]
152    fn open_small_sections<F: Family>(
153        ctx: deterministic::Context,
154    ) -> Pin<
155        Box<
156            dyn Future<Output = Db<F, deterministic::Context, Digest, Digest, Sha256, TwoCap>>
157                + Send,
158        >,
159    > {
160        Box::pin(open_small_sections_db::<F>(ctx))
161    }
162
163    #[test_traced("WARN")]
164    fn test_variable_empty() {
165        let executor = deterministic::Runner::default();
166        executor.start(|ctx| async move {
167            test::test_immutable_empty(ctx, open::<mmr::Family>).await;
168        });
169    }
170
171    #[test_traced("DEBUG")]
172    fn test_variable_build_basic() {
173        let executor = deterministic::Runner::default();
174        executor.start(|ctx| async move {
175            test::test_immutable_build_basic(ctx, open::<mmr::Family>).await;
176        });
177    }
178
179    #[test_traced("WARN")]
180    fn test_variable_proof_verify() {
181        let executor = deterministic::Runner::default();
182        executor.start(|ctx| async move {
183            test::test_immutable_proof_verify(ctx, open::<mmr::Family>).await;
184        });
185    }
186
187    #[test_traced("DEBUG")]
188    fn test_variable_prune() {
189        let executor = deterministic::Runner::default();
190        executor.start(|ctx| async move {
191            test::test_immutable_prune(ctx, open::<mmr::Family>).await;
192        });
193    }
194
195    #[test_traced("DEBUG")]
196    fn test_variable_batch_chain() {
197        let executor = deterministic::Runner::default();
198        executor.start(|ctx| async move {
199            test::test_immutable_batch_chain(ctx, open::<mmr::Family>).await;
200        });
201    }
202
203    #[test_traced("WARN")]
204    fn test_variable_build_and_authenticate() {
205        let executor = deterministic::Runner::default();
206        executor.start(|ctx| async move {
207            test::test_immutable_build_and_authenticate(ctx, open::<mmr::Family>).await;
208        });
209    }
210
211    #[test_traced("WARN")]
212    fn test_variable_recovery_from_failed_merkle_sync() {
213        let executor = deterministic::Runner::default();
214        executor.start(|ctx| async move {
215            test::test_immutable_recovery_from_failed_merkle_sync(ctx, open::<mmr::Family>).await;
216        });
217    }
218
219    #[test_traced("WARN")]
220    fn test_variable_recovery_from_failed_log_sync() {
221        let executor = deterministic::Runner::default();
222        executor.start(|ctx| async move {
223            test::test_immutable_recovery_from_failed_log_sync(ctx, open::<mmr::Family>).await;
224        });
225    }
226
227    #[test_traced("WARN")]
228    fn test_variable_pruning() {
229        let executor = deterministic::Runner::default();
230        executor.start(|ctx| async move {
231            test::test_immutable_pruning(ctx, open::<mmr::Family>).await;
232        });
233    }
234
235    #[test_traced("INFO")]
236    fn test_variable_prune_beyond_commit() {
237        let executor = deterministic::Runner::default();
238        executor.start(|ctx| async move {
239            test::test_immutable_prune_beyond_commit(ctx, open::<mmr::Family>).await;
240        });
241    }
242
243    #[test_traced("INFO")]
244    fn test_variable_batch_get_read_through() {
245        let executor = deterministic::Runner::default();
246        executor.start(|ctx| async move {
247            test::test_immutable_batch_get_read_through(ctx, open::<mmr::Family>).await;
248        });
249    }
250
251    #[test_traced("INFO")]
252    fn test_variable_batch_stacked_get() {
253        let executor = deterministic::Runner::default();
254        executor.start(|ctx| async move {
255            test::test_immutable_batch_stacked_get(ctx, open::<mmr::Family>).await;
256        });
257    }
258
259    #[test_traced("INFO")]
260    fn test_variable_batch_stacked_apply() {
261        let executor = deterministic::Runner::default();
262        executor.start(|ctx| async move {
263            test::test_immutable_batch_stacked_apply(ctx, open::<mmr::Family>).await;
264        });
265    }
266
267    #[test_traced("INFO")]
268    fn test_variable_batch_speculative_root() {
269        let executor = deterministic::Runner::default();
270        executor.start(|ctx| async move {
271            test::test_immutable_batch_speculative_root(ctx, open::<mmr::Family>).await;
272        });
273    }
274
275    #[test_traced("INFO")]
276    fn test_variable_merkleized_batch_get() {
277        let executor = deterministic::Runner::default();
278        executor.start(|ctx| async move {
279            test::test_immutable_merkleized_batch_get(ctx, open::<mmr::Family>).await;
280        });
281    }
282
283    #[test_traced("INFO")]
284    fn test_variable_batch_sequential_apply() {
285        let executor = deterministic::Runner::default();
286        executor.start(|ctx| async move {
287            test::test_immutable_batch_sequential_apply(ctx, open::<mmr::Family>).await;
288        });
289    }
290
291    #[test_traced("INFO")]
292    fn test_variable_batch_many_sequential() {
293        let executor = deterministic::Runner::default();
294        executor.start(|ctx| async move {
295            test::test_immutable_batch_many_sequential(ctx, open::<mmr::Family>).await;
296        });
297    }
298
299    #[test_traced("INFO")]
300    fn test_variable_batch_empty_batch() {
301        let executor = deterministic::Runner::default();
302        executor.start(|ctx| async move {
303            test::test_immutable_batch_empty_batch(ctx, open::<mmr::Family>).await;
304        });
305    }
306
307    #[test_traced("INFO")]
308    fn test_variable_batch_chained_merkleized_get() {
309        let executor = deterministic::Runner::default();
310        executor.start(|ctx| async move {
311            test::test_immutable_batch_chained_merkleized_get(ctx, open::<mmr::Family>).await;
312        });
313    }
314
315    #[test_traced("INFO")]
316    fn test_variable_batch_large() {
317        let executor = deterministic::Runner::default();
318        executor.start(|ctx| async move {
319            test::test_immutable_batch_large(ctx, open::<mmr::Family>).await;
320        });
321    }
322
323    #[test_traced("INFO")]
324    fn test_variable_batch_chained_key_override() {
325        let executor = deterministic::Runner::default();
326        executor.start(|ctx| async move {
327            test::test_immutable_batch_chained_key_override(ctx, open::<mmr::Family>).await;
328        });
329    }
330
331    #[test_traced("INFO")]
332    fn test_variable_batch_sequential_key_override() {
333        let executor = deterministic::Runner::default();
334        executor.start(|ctx| async move {
335            test::test_immutable_batch_sequential_key_override(
336                ctx,
337                open_small_sections::<mmr::Family>,
338            )
339            .await;
340        });
341    }
342
343    #[test_traced("INFO")]
344    fn test_variable_batch_metadata() {
345        let executor = deterministic::Runner::default();
346        executor.start(|ctx| async move {
347            test::test_immutable_batch_metadata(ctx, open::<mmr::Family>).await;
348        });
349    }
350
351    #[test_traced]
352    fn test_variable_stale_batch_rejected() {
353        let executor = deterministic::Runner::default();
354        executor.start(|ctx| async move {
355            test::test_immutable_stale_batch_rejected(ctx, open::<mmr::Family>).await;
356        });
357    }
358
359    #[test_traced]
360    fn test_variable_stale_batch_chained() {
361        let executor = deterministic::Runner::default();
362        executor.start(|ctx| async move {
363            test::test_immutable_stale_batch_chained(ctx, open::<mmr::Family>).await;
364        });
365    }
366
367    #[test_traced]
368    fn test_variable_sequential_commit_parent_then_child() {
369        let executor = deterministic::Runner::default();
370        executor.start(|ctx| async move {
371            test::test_immutable_sequential_commit_parent_then_child(ctx, open::<mmr::Family>)
372                .await;
373        });
374    }
375
376    #[test_traced]
377    fn test_variable_stale_batch_child_applied_before_parent() {
378        let executor = deterministic::Runner::default();
379        executor.start(|ctx| async move {
380            test::test_immutable_stale_batch_child_applied_before_parent(ctx, open::<mmr::Family>)
381                .await;
382        });
383    }
384
385    #[test_traced]
386    fn test_variable_partial_ancestor_commit() {
387        let executor = deterministic::Runner::default();
388        executor.start(|ctx| async move {
389            test::test_immutable_partial_ancestor_commit(ctx, open::<mmr::Family>).await;
390        });
391    }
392
393    #[test_traced]
394    fn test_variable_child_root_matches_pending_and_committed() {
395        let executor = deterministic::Runner::default();
396        executor.start(|ctx| async move {
397            test::test_immutable_child_root_matches_pending_and_committed(ctx, open::<mmr::Family>)
398                .await;
399        });
400    }
401
402    #[test_traced]
403    fn test_variable_to_batch() {
404        let executor = deterministic::Runner::default();
405        executor.start(|ctx| async move {
406            test::test_immutable_to_batch(ctx, open::<mmr::Family>).await;
407        });
408    }
409
410    #[test_traced("INFO")]
411    fn test_variable_rewind_recovery() {
412        let executor = deterministic::Runner::default();
413        executor.start(|ctx| async move {
414            test::test_immutable_rewind_recovery(ctx, open::<mmr::Family>).await;
415        });
416    }
417
418    #[test_traced("INFO")]
419    fn test_variable_rewind_pruned_target_errors() {
420        let executor = deterministic::Runner::default();
421        executor.start(|ctx| async move {
422            test::test_immutable_rewind_pruned_target_errors(
423                ctx,
424                open_small_sections::<mmr::Family>,
425            )
426            .await;
427        });
428    }
429
430    // -- MMB test wrappers --
431
432    #[test_traced("WARN")]
433    fn test_variable_empty_mmb() {
434        let executor = deterministic::Runner::default();
435        executor.start(|ctx| async move {
436            test::test_immutable_empty(ctx, open::<mmb::Family>).await;
437        });
438    }
439
440    #[test_traced("DEBUG")]
441    fn test_variable_build_basic_mmb() {
442        let executor = deterministic::Runner::default();
443        executor.start(|ctx| async move {
444            test::test_immutable_build_basic(ctx, open::<mmb::Family>).await;
445        });
446    }
447
448    #[test_traced("WARN")]
449    fn test_variable_proof_verify_mmb() {
450        let executor = deterministic::Runner::default();
451        executor.start(|ctx| async move {
452            test::test_immutable_proof_verify(ctx, open::<mmb::Family>).await;
453        });
454    }
455
456    #[test_traced("DEBUG")]
457    fn test_variable_prune_mmb() {
458        let executor = deterministic::Runner::default();
459        executor.start(|ctx| async move {
460            test::test_immutable_prune(ctx, open::<mmb::Family>).await;
461        });
462    }
463
464    #[test_traced("DEBUG")]
465    fn test_variable_batch_chain_mmb() {
466        let executor = deterministic::Runner::default();
467        executor.start(|ctx| async move {
468            test::test_immutable_batch_chain(ctx, open::<mmb::Family>).await;
469        });
470    }
471
472    #[test_traced("WARN")]
473    fn test_variable_build_and_authenticate_mmb() {
474        let executor = deterministic::Runner::default();
475        executor.start(|ctx| async move {
476            test::test_immutable_build_and_authenticate(ctx, open::<mmb::Family>).await;
477        });
478    }
479
480    #[test_traced("WARN")]
481    fn test_variable_recovery_from_failed_merkle_sync_mmb() {
482        let executor = deterministic::Runner::default();
483        executor.start(|ctx| async move {
484            test::test_immutable_recovery_from_failed_merkle_sync(ctx, open::<mmb::Family>).await;
485        });
486    }
487
488    #[test_traced("WARN")]
489    fn test_variable_recovery_from_failed_log_sync_mmb() {
490        let executor = deterministic::Runner::default();
491        executor.start(|ctx| async move {
492            test::test_immutable_recovery_from_failed_log_sync(ctx, open::<mmb::Family>).await;
493        });
494    }
495
496    #[test_traced("WARN")]
497    fn test_variable_pruning_mmb() {
498        let executor = deterministic::Runner::default();
499        executor.start(|ctx| async move {
500            test::test_immutable_pruning(ctx, open::<mmb::Family>).await;
501        });
502    }
503
504    #[test_traced("INFO")]
505    fn test_variable_prune_beyond_commit_mmb() {
506        let executor = deterministic::Runner::default();
507        executor.start(|ctx| async move {
508            test::test_immutable_prune_beyond_commit(ctx, open::<mmb::Family>).await;
509        });
510    }
511
512    #[test_traced("INFO")]
513    fn test_variable_batch_get_read_through_mmb() {
514        let executor = deterministic::Runner::default();
515        executor.start(|ctx| async move {
516            test::test_immutable_batch_get_read_through(ctx, open::<mmb::Family>).await;
517        });
518    }
519
520    #[test_traced("INFO")]
521    fn test_variable_batch_stacked_get_mmb() {
522        let executor = deterministic::Runner::default();
523        executor.start(|ctx| async move {
524            test::test_immutable_batch_stacked_get(ctx, open::<mmb::Family>).await;
525        });
526    }
527
528    #[test_traced("INFO")]
529    fn test_variable_batch_stacked_apply_mmb() {
530        let executor = deterministic::Runner::default();
531        executor.start(|ctx| async move {
532            test::test_immutable_batch_stacked_apply(ctx, open::<mmb::Family>).await;
533        });
534    }
535
536    #[test_traced("INFO")]
537    fn test_variable_batch_speculative_root_mmb() {
538        let executor = deterministic::Runner::default();
539        executor.start(|ctx| async move {
540            test::test_immutable_batch_speculative_root(ctx, open::<mmb::Family>).await;
541        });
542    }
543
544    #[test_traced("INFO")]
545    fn test_variable_merkleized_batch_get_mmb() {
546        let executor = deterministic::Runner::default();
547        executor.start(|ctx| async move {
548            test::test_immutable_merkleized_batch_get(ctx, open::<mmb::Family>).await;
549        });
550    }
551
552    #[test_traced("INFO")]
553    fn test_variable_batch_sequential_apply_mmb() {
554        let executor = deterministic::Runner::default();
555        executor.start(|ctx| async move {
556            test::test_immutable_batch_sequential_apply(ctx, open::<mmb::Family>).await;
557        });
558    }
559
560    #[test_traced("INFO")]
561    fn test_variable_batch_many_sequential_mmb() {
562        let executor = deterministic::Runner::default();
563        executor.start(|ctx| async move {
564            test::test_immutable_batch_many_sequential(ctx, open::<mmb::Family>).await;
565        });
566    }
567
568    #[test_traced("INFO")]
569    fn test_variable_batch_empty_batch_mmb() {
570        let executor = deterministic::Runner::default();
571        executor.start(|ctx| async move {
572            test::test_immutable_batch_empty_batch(ctx, open::<mmb::Family>).await;
573        });
574    }
575
576    #[test_traced("INFO")]
577    fn test_variable_batch_chained_merkleized_get_mmb() {
578        let executor = deterministic::Runner::default();
579        executor.start(|ctx| async move {
580            test::test_immutable_batch_chained_merkleized_get(ctx, open::<mmb::Family>).await;
581        });
582    }
583
584    #[test_traced("INFO")]
585    fn test_variable_batch_large_mmb() {
586        let executor = deterministic::Runner::default();
587        executor.start(|ctx| async move {
588            test::test_immutable_batch_large(ctx, open::<mmb::Family>).await;
589        });
590    }
591
592    #[test_traced("INFO")]
593    fn test_variable_batch_chained_key_override_mmb() {
594        let executor = deterministic::Runner::default();
595        executor.start(|ctx| async move {
596            test::test_immutable_batch_chained_key_override(ctx, open::<mmb::Family>).await;
597        });
598    }
599
600    #[test_traced("INFO")]
601    fn test_variable_batch_sequential_key_override_mmb() {
602        let executor = deterministic::Runner::default();
603        executor.start(|ctx| async move {
604            test::test_immutable_batch_sequential_key_override(
605                ctx,
606                open_small_sections::<mmb::Family>,
607            )
608            .await;
609        });
610    }
611
612    #[test_traced("INFO")]
613    fn test_variable_batch_metadata_mmb() {
614        let executor = deterministic::Runner::default();
615        executor.start(|ctx| async move {
616            test::test_immutable_batch_metadata(ctx, open::<mmb::Family>).await;
617        });
618    }
619
620    #[test_traced]
621    fn test_variable_stale_batch_rejected_mmb() {
622        let executor = deterministic::Runner::default();
623        executor.start(|ctx| async move {
624            test::test_immutable_stale_batch_rejected(ctx, open::<mmb::Family>).await;
625        });
626    }
627
628    #[test_traced]
629    fn test_variable_stale_batch_chained_mmb() {
630        let executor = deterministic::Runner::default();
631        executor.start(|ctx| async move {
632            test::test_immutable_stale_batch_chained(ctx, open::<mmb::Family>).await;
633        });
634    }
635
636    #[test_traced]
637    fn test_variable_sequential_commit_parent_then_child_mmb() {
638        let executor = deterministic::Runner::default();
639        executor.start(|ctx| async move {
640            test::test_immutable_sequential_commit_parent_then_child(ctx, open::<mmb::Family>)
641                .await;
642        });
643    }
644
645    #[test_traced]
646    fn test_variable_stale_batch_child_applied_before_parent_mmb() {
647        let executor = deterministic::Runner::default();
648        executor.start(|ctx| async move {
649            test::test_immutable_stale_batch_child_applied_before_parent(ctx, open::<mmb::Family>)
650                .await;
651        });
652    }
653
654    #[test_traced]
655    fn test_variable_child_root_matches_pending_and_committed_mmb() {
656        let executor = deterministic::Runner::default();
657        executor.start(|ctx| async move {
658            test::test_immutable_child_root_matches_pending_and_committed(ctx, open::<mmb::Family>)
659                .await;
660        });
661    }
662
663    #[test_traced]
664    fn test_variable_to_batch_mmb() {
665        let executor = deterministic::Runner::default();
666        executor.start(|ctx| async move {
667            test::test_immutable_to_batch(ctx, open::<mmb::Family>).await;
668        });
669    }
670
671    #[test_traced("INFO")]
672    fn test_variable_rewind_recovery_mmb() {
673        let executor = deterministic::Runner::default();
674        executor.start(|ctx| async move {
675            test::test_immutable_rewind_recovery(ctx, open::<mmb::Family>).await;
676        });
677    }
678
679    #[test_traced("INFO")]
680    fn test_variable_rewind_pruned_target_errors_mmb() {
681        let executor = deterministic::Runner::default();
682        executor.start(|ctx| async move {
683            test::test_immutable_rewind_pruned_target_errors(
684                ctx,
685                open_small_sections::<mmb::Family>,
686            )
687            .await;
688        });
689    }
690
691    #[test_traced("WARN")]
692    fn test_variable_apply_after_ancestor_dropped() {
693        let executor = deterministic::Runner::default();
694        executor.start(|ctx| async move {
695            test::test_immutable_apply_after_ancestor_dropped(ctx, open::<mmr::Family>).await;
696        });
697    }
698}