Skip to main content

commonware_storage/qmdb/current/unordered/
fixed.rs

1//! An _unordered_ variant of a [crate::qmdb::current] authenticated database optimized for
2//! fixed-size values.
3//!
4//! This variant does not maintain key ordering, so it cannot generate exclusion proofs. Use
5//! [crate::qmdb::current::ordered::fixed] if exclusion proofs are required.
6//!
7//! See [Db] for the main database type.
8
9pub use super::db::KeyValueProof;
10use crate::{
11    index::unordered::Index,
12    journal::contiguous::fixed::Journal,
13    merkle::{Graftable, Location},
14    qmdb::{
15        any::{unordered::fixed::Operation, value::FixedEncoding, FixedValue},
16        current::FixedConfig as Config,
17        Error,
18    },
19    translator::Translator,
20    Context,
21};
22use commonware_cryptography::Hasher;
23use commonware_parallel::Strategy;
24use commonware_utils::Array;
25
26/// A specialization of [super::db::Db] for unordered key spaces and fixed-size values.
27pub type Db<F, E, K, V, H, T, const N: usize, S> = super::db::Db<
28    F,
29    E,
30    Journal<E, Operation<F, K, V>>,
31    K,
32    FixedEncoding<V>,
33    Index<T, Location<F>>,
34    H,
35    N,
36    S,
37>;
38
39impl<
40        F: Graftable,
41        E: Context,
42        K: Array,
43        V: FixedValue,
44        H: Hasher,
45        T: Translator,
46        const N: usize,
47        S: Strategy,
48    > Db<F, E, K, V, H, T, N, S>
49{
50    /// Initializes a [Db] authenticated database from the given `config`.
51    /// The configured [`Strategy`] is used to parallelize merkleization.
52    pub async fn init(context: E, config: Config<T, S>) -> Result<Self, Error<F>> {
53        crate::qmdb::current::init(context, config).await
54    }
55}
56
57pub mod partitioned {
58    //! A partitioned variant of [super] that uses a partitioned index for the snapshot.
59    //!
60    //! See [crate::qmdb::any::unordered::fixed::partitioned] for details on partitioned indices and
61    //! when to use them.
62
63    use super::*;
64    use crate::index::partitioned::unordered::Index;
65
66    /// A partitioned variant of [super::Db].
67    ///
68    /// The const generic `P` specifies the number of prefix bytes used for partitioning:
69    /// - `P = 1`: 256 partitions
70    /// - `P = 2`: 65,536 partitions
71    /// - `P = 3`: ~16 million partitions
72    pub type Db<F, E, K, V, H, T, const P: usize, const N: usize, S> =
73        crate::qmdb::current::unordered::db::Db<
74            F,
75            E,
76            Journal<E, Operation<F, K, V>>,
77            K,
78            FixedEncoding<V>,
79            Index<T, Location<F>, P>,
80            H,
81            N,
82            S,
83        >;
84
85    impl<
86            F: Graftable,
87            E: Context,
88            K: Array,
89            V: FixedValue,
90            H: Hasher,
91            T: Translator,
92            const P: usize,
93            const N: usize,
94            S: Strategy,
95        > Db<F, E, K, V, H, T, P, N, S>
96    {
97        /// Initializes a [Db] authenticated database from the given `config`.
98        /// The configured [`Strategy`] is used to parallelize merkleization.
99        pub async fn init(context: E, config: Config<T, S>) -> Result<Self, Error<F>> {
100            crate::qmdb::current::init(context, config).await
101        }
102    }
103}
104
105#[cfg(test)]
106pub mod test {
107    use super::*;
108    use crate::{
109        mmr,
110        qmdb::current::{tests::fixed_config, unordered::tests as shared},
111        translator::TwoCap,
112    };
113    use commonware_cryptography::{sha256::Digest, Sha256};
114    use commonware_macros::test_traced;
115    use commonware_runtime::{deterministic, Metrics, Runner as _, Supervisor as _};
116
117    /// A type alias for the concrete [Db] type used in these unit tests.
118    type CurrentTest = Db<
119        mmr::Family,
120        deterministic::Context,
121        Digest,
122        Digest,
123        Sha256,
124        TwoCap,
125        32,
126        commonware_parallel::Sequential,
127    >;
128
129    /// Return a [Db] database initialized with a fixed config.
130    async fn open_db(context: deterministic::Context, partition_prefix: String) -> CurrentTest {
131        let cfg = fixed_config::<TwoCap>(&partition_prefix, &context);
132        CurrentTest::init(context, cfg).await.unwrap()
133    }
134
135    #[test_traced("INFO")]
136    pub fn test_current_unordered_fixed_metrics() {
137        deterministic::Runner::default().start(|ctx| async move {
138            let mut db = open_db(ctx.child("current"), "metrics".to_string()).await;
139            let key = Sha256::fill(1u8);
140            let value = Sha256::fill(2u8);
141            let batch = db
142                .new_batch()
143                .write(key, Some(value))
144                .merkleize(&db, None)
145                .await
146                .unwrap();
147            db.apply_batch(batch).await.unwrap();
148            assert_eq!(db.get(&key).await.unwrap(), Some(value));
149            db.sync().await.unwrap();
150            db.prune(db.sync_boundary()).await.unwrap();
151
152            let metrics = ctx.encode();
153            for expected in [
154                "current_apply_batch_calls_total 1",
155                "current_sync_calls_total 1",
156                "current_prune_calls_total 1",
157                "current_pruned_chunks 0",
158                "current_sync_boundary 0",
159                "current_apply_batch_duration_count 1",
160                "current_sync_duration_count 1",
161                "current_prune_duration_count 1",
162                "current_any_get_calls_total 1",
163                "current_any_apply_batch_calls_total 1",
164            ] {
165                assert!(metrics.contains(expected), "missing {expected}\n{metrics}");
166            }
167            assert!(!metrics.contains("current_get_calls_total"));
168        });
169    }
170
171    #[test_traced("DEBUG")]
172    pub fn test_current_db_verify_proof_over_bits_in_uncommitted_chunk() {
173        shared::test_verify_proof_over_bits_in_uncommitted_chunk(open_db);
174    }
175
176    #[test_traced("DEBUG")]
177    pub fn test_current_db_range_proofs() {
178        shared::test_range_proofs(open_db);
179    }
180
181    #[test_traced("DEBUG")]
182    pub fn test_current_db_key_value_proof() {
183        shared::test_key_value_proof(open_db);
184    }
185
186    #[test_traced("WARN")]
187    pub fn test_current_db_proving_repeated_updates() {
188        shared::test_proving_repeated_updates(open_db);
189    }
190}