1pub use super::db::KeyValueProof;
11use crate::{
12 Context,
13 index::ordered::Index,
14 journal::contiguous::fixed::Journal,
15 merkle::{Graftable, Location},
16 qmdb::{
17 Error,
18 any::{FixedValue, ordered::fixed::Operation, value::FixedEncoding},
19 current::FixedConfig as Config,
20 },
21 translator::Translator,
22};
23use commonware_cryptography::Hasher;
24use commonware_parallel::Strategy;
25use commonware_runtime::Spawner;
26use commonware_utils::Array;
27
28pub type Db<F, E, K, V, H, T, const N: usize, S> = super::db::Db<
29 F,
30 E,
31 Journal<E, Operation<F, K, V>>,
32 K,
33 FixedEncoding<V>,
34 Index<T, Location<F>>,
35 H,
36 N,
37 S,
38>;
39
40impl<
41 F: Graftable,
42 E: Context + Spawner,
43 K: Array,
44 V: FixedValue,
45 H: Hasher,
46 T: Translator,
47 const N: usize,
48 S: Strategy,
49> Db<F, E, K, V, H, T, N, S>
50{
51 pub async fn init(context: E, config: Config<T, S>) -> Result<Self, Error<F>> {
54 crate::qmdb::current::init(context, config).await
55 }
56}
57
58pub mod partitioned {
59 use super::*;
62 use crate::index::partitioned::ordered::Index;
63
64 pub type Db<F, E, K, V, H, T, const P: usize, const N: usize, S> =
71 crate::qmdb::current::ordered::db::Db<
72 F,
73 E,
74 Journal<E, Operation<F, K, V>>,
75 K,
76 FixedEncoding<V>,
77 Index<T, Location<F>, P>,
78 H,
79 N,
80 S,
81 >;
82
83 impl<
84 F: Graftable,
85 E: Context + Spawner,
86 K: Array,
87 V: FixedValue,
88 H: Hasher,
89 T: Translator,
90 const P: usize,
91 const N: usize,
92 S: Strategy,
93 > Db<F, E, K, V, H, T, P, N, S>
94 {
95 pub async fn init(
97 context: E,
98 config: Config<T, S, core::num::NonZeroUsize>,
99 ) -> 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::{
111 Error,
112 current::{
113 ordered::tests as shared,
114 tests::{fixed_config, fixed_config_partitioned},
115 },
116 },
117 translator::OneCap,
118 };
119 use commonware_cryptography::{Sha256, sha256::Digest};
120 use commonware_macros::{boxed, test_traced};
121 use commonware_parallel::Sequential;
122 use commonware_runtime::{Runner as _, Supervisor as _, deterministic};
123 use commonware_utils::{
124 NZU64,
125 bitmap::{Prunable as BitMap, Readable as _},
126 };
127
128 type CurrentTest =
130 Db<mmr::Family, deterministic::Context, Digest, Digest, Sha256, OneCap, 32, Sequential>;
131
132 async fn open_db(context: deterministic::Context, partition_prefix: String) -> CurrentTest {
134 let cfg = fixed_config::<OneCap>(&partition_prefix, &context);
135 CurrentTest::init(context, cfg).await.unwrap()
136 }
137
138 #[test_traced("DEBUG")]
139 pub fn test_current_db_verify_proof_over_bits_in_uncommitted_chunk() {
140 shared::test_verify_proof_over_bits_in_uncommitted_chunk(open_db);
141 }
142
143 #[test_traced("DEBUG")]
144 pub fn test_current_db_range_proofs() {
145 shared::test_range_proofs(open_db);
146 }
147
148 #[test_traced("DEBUG")]
151 pub fn test_range_proof_returns_error_on_pruned_chunks() {
152 let executor = deterministic::Runner::default();
153 executor.start(|context| async move {
154 let partition = "range-proofs-pruned".to_string();
155 let mut db = open_db(context.child("db"), partition).await;
156
157 let chunk_bits = BitMap::<32>::CHUNK_SIZE_BITS;
158
159 let key = Sha256::fill(0x11);
162 for i in 0..chunk_bits + 10 {
163 let value = Sha256::hash(&[&i.to_be_bytes()]);
164 let merkleized = db
165 .new_batch()
166 .write(key, Some(value))
167 .merkleize(&db, None)
168 .await
169 .unwrap();
170 (db, _) = db.apply_batch(merkleized).await.unwrap();
171 }
172
173 let boundary = db.sync_boundary();
175 let db = db.prune(boundary).await.unwrap();
176
177 assert!(
178 db.any.bitmap.pruned_chunks() > 0,
179 "expected at least one pruned chunk"
180 );
181
182 let result = db.range_proof(Location::new(0), NZU64!(1)).await;
185 assert!(
186 matches!(result, Err(Error::OperationPruned(_))),
187 "expected OperationPruned, got {result:?}"
188 );
189
190 db.destroy().await.unwrap();
191 });
192 }
193
194 #[test_traced("DEBUG")]
195 pub fn test_current_db_key_value_proof() {
196 shared::test_key_value_proof(open_db);
197 }
198
199 #[test_traced("WARN")]
200 pub fn test_current_db_proving_repeated_updates() {
201 shared::test_proving_repeated_updates(open_db);
202 }
203
204 #[test_traced("DEBUG")]
205 pub fn test_current_db_exclusion_proofs() {
206 shared::test_exclusion_proofs(open_db);
207 }
208
209 crate::qmdb::current::tests::staged_merkleize_parity_test!(
210 test_current_ordered_fixed_staged_merkleize_parity,
211 open_db
212 );
213
214 #[boxed]
221 async fn check_current_parallel_init_equivalence<const P: usize>(
222 context: deterministic::Context,
223 partition: &'static str,
224 concurrency_sweep: &[usize],
225 ) {
226 type PartDb<const P: usize, S> = partitioned::Db<
227 mmr::Family,
228 deterministic::Context,
229 Digest,
230 Digest,
231 Sha256,
232 OneCap,
233 P,
234 32,
235 S,
236 >;
237
238 fn expected_value(i: u64) -> Option<Digest> {
240 if i % 7 == 1 {
241 None
242 } else if i.is_multiple_of(3) {
243 Some(Sha256::hash(&[&((i + 1) * 11).to_be_bytes()]))
244 } else {
245 Some(Sha256::hash(&[&(i * 7).to_be_bytes()]))
246 }
247 }
248
249 let cfg = fixed_config_partitioned::<OneCap>(partition, &context);
250 let db = PartDb::<P, Sequential>::init(context.child("populate"), cfg)
251 .await
252 .unwrap();
253
254 let mut batch = db.new_batch();
256 for i in 0u64..2000 {
257 let k = Sha256::hash(&[&i.to_be_bytes()]);
258 let v = Sha256::hash(&[&(i * 7).to_be_bytes()]);
259 batch = batch.write(k, Some(v));
260 }
261 let merkleized = batch.merkleize(&db, None).await.unwrap();
262 let (db, _) = db.apply_batch(merkleized).await.unwrap();
263 let db = db.commit().await.unwrap();
264
265 let mut batch = db.new_batch();
267 for i in (0u64..2000).step_by(3) {
268 let k = Sha256::hash(&[&i.to_be_bytes()]);
269 let v = Sha256::hash(&[&((i + 1) * 11).to_be_bytes()]);
270 batch = batch.write(k, Some(v));
271 }
272 for i in (1u64..2000).step_by(7) {
273 let k = Sha256::hash(&[&i.to_be_bytes()]);
274 batch = batch.write(k, None);
275 }
276 let merkleized = batch.merkleize(&db, None).await.unwrap();
277 let (db, _) = db.apply_batch(merkleized).await.unwrap();
278 let db = db.commit().await.unwrap();
279
280 let boundary = db.sync_boundary();
282 let db = db.prune(boundary).await.unwrap();
283 let db = db.sync().await.unwrap();
284 let root = db.root();
285 drop(db);
286
287 for &concurrency in concurrency_sweep {
290 let mut cfg = fixed_config_partitioned::<OneCap>(partition, &context);
291 cfg.init_concurrency = core::num::NonZeroUsize::new(concurrency).unwrap();
292 let ctx = context
293 .child("reopen")
294 .with_attribute("concurrency", concurrency);
295 let db = PartDb::<P, Sequential>::init(ctx, cfg).await.unwrap();
296 assert_eq!(
297 db.root(),
298 root,
299 "current root mismatch at P={P} concurrency={concurrency}"
300 );
301 for i in 0u64..2000 {
302 let k = Sha256::hash(&[&i.to_be_bytes()]);
303 assert_eq!(
304 db.get(&k).await.unwrap(),
305 expected_value(i),
306 "value mismatch for key {i}"
307 );
308 }
309 drop(db);
310 }
311 }
312
313 #[test_traced("WARN")]
314 fn test_current_ordered_partitioned_p1_parallel_init_equivalence() {
315 deterministic::Runner::default().start(|context| async move {
316 check_current_parallel_init_equivalence::<1>(
317 context,
318 "current_parallel_equiv_p1",
319 &[1, 2, 3, 5],
320 )
321 .await;
322 });
323 }
324
325 #[test_traced("WARN")]
326 fn test_current_ordered_partitioned_p2_parallel_init_equivalence() {
327 deterministic::Runner::default().start(|context| async move {
328 check_current_parallel_init_equivalence::<2>(
329 context,
330 "current_parallel_equiv_p2",
331 &[1, 2, 3, 5],
332 )
333 .await;
334 });
335 }
336
337 #[test_traced("WARN")]
341 #[ignore]
342 fn test_current_ordered_partitioned_p3_parallel_init_equivalence() {
343 deterministic::Runner::default().start(|context| async move {
344 check_current_parallel_init_equivalence::<3>(
345 context,
346 "current_parallel_equiv_p3",
347 &[1, 3],
348 )
349 .await;
350 });
351 }
352}