Skip to main content

commonware_storage/qmdb/sync/
resolver.rs

1use crate::{
2    merkle::{Family, Location, Proof},
3    qmdb::{
4        self,
5        any::{
6            ordered::{
7                fixed::{Db as OrderedFixedDb, Operation as OrderedFixedOperation},
8                variable::{Db as OrderedVariableDb, Operation as OrderedVariableOperation},
9            },
10            unordered::{
11                fixed::{Db as FixedDb, Operation as FixedOperation},
12                variable::{Db as VariableDb, Operation as VariableOperation},
13            },
14            FixedValue, VariableValue,
15        },
16        immutable::{
17            fixed::{Db as ImmutableFixedDb, Operation as ImmutableFixedOp},
18            variable::{Db as ImmutableVariableDb, Operation as ImmutableVariableOp},
19        },
20        keyless::{
21            fixed::{Db as KeylessFixedDb, Operation as KeylessFixedOp},
22            variable::{Db as KeylessVariableDb, Operation as KeylessVariableOp},
23        },
24        operation::Key,
25    },
26    translator::Translator,
27    Context,
28};
29use commonware_cryptography::{Digest, Hasher};
30use commonware_parallel::Strategy;
31use commonware_utils::{channel::oneshot, sync::AsyncRwLock, Array};
32use std::{future::Future, num::NonZeroU64, sync::Arc};
33
34/// Result from a fetch operation.
35pub struct FetchResult<F: Family, Op, D: Digest> {
36    /// The proof for the operations
37    pub proof: Proof<F, D>,
38    /// The operations that were fetched
39    pub operations: Vec<Op>,
40    /// Pinned merkle nodes at the start location, if requested
41    pub pinned_nodes: Option<Vec<D>>,
42    /// Optional callback for resolvers that observe downstream validation feedback.
43    pub callback: Option<oneshot::Sender<bool>>,
44}
45
46impl<F: Family, Op, D: Digest> FetchResult<F, Op, D> {
47    /// Creates a fetch result that does not observe the validation acknowledgement.
48    pub const fn new(
49        proof: Proof<F, D>,
50        operations: Vec<Op>,
51        pinned_nodes: Option<Vec<D>>,
52    ) -> Self {
53        Self {
54            proof,
55            operations,
56            pinned_nodes,
57            callback: None,
58        }
59    }
60
61    /// Creates a fetch result using an externally managed validation callback.
62    pub const fn with_callback(
63        proof: Proof<F, D>,
64        operations: Vec<Op>,
65        pinned_nodes: Option<Vec<D>>,
66        callback: oneshot::Sender<bool>,
67    ) -> Self {
68        Self {
69            proof,
70            operations,
71            pinned_nodes,
72            callback: Some(callback),
73        }
74    }
75}
76
77/// Operations fetched from a resolver before packaging as a [`FetchResult`].
78pub struct FetchedOperations<F: Family, Op, D: Digest> {
79    /// The proof for the operations
80    pub proof: Proof<F, D>,
81    /// The operations that were fetched
82    pub operations: Vec<Op>,
83    /// Pinned merkle nodes at the start location, if requested
84    pub pinned_nodes: Option<Vec<D>>,
85}
86
87impl<F: Family, Op, D: Digest> FetchedOperations<F, Op, D> {
88    /// Creates fetched operations with optional pinned nodes.
89    pub const fn new(
90        proof: Proof<F, D>,
91        operations: Vec<Op>,
92        pinned_nodes: Option<Vec<D>>,
93    ) -> Self {
94        Self {
95            proof,
96            operations,
97            pinned_nodes,
98        }
99    }
100}
101
102impl<F: Family, Op: std::fmt::Debug, D: Digest> std::fmt::Debug for FetchResult<F, Op, D> {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        f.debug_struct("FetchResult")
105            .field("proof", &self.proof)
106            .field("operations", &self.operations)
107            .field("pinned_nodes", &self.pinned_nodes)
108            .field("callback", &self.callback.as_ref().map(|_| "<callback>"))
109            .finish()
110    }
111}
112
113/// Fetch an operation range with a caller-provided callback and package it as a
114/// [`FetchResult`].
115///
116/// Use this when the source returns the proof, operations, and optional pinned nodes together,
117/// such as a network `get_operations` request.
118pub async fn fetch_operation_range<F, Op, D, Error, Fetch, FetchFuture>(
119    op_count: Location<F>,
120    start_loc: Location<F>,
121    max_ops: NonZeroU64,
122    include_pinned_nodes: bool,
123    fetch: Fetch,
124) -> Result<FetchResult<F, Op, D>, Error>
125where
126    F: Family,
127    D: Digest,
128    Fetch: FnOnce(Location<F>, Location<F>, NonZeroU64, bool) -> FetchFuture,
129    FetchFuture: Future<Output = Result<FetchedOperations<F, Op, D>, Error>>,
130{
131    let FetchedOperations {
132        proof,
133        operations,
134        pinned_nodes,
135    } = fetch(op_count, start_loc, max_ops, include_pinned_nodes).await?;
136    Ok(FetchResult::new(proof, operations, pinned_nodes))
137}
138
139/// Fetch an operation range from separate local-store callbacks and package it as a
140/// [`FetchResult`].
141///
142/// Use this for database APIs that expose `historical_proof` separately from
143/// `pinned_nodes_at`; pinned nodes are fetched only when `include_pinned_nodes` is true.
144pub async fn fetch_operations<
145    F,
146    Op,
147    D,
148    Error,
149    HistoricalProof,
150    HistoricalFuture,
151    Pins,
152    PinsFuture,
153>(
154    op_count: Location<F>,
155    start_loc: Location<F>,
156    max_ops: NonZeroU64,
157    include_pinned_nodes: bool,
158    historical_proof: HistoricalProof,
159    pinned_nodes_at: Pins,
160) -> Result<FetchResult<F, Op, D>, Error>
161where
162    F: Family,
163    D: Digest,
164    HistoricalProof: FnOnce(Location<F>, Location<F>, NonZeroU64) -> HistoricalFuture,
165    HistoricalFuture: Future<Output = Result<(Proof<F, D>, Vec<Op>), Error>>,
166    Pins: FnOnce(Location<F>) -> PinsFuture,
167    PinsFuture: Future<Output = Result<Vec<D>, Error>>,
168{
169    fetch_operation_range(
170        op_count,
171        start_loc,
172        max_ops,
173        include_pinned_nodes,
174        |op_count, start_loc, max_ops, include_pinned_nodes| async move {
175            let (proof, operations) = historical_proof(op_count, start_loc, max_ops).await?;
176            let pinned_nodes = if include_pinned_nodes {
177                Some(pinned_nodes_at(start_loc).await?)
178            } else {
179                None
180            };
181            Ok(FetchedOperations::new(proof, operations, pinned_nodes))
182        },
183    )
184    .await
185}
186
187/// Trait for network communication with the sync server.
188pub trait Resolver: Send + Sync + Clone + 'static {
189    /// The merkle family backing the resolver's proofs
190    type Family: Family;
191
192    /// The digest type used in proofs returned by the resolver
193    type Digest: Digest;
194
195    /// The type of operations returned by the resolver
196    type Op;
197
198    /// The error type returned by the resolver
199    type Error: std::error::Error + Send + 'static;
200
201    /// Get the operations starting at `start_loc` in the database, up to `max_ops` operations.
202    /// Returns the operations and a proof that they were present in the database when it had
203    /// `op_count` operations. If `include_pinned_nodes` is true, the result will include the
204    /// pinned merkle nodes at `start_loc`.
205    ///
206    /// The corresponding `cancel_tx` is dropped when the engine no longer needs this
207    /// request (e.g. due to a target update), causing `cancel_rx.await` to return
208    /// `Err`. Implementations may `select!` on it to abort in-flight work early.
209    #[allow(clippy::type_complexity)]
210    fn get_operations<'a>(
211        &'a self,
212        op_count: Location<Self::Family>,
213        start_loc: Location<Self::Family>,
214        max_ops: NonZeroU64,
215        include_pinned_nodes: bool,
216        cancel_rx: oneshot::Receiver<()>,
217    ) -> impl Future<Output = Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error>>
218           + Send
219           + 'a;
220}
221
222macro_rules! impl_resolver {
223    ($db:ident, $op:ident, $val_bound:ident) => {
224        impl<F, E, K, V, H, T, S> Resolver for Arc<$db<F, E, K, V, H, T, S>>
225        where
226            F: Family,
227            E: Context,
228            K: Array,
229            V: $val_bound + Send + Sync + 'static,
230            H: Hasher,
231            T: Translator + Send + Sync + 'static,
232            T::Key: Send + Sync,
233            S: Strategy,
234        {
235            type Family = F;
236            type Digest = H::Digest;
237            type Op = $op<F, K, V>;
238            type Error = qmdb::Error<F>;
239
240            async fn get_operations(
241                &self,
242                op_count: Location<Self::Family>,
243                start_loc: Location<Self::Family>,
244                max_ops: NonZeroU64,
245                include_pinned_nodes: bool,
246                _cancel_rx: oneshot::Receiver<()>,
247            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
248                fetch_operations(
249                    op_count,
250                    start_loc,
251                    max_ops,
252                    include_pinned_nodes,
253                    |op_count, start_loc, max_ops| {
254                        self.historical_proof(op_count, start_loc, max_ops)
255                    },
256                    |start_loc| self.pinned_nodes_at(start_loc),
257                )
258                .await
259            }
260        }
261
262        impl<F, E, K, V, H, T, S> Resolver for Arc<AsyncRwLock<$db<F, E, K, V, H, T, S>>>
263        where
264            F: Family,
265            E: Context,
266            K: Array,
267            V: $val_bound + Send + Sync + 'static,
268            H: Hasher,
269            T: Translator + Send + Sync + 'static,
270            T::Key: Send + Sync,
271            S: Strategy,
272        {
273            type Family = F;
274            type Digest = H::Digest;
275            type Op = $op<F, K, V>;
276            type Error = qmdb::Error<F>;
277
278            async fn get_operations(
279                &self,
280                op_count: Location<Self::Family>,
281                start_loc: Location<Self::Family>,
282                max_ops: NonZeroU64,
283                include_pinned_nodes: bool,
284                _cancel_rx: oneshot::Receiver<()>,
285            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
286                let db = self.read().await;
287                fetch_operations(
288                    op_count,
289                    start_loc,
290                    max_ops,
291                    include_pinned_nodes,
292                    |op_count, start_loc, max_ops| {
293                        db.historical_proof(op_count, start_loc, max_ops)
294                    },
295                    |start_loc| db.pinned_nodes_at(start_loc),
296                )
297                .await
298            }
299        }
300
301        impl<F, E, K, V, H, T, S> Resolver for Arc<AsyncRwLock<Option<$db<F, E, K, V, H, T, S>>>>
302        where
303            F: Family,
304            E: Context,
305            K: Array,
306            V: $val_bound + Send + Sync + 'static,
307            H: Hasher,
308            T: Translator + Send + Sync + 'static,
309            T::Key: Send + Sync,
310            S: Strategy,
311        {
312            type Family = F;
313            type Digest = H::Digest;
314            type Op = $op<F, K, V>;
315            type Error = qmdb::Error<F>;
316
317            async fn get_operations(
318                &self,
319                op_count: Location<Self::Family>,
320                start_loc: Location<Self::Family>,
321                max_ops: NonZeroU64,
322                include_pinned_nodes: bool,
323                _cancel_rx: oneshot::Receiver<()>,
324            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
325                let guard = self.read().await;
326                let db = guard.as_ref().ok_or(qmdb::Error::KeyNotFound)?;
327                fetch_operations(
328                    op_count,
329                    start_loc,
330                    max_ops,
331                    include_pinned_nodes,
332                    |op_count, start_loc, max_ops| {
333                        db.historical_proof(op_count, start_loc, max_ops)
334                    },
335                    |start_loc| db.pinned_nodes_at(start_loc),
336                )
337                .await
338            }
339        }
340    };
341}
342
343// Unordered Fixed
344impl_resolver!(FixedDb, FixedOperation, FixedValue);
345
346// Unordered Variable
347impl_resolver!(VariableDb, VariableOperation, VariableValue);
348
349// Ordered Fixed
350impl_resolver!(OrderedFixedDb, OrderedFixedOperation, FixedValue);
351
352// Ordered Variable
353impl_resolver!(OrderedVariableDb, OrderedVariableOperation, VariableValue);
354
355// Immutable types need a separate macro because the key bound varies
356// (Array for fixed, Key for variable) unlike the other DB types which
357// always use Array.
358macro_rules! impl_resolver_immutable {
359    ($db:ident, $op:ident, $val_bound:ident, $key_bound:path) => {
360        impl<F, E, K, V, H, T, S> Resolver for Arc<$db<F, E, K, V, H, T, S>>
361        where
362            F: Family,
363            E: Context,
364            K: $key_bound,
365            V: $val_bound + Send + Sync + 'static,
366            H: Hasher,
367            T: Translator + Send + Sync + 'static,
368            T::Key: Send + Sync,
369            S: Strategy,
370        {
371            type Family = F;
372            type Digest = H::Digest;
373            type Op = $op<F, K, V>;
374            type Error = qmdb::Error<F>;
375
376            async fn get_operations(
377                &self,
378                op_count: Location<Self::Family>,
379                start_loc: Location<Self::Family>,
380                max_ops: NonZeroU64,
381                include_pinned_nodes: bool,
382                _cancel_rx: oneshot::Receiver<()>,
383            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
384                fetch_operations(
385                    op_count,
386                    start_loc,
387                    max_ops,
388                    include_pinned_nodes,
389                    |op_count, start_loc, max_ops| {
390                        self.historical_proof(op_count, start_loc, max_ops)
391                    },
392                    |start_loc| self.pinned_nodes_at(start_loc),
393                )
394                .await
395            }
396        }
397
398        impl<F, E, K, V, H, T, S> Resolver for Arc<AsyncRwLock<$db<F, E, K, V, H, T, S>>>
399        where
400            F: Family,
401            E: Context,
402            K: $key_bound,
403            V: $val_bound + Send + Sync + 'static,
404            H: Hasher,
405            T: Translator + Send + Sync + 'static,
406            T::Key: Send + Sync,
407            S: Strategy,
408        {
409            type Family = F;
410            type Digest = H::Digest;
411            type Op = $op<F, K, V>;
412            type Error = qmdb::Error<F>;
413
414            async fn get_operations(
415                &self,
416                op_count: Location<Self::Family>,
417                start_loc: Location<Self::Family>,
418                max_ops: NonZeroU64,
419                include_pinned_nodes: bool,
420                _cancel_rx: oneshot::Receiver<()>,
421            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
422                let db = self.read().await;
423                fetch_operations(
424                    op_count,
425                    start_loc,
426                    max_ops,
427                    include_pinned_nodes,
428                    |op_count, start_loc, max_ops| {
429                        db.historical_proof(op_count, start_loc, max_ops)
430                    },
431                    |start_loc| db.pinned_nodes_at(start_loc),
432                )
433                .await
434            }
435        }
436
437        impl<F, E, K, V, H, T, S> Resolver for Arc<AsyncRwLock<Option<$db<F, E, K, V, H, T, S>>>>
438        where
439            F: Family,
440            E: Context,
441            K: $key_bound,
442            V: $val_bound + Send + Sync + 'static,
443            H: Hasher,
444            T: Translator + Send + Sync + 'static,
445            T::Key: Send + Sync,
446            S: Strategy,
447        {
448            type Family = F;
449            type Digest = H::Digest;
450            type Op = $op<F, K, V>;
451            type Error = qmdb::Error<F>;
452
453            async fn get_operations(
454                &self,
455                op_count: Location<Self::Family>,
456                start_loc: Location<Self::Family>,
457                max_ops: NonZeroU64,
458                include_pinned_nodes: bool,
459                _cancel_rx: oneshot::Receiver<()>,
460            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
461                let guard = self.read().await;
462                let db = guard.as_ref().ok_or(qmdb::Error::KeyNotFound)?;
463                fetch_operations(
464                    op_count,
465                    start_loc,
466                    max_ops,
467                    include_pinned_nodes,
468                    |op_count, start_loc, max_ops| {
469                        db.historical_proof(op_count, start_loc, max_ops)
470                    },
471                    |start_loc| db.pinned_nodes_at(start_loc),
472                )
473                .await
474            }
475        }
476    };
477}
478
479// Immutable Fixed
480impl_resolver_immutable!(ImmutableFixedDb, ImmutableFixedOp, FixedValue, Array);
481
482// Immutable Variable
483impl_resolver_immutable!(ImmutableVariableDb, ImmutableVariableOp, VariableValue, Key);
484
485// Keyless types have no key or translator, so they need their own macro.
486macro_rules! impl_resolver_keyless {
487    ($db:ident, $op:ident, $val_bound:ident) => {
488        impl<F, E, V, H, S> Resolver for Arc<$db<F, E, V, H, S>>
489        where
490            F: Family,
491            E: Context,
492            V: $val_bound + Send + Sync + 'static,
493            H: Hasher,
494            S: Strategy,
495        {
496            type Family = F;
497            type Digest = H::Digest;
498            type Op = $op<F, V>;
499            type Error = qmdb::Error<F>;
500
501            async fn get_operations(
502                &self,
503                op_count: Location<Self::Family>,
504                start_loc: Location<Self::Family>,
505                max_ops: NonZeroU64,
506                include_pinned_nodes: bool,
507                _cancel_rx: oneshot::Receiver<()>,
508            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
509                fetch_operations(
510                    op_count,
511                    start_loc,
512                    max_ops,
513                    include_pinned_nodes,
514                    |op_count, start_loc, max_ops| {
515                        self.historical_proof(op_count, start_loc, max_ops)
516                    },
517                    |start_loc| self.pinned_nodes_at(start_loc),
518                )
519                .await
520            }
521        }
522
523        impl<F, E, V, H, S> Resolver for Arc<AsyncRwLock<$db<F, E, V, H, S>>>
524        where
525            F: Family,
526            E: Context,
527            V: $val_bound + Send + Sync + 'static,
528            H: Hasher,
529            S: Strategy,
530        {
531            type Family = F;
532            type Digest = H::Digest;
533            type Op = $op<F, V>;
534            type Error = qmdb::Error<F>;
535
536            async fn get_operations(
537                &self,
538                op_count: Location<Self::Family>,
539                start_loc: Location<Self::Family>,
540                max_ops: NonZeroU64,
541                include_pinned_nodes: bool,
542                _cancel_rx: oneshot::Receiver<()>,
543            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
544                let db = self.read().await;
545                fetch_operations(
546                    op_count,
547                    start_loc,
548                    max_ops,
549                    include_pinned_nodes,
550                    |op_count, start_loc, max_ops| {
551                        db.historical_proof(op_count, start_loc, max_ops)
552                    },
553                    |start_loc| db.pinned_nodes_at(start_loc),
554                )
555                .await
556            }
557        }
558
559        impl<F, E, V, H, S> Resolver for Arc<AsyncRwLock<Option<$db<F, E, V, H, S>>>>
560        where
561            F: Family,
562            E: Context,
563            V: $val_bound + Send + Sync + 'static,
564            H: Hasher,
565            S: Strategy,
566        {
567            type Family = F;
568            type Digest = H::Digest;
569            type Op = $op<F, V>;
570            type Error = qmdb::Error<F>;
571
572            async fn get_operations(
573                &self,
574                op_count: Location<Self::Family>,
575                start_loc: Location<Self::Family>,
576                max_ops: NonZeroU64,
577                include_pinned_nodes: bool,
578                _cancel_rx: oneshot::Receiver<()>,
579            ) -> Result<FetchResult<Self::Family, Self::Op, Self::Digest>, Self::Error> {
580                let guard = self.read().await;
581                let db = guard.as_ref().ok_or(qmdb::Error::KeyNotFound)?;
582                fetch_operations(
583                    op_count,
584                    start_loc,
585                    max_ops,
586                    include_pinned_nodes,
587                    |op_count, start_loc, max_ops| {
588                        db.historical_proof(op_count, start_loc, max_ops)
589                    },
590                    |start_loc| db.pinned_nodes_at(start_loc),
591                )
592                .await
593            }
594        }
595    };
596}
597
598// Keyless Fixed
599impl_resolver_keyless!(KeylessFixedDb, KeylessFixedOp, FixedValue);
600
601// Keyless Variable
602impl_resolver_keyless!(KeylessVariableDb, KeylessVariableOp, VariableValue);
603
604#[cfg(test)]
605pub(crate) mod tests {
606    use super::*;
607    use crate::{
608        merkle::mmr,
609        translator::{OneCap, TwoCap},
610    };
611    use commonware_cryptography::{sha256::Digest as ShaDigest, Sha256};
612    use commonware_parallel::Rayon;
613    use commonware_runtime::deterministic;
614    use commonware_utils::sync::AsyncRwLock;
615    use std::{marker::PhantomData, sync::Arc};
616
617    macro_rules! assert_resolver_variants {
618        ($db:ty) => {
619            assert_resolver::<Arc<$db>>();
620            assert_resolver::<Arc<AsyncRwLock<$db>>>();
621            assert_resolver::<Arc<AsyncRwLock<Option<$db>>>>();
622        };
623    }
624
625    fn assert_resolver<R: Resolver>() {}
626
627    fn empty_proof() -> Proof<mmr::Family, ShaDigest> {
628        Proof {
629            leaves: Location::new(0),
630            inactive_peaks: 0,
631            digests: vec![],
632        }
633    }
634
635    #[test]
636    fn test_fetch_result_new_has_no_success_acknowledgement() {
637        let result = FetchResult::<mmr::Family, (), ShaDigest>::new(empty_proof(), vec![], None);
638        assert!(result.callback.is_none());
639    }
640
641    #[test]
642    fn test_fetch_result_with_callback_reports_to_external_receiver() {
643        let (success_tx, mut success_rx) = oneshot::channel();
644        let result = FetchResult::<mmr::Family, (), ShaDigest>::with_callback(
645            empty_proof(),
646            vec![],
647            None,
648            success_tx,
649        );
650        assert!(result.callback.expect("success sender").send(true).is_ok());
651        assert_eq!(success_rx.try_recv(), Ok(true));
652    }
653
654    /// A resolver that always fails.
655    #[derive(Clone)]
656    pub struct FailResolver<F: Family, Op, D> {
657        _phantom: PhantomData<(F, Op, D)>,
658    }
659
660    impl<F, Op, D> Resolver for FailResolver<F, Op, D>
661    where
662        F: Family,
663        D: Digest,
664        Op: Send + Sync + Clone + 'static,
665    {
666        type Family = F;
667        type Digest = D;
668        type Op = Op;
669        type Error = qmdb::Error<F>;
670
671        async fn get_operations(
672            &self,
673            _op_count: Location<F>,
674            _start_loc: Location<F>,
675            _max_ops: NonZeroU64,
676            _include_pinned_nodes: bool,
677            _cancel: oneshot::Receiver<()>,
678        ) -> Result<FetchResult<F, Op, D>, qmdb::Error<F>> {
679            Err(qmdb::Error::KeyNotFound) // Arbitrary dummy error
680        }
681    }
682
683    impl<F: Family, Op, D> FailResolver<F, Op, D> {
684        pub fn new() -> Self {
685            Self {
686                _phantom: PhantomData,
687            }
688        }
689    }
690
691    #[test]
692    fn test_all_qmdb_variants_implement_strategy_resolvers() {
693        type AnyOrderedFixed = crate::qmdb::any::ordered::fixed::Db<
694            mmr::Family,
695            deterministic::Context,
696            ShaDigest,
697            ShaDigest,
698            Sha256,
699            OneCap,
700            Rayon,
701        >;
702        type AnyOrderedVariable = crate::qmdb::any::ordered::variable::Db<
703            mmr::Family,
704            deterministic::Context,
705            ShaDigest,
706            Vec<u8>,
707            Sha256,
708            OneCap,
709            Rayon,
710        >;
711        type AnyUnorderedFixed = crate::qmdb::any::unordered::fixed::Db<
712            mmr::Family,
713            deterministic::Context,
714            ShaDigest,
715            ShaDigest,
716            Sha256,
717            TwoCap,
718            Rayon,
719        >;
720        type AnyUnorderedVariable = crate::qmdb::any::unordered::variable::Db<
721            mmr::Family,
722            deterministic::Context,
723            ShaDigest,
724            Vec<u8>,
725            Sha256,
726            TwoCap,
727            Rayon,
728        >;
729        type CurrentOrderedFixed = crate::qmdb::current::ordered::fixed::Db<
730            mmr::Family,
731            deterministic::Context,
732            ShaDigest,
733            ShaDigest,
734            Sha256,
735            OneCap,
736            32,
737            Rayon,
738        >;
739        type CurrentOrderedVariable = crate::qmdb::current::ordered::variable::Db<
740            mmr::Family,
741            deterministic::Context,
742            ShaDigest,
743            Vec<u8>,
744            Sha256,
745            OneCap,
746            32,
747            Rayon,
748        >;
749        type CurrentUnorderedFixed = crate::qmdb::current::unordered::fixed::Db<
750            mmr::Family,
751            deterministic::Context,
752            ShaDigest,
753            ShaDigest,
754            Sha256,
755            TwoCap,
756            32,
757            Rayon,
758        >;
759        type CurrentUnorderedVariable = crate::qmdb::current::unordered::variable::Db<
760            mmr::Family,
761            deterministic::Context,
762            ShaDigest,
763            Vec<u8>,
764            Sha256,
765            TwoCap,
766            32,
767            Rayon,
768        >;
769        type ImmutableFixed = crate::qmdb::immutable::fixed::Db<
770            mmr::Family,
771            deterministic::Context,
772            ShaDigest,
773            ShaDigest,
774            Sha256,
775            TwoCap,
776            Rayon,
777        >;
778        type ImmutableVariable = crate::qmdb::immutable::variable::Db<
779            mmr::Family,
780            deterministic::Context,
781            ShaDigest,
782            Vec<u8>,
783            Sha256,
784            TwoCap,
785            Rayon,
786        >;
787        type KeylessFixed = crate::qmdb::keyless::fixed::Db<
788            mmr::Family,
789            deterministic::Context,
790            ShaDigest,
791            Sha256,
792            Rayon,
793        >;
794        type KeylessVariable = crate::qmdb::keyless::variable::Db<
795            mmr::Family,
796            deterministic::Context,
797            Vec<u8>,
798            Sha256,
799            Rayon,
800        >;
801
802        assert_resolver_variants!(AnyOrderedFixed);
803        assert_resolver_variants!(AnyOrderedVariable);
804        assert_resolver_variants!(AnyUnorderedFixed);
805        assert_resolver_variants!(AnyUnorderedVariable);
806        assert_resolver_variants!(CurrentOrderedFixed);
807        assert_resolver_variants!(CurrentOrderedVariable);
808        assert_resolver_variants!(CurrentUnorderedFixed);
809        assert_resolver_variants!(CurrentUnorderedVariable);
810        assert_resolver_variants!(ImmutableFixed);
811        assert_resolver_variants!(ImmutableVariable);
812        assert_resolver_variants!(KeylessFixed);
813        assert_resolver_variants!(KeylessVariable);
814    }
815}