use super::proof::constant::{ExclusionProof, KeyValueProof};
use crate::{
Context,
index::Ordered as OrderedIndex,
journal::contiguous::{Contiguous, Mutable},
merkle::{self, Location},
qmdb::{
Error,
any::{
ValueEncoding,
ordered::{Operation, Update},
},
operation::Key,
},
};
use commonware_codec::Codec;
use commonware_cryptography::Hasher;
use commonware_parallel::Strategy;
use core::ops::RangeBounds;
use futures::stream::Stream;
pub type Db<F, E, C, K, V, I, H, const N: usize, S> =
crate::qmdb::current::db::Db<F, E, C, I, H, Update<K, V>, N, S>;
impl<
F: merkle::Graftable,
E: Context,
C: Contiguous<Item = Operation<F, K, V>>,
K: Key,
V: ValueEncoding,
I: OrderedIndex<Value = Location<F>>,
H: Hasher,
const N: usize,
S: Strategy,
> Db<F, E, C, K, V, I, H, N, S>
where
Operation<F, K, V>: Codec,
{
pub async fn get(&self, key: &K) -> Result<Option<V::Value>, Error<F>> {
self.any.get(key).await
}
pub async fn get_span(&self, key: &K) -> Result<Option<(Location<F>, Update<K, V>)>, Error<F>> {
self.any.get_span(key).await
}
pub async fn get_next_key(&self, key: &K) -> Result<Option<K>, Error<F>> {
self.any.get_next_key(key).await
}
pub async fn get_prev_key(&self, key: &K) -> Result<Option<K>, Error<F>> {
self.any.get_prev_key(key).await
}
pub fn stream_range<'a>(
&'a self,
range: impl RangeBounds<K> + Send + 'a,
) -> impl Stream<Item = Result<(K, V::Value), Error<F>>> + Send + 'a {
self.any.stream_range(range)
}
pub fn keys<'a>(
&'a self,
range: impl RangeBounds<K> + Send + 'a,
) -> impl Stream<Item = Result<K, Error<F>>> + Send + 'a {
self.any.keys(range)
}
}
impl<
F: merkle::Graftable,
E: Context,
C: Mutable<Item = Operation<F, K, V>>,
K: Key,
V: ValueEncoding,
I: OrderedIndex<Value = Location<F>>,
H: Hasher,
const N: usize,
S: Strategy,
> Db<F, E, C, K, V, I, H, N, S>
where
Operation<F, K, V>: Codec,
{
pub async fn key_value_proof(
&self,
key: K,
) -> Result<KeyValueProof<F, K, H::Digest, N>, Error<F>> {
let op_loc = self.any.get_with_loc(&key).await?;
let Some((data, loc)) = op_loc else {
return Err(Error::<F>::KeyNotFound);
};
let proof = self.operation_proof(loc).await?;
Ok(KeyValueProof {
proof,
next_key: data.next_key,
})
}
pub async fn exclusion_proof(
&self,
key: &K,
) -> Result<ExclusionProof<F, K, V, H::Digest, N>, Error<F>> {
match self.any.get_span(key).await? {
Some((loc, key_data)) => {
if key_data.key == *key {
return Err(Error::<F>::KeyExists);
}
let op_proof = self.operation_proof(loc).await?;
Ok(ExclusionProof::KeyValue(op_proof, key_data))
}
None => {
let last_commit_loc = self.any.log.size() - 1;
let op = self.any.log.read(*last_commit_loc).await?;
let Operation::CommitFloor(value, floor) = op else {
unreachable!("last_commit_loc should always point to a CommitFloor");
};
assert_eq!(
floor, last_commit_loc,
"inconsistent commit floor: expected last_commit_loc={}, got floor={}",
last_commit_loc, floor
);
let op_proof = self.operation_proof(last_commit_loc).await?;
Ok(ExclusionProof::Commit(op_proof, value))
}
}
}
}