1use crate::{
6 Context,
7 journal::{
8 authenticated,
9 contiguous::fixed::{self, Config as JournalConfig},
10 },
11 merkle::Family,
12 qmdb::{
13 Error, ROOT_BAGGING,
14 any::value::{FixedEncoding, FixedValue},
15 keyless::operation::Operation as BaseOperation,
16 operation::Committable,
17 },
18};
19use commonware_cryptography::Hasher;
20use commonware_parallel::Strategy;
21
22pub type Operation<F, V> = BaseOperation<F, FixedEncoding<V>>;
24
25pub type Db<F, E, V, H, S> =
27 super::Keyless<F, E, FixedEncoding<V>, fixed::Journal<E, Operation<F, V>>, H, S>;
28
29pub type CompactDb<F, E, V, H, S> = super::CompactDb<F, E, FixedEncoding<V>, H, (), S>;
31
32type Journal<F, E, V, H, S> =
33 authenticated::Journal<F, E, fixed::Journal<E, Operation<F, V>>, H, S>;
34
35pub type Config<S> = super::Config<JournalConfig, S>;
37
38pub type CompactConfig<S> = super::CompactConfig<(), S>;
40
41impl<F: Family, E: Context, V: FixedValue, H: Hasher, S: Strategy> Db<F, E, V, H, S> {
42 pub async fn init(context: E, cfg: Config<S>) -> Result<Self, Error<F>> {
45 let journal: Journal<F, E, V, H, S> = Journal::new(
46 context.child("journal"),
47 cfg.merkle,
48 cfg.log,
49 Operation::<F, V>::is_commit,
50 ROOT_BAGGING,
51 )
52 .await?;
53 Self::init_from_journal(journal, context).await
54 }
55}
56
57impl<F: Family, E: Context, V: FixedValue, H: Hasher, S: Strategy> CompactDb<F, E, V, H, S> {
58 pub async fn init(context: E, cfg: CompactConfig<S>) -> Result<Self, Error<F>> {
60 let merkle = crate::merkle::compact::Merkle::new(cfg.strategy);
61 Self::init_from_merkle(merkle, context.child("witness"), cfg.witness, ()).await
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::{
69 merkle::{Location, mmb, mmr},
70 qmdb::keyless::tests::{self, keyless_tests},
71 };
72 use commonware_cryptography::Sha256;
73 use commonware_macros::{boxed, test_traced};
74 use commonware_parallel::{Rayon, Sequential, Strategy};
75 use commonware_runtime::{
76 BufferPooler, Metrics as _, Runner as _, Spawner as _, Strategizer as _, Supervisor as _,
77 buffer::paged::CacheRef,
78 deterministic,
79 mocks::{DelayedSyncContext, PendingSyncs, drive_pending_syncs},
80 reschedule,
81 };
82 use commonware_utils::{NZU16, NZU64, NZUsize, sequence::U64};
83 use core::future::Future;
84 use futures::FutureExt as _;
85 use std::num::{NonZeroU16, NonZeroUsize};
86
87 const PAGE_SIZE: NonZeroU16 = NZU16!(101);
88 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11);
89
90 fn db_config<S: Strategy>(suffix: &str, pooler: &impl BufferPooler, strategy: S) -> Config<S> {
91 let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
92 Config {
93 merkle: crate::merkle::full::Config {
94 journal_partition: format!("fixed-journal-{suffix}"),
95 metadata_partition: format!("fixed-metadata-{suffix}"),
96 items_per_blob: NZU64!(11),
97 write_buffer: NZUsize!(1024),
98 replay_buffer: NZUsize!(1024),
99 strategy,
100 page_cache: page_cache.clone(),
101 },
102 log: JournalConfig {
103 partition: format!("fixed-log-journal-{suffix}"),
104 items_per_blob: NZU64!(7),
105 page_cache,
106 write_buffer: NZUsize!(1024),
107 replay_buffer: NZUsize!(1024),
108 },
109 }
110 }
111
112 type TestDb<F> =
113 Db<F, deterministic::Context, commonware_utils::sequence::U64, Sha256, Sequential>;
114 type TestRayonDb<F> =
115 Db<F, deterministic::Context, commonware_utils::sequence::U64, Sha256, Rayon>;
116 type TestCompactDb<F> =
117 CompactDb<F, deterministic::Context, commonware_utils::sequence::U64, Sha256, Sequential>;
118
119 async fn open_db<F: Family>(context: deterministic::Context) -> TestDb<F> {
120 open_db_with_suffix("partition", context).await
121 }
122
123 async fn open_db_with_suffix<F: Family>(
124 suffix: &str,
125 context: deterministic::Context,
126 ) -> TestDb<F> {
127 let cfg = db_config(suffix, &context, Sequential);
128 TestDb::init(context, cfg).await.unwrap()
129 }
130
131 async fn open_rayon_db<F: Family>(context: deterministic::Context) -> TestRayonDb<F> {
132 let strategy = context.strategy(NZUsize!(2));
133 let cfg = db_config("rayon", &context, strategy);
134 TestRayonDb::init(context, cfg).await.unwrap()
135 }
136
137 async fn open_compact<F: crate::merkle::Family>(
138 context: deterministic::Context,
139 ) -> TestCompactDb<F> {
140 let cfg = CompactConfig {
141 strategy: Sequential,
142 witness: crate::journal::contiguous::variable::Config {
143 partition: "compact-keyless-fixed-witness".into(),
144 items_per_section: NZU64!(64),
145 compression: None,
146 codec_config: (),
147 page_cache: CacheRef::from_pooler(&context, PAGE_SIZE, PAGE_CACHE_SIZE),
148 write_buffer: NZUsize!(1024),
149 replay_buffer: NZUsize!(1024),
150 },
151 commit_codec_config: (),
152 };
153 TestCompactDb::init(context, cfg).await.unwrap()
154 }
155
156 fn reopen<F: Family>() -> tests::Reopen<TestDb<F>> {
157 Box::new(|ctx| Box::pin(open_db(ctx)))
158 }
159
160 type DelayedDb =
162 Db<mmr::Family, DelayedSyncContext<deterministic::Context>, U64, Sha256, Sequential>;
163
164 fn open_delayed_db(
171 context: &deterministic::Context,
172 label: &'static str,
173 suffix: &str,
174 pending: &PendingSyncs,
175 ) -> impl Future<Output = Result<DelayedDb, Error<mmr::Family>>> {
176 let mut cfg = db_config(suffix, context, Sequential);
177 let page_cache = CacheRef::from_pooler(context, NZU16!(1024), NZUsize!(8));
178 cfg.log.items_per_blob = NZU64!(1000);
179 cfg.log.page_cache = page_cache.clone();
180 cfg.merkle.items_per_blob = NZU64!(1000);
181 cfg.merkle.page_cache = page_cache;
182 DelayedDb::init(
183 DelayedSyncContext {
184 inner: context.child(label),
185 pending: pending.clone(),
186 },
187 cfg,
188 )
189 }
190
191 async fn apply_append(
194 db: DelayedDb,
195 value: U64,
196 floor: Location<mmr::Family>,
197 ) -> (DelayedDb, Location<mmr::Family>) {
198 let batch = db
199 .new_batch()
200 .append(value)
201 .merkleize(&db, None, floor)
202 .await;
203 let (db, range) = db.apply_batch(batch).await.unwrap();
204 (db, range.start)
205 }
206
207 #[test_traced]
209 fn test_keyless_fixed_start_sync_overlaps_work() {
210 deterministic::Runner::default().start(|ctx| async move {
211 let pending = PendingSyncs::default();
212 let open = open_delayed_db(&ctx, "delayed", "start-sync-overlap", &pending);
213 let mut db = drive_pending_syncs(&pending, open).await.unwrap();
214 let value0 = U64::new(1);
215 let loc0;
216 let floor = db.inactivity_floor_loc();
217 (db, loc0) = apply_append(db, value0.clone(), floor).await;
218
219 let starts_before = pending.starts();
220 let entered_before = pending.entered();
221 let completions_before = pending.completions();
222 let handle;
223 (db, handle) = db.start_sync().await.unwrap();
224 assert!(pending.starts() > starts_before);
225 assert_eq!(pending.completions(), completions_before);
226
227 let waiter = ctx
229 .child("await_sync")
230 .spawn(|_| async move { handle.await.unwrap() });
231 while pending.entered() == entered_before {
232 reschedule().await;
233 }
234
235 assert_eq!(db.get(loc0).await.unwrap(), Some(value0));
237 let value1 = U64::new(2);
238 let loc1;
239 let floor = db.inactivity_floor_loc();
240 (db, loc1) = apply_append(db, value1.clone(), floor).await;
241 assert_eq!(
242 pending.completions(),
243 completions_before,
244 "the database made progress while the sync was still in flight"
245 );
246
247 pending.unblock();
248 waiter.await.unwrap();
249
250 let handle;
252 (db, handle) = db.start_sync().await.unwrap();
253 handle.await.unwrap();
254 let root = db.root();
255 drop(db);
256
257 let db = open_delayed_db(&ctx, "reopen", "start-sync-overlap", &pending)
258 .await
259 .unwrap();
260 assert_eq!(db.root(), root);
261 assert_eq!(db.get(loc1).await.unwrap(), Some(value1));
262 db.destroy().await.unwrap();
263 });
264 }
265
266 #[test_traced]
269 fn test_keyless_fixed_start_sync_failure_propagates() {
270 deterministic::Runner::default().start(|ctx| async move {
271 let pending = PendingSyncs::default();
273 pending.unblock();
274 let mut db = open_delayed_db(&ctx, "delayed", "start-sync-fail", &pending)
275 .await
276 .unwrap();
277 let floor = db.inactivity_floor_loc();
278 (db, _) = apply_append(db, U64::new(1), floor).await;
279
280 pending.arm_fail();
282
283 let handle;
284 (db, handle) = db.start_sync().await.unwrap();
285 assert!(
286 handle.await.is_err(),
287 "the sync handle surfaces the failure"
288 );
289 let starts_before = pending.starts();
290 assert!(
292 db.commit().await.is_err(),
293 "the next durability op surfaces the failed in-flight sync"
294 );
295 assert_eq!(
296 pending.starts(),
297 starts_before,
298 "the surfaced error is the retained failure, not a fresh sync's"
299 );
300 });
301 }
302
303 #[test_traced]
305 fn test_keyless_fixed_start_sync_recovery() {
306 deterministic::Runner::default().start(|ctx| async move {
307 let pending = PendingSyncs::default();
308 pending.unblock();
309 let mut db = open_delayed_db(&ctx, "delayed", "start-sync-recovery", &pending)
310 .await
311 .unwrap();
312 let value = U64::new(1);
313 let loc;
314 let floor = db.inactivity_floor_loc();
315 (db, loc) = apply_append(db, value.clone(), floor).await;
316
317 let handle;
318 (db, handle) = db.start_sync().await.unwrap();
319 handle.await.unwrap();
320 let root = db.root();
321 drop(db);
322
323 let db = open_delayed_db(&ctx, "reopen", "start-sync-recovery", &pending)
324 .await
325 .unwrap();
326 assert_eq!(db.root(), root);
327 assert_eq!(db.get(loc).await.unwrap(), Some(value));
328 db.destroy().await.unwrap();
329 });
330 }
331
332 #[test_traced]
334 fn test_keyless_fixed_start_sync_prune_waits() {
335 deterministic::Runner::default().start(|ctx| async move {
336 let pending = PendingSyncs::default();
337 let open = open_delayed_db(&ctx, "delayed", "start-sync-prune", &pending);
338 let mut db = drive_pending_syncs(&pending, open).await.unwrap();
339 (db, _) = apply_append(db, U64::new(1), Location::new(0)).await;
341 (db, _) = apply_append(db, U64::new(2), Location::new(2)).await;
342
343 let starts_before = pending.starts();
344 let handle;
345 (db, handle) = db.start_sync().await.unwrap();
346 assert!(pending.starts() > starts_before);
347
348 let floor = db.inactivity_floor_loc();
349 assert!(*floor > 0);
350 let db = {
351 let mut prune = std::pin::pin!(db.prune(floor));
352 assert!(
353 prune.as_mut().now_or_never().is_none(),
354 "prune proceeded while the started sync was pending"
355 );
356 pending.unblock();
357 prune.await.unwrap()
358 };
359 handle.await.unwrap();
360 db.destroy().await.unwrap();
361 });
362 }
363
364 #[test_traced]
366 fn test_keyless_fixed_start_sync_rewind_waits() {
367 deterministic::Runner::default().start(|ctx| async move {
368 let pending = PendingSyncs::default();
369 let open = open_delayed_db(&ctx, "delayed", "start-sync-rewind", &pending);
370 let mut db = drive_pending_syncs(&pending, open).await.unwrap();
371 (db, _) = apply_append(db, U64::new(1), Location::new(0)).await;
372 db = drive_pending_syncs(&pending, db.commit()).await.unwrap();
373 let committed_root = db.root();
374 let committed_size = db.bounds().end;
375 (db, _) = apply_append(db, U64::new(2), Location::new(0)).await;
376
377 let handle;
378 (db, handle) = db.start_sync().await.unwrap();
379
380 let db = {
381 let mut rewind = std::pin::pin!(db.rewind(committed_size));
382 assert!(
383 rewind.as_mut().now_or_never().is_none(),
384 "rewind proceeded while the started sync was pending"
385 );
386 pending.unblock();
387 rewind.await.unwrap()
388 };
389 handle.await.unwrap();
390 assert_eq!(db.root(), committed_root);
391 db.destroy().await.unwrap();
392 });
393 }
394
395 #[test_traced("INFO")]
396 fn test_keyless_fixed_metrics() {
397 deterministic::Runner::default().start(|ctx| async move {
398 let db = open_db::<mmr::Family>(ctx.child("db")).await;
399 let value = commonware_utils::sequence::U64::new(7);
400 let floor = db.inactivity_floor_loc();
401 let batch = db
402 .new_batch()
403 .append(value.clone())
404 .merkleize(&db, None, floor)
405 .await;
406 let (db, range) = db.apply_batch(batch).await.unwrap();
407 assert_eq!(db.get(range.start).await.unwrap(), Some(value.clone()));
408 assert_eq!(
409 db.get_many(&[range.start]).await.unwrap(),
410 vec![Some(value)]
411 );
412 let db = db.commit().await.unwrap();
413 let db = db.sync().await.unwrap();
414 let (db, handle) = db.start_sync().await.unwrap();
415 handle.await.unwrap();
416 let _db = db.prune(crate::merkle::Location::new(0)).await.unwrap();
417
418 let metrics = ctx.encode();
419 for expected in [
420 "db_size 3",
421 "db_pruning_boundary 0",
422 "db_retained 3",
423 "db_inactivity_floor 0",
424 "db_last_commit 2",
425 "db_get_calls_total 1",
426 "db_get_many_calls_total 1",
427 "db_lookups_requested_total 2",
428 "db_apply_batch_calls_total 1",
429 "db_operations_applied_total 2",
430 "db_commit_calls_total 1",
431 "db_sync_calls_total 1",
432 "db_start_sync_calls_total 1",
433 "db_prune_calls_total 1",
434 "db_get_duration_count 1",
435 "db_get_many_duration_count 1",
436 "db_apply_batch_duration_count 1",
437 "db_commit_duration_count 1",
438 "db_sync_duration_count 1",
439 "db_prune_duration_count 1",
440 ] {
441 assert!(metrics.contains(expected), "missing {expected}\n{metrics}");
442 }
443 });
444 }
445
446 keyless_tests! {
447 test_keyless_fixed_empty => run_empty, reopen_indexed;
448 test_keyless_fixed_build_basic => run_build_basic, reopen_indexed;
449 test_keyless_fixed_recovery => run_recovery, reopen_indexed;
450 test_keyless_fixed_non_empty_recovery => run_non_empty_recovery, reopen_indexed;
451 test_keyless_fixed_proof => run_proof, db;
452 test_keyless_fixed_proof_comprehensive => run_proof_comprehensive, db;
453 test_keyless_fixed_proof_with_pruning => run_proof_with_pruning, reopen_indexed;
454 test_keyless_fixed_empty_db_recovery => run_empty_db_recovery, reopen_indexed;
455 test_keyless_fixed_replay_with_trailing_appends => run_replay_with_trailing_appends, reopen_indexed;
456 test_keyless_fixed_get_out_of_bounds => run_get_out_of_bounds, db;
457 test_keyless_fixed_metadata => run_metadata, db;
458 test_keyless_fixed_pruning => run_pruning, reopen;
459 test_keyless_fixed_batch_get => run_batch_get, db;
460 test_keyless_fixed_batch_stacked_get => run_batch_stacked_get, db;
461 test_keyless_fixed_batch_speculative_root => run_batch_speculative_root, db;
462 test_keyless_fixed_merkleized_batch_get => run_merkleized_batch_get, db;
463 test_keyless_fixed_batch_chained => run_batch_chained, db;
464 test_keyless_fixed_operations_match_applied_log => run_operations_match_applied_log, db;
465 test_keyless_fixed_batch_chained_apply_sequential => run_batch_chained_apply_sequential, db;
466 test_keyless_fixed_batch_many_sequential => run_batch_many_sequential, db;
467 test_keyless_fixed_batch_empty => run_batch_empty, db;
468 test_keyless_fixed_batch_chained_merkleized_get => run_batch_chained_merkleized_get, db;
469 test_keyless_fixed_batch_large => run_batch_large, db;
470 test_keyless_fixed_stale_batch => run_stale_batch, reopen;
471 test_keyless_fixed_stale_batch_chained => run_stale_batch_chained, db;
472 test_keyless_fixed_sequential_commit_parent_then_child => run_sequential_commit_parent_then_child, db;
473 test_keyless_fixed_stale_batch_child_before_parent => run_stale_batch_child_before_parent, db;
474 test_keyless_fixed_to_batch => run_to_batch, db;
475 test_keyless_fixed_child_root_matches_pending_and_committed => run_child_root_matches_pending_and_committed, db;
476 test_keyless_fixed_rewind_recovery => run_rewind_recovery, reopen;
477 test_keyless_fixed_rewind_pruned_target_errors => run_rewind_pruned_target_errors, reopen;
478 test_keyless_fixed_floor_tracking => run_floor_tracking, reopen_indexed;
479 test_keyless_fixed_floor_regression_rejected => run_floor_regression_rejected, reopen;
480 test_keyless_fixed_floor_beyond_commit_loc_rejected => run_floor_beyond_commit_loc_rejected, reopen;
481 test_keyless_fixed_rewind_restores_floor => run_rewind_restores_floor, db;
482 test_keyless_fixed_floor_at_commit_loc_accepted => run_floor_at_commit_loc_accepted, db;
483 test_keyless_fixed_rewind_after_reopen_with_floor => run_rewind_after_reopen_with_floor, reopen_indexed;
484 test_keyless_fixed_ancestor_floor_regression_rejected => run_ancestor_floor_regression_rejected, reopen;
485 test_keyless_fixed_ancestor_floor_beyond_commit_loc_rejected => run_ancestor_floor_beyond_commit_loc_rejected, db;
486 test_keyless_fixed_chained_apply_with_valid_floors_succeeds => run_chained_apply_with_valid_floors_succeeds, db;
487 test_keyless_fixed_single_commit_live_set => run_single_commit_live_set, reopen_indexed;
488 test_keyless_fixed_commit_after_sync_recovery => run_commit_after_sync_recovery, reopen_indexed;
489 test_keyless_fixed_get_many => run_get_many, db;
490 }
491
492 #[test_traced("INFO")]
493 fn test_keyless_fixed_shared_helper_accepts_rayon_strategy() {
494 deterministic::Runner::default().start(|ctx| async move {
495 let db = open_rayon_db::<mmr::Family>(ctx.child("db").with_attribute("index", 1)).await;
496 tests::run_metadata(db).await;
497 });
498 }
499
500 #[boxed]
501 async fn assert_compact_root_compatibility<F: crate::merkle::Family>(
502 ctx: deterministic::Context,
503 ) {
504 let db = open_db::<F>(ctx.child("db")).await;
505 let compact = open_compact::<F>(ctx.child("compact")).await;
506 assert_eq!(db.root(), compact.root());
507
508 let v1 = commonware_utils::sequence::U64::new(1);
509 let v2 = commonware_utils::sequence::U64::new(2);
510 let metadata = commonware_utils::sequence::U64::new(99);
511
512 let floor = db.inactivity_floor_loc();
513 let retained = db
514 .new_batch()
515 .append(v1.clone())
516 .append(v2.clone())
517 .merkleize(&db, Some(metadata.clone()), floor)
518 .await;
519 let compact_batch = compact
520 .new_batch()
521 .append(v1)
522 .append(v2)
523 .merkleize(&compact, Some(metadata.clone()), floor)
524 .await;
525
526 assert_eq!(retained.root(), compact_batch.root());
527
528 let (db, _) = db.apply_batch(retained).await.unwrap();
529 let (compact, _) = compact.apply_batch(compact_batch).await.unwrap();
530 let db = db.commit().await.unwrap();
531 let compact = compact.sync().await.unwrap();
532
533 assert_eq!(db.root(), compact.root());
534 assert_eq!(compact.get_metadata(), Some(metadata.clone()));
535
536 drop(compact);
537 let reopened = open_compact::<F>(ctx.child("reopen")).await;
538 assert_eq!(db.root(), reopened.root());
539 assert_eq!(reopened.get_metadata(), Some(metadata));
540
541 reopened.destroy().await.unwrap();
542 db.destroy().await.unwrap();
543 }
544
545 #[test_traced("INFO")]
546 fn test_keyless_fixed_compact_root_compatibility() {
547 deterministic::Runner::default().start(|ctx| async move {
548 assert_compact_root_compatibility::<mmr::Family>(ctx).await;
549 });
550 }
551
552 #[test_traced("INFO")]
553 fn test_keyless_fixed_compact_root_compatibility_mmb() {
554 deterministic::Runner::default().start(|ctx| async move {
555 assert_compact_root_compatibility::<mmb::Family>(ctx).await;
556 });
557 }
558
559 #[test_traced("INFO")]
560 fn test_keyless_fixed_floor_changes_root() {
561 deterministic::Runner::default().start(|ctx| async move {
562 let db_a = open_db_with_suffix::<mmr::Family>("root-a", ctx.child("a")).await;
563 let db_b = open_db_with_suffix::<mmr::Family>("root-b", ctx.child("b")).await;
564 tests::run_floor_changes_root(db_a, db_b).await;
565 });
566 }
567
568 #[test_traced("INFO")]
569 fn test_keyless_fixed_floor_changes_root_mmb() {
570 deterministic::Runner::default().start(|ctx| async move {
571 let db_a = open_db_with_suffix::<mmb::Family>("root-a", ctx.child("a")).await;
572 let db_b = open_db_with_suffix::<mmb::Family>("root-b", ctx.child("b")).await;
573 tests::run_floor_changes_root(db_a, db_b).await;
574 });
575 }
576
577 #[test_traced("WARN")]
581 fn test_keyless_fixed_sync() {
582 use crate::{
583 merkle::Location,
584 qmdb::sync::{self, Target, engine::Config},
585 };
586 use commonware_utils::{non_empty_range, sequence::U64};
587 use std::sync::Arc;
588
589 deterministic::Runner::default().start(|ctx| async move {
590 let target_config = db_config("sync-target", &ctx, Sequential);
591 let target_db: TestDb<mmr::Family> = TestDb::init(ctx.child("target"), target_config)
592 .await
593 .unwrap();
594
595 let mut batch = target_db.new_batch();
596 for i in 0..20u64 {
597 batch = batch.append(U64::new(i * 10 + 1));
598 }
599 let floor = target_db.inactivity_floor_loc();
600 let merkleized = batch.merkleize(&target_db, None, floor).await;
601 let (target_db, _) = target_db.apply_batch(merkleized).await.unwrap();
602
603 let target_root = target_db.root();
604 let bounds = target_db.bounds();
605 let lower_bound = bounds.start;
606 let upper_bound = bounds.end;
607
608 let client_config = db_config("sync-client", &ctx, Sequential);
609 let target_db = Arc::new(target_db);
610 let config = Config {
611 db_config: client_config,
612 fetch_batch_size: NZU64!(5),
613 target: Target {
614 root: target_root,
615 range: non_empty_range!(lower_bound, upper_bound),
616 },
617 context: ctx.child("client"),
618 source: target_db.clone(),
619 apply_batch_size: NZU64!(1024),
620 max_outstanding_requests: 1,
621 update_rx: None,
622 finish_rx: None,
623 reached_target_tx: None,
624 max_retained_roots: 8,
625 };
626 let synced_db: TestDb<mmr::Family> = sync::sync(config).await.unwrap();
627
628 assert_eq!(synced_db.root(), target_root);
629 let bounds = synced_db.bounds();
630 assert_eq!(bounds.end, upper_bound);
631 assert_eq!(bounds.start, lower_bound);
632
633 for i in 0..20u64 {
634 let got = synced_db.get(Location::new(i + 1)).await.unwrap();
635 assert_eq!(got, Some(U64::new(i * 10 + 1)));
636 }
637
638 synced_db.destroy().await.unwrap();
639 let target_db =
640 Arc::try_unwrap(target_db).unwrap_or_else(|_| panic!("failed to unwrap Arc"));
641 target_db.destroy().await.unwrap();
642 });
643 }
644}