Skip to main content

commonware_storage/qmdb/immutable/
fixed.rs

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