use crate::{
merkle::mmr::{self, Location, Proof},
qmdb::{
self,
any::{
ordered::{
fixed::{Db as OrderedFixedDb, Operation as OrderedFixedOperation},
variable::{Db as OrderedVariableDb, Operation as OrderedVariableOperation},
},
unordered::{
fixed::{Db as FixedDb, Operation as FixedOperation},
variable::{Db as VariableDb, Operation as VariableOperation},
},
FixedValue, VariableValue,
},
immutable::{
fixed::{Db as ImmutableFixedDb, Operation as ImmutableFixedOp},
variable::{Db as ImmutableVariableDb, Operation as ImmutableVariableOp},
},
operation::Key,
},
translator::Translator,
Context,
};
use commonware_cryptography::{Digest, Hasher};
use commonware_utils::{channel::oneshot, sync::AsyncRwLock, Array};
use std::{future::Future, num::NonZeroU64, sync::Arc};
pub struct FetchResult<Op, D: Digest> {
pub proof: Proof<D>,
pub operations: Vec<Op>,
pub success_tx: oneshot::Sender<bool>,
pub pinned_nodes: Option<Vec<D>>,
}
impl<Op: std::fmt::Debug, D: Digest> std::fmt::Debug for FetchResult<Op, D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FetchResult")
.field("proof", &self.proof)
.field("operations", &self.operations)
.field("success_tx", &"<callback>")
.field("pinned_nodes", &self.pinned_nodes)
.finish()
}
}
pub trait Resolver: Send + Sync + Clone + 'static {
type Digest: Digest;
type Op;
type Error: std::error::Error + Send + 'static;
#[allow(clippy::type_complexity)]
fn get_operations<'a>(
&'a self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
cancel_rx: oneshot::Receiver<()>,
) -> impl Future<Output = Result<FetchResult<Self::Op, Self::Digest>, Self::Error>> + Send + 'a;
}
macro_rules! impl_resolver {
($db:ident, $op:ident, $val_bound:ident) => {
impl<E, K, V, H, T> Resolver
for Arc<$db<crate::merkle::mmr::Family, E, K, V, H, T>>
where
E: Context,
K: Array,
V: $val_bound + Send + Sync + 'static,
H: Hasher,
T: Translator + Send + Sync + 'static,
T::Key: Send + Sync,
{
type Digest = H::Digest;
type Op = $op<crate::merkle::mmr::Family, K, V>;
type Error = qmdb::Error<crate::merkle::mmr::Family>;
async fn get_operations(
&self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
_cancel_rx: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, Self::Error> {
let (proof, operations) =
self.historical_proof(op_count, start_loc, max_ops).await?;
let pinned_nodes = if include_pinned_nodes {
Some(self.pinned_nodes_at(start_loc).await?)
} else {
None
};
Ok(FetchResult {
proof,
operations,
success_tx: oneshot::channel().0,
pinned_nodes,
})
}
}
impl<E, K, V, H, T> Resolver
for Arc<AsyncRwLock<$db<crate::merkle::mmr::Family, E, K, V, H, T>>>
where
E: Context,
K: Array,
V: $val_bound + Send + Sync + 'static,
H: Hasher,
T: Translator + Send + Sync + 'static,
T::Key: Send + Sync,
{
type Digest = H::Digest;
type Op = $op<crate::merkle::mmr::Family, K, V>;
type Error = qmdb::Error<crate::merkle::mmr::Family>;
async fn get_operations(
&self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
_cancel_rx: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, qmdb::Error<crate::merkle::mmr::Family>> {
let db = self.read().await;
let (proof, operations) = db.historical_proof(op_count, start_loc, max_ops).await?;
let pinned_nodes = if include_pinned_nodes {
Some(db.pinned_nodes_at(start_loc).await?)
} else {
None
};
Ok(FetchResult {
proof,
operations,
success_tx: oneshot::channel().0,
pinned_nodes,
})
}
}
impl<E, K, V, H, T> Resolver
for Arc<AsyncRwLock<Option<$db<crate::merkle::mmr::Family, E, K, V, H, T>>>>
where
E: Context,
K: Array,
V: $val_bound + Send + Sync + 'static,
H: Hasher,
T: Translator + Send + Sync + 'static,
T::Key: Send + Sync,
{
type Digest = H::Digest;
type Op = $op<crate::merkle::mmr::Family, K, V>;
type Error = qmdb::Error<crate::merkle::mmr::Family>;
async fn get_operations(
&self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
_cancel_rx: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, qmdb::Error<crate::merkle::mmr::Family>> {
let guard = self.read().await;
let db = guard.as_ref().ok_or(qmdb::Error::KeyNotFound)?;
let (proof, operations) = db.historical_proof(op_count, start_loc, max_ops).await?;
let pinned_nodes = if include_pinned_nodes {
Some(db.pinned_nodes_at(start_loc).await?)
} else {
None
};
Ok(FetchResult {
proof,
operations,
success_tx: oneshot::channel().0,
pinned_nodes,
})
}
}
};
}
impl_resolver!(FixedDb, FixedOperation, FixedValue);
impl_resolver!(VariableDb, VariableOperation, VariableValue);
impl_resolver!(OrderedFixedDb, OrderedFixedOperation, FixedValue);
impl_resolver!(OrderedVariableDb, OrderedVariableOperation, VariableValue);
macro_rules! impl_resolver_immutable {
($db:ident, $op:ident, $val_bound:ident, $key_bound:path) => {
impl<E, K, V, H, T> Resolver for Arc<$db<mmr::Family, E, K, V, H, T>>
where
E: Context,
K: $key_bound,
V: $val_bound + Send + Sync + 'static,
H: Hasher,
T: Translator + Send + Sync + 'static,
T::Key: Send + Sync,
{
type Digest = H::Digest;
type Op = $op<K, V>;
type Error = qmdb::Error<mmr::Family>;
async fn get_operations(
&self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
_cancel_rx: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, Self::Error> {
let (proof, operations) =
self.historical_proof(op_count, start_loc, max_ops).await?;
let pinned_nodes = if include_pinned_nodes {
Some(self.pinned_nodes_at(start_loc).await?)
} else {
None
};
Ok(FetchResult {
proof,
operations,
success_tx: oneshot::channel().0,
pinned_nodes,
})
}
}
impl<E, K, V, H, T> Resolver for Arc<AsyncRwLock<$db<mmr::Family, E, K, V, H, T>>>
where
E: Context,
K: $key_bound,
V: $val_bound + Send + Sync + 'static,
H: Hasher,
T: Translator + Send + Sync + 'static,
T::Key: Send + Sync,
{
type Digest = H::Digest;
type Op = $op<K, V>;
type Error = qmdb::Error<mmr::Family>;
async fn get_operations(
&self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
_cancel_rx: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, qmdb::Error<mmr::Family>> {
let db = self.read().await;
let (proof, operations) = db.historical_proof(op_count, start_loc, max_ops).await?;
let pinned_nodes = if include_pinned_nodes {
Some(db.pinned_nodes_at(start_loc).await?)
} else {
None
};
Ok(FetchResult {
proof,
operations,
success_tx: oneshot::channel().0,
pinned_nodes,
})
}
}
impl<E, K, V, H, T> Resolver for Arc<AsyncRwLock<Option<$db<mmr::Family, E, K, V, H, T>>>>
where
E: Context,
K: $key_bound,
V: $val_bound + Send + Sync + 'static,
H: Hasher,
T: Translator + Send + Sync + 'static,
T::Key: Send + Sync,
{
type Digest = H::Digest;
type Op = $op<K, V>;
type Error = qmdb::Error<mmr::Family>;
async fn get_operations(
&self,
op_count: Location,
start_loc: Location,
max_ops: NonZeroU64,
include_pinned_nodes: bool,
_cancel_rx: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, qmdb::Error<mmr::Family>> {
let guard = self.read().await;
let db = guard.as_ref().ok_or(qmdb::Error::KeyNotFound)?;
let (proof, operations) = db.historical_proof(op_count, start_loc, max_ops).await?;
let pinned_nodes = if include_pinned_nodes {
Some(db.pinned_nodes_at(start_loc).await?)
} else {
None
};
Ok(FetchResult {
proof,
operations,
success_tx: oneshot::channel().0,
pinned_nodes,
})
}
}
};
}
impl_resolver_immutable!(ImmutableFixedDb, ImmutableFixedOp, FixedValue, Array);
impl_resolver_immutable!(ImmutableVariableDb, ImmutableVariableOp, VariableValue, Key);
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use std::marker::PhantomData;
#[derive(Clone)]
pub struct FailResolver<Op, D> {
_phantom: PhantomData<(Op, D)>,
}
impl<Op, D> Resolver for FailResolver<Op, D>
where
D: Digest,
Op: Send + Sync + Clone + 'static,
{
type Digest = D;
type Op = Op;
type Error = qmdb::Error<crate::merkle::mmr::Family>;
async fn get_operations(
&self,
_op_count: Location,
_start_loc: Location,
_max_ops: NonZeroU64,
_include_pinned_nodes: bool,
_cancel: oneshot::Receiver<()>,
) -> Result<FetchResult<Self::Op, Self::Digest>, qmdb::Error<crate::merkle::mmr::Family>>
{
Err(qmdb::Error::KeyNotFound) }
}
impl<Op, D> FailResolver<Op, D> {
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
}