1use crate::stateful::db::{
10 BatchContext, ManagedDb, Merkleized as MerkleizedTrait, Shared, StateSyncDb, SyncEngineConfig,
11 Unmerkleized as UnmerkleizedTrait, sync_standard_db,
12};
13use commonware_codec::{Codec, Read as CodecRead};
14use commonware_cryptography::Hasher;
15use commonware_parallel::Strategy;
16use commonware_runtime::{Handle, Spawner};
17use commonware_storage::{
18 Context,
19 index::{
20 Ordered as OrderedIndex, Unordered as UnorderedIndex, ordered::Index as OrderedIdx,
21 unordered::Index as UnorderedIdx,
22 },
23 journal::contiguous::{
24 Contiguous, Mutable, fixed::Journal as FixedJournal, variable::Journal as VariableJournal,
25 },
26 merkle::{Graftable, Location},
27 qmdb::{
28 Error,
29 any::{
30 initial_root,
31 operation::{Operation, Update},
32 ordered, unordered,
33 value::{self, FixedEncoding, ValueEncoding, VariableEncoding},
34 },
35 current::{
36 FixedConfig, VariableConfig,
37 batch::{MerkleizedBatch, Staged, UnmerkleizedBatch},
38 db::Db,
39 },
40 operation::Key,
41 sync::{self, Target as CurrentSyncTarget},
42 },
43 translator::Translator,
44};
45use commonware_utils::{Array, channel::mpsc, non_empty_range};
46use std::{
47 ops::{Deref, Range},
48 sync::Arc,
49};
50
51pub struct CurrentUnmerkleized<F, E, C, I, H, U, const N: usize, S>
54where
55 F: Graftable,
56 E: Context,
57 U: Update,
58 C: Contiguous<Item = Operation<F, U>>,
59 I: UnorderedIndex<Value = Location<F>>,
60 H: Hasher,
61 S: Strategy,
62 Operation<F, U>: Codec,
63{
64 batch: UnmerkleizedBatch<F, H, U, N, S>,
65 db: Shared<Db<F, E, C, I, H, U, N, S>>,
66 metadata: Option<U::Value>,
67}
68
69pub struct CurrentStaged<F, E, C, I, H, U, const N: usize, S>
76where
77 F: Graftable,
78 E: Context,
79 U: Update,
80 C: Contiguous<Item = Operation<F, U>>,
81 I: UnorderedIndex<Value = Location<F>>,
82 H: Hasher,
83 S: Strategy,
84 Operation<F, U>: Codec,
85{
86 staged: Staged<F, H, U, N, S>,
87 db: Shared<Db<F, E, C, I, H, U, N, S>>,
88 metadata: Option<U::Value>,
89}
90
91impl<F, E, C, I, H, U, const N: usize, S> CurrentUnmerkleized<F, E, C, I, H, U, N, S>
93where
94 F: Graftable,
95 E: Context,
96 U: Update,
97 C: Contiguous<Item = Operation<F, U>>,
98 I: UnorderedIndex<Value = Location<F>> + 'static,
99 H: Hasher,
100 S: Strategy,
101 Operation<F, U>: Codec,
102{
103 pub fn with_metadata(mut self, metadata: U::Value) -> Self {
106 self.metadata = Some(metadata);
107 self
108 }
109
110 pub async fn get(&self, key: &U::Key) -> Result<Option<U::Value>, Error<F>> {
112 let db = self.db.read().await;
113 self.batch.get(key, &db).await
114 }
115
116 pub async fn get_many(&self, keys: &[&U::Key]) -> Result<Vec<Option<U::Value>>, Error<F>> {
120 let db = self.db.read().await;
121 self.batch.get_many(keys, &db).await
122 }
123
124 pub async fn stage(
128 self,
129 keys: &[&U::Key],
130 ) -> Result<(Vec<Option<U::Value>>, CurrentStaged<F, E, C, I, H, U, N, S>), Error<F>> {
131 let Self {
132 batch,
133 db,
134 metadata,
135 } = self;
136 let (values, staged) = {
137 let guard = db.read().await;
138 batch.stage(keys, &guard).await?
139 };
140 Ok((
141 values,
142 CurrentStaged {
143 staged,
144 db,
145 metadata,
146 },
147 ))
148 }
149
150 pub fn write(mut self, key: U::Key, value: Option<U::Value>) -> Self {
152 self.batch = self.batch.write(key, value);
153 self
154 }
155}
156
157pub struct CurrentMerkleized<F, E, C, I, H, U, const N: usize, S>
160where
161 F: Graftable,
162 E: Context,
163 U: Update,
164 C: Contiguous<Item = Operation<F, U>>,
165 I: UnorderedIndex<Value = Location<F>>,
166 H: Hasher,
167 S: Strategy,
168 Operation<F, U>: Codec,
169{
170 inner: Arc<MerkleizedBatch<F, H::Digest, U, N, S>>,
171 db: Shared<Db<F, E, C, I, H, U, N, S>>,
172}
173
174impl<F, E, C, I, H, U, const N: usize, S> Clone for CurrentMerkleized<F, E, C, I, H, U, N, S>
175where
176 F: Graftable,
177 E: Context,
178 U: Update,
179 C: Contiguous<Item = Operation<F, U>>,
180 I: UnorderedIndex<Value = Location<F>>,
181 H: Hasher,
182 S: Strategy,
183 Operation<F, U>: Codec,
184{
185 fn clone(&self) -> Self {
186 Self {
187 inner: Arc::clone(&self.inner),
188 db: self.db.clone(),
189 }
190 }
191}
192
193impl<F, E, C, I, H, U, const N: usize, S> Deref for CurrentUnmerkleized<F, E, C, I, H, U, N, S>
194where
195 F: Graftable,
196 E: Context,
197 U: Update,
198 C: Contiguous<Item = Operation<F, U>>,
199 I: UnorderedIndex<Value = Location<F>>,
200 H: Hasher,
201 S: Strategy,
202 Operation<F, U>: Codec,
203{
204 type Target = UnmerkleizedBatch<F, H, U, N, S>;
205
206 fn deref(&self) -> &Self::Target {
207 &self.batch
208 }
209}
210
211impl<F, E, C, I, H, U, const N: usize, S> Deref for CurrentMerkleized<F, E, C, I, H, U, N, S>
212where
213 F: Graftable,
214 E: Context,
215 U: Update,
216 C: Contiguous<Item = Operation<F, U>>,
217 I: UnorderedIndex<Value = Location<F>>,
218 H: Hasher,
219 S: Strategy,
220 Operation<F, U>: Codec,
221{
222 type Target = MerkleizedBatch<F, H::Digest, U, N, S>;
223
224 fn deref(&self) -> &Self::Target {
225 &self.inner
226 }
227}
228
229impl<F, E, C, I, H, U, const N: usize, S> CurrentStaged<F, E, C, I, H, U, N, S>
231where
232 F: Graftable,
233 E: Context,
234 U: Update,
235 C: Contiguous<Item = Operation<F, U>>,
236 I: UnorderedIndex<Value = Location<F>> + 'static,
237 H: Hasher,
238 S: Strategy,
239 Operation<F, U>: Codec,
240{
241 pub fn with_metadata(mut self, metadata: U::Value) -> Self {
244 self.metadata = Some(metadata);
245 self
246 }
247
248 pub async fn expand(
255 self,
256 keys: &[&U::Key],
257 ) -> Result<(Range<usize>, Vec<Option<U::Value>>, Self), Error<F>> {
258 let Self {
259 staged,
260 db,
261 metadata,
262 } = self;
263 let (range, values, staged) = {
264 let guard = db.read().await;
265 staged.expand(keys, &guard).await?
266 };
267 Ok((
268 range,
269 values,
270 Self {
271 staged,
272 db,
273 metadata,
274 },
275 ))
276 }
277}
278
279impl<F, E, C, I, H, K, V, const N: usize, S>
281 CurrentStaged<F, E, C, I, H, unordered::Update<K, V>, N, S>
282where
283 F: Graftable,
284 E: Context,
285 K: Key,
286 V: ValueEncoding + 'static,
287 C: Mutable<Item = Operation<F, unordered::Update<K, V>>>,
288 I: UnorderedIndex<Value = Location<F>> + 'static,
289 H: Hasher,
290 S: Strategy,
291 Operation<F, unordered::Update<K, V>>: Codec,
292{
293 pub async fn merkleize(
307 self,
308 updates: Vec<(usize, Option<V::Value>)>,
309 upserts: Vec<(K, Option<V::Value>)>,
310 ) -> Result<CurrentMerkleized<F, E, C, I, H, unordered::Update<K, V>, N, S>, Error<F>> {
311 let Self {
312 staged,
313 db,
314 metadata,
315 } = self;
316 let inner = {
317 let guard = db.read().await;
318 staged.merkleize(updates, upserts, metadata, &guard).await?
319 };
320 Ok(CurrentMerkleized { inner, db })
321 }
322}
323
324impl<F, E, C, I, H, K, V, const N: usize, S>
326 CurrentStaged<F, E, C, I, H, ordered::Update<K, V>, N, S>
327where
328 F: Graftable,
329 E: Context,
330 K: Key,
331 V: ValueEncoding + 'static,
332 C: Mutable<Item = Operation<F, ordered::Update<K, V>>>,
333 I: OrderedIndex<Value = Location<F>> + 'static,
334 H: Hasher,
335 S: Strategy,
336 Operation<F, ordered::Update<K, V>>: Codec,
337{
338 pub async fn merkleize(
352 self,
353 updates: Vec<(usize, Option<V::Value>)>,
354 upserts: Vec<(K, Option<V::Value>)>,
355 ) -> Result<CurrentMerkleized<F, E, C, I, H, ordered::Update<K, V>, N, S>, Error<F>> {
356 let Self {
357 staged,
358 db,
359 metadata,
360 } = self;
361 let inner = {
362 let guard = db.read().await;
363 staged.merkleize(updates, upserts, metadata, &guard).await?
364 };
365 Ok(CurrentMerkleized { inner, db })
366 }
367}
368
369impl<F, E, C, I, H, U, const N: usize, S> CurrentMerkleized<F, E, C, I, H, U, N, S>
371where
372 F: Graftable,
373 E: Context,
374 U: Update,
375 C: Contiguous<Item = Operation<F, U>>,
376 I: UnorderedIndex<Value = Location<F>> + 'static,
377 H: Hasher,
378 S: Strategy,
379 Operation<F, U>: Codec,
380{
381 pub async fn get(&self, key: &U::Key) -> Result<Option<U::Value>, Error<F>> {
383 let db = self.db.read().await;
384 self.inner.get(key, &db).await
385 }
386
387 pub async fn get_many(&self, keys: &[&U::Key]) -> Result<Vec<Option<U::Value>>, Error<F>> {
391 let db = self.db.read().await;
392 self.inner.get_many(keys, &db).await
393 }
394}
395
396impl<F, E, C, I, H, K, V, const N: usize, S> UnmerkleizedTrait
398 for CurrentUnmerkleized<F, E, C, I, H, unordered::Update<K, V>, N, S>
399where
400 F: Graftable,
401 E: Context,
402 K: Key,
403 V: ValueEncoding + 'static,
404 C: Mutable<Item = Operation<F, unordered::Update<K, V>>>,
405 I: UnorderedIndex<Value = Location<F>> + 'static,
406 H: Hasher,
407 S: Strategy,
408 Operation<F, unordered::Update<K, V>>: Codec,
409{
410 type Merkleized = CurrentMerkleized<F, E, C, I, H, unordered::Update<K, V>, N, S>;
411 type Error = Error<F>;
412
413 async fn merkleize(self) -> Result<Self::Merkleized, Error<F>> {
414 let db = self.db.read().await;
415 let merkleized = self.batch.merkleize(&db, self.metadata).await?;
416 Ok(CurrentMerkleized {
417 inner: merkleized,
418 db: self.db.clone(),
419 })
420 }
421}
422
423impl<F, E, C, I, H, K, V, const N: usize, S> UnmerkleizedTrait
425 for CurrentUnmerkleized<F, E, C, I, H, ordered::Update<K, V>, N, S>
426where
427 F: Graftable,
428 E: Context,
429 K: Key,
430 V: ValueEncoding + 'static,
431 C: Mutable<Item = Operation<F, ordered::Update<K, V>>>,
432 I: OrderedIndex<Value = Location<F>> + 'static,
433 H: Hasher,
434 S: Strategy,
435 Operation<F, ordered::Update<K, V>>: Codec,
436{
437 type Merkleized = CurrentMerkleized<F, E, C, I, H, ordered::Update<K, V>, N, S>;
438 type Error = Error<F>;
439
440 async fn merkleize(self) -> Result<Self::Merkleized, Error<F>> {
441 let db = self.db.read().await;
442 let merkleized = self.batch.merkleize(&db, self.metadata).await?;
443 Ok(CurrentMerkleized {
444 inner: merkleized,
445 db: self.db.clone(),
446 })
447 }
448}
449
450impl<F, E, C, I, H, U, const N: usize, S> MerkleizedTrait
452 for CurrentMerkleized<F, E, C, I, H, U, N, S>
453where
454 F: Graftable,
455 E: Context,
456 U: Update,
457 C: Mutable<Item = Operation<F, U>>,
458 I: UnorderedIndex<Value = Location<F>> + 'static,
459 H: Hasher,
460 S: Strategy,
461 Operation<F, U>: Codec,
462 CurrentUnmerkleized<F, E, C, I, H, U, N, S>: UnmerkleizedTrait,
463{
464 type Digest = H::Digest;
465 type Unmerkleized = CurrentUnmerkleized<F, E, C, I, H, U, N, S>;
466
467 fn root(&self) -> H::Digest {
468 self.inner.root()
469 }
470
471 fn new_batch(&self) -> Self::Unmerkleized {
472 CurrentUnmerkleized {
473 batch: self.inner.new_batch::<H>(),
474 db: self.db.clone(),
475 metadata: None,
476 }
477 }
478}
479
480impl<F, E, K, V, H, T, const N: usize, S> ManagedDb<E>
482 for Db<
483 F,
484 E,
485 FixedJournal<E, Operation<F, unordered::Update<K, FixedEncoding<V>>>>,
486 UnorderedIdx<T, Location<F>>,
487 H,
488 unordered::Update<K, FixedEncoding<V>>,
489 N,
490 S,
491 >
492where
493 F: Graftable,
494 E: Context + Spawner,
495 K: Array,
496 V: value::FixedValue + 'static,
497 H: Hasher + 'static,
498 T: Translator,
499 S: Strategy,
500{
501 type Unmerkleized = CurrentUnmerkleized<
502 F,
503 E,
504 FixedJournal<E, Operation<F, unordered::Update<K, FixedEncoding<V>>>>,
505 UnorderedIdx<T, Location<F>>,
506 H,
507 unordered::Update<K, FixedEncoding<V>>,
508 N,
509 S,
510 >;
511 type Merkleized = CurrentMerkleized<
512 F,
513 E,
514 FixedJournal<E, Operation<F, unordered::Update<K, FixedEncoding<V>>>>,
515 UnorderedIdx<T, Location<F>>,
516 H,
517 unordered::Update<K, FixedEncoding<V>>,
518 N,
519 S,
520 >;
521 type Error = Error<F>;
522 type Config = FixedConfig<T, S>;
523 type SyncTarget = CurrentSyncTarget<F, H::Digest>;
524
525 async fn init(context: E, config: Self::Config) -> Result<Self, Error<F>> {
526 <Self>::init(context, config).await
527 }
528
529 fn initial_sync_target() -> Self::SyncTarget {
530 CurrentSyncTarget::new(
531 initial_root::<F, unordered::Update<K, FixedEncoding<V>>, H>(),
532 non_empty_range!(Location::new(0), Location::new(1)),
533 )
534 }
535
536 fn new_batch(database: BatchContext<'_, Self>) -> Self::Unmerkleized {
537 let (database, shared) = database.into_parts();
538 CurrentUnmerkleized {
539 batch: database.new_batch(),
540 db: shared,
541 metadata: None,
542 }
543 }
544
545 fn matches_sync_target(batch: &Self::Merkleized, target: &Self::SyncTarget) -> bool {
546 batch.ops_root() == target.root
547 && *target.range.start() == batch.sync_boundary()
548 && *target.range.end() == batch.bounds().tip.size
549 }
550
551 async fn apply(self, batch: Self::Merkleized) -> Result<Self, Error<F>> {
552 let (db, _) = self.apply_batch(batch.inner).await?;
553 Ok(db)
554 }
555
556 async fn finalize(self) -> Result<(Self, Handle<()>), Error<F>> {
557 self.start_sync().await
558 }
559
560 async fn prune(self, target: &Self::SyncTarget) -> Result<Self, Error<F>> {
561 self.prune((*target.range.start()).into()).await
562 }
563
564 fn sync_target(&self) -> Self::SyncTarget {
565 let bounds = self.bounds();
566 CurrentSyncTarget::new(
567 self.ops_root(),
568 non_empty_range!(self.sync_boundary(), bounds.end),
569 )
570 }
571
572 async fn rewind_to_target(self, target: Self::SyncTarget) -> Result<Self, Error<F>> {
573 let db = self.rewind(target.range.end()).await?;
574 let db = db.sync().await?;
575
576 let rewound_target = db.sync_target();
577 assert_eq!(
578 rewound_target, target,
579 "rewound database target mismatch after rewind",
580 );
581 Ok(db)
582 }
583}
584
585impl<F, E, K, V, H, T, const N: usize, S> ManagedDb<E>
587 for Db<
588 F,
589 E,
590 FixedJournal<E, Operation<F, ordered::Update<K, FixedEncoding<V>>>>,
591 OrderedIdx<T, Location<F>>,
592 H,
593 ordered::Update<K, FixedEncoding<V>>,
594 N,
595 S,
596 >
597where
598 F: Graftable,
599 E: Context + Spawner,
600 K: Array,
601 V: value::FixedValue + 'static,
602 H: Hasher + 'static,
603 T: Translator,
604 S: Strategy,
605{
606 type Unmerkleized = CurrentUnmerkleized<
607 F,
608 E,
609 FixedJournal<E, Operation<F, ordered::Update<K, FixedEncoding<V>>>>,
610 OrderedIdx<T, Location<F>>,
611 H,
612 ordered::Update<K, FixedEncoding<V>>,
613 N,
614 S,
615 >;
616 type Merkleized = CurrentMerkleized<
617 F,
618 E,
619 FixedJournal<E, Operation<F, ordered::Update<K, FixedEncoding<V>>>>,
620 OrderedIdx<T, Location<F>>,
621 H,
622 ordered::Update<K, FixedEncoding<V>>,
623 N,
624 S,
625 >;
626 type Error = Error<F>;
627 type Config = FixedConfig<T, S>;
628 type SyncTarget = CurrentSyncTarget<F, H::Digest>;
629
630 async fn init(context: E, config: Self::Config) -> Result<Self, Error<F>> {
631 <Self>::init(context, config).await
632 }
633
634 fn initial_sync_target() -> Self::SyncTarget {
635 CurrentSyncTarget::new(
636 initial_root::<F, ordered::Update<K, FixedEncoding<V>>, H>(),
637 non_empty_range!(Location::new(0), Location::new(1)),
638 )
639 }
640
641 fn new_batch(database: BatchContext<'_, Self>) -> Self::Unmerkleized {
642 let (database, shared) = database.into_parts();
643 CurrentUnmerkleized {
644 batch: database.new_batch(),
645 db: shared,
646 metadata: None,
647 }
648 }
649
650 fn matches_sync_target(batch: &Self::Merkleized, target: &Self::SyncTarget) -> bool {
651 batch.ops_root() == target.root
652 && *target.range.start() == batch.sync_boundary()
653 && *target.range.end() == batch.bounds().tip.size
654 }
655
656 async fn apply(self, batch: Self::Merkleized) -> Result<Self, Error<F>> {
657 let (db, _) = self.apply_batch(batch.inner).await?;
658 Ok(db)
659 }
660
661 async fn finalize(self) -> Result<(Self, Handle<()>), Error<F>> {
662 self.start_sync().await
663 }
664
665 async fn prune(self, target: &Self::SyncTarget) -> Result<Self, Error<F>> {
666 self.prune((*target.range.start()).into()).await
667 }
668
669 fn sync_target(&self) -> Self::SyncTarget {
670 let bounds = self.bounds();
671 CurrentSyncTarget::new(
672 self.ops_root(),
673 non_empty_range!(self.sync_boundary(), bounds.end),
674 )
675 }
676
677 async fn rewind_to_target(self, target: Self::SyncTarget) -> Result<Self, Error<F>> {
678 let db = self.rewind(target.range.end()).await?;
679 let db = db.sync().await?;
680
681 let rewound_target = db.sync_target();
682 assert_eq!(
683 rewound_target, target,
684 "rewound database target mismatch after rewind",
685 );
686 Ok(db)
687 }
688}
689
690mod open {
699 use commonware_codec::{Codec, Read};
700 use commonware_cryptography::Hasher;
701 use commonware_parallel::Strategy;
702 use commonware_runtime::Spawner;
703 use commonware_storage::{
704 Context,
705 merkle::Graftable,
706 qmdb::{
707 Error,
708 any::{
709 operation::Operation,
710 ordered, unordered,
711 value::{VariableEncoding, VariableValue},
712 },
713 current::{
714 VariableConfig, ordered::variable::Db as OrderedVariableDb, unordered::variable::Db,
715 },
716 },
717 };
718 type VConfig<T, F, K, V, S> = VariableConfig<
719 T,
720 <Operation<F, unordered::Update<K, VariableEncoding<V>>> as Read>::Cfg,
721 S,
722 >;
723 type OrderedVConfig<T, F, K, V, S> =
724 VariableConfig<T, <Operation<F, ordered::Update<K, VariableEncoding<V>>> as Read>::Cfg, S>;
725
726 pub(super) async fn variable<F, E, K, V, H, T, const N: usize, S>(
727 context: E,
728 config: VConfig<T, F, K, V, S>,
729 ) -> Result<Db<F, E, K, V, H, T, N, S>, Error<F>>
730 where
731 F: Graftable,
732 E: Context + Spawner,
733 K: commonware_storage::qmdb::operation::Key,
734 V: VariableValue + 'static,
735 H: Hasher,
736 T: commonware_storage::translator::Translator,
737 S: Strategy,
738 Operation<F, unordered::Update<K, VariableEncoding<V>>>: Codec,
739 {
740 Db::init(context, config).await
741 }
742
743 pub(super) async fn ordered_variable<F, E, K, V, H, T, const N: usize, S>(
744 context: E,
745 config: OrderedVConfig<T, F, K, V, S>,
746 ) -> Result<OrderedVariableDb<F, E, K, V, H, T, N, S>, Error<F>>
747 where
748 F: Graftable,
749 E: Context + Spawner,
750 K: commonware_storage::qmdb::operation::Key,
751 V: VariableValue + 'static,
752 H: Hasher,
753 T: commonware_storage::translator::Translator,
754 S: Strategy,
755 Operation<F, ordered::Update<K, VariableEncoding<V>>>: Codec,
756 {
757 OrderedVariableDb::init(context, config).await
758 }
759}
760
761impl<F, E, K, V, H, T, const N: usize, S> ManagedDb<E>
763 for Db<
764 F,
765 E,
766 VariableJournal<E, Operation<F, unordered::Update<K, VariableEncoding<V>>>>,
767 UnorderedIdx<T, Location<F>>,
768 H,
769 unordered::Update<K, VariableEncoding<V>>,
770 N,
771 S,
772 >
773where
774 F: Graftable,
775 E: Context + Spawner,
776 K: Key,
777 V: value::VariableValue + 'static,
778 H: Hasher,
779 T: Translator,
780 S: Strategy,
781 Operation<F, unordered::Update<K, VariableEncoding<V>>>: Codec,
782{
783 type Unmerkleized = CurrentUnmerkleized<
784 F,
785 E,
786 VariableJournal<E, Operation<F, unordered::Update<K, VariableEncoding<V>>>>,
787 UnorderedIdx<T, Location<F>>,
788 H,
789 unordered::Update<K, VariableEncoding<V>>,
790 N,
791 S,
792 >;
793 type Merkleized = CurrentMerkleized<
794 F,
795 E,
796 VariableJournal<E, Operation<F, unordered::Update<K, VariableEncoding<V>>>>,
797 UnorderedIdx<T, Location<F>>,
798 H,
799 unordered::Update<K, VariableEncoding<V>>,
800 N,
801 S,
802 >;
803 type Error = Error<F>;
804 type Config = VariableConfig<
805 T,
806 <Operation<F, unordered::Update<K, VariableEncoding<V>>> as CodecRead>::Cfg,
807 S,
808 >;
809 type SyncTarget = CurrentSyncTarget<F, H::Digest>;
810
811 async fn init(context: E, config: Self::Config) -> Result<Self, Error<F>> {
812 open::variable(context, config).await
813 }
814
815 fn initial_sync_target() -> Self::SyncTarget {
816 CurrentSyncTarget::new(
817 initial_root::<F, unordered::Update<K, VariableEncoding<V>>, H>(),
818 non_empty_range!(Location::new(0), Location::new(1)),
819 )
820 }
821
822 fn new_batch(database: BatchContext<'_, Self>) -> Self::Unmerkleized {
823 let (database, shared) = database.into_parts();
824 CurrentUnmerkleized {
825 batch: database.new_batch(),
826 db: shared,
827 metadata: None,
828 }
829 }
830
831 fn matches_sync_target(batch: &Self::Merkleized, target: &Self::SyncTarget) -> bool {
832 batch.ops_root() == target.root
833 && *target.range.start() == batch.sync_boundary()
834 && *target.range.end() == batch.bounds().tip.size
835 }
836
837 async fn apply(self, batch: Self::Merkleized) -> Result<Self, Error<F>> {
838 let (db, _) = self.apply_batch(batch.inner).await?;
839 Ok(db)
840 }
841
842 async fn finalize(self) -> Result<(Self, Handle<()>), Error<F>> {
843 self.start_sync().await
844 }
845
846 async fn prune(self, target: &Self::SyncTarget) -> Result<Self, Error<F>> {
847 self.prune((*target.range.start()).into()).await
848 }
849
850 fn sync_target(&self) -> Self::SyncTarget {
851 let bounds = self.bounds();
852 CurrentSyncTarget::new(
853 self.ops_root(),
854 non_empty_range!(self.sync_boundary(), bounds.end),
855 )
856 }
857
858 async fn rewind_to_target(self, target: Self::SyncTarget) -> Result<Self, Error<F>> {
859 let db = self.rewind(target.range.end()).await?;
860 let db = db.sync().await?;
861
862 let rewound_target = db.sync_target();
863 assert_eq!(
864 rewound_target, target,
865 "rewound database target mismatch after rewind",
866 );
867 Ok(db)
868 }
869}
870
871impl<F, E, K, V, H, T, const N: usize, S> ManagedDb<E>
873 for Db<
874 F,
875 E,
876 VariableJournal<E, Operation<F, ordered::Update<K, VariableEncoding<V>>>>,
877 OrderedIdx<T, Location<F>>,
878 H,
879 ordered::Update<K, VariableEncoding<V>>,
880 N,
881 S,
882 >
883where
884 F: Graftable,
885 E: Context + Spawner,
886 K: Key,
887 V: value::VariableValue + 'static,
888 H: Hasher,
889 T: Translator,
890 S: Strategy,
891 Operation<F, ordered::Update<K, VariableEncoding<V>>>: Codec,
892{
893 type Unmerkleized = CurrentUnmerkleized<
894 F,
895 E,
896 VariableJournal<E, Operation<F, ordered::Update<K, VariableEncoding<V>>>>,
897 OrderedIdx<T, Location<F>>,
898 H,
899 ordered::Update<K, VariableEncoding<V>>,
900 N,
901 S,
902 >;
903 type Merkleized = CurrentMerkleized<
904 F,
905 E,
906 VariableJournal<E, Operation<F, ordered::Update<K, VariableEncoding<V>>>>,
907 OrderedIdx<T, Location<F>>,
908 H,
909 ordered::Update<K, VariableEncoding<V>>,
910 N,
911 S,
912 >;
913 type Error = Error<F>;
914 type Config = VariableConfig<
915 T,
916 <Operation<F, ordered::Update<K, VariableEncoding<V>>> as CodecRead>::Cfg,
917 S,
918 >;
919 type SyncTarget = CurrentSyncTarget<F, H::Digest>;
920
921 async fn init(context: E, config: Self::Config) -> Result<Self, Error<F>> {
922 open::ordered_variable(context, config).await
923 }
924
925 fn initial_sync_target() -> Self::SyncTarget {
926 CurrentSyncTarget::new(
927 initial_root::<F, ordered::Update<K, VariableEncoding<V>>, H>(),
928 non_empty_range!(Location::new(0), Location::new(1)),
929 )
930 }
931
932 fn new_batch(database: BatchContext<'_, Self>) -> Self::Unmerkleized {
933 let (database, shared) = database.into_parts();
934 CurrentUnmerkleized {
935 batch: database.new_batch(),
936 db: shared,
937 metadata: None,
938 }
939 }
940
941 fn matches_sync_target(batch: &Self::Merkleized, target: &Self::SyncTarget) -> bool {
942 batch.ops_root() == target.root
943 && *target.range.start() == batch.sync_boundary()
944 && *target.range.end() == batch.bounds().tip.size
945 }
946
947 async fn apply(self, batch: Self::Merkleized) -> Result<Self, Error<F>> {
948 let (db, _) = self.apply_batch(batch.inner).await?;
949 Ok(db)
950 }
951
952 async fn finalize(self) -> Result<(Self, Handle<()>), Error<F>> {
953 self.start_sync().await
954 }
955
956 async fn prune(self, target: &Self::SyncTarget) -> Result<Self, Error<F>> {
957 self.prune((*target.range.start()).into()).await
958 }
959
960 fn sync_target(&self) -> Self::SyncTarget {
961 let bounds = self.bounds();
962 CurrentSyncTarget::new(
963 self.ops_root(),
964 non_empty_range!(self.sync_boundary(), bounds.end),
965 )
966 }
967
968 async fn rewind_to_target(self, target: Self::SyncTarget) -> Result<Self, Error<F>> {
969 let db = self.rewind(target.range.end()).await?;
970 let db = db.sync().await?;
971
972 let rewound_target = db.sync_target();
973 assert_eq!(
974 rewound_target, target,
975 "rewound database target mismatch after rewind",
976 );
977 Ok(db)
978 }
979}
980
981impl<F, E, K, V, H, T, R, const N: usize, S> StateSyncDb<E, R>
983 for Db<
984 F,
985 E,
986 FixedJournal<E, Operation<F, unordered::Update<K, FixedEncoding<V>>>>,
987 UnorderedIdx<T, Location<F>>,
988 H,
989 unordered::Update<K, FixedEncoding<V>>,
990 N,
991 S,
992 >
993where
994 F: Graftable,
995 E: Context + Spawner,
996 K: Array,
997 V: value::FixedValue + 'static,
998 H: Hasher,
999 T: Translator,
1000 S: Strategy,
1001 R: sync::SourceFor<Self>,
1002{
1003 type SyncError = sync::Error<F, R::Error, H::Digest>;
1004
1005 async fn sync_db(
1006 context: E,
1007 config: Self::Config,
1008 source: R,
1009 target: Self::SyncTarget,
1010 tip_updates: mpsc::Receiver<Self::SyncTarget>,
1011 finish: Option<mpsc::Receiver<()>>,
1012 reached_target: Option<mpsc::Sender<Self::SyncTarget>>,
1013 sync_config: SyncEngineConfig,
1014 ) -> Result<Self, Self::SyncError> {
1015 sync_standard_db(
1016 context,
1017 config,
1018 source,
1019 target,
1020 tip_updates,
1021 finish,
1022 reached_target,
1023 sync_config,
1024 )
1025 .await
1026 }
1027}
1028
1029impl<F, E, K, V, H, T, R, const N: usize, S> StateSyncDb<E, R>
1031 for Db<
1032 F,
1033 E,
1034 FixedJournal<E, Operation<F, ordered::Update<K, FixedEncoding<V>>>>,
1035 OrderedIdx<T, Location<F>>,
1036 H,
1037 ordered::Update<K, FixedEncoding<V>>,
1038 N,
1039 S,
1040 >
1041where
1042 F: Graftable,
1043 E: Context + Spawner,
1044 K: Array,
1045 V: value::FixedValue + 'static,
1046 H: Hasher,
1047 T: Translator,
1048 S: Strategy,
1049 R: sync::SourceFor<Self>,
1050{
1051 type SyncError = sync::Error<F, R::Error, H::Digest>;
1052
1053 async fn sync_db(
1054 context: E,
1055 config: Self::Config,
1056 source: R,
1057 target: Self::SyncTarget,
1058 tip_updates: mpsc::Receiver<Self::SyncTarget>,
1059 finish: Option<mpsc::Receiver<()>>,
1060 reached_target: Option<mpsc::Sender<Self::SyncTarget>>,
1061 sync_config: SyncEngineConfig,
1062 ) -> Result<Self, Self::SyncError> {
1063 sync_standard_db(
1064 context,
1065 config,
1066 source,
1067 target,
1068 tip_updates,
1069 finish,
1070 reached_target,
1071 sync_config,
1072 )
1073 .await
1074 }
1075}
1076
1077impl<F, E, K, V, H, T, R, const N: usize, S> StateSyncDb<E, R>
1079 for Db<
1080 F,
1081 E,
1082 VariableJournal<E, Operation<F, unordered::Update<K, VariableEncoding<V>>>>,
1083 UnorderedIdx<T, Location<F>>,
1084 H,
1085 unordered::Update<K, VariableEncoding<V>>,
1086 N,
1087 S,
1088 >
1089where
1090 F: Graftable,
1091 E: Context + Spawner,
1092 K: Key,
1093 V: value::VariableValue + 'static,
1094 H: Hasher,
1095 T: Translator,
1096 S: Strategy,
1097 Operation<F, unordered::Update<K, VariableEncoding<V>>>: Codec,
1098 R: sync::SourceFor<Self>,
1099{
1100 type SyncError = sync::Error<F, R::Error, H::Digest>;
1101
1102 async fn sync_db(
1103 context: E,
1104 config: Self::Config,
1105 source: R,
1106 target: Self::SyncTarget,
1107 tip_updates: mpsc::Receiver<Self::SyncTarget>,
1108 finish: Option<mpsc::Receiver<()>>,
1109 reached_target: Option<mpsc::Sender<Self::SyncTarget>>,
1110 sync_config: SyncEngineConfig,
1111 ) -> Result<Self, Self::SyncError> {
1112 sync_standard_db(
1113 context,
1114 config,
1115 source,
1116 target,
1117 tip_updates,
1118 finish,
1119 reached_target,
1120 sync_config,
1121 )
1122 .await
1123 }
1124}
1125
1126impl<F, E, K, V, H, T, R, const N: usize, S> StateSyncDb<E, R>
1128 for Db<
1129 F,
1130 E,
1131 VariableJournal<E, Operation<F, ordered::Update<K, VariableEncoding<V>>>>,
1132 OrderedIdx<T, Location<F>>,
1133 H,
1134 ordered::Update<K, VariableEncoding<V>>,
1135 N,
1136 S,
1137 >
1138where
1139 F: Graftable,
1140 E: Context + Spawner,
1141 K: Key,
1142 V: value::VariableValue + 'static,
1143 H: Hasher,
1144 T: Translator,
1145 S: Strategy,
1146 Operation<F, ordered::Update<K, VariableEncoding<V>>>: Codec,
1147 R: sync::SourceFor<Self>,
1148{
1149 type SyncError = sync::Error<F, R::Error, H::Digest>;
1150
1151 async fn sync_db(
1152 context: E,
1153 config: Self::Config,
1154 source: R,
1155 target: Self::SyncTarget,
1156 tip_updates: mpsc::Receiver<Self::SyncTarget>,
1157 finish: Option<mpsc::Receiver<()>>,
1158 reached_target: Option<mpsc::Sender<Self::SyncTarget>>,
1159 sync_config: SyncEngineConfig,
1160 ) -> Result<Self, Self::SyncError> {
1161 sync_standard_db(
1162 context,
1163 config,
1164 source,
1165 target,
1166 tip_updates,
1167 finish,
1168 reached_target,
1169 sync_config,
1170 )
1171 .await
1172 }
1173}
1174
1175#[cfg(test)]
1176mod tests {
1177 use super::*;
1178 use commonware_cryptography::{Sha256, sha256::Digest};
1179 use commonware_macros::boxed;
1180 use commonware_parallel::Sequential;
1181 use commonware_runtime::{
1182 BufferPooler, Runner as _, Supervisor as _, buffer::paged::CacheRef, deterministic,
1183 };
1184 use commonware_storage::{
1185 journal::contiguous::{
1186 fixed::Config as FixedJournalConfig, variable::Config as VariableJournalConfig,
1187 },
1188 merkle::{full::Config as MerkleConfig, mmr},
1189 qmdb::current::{
1190 ordered::{fixed as ordered_fixed, variable as ordered_variable},
1191 unordered::{fixed, variable},
1192 },
1193 translator::TwoCap,
1194 };
1195 use commonware_utils::{NZU16, NZU64, NZUsize, non_empty_range};
1196 use std::num::{NonZeroU16, NonZeroUsize};
1197
1198 #[boxed]
1199 async fn apply_and_finalize<D: ManagedDb<deterministic::Context>>(
1200 db: D,
1201 batch: D::Merkleized,
1202 ) -> D {
1203 let db = D::apply(db, batch).await.unwrap();
1204 let (db, sync) = D::finalize(db).await.unwrap();
1205 sync.await.expect("database sync failed");
1206 db
1207 }
1208
1209 type FixedDb = fixed::Db<
1210 mmr::Family,
1211 deterministic::Context,
1212 Digest,
1213 Digest,
1214 Sha256,
1215 TwoCap,
1216 64,
1217 Sequential,
1218 >;
1219 type OrderedFixedDb = ordered_fixed::Db<
1220 mmr::Family,
1221 deterministic::Context,
1222 Digest,
1223 Digest,
1224 Sha256,
1225 TwoCap,
1226 64,
1227 Sequential,
1228 >;
1229 type OrderedVariableDb = ordered_variable::Db<
1230 mmr::Family,
1231 deterministic::Context,
1232 Digest,
1233 Digest,
1234 Sha256,
1235 TwoCap,
1236 64,
1237 Sequential,
1238 >;
1239
1240 type VariableDb = variable::Db<
1242 mmr::Family,
1243 deterministic::Context,
1244 Vec<u8>,
1245 Digest,
1246 Sha256,
1247 TwoCap,
1248 64,
1249 Sequential,
1250 >;
1251
1252 const PAGE_SIZE: NonZeroU16 = NZU16!(101);
1253 const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(11);
1254
1255 fn fixed_config(suffix: &str, pooler: &impl BufferPooler) -> FixedConfig<TwoCap, Sequential> {
1256 let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
1257 FixedConfig {
1258 merkle_config: MerkleConfig {
1259 journal_partition: format!("stateful-current-journal-{suffix}"),
1260 metadata_partition: format!("stateful-current-metadata-{suffix}"),
1261 items_per_blob: NZU64!(11),
1262 write_buffer: NZUsize!(1024),
1263 replay_buffer: NZUsize!(1024),
1264 strategy: Sequential,
1265 page_cache: page_cache.clone(),
1266 },
1267 journal_config: FixedJournalConfig {
1268 partition: format!("stateful-current-log-{suffix}"),
1269 items_per_blob: NZU64!(7),
1270 page_cache,
1271 write_buffer: NZUsize!(1024),
1272 replay_buffer: NZUsize!(1024),
1273 },
1274 grafted_metadata_partition: format!("stateful-current-grafted-{suffix}"),
1275 translator: TwoCap,
1276 init_cache_size: Some(NZUsize!(1024)),
1277 init_buffer: NZUsize!(1 << 21),
1278 init_concurrency: (),
1279 }
1280 }
1281
1282 fn variable_config(
1283 suffix: &str,
1284 pooler: &impl BufferPooler,
1285 ) -> VariableConfig<TwoCap, ((), ()), Sequential> {
1286 let page_cache = CacheRef::from_pooler(pooler, PAGE_SIZE, PAGE_CACHE_SIZE);
1287 VariableConfig {
1288 merkle_config: MerkleConfig {
1289 journal_partition: format!("stateful-current-journal-{suffix}"),
1290 metadata_partition: format!("stateful-current-metadata-{suffix}"),
1291 items_per_blob: NZU64!(11),
1292 write_buffer: NZUsize!(1024),
1293 replay_buffer: NZUsize!(1024),
1294 strategy: Sequential,
1295 page_cache: page_cache.clone(),
1296 },
1297 journal_config: VariableJournalConfig {
1298 partition: format!("stateful-current-log-{suffix}"),
1299 items_per_section: NZU64!(7),
1300 compression: None,
1301 codec_config: ((), ()),
1302 page_cache,
1303 write_buffer: NZUsize!(1024),
1304 replay_buffer: NZUsize!(1024),
1305 },
1306 grafted_metadata_partition: format!("stateful-current-grafted-{suffix}"),
1307 translator: TwoCap,
1308 init_cache_size: Some(NZUsize!(1024)),
1309 init_buffer: NZUsize!(1 << 21),
1310 init_concurrency: (),
1311 }
1312 }
1313
1314 fn assert_managed_db<T: ManagedDb<deterministic::Context>>() {}
1315
1316 fn assert_state_sync_db<T, R>()
1317 where
1318 T: StateSyncDb<deterministic::Context, R>,
1319 {
1320 }
1321
1322 fn assert_database_set<T: crate::stateful::db::DatabaseSet<deterministic::Context>>() {}
1323
1324 #[test]
1325 fn ordered_current_db_trait_impls_compile() {
1326 assert_managed_db::<OrderedFixedDb>();
1327 assert_managed_db::<OrderedVariableDb>();
1328 assert_state_sync_db::<OrderedFixedDb, Arc<OrderedFixedDb>>();
1329 assert_state_sync_db::<OrderedVariableDb, Arc<OrderedVariableDb>>();
1330 assert_database_set::<Shared<OrderedFixedDb>>();
1331 assert_database_set::<Shared<OrderedVariableDb>>();
1332 }
1333
1334 #[test]
1335 fn variable_current_db_trait_impls_compile() {
1336 assert_managed_db::<VariableDb>();
1337 assert_state_sync_db::<VariableDb, Arc<VariableDb>>();
1338 assert_database_set::<Shared<VariableDb>>();
1339 }
1340
1341 #[test]
1342 fn ordered_fixed_managed_db_applies_batch_and_proves_exclusion() {
1343 deterministic::Runner::default().start(|context| async move {
1344 let config = fixed_config("ordered-fixed-managed-db", &context);
1345 let db = <OrderedFixedDb as ManagedDb<_>>::init(context.child("db"), config)
1346 .await
1347 .unwrap();
1348 let db = Shared::new("test", db);
1349 let key = Sha256::hash(&[b"key"]);
1350 let value = Sha256::hash(&[b"value"]);
1351 let metadata = Sha256::hash(&[b"metadata"]);
1352 let missing = Sha256::hash(&[b"missing"]);
1353
1354 let batch = db
1355 .new_batch_for_test::<_>()
1356 .await
1357 .write(key, Some(value))
1358 .with_metadata(metadata);
1359 let merkleized = crate::stateful::db::Unmerkleized::merkleize(batch)
1360 .await
1361 .unwrap();
1362 let expected_root = merkleized.root();
1363
1364 {
1365 let (slot, database) = db.write().await;
1366 slot.put(apply_and_finalize::<OrderedFixedDb>(database, merkleized).await);
1367 }
1368
1369 let guard = db.read().await;
1370 assert_eq!(guard.root(), expected_root);
1371 assert_eq!(guard.get(&key).await.unwrap(), Some(value));
1372
1373 let proof = guard.exclusion_proof(&missing).await.unwrap();
1374 assert!(OrderedFixedDb::verify_exclusion_proof(
1375 &missing,
1376 &proof,
1377 &guard.root(),
1378 ));
1379 });
1380 }
1381
1382 #[test]
1388 fn ordered_fixed_staged_merkleize_matches_explicit_writes() {
1389 deterministic::Runner::default().start(|context| async move {
1390 let config = fixed_config("ordered-fixed-glue-staged", &context);
1391 let db = <OrderedFixedDb as ManagedDb<_>>::init(context.child("db"), config)
1392 .await
1393 .unwrap();
1394 let db = Shared::new("test", db);
1395
1396 let key = |i: u64| Sha256::hash(&[&i.to_be_bytes()]);
1397 let val = |i: u64| Sha256::hash(&[&(i + 10_000).to_be_bytes()]);
1398 let metadata = Sha256::hash(&[b"metadata"]);
1399
1400 let mut seed = db.new_batch_for_test::<_>().await;
1402 for i in 0..50u64 {
1403 seed = seed.write(key(i), Some(val(i)));
1404 }
1405 let merkleized = crate::stateful::db::Unmerkleized::merkleize(seed)
1406 .await
1407 .unwrap();
1408 {
1409 let (slot, database) = db.write().await;
1410 slot.put(apply_and_finalize::<OrderedFixedDb>(database, merkleized).await);
1411 }
1412
1413 let read_keys = [key(1), key(2), key(999)];
1415 let keys: Vec<&Digest> = read_keys.iter().collect();
1416 let indexed_updates = vec![(0, Some(val(1_000))), (1, None), (2, Some(val(1_001)))];
1417 let upserts = vec![(key(3), Some(val(1_002)))];
1418
1419 let mut explicit = db.new_batch_for_test::<_>().await;
1421 let explicit_values = explicit.get_many(&keys).await.unwrap();
1422 for (slot, value) in &indexed_updates {
1423 explicit = explicit.write(read_keys[*slot], *value);
1424 }
1425 for (k, v) in &upserts {
1426 explicit = explicit.write(*k, *v);
1427 }
1428 let explicit_root =
1429 crate::stateful::db::Unmerkleized::merkleize(explicit.with_metadata(metadata))
1430 .await
1431 .unwrap()
1432 .root();
1433
1434 let staged_batch = db.new_batch_for_test::<_>().await;
1436 let split = 2;
1437 let (mut staged_values, staged) = staged_batch.stage(&keys[..split]).await.unwrap();
1438 let (range, suffix_values, staged) = staged.expand(&keys[split..]).await.unwrap();
1439 assert_eq!(range, split..keys.len());
1440 staged_values.extend(suffix_values);
1441 let staged_root = staged
1442 .with_metadata(metadata)
1443 .merkleize(indexed_updates.clone(), upserts.clone())
1444 .await
1445 .unwrap()
1446 .root();
1447
1448 assert_eq!(explicit_values, staged_values);
1449 assert_eq!(explicit_root, staged_root);
1450
1451 let carried_batch = db.new_batch_for_test::<_>().await.with_metadata(metadata);
1453 let (carried_values, staged) = carried_batch.stage(&keys).await.unwrap();
1454 let carried_root = staged
1455 .merkleize(indexed_updates.clone(), upserts.clone())
1456 .await
1457 .unwrap()
1458 .root();
1459 assert_eq!(explicit_values, carried_values);
1460 assert_eq!(explicit_root, carried_root);
1461 });
1462 }
1463
1464 #[test]
1465 fn ordered_variable_managed_db_applies_batch_and_proves_exclusion() {
1466 deterministic::Runner::default().start(|context| async move {
1467 let config = variable_config("ordered-variable-managed-db", &context);
1468 let db = <OrderedVariableDb as ManagedDb<_>>::init(context.child("db"), config)
1469 .await
1470 .unwrap();
1471 let db = Shared::new("test", db);
1472 let key = Sha256::hash(&[b"key"]);
1473 let value = Sha256::hash(&[b"value"]);
1474 let metadata = Sha256::hash(&[b"metadata"]);
1475 let missing = Sha256::hash(&[b"missing"]);
1476
1477 let batch = db
1478 .new_batch_for_test::<_>()
1479 .await
1480 .write(key, Some(value))
1481 .with_metadata(metadata);
1482 let merkleized = crate::stateful::db::Unmerkleized::merkleize(batch)
1483 .await
1484 .unwrap();
1485 let expected_root = merkleized.root();
1486
1487 {
1488 let (slot, database) = db.write().await;
1489 slot.put(apply_and_finalize::<OrderedVariableDb>(database, merkleized).await);
1490 }
1491
1492 let guard = db.read().await;
1493 assert_eq!(guard.root(), expected_root);
1494 assert_eq!(guard.get(&key).await.unwrap(), Some(value));
1495
1496 let proof = guard.exclusion_proof(&missing).await.unwrap();
1497 assert!(OrderedVariableDb::verify_exclusion_proof(
1498 &missing,
1499 &proof,
1500 &guard.root(),
1501 ));
1502 });
1503 }
1504
1505 #[test]
1506 fn ordered_managed_db_matches_sync_target_rejects_wrong_ops_root_and_range() {
1507 deterministic::Runner::default().start(|context| async move {
1508 let config = fixed_config("ordered-matches-sync-target", &context);
1509 let db = <OrderedFixedDb as ManagedDb<_>>::init(context.child("db"), config.clone())
1510 .await
1511 .unwrap();
1512 let db = Shared::new("test", db);
1513
1514 let key = Sha256::hash(&[b"key"]);
1515 let value = Sha256::hash(&[b"value"]);
1516 let metadata = Sha256::hash(&[b"metadata"]);
1517
1518 let batch = db
1519 .new_batch_for_test::<_>()
1520 .await
1521 .write(key, Some(value))
1522 .with_metadata(metadata);
1523 let merkleized = crate::stateful::db::Unmerkleized::merkleize(batch)
1524 .await
1525 .unwrap();
1526
1527 let verification_db =
1528 <OrderedFixedDb as ManagedDb<_>>::init(context.child("verification_db"), config)
1529 .await
1530 .unwrap();
1531 let (verification_db, _) = verification_db
1532 .apply_batch(merkleized.inner.clone())
1533 .await
1534 .unwrap();
1535 let verification_db = verification_db.sync().await.unwrap();
1536
1537 let valid_target = <OrderedFixedDb as ManagedDb<_>>::sync_target(&verification_db);
1538 assert!(<OrderedFixedDb as ManagedDb<_>>::matches_sync_target(
1539 &merkleized,
1540 &valid_target,
1541 ));
1542
1543 let mut wrong_root = valid_target.clone();
1544 wrong_root.root = Sha256::hash(&[b"wrong ops root"]);
1545 assert!(!<OrderedFixedDb as ManagedDb<_>>::matches_sync_target(
1546 &merkleized,
1547 &wrong_root,
1548 ));
1549
1550 let mut wrong_range = valid_target.clone();
1551 wrong_range.range =
1552 non_empty_range!(valid_target.range.start(), valid_target.range.end() + 1);
1553 assert!(!<OrderedFixedDb as ManagedDb<_>>::matches_sync_target(
1554 &merkleized,
1555 &wrong_range,
1556 ));
1557 });
1558 }
1559
1560 #[test]
1561 fn ordered_managed_db_rewind_to_target_round_trips() {
1562 deterministic::Runner::default().start(|context| async move {
1563 let config = fixed_config("ordered-rewind-round-trip", &context);
1564 let db = <OrderedFixedDb as ManagedDb<_>>::init(context.child("db"), config)
1565 .await
1566 .unwrap();
1567 let db = Shared::new("test", db);
1568
1569 let key1 = Sha256::hash(&[b"key1"]);
1570 let value1 = Sha256::hash(&[b"value1"]);
1571 let metadata1 = Sha256::hash(&[b"metadata1"]);
1572 let batch1 = db
1573 .new_batch_for_test::<_>()
1574 .await
1575 .write(key1, Some(value1))
1576 .with_metadata(metadata1);
1577 let merkleized1 = crate::stateful::db::Unmerkleized::merkleize(batch1)
1578 .await
1579 .unwrap();
1580 {
1581 let (slot, database) = db.write().await;
1582 slot.put(apply_and_finalize::<OrderedFixedDb>(database, merkleized1).await);
1583 }
1584 let target_after_first = {
1585 let guard = db.read().await;
1586 <OrderedFixedDb as ManagedDb<_>>::sync_target(&guard)
1587 };
1588
1589 let key2 = Sha256::hash(&[b"key2"]);
1590 let value2 = Sha256::hash(&[b"value2"]);
1591 let metadata2 = Sha256::hash(&[b"metadata2"]);
1592 let batch2 = db
1593 .new_batch_for_test::<_>()
1594 .await
1595 .write(key2, Some(value2))
1596 .with_metadata(metadata2);
1597 let merkleized2 = crate::stateful::db::Unmerkleized::merkleize(batch2)
1598 .await
1599 .unwrap();
1600 {
1601 let (slot, database) = db.write().await;
1602 slot.put(apply_and_finalize::<OrderedFixedDb>(database, merkleized2).await);
1603 }
1604
1605 {
1606 let (slot, database) = db.write().await;
1607 slot.put(
1608 <OrderedFixedDb as ManagedDb<_>>::rewind_to_target(
1609 database,
1610 target_after_first.clone(),
1611 )
1612 .await
1613 .unwrap(),
1614 );
1615 }
1616 let target_after_rewind = {
1617 let guard = db.read().await;
1618 <OrderedFixedDb as ManagedDb<_>>::sync_target(&guard)
1619 };
1620 assert_eq!(target_after_rewind, target_after_first);
1621 });
1622 }
1623
1624 #[test]
1625 fn managed_db_matches_sync_target_rejects_wrong_ops_root_and_range() {
1626 deterministic::Runner::default().start(|context| async move {
1627 let config = fixed_config("matches-sync-target", &context);
1628 let db = FixedDb::init(context.child("db"), config.clone())
1629 .await
1630 .unwrap();
1631 let db = Shared::new("test", db);
1632
1633 let key = Sha256::hash(&[b"key"]);
1634 let value = Sha256::hash(&[b"value"]);
1635 let metadata = Sha256::hash(&[b"metadata"]);
1636
1637 let batch = db
1638 .new_batch_for_test::<_>()
1639 .await
1640 .write(key, Some(value))
1641 .with_metadata(metadata);
1642 let merkleized = crate::stateful::db::Unmerkleized::merkleize(batch)
1643 .await
1644 .unwrap();
1645
1646 let verification_db = FixedDb::init(context.child("verification_db"), config)
1647 .await
1648 .unwrap();
1649 let (verification_db, _) = verification_db
1650 .apply_batch(merkleized.inner.clone())
1651 .await
1652 .unwrap();
1653 let verification_db = verification_db.sync().await.unwrap();
1654
1655 let valid_target = <FixedDb as ManagedDb<_>>::sync_target(&verification_db);
1656 assert!(<FixedDb as ManagedDb<_>>::matches_sync_target(
1657 &merkleized,
1658 &valid_target,
1659 ));
1660
1661 let mut wrong_root = valid_target.clone();
1662 wrong_root.root = Sha256::hash(&[b"wrong ops root"]);
1663 assert!(!<FixedDb as ManagedDb<_>>::matches_sync_target(
1664 &merkleized,
1665 &wrong_root,
1666 ));
1667
1668 let mut wrong_range = valid_target.clone();
1669 wrong_range.range =
1670 non_empty_range!(valid_target.range.start(), valid_target.range.end() + 1);
1671 assert!(!<FixedDb as ManagedDb<_>>::matches_sync_target(
1672 &merkleized,
1673 &wrong_range,
1674 ));
1675 });
1676 }
1677}