1use crate::{
7 index::Ordered as OrderedIndex,
8 journal::contiguous::{Contiguous, Mutable, Reader},
9 kv,
10 mmr::Location,
11 qmdb::{
12 any::{
13 ordered::{Operation, Update},
14 ValueEncoding,
15 },
16 current::proof::OperationProof,
17 operation::Key,
18 Error,
19 },
20};
21use commonware_codec::Codec;
22use commonware_cryptography::{Digest, Hasher};
23use commonware_runtime::{Clock, Metrics, Storage};
24use futures::stream::Stream;
25
26#[derive(Clone, Eq, PartialEq, Debug)]
28pub struct KeyValueProof<K: Key, D: Digest, const N: usize> {
29 pub proof: OperationProof<D, N>,
30 pub next_key: K,
31}
32
33pub type Db<E, C, K, V, I, H, const N: usize> =
38 crate::qmdb::current::db::Db<E, C, I, H, Update<K, V>, N>;
39
40impl<
42 E: Storage + Clock + Metrics,
43 C: Contiguous<Item = Operation<K, V>>,
44 K: Key,
45 V: ValueEncoding,
46 I: OrderedIndex<Value = Location>,
47 H: Hasher,
48 const N: usize,
49 > Db<E, C, K, V, I, H, N>
50where
51 Operation<K, V>: Codec,
52 V::Value: Send + Sync,
53{
54 pub async fn get(&self, key: &K) -> Result<Option<V::Value>, Error> {
56 self.any.get(key).await
57 }
58
59 pub fn verify_key_value_proof(
62 hasher: &mut H,
63 key: K,
64 value: V::Value,
65 proof: &KeyValueProof<K, H::Digest, N>,
66 root: &H::Digest,
67 ) -> bool {
68 let op = Operation::Update(Update {
69 key,
70 value,
71 next_key: proof.next_key.clone(),
72 });
73
74 proof.proof.verify(hasher, op, root)
75 }
76
77 pub async fn get_span(&self, key: &K) -> Result<Option<(Location, Update<K, V>)>, Error> {
80 self.any.get_span(key).await
81 }
82
83 pub async fn stream_range<'a>(
86 &'a self,
87 start: K,
88 ) -> Result<impl Stream<Item = Result<(K, V::Value), Error>> + 'a, Error>
89 where
90 V: 'a,
91 {
92 self.any.stream_range(start).await
93 }
94
95 pub fn verify_exclusion_proof(
98 hasher: &mut H,
99 key: &K,
100 proof: &super::ExclusionProof<K, V, H::Digest, N>,
101 root: &H::Digest,
102 ) -> bool {
103 let (op_proof, op) = match proof {
104 super::ExclusionProof::KeyValue(op_proof, data) => {
105 if data.key == *key {
106 return false;
108 }
109 if !crate::qmdb::any::db::Db::<E, C, I, H, Update<K, V>>::span_contains(
110 &data.key,
111 &data.next_key,
112 key,
113 ) {
114 return false;
117 }
118
119 (op_proof, Operation::Update(data.clone()))
120 }
121 super::ExclusionProof::Commit(op_proof, metadata) => {
122 let floor_loc = op_proof.loc;
126 (
127 op_proof,
128 Operation::CommitFloor(metadata.clone(), floor_loc),
129 )
130 }
131 };
132
133 op_proof.verify(hasher, op, root)
134 }
135}
136
137impl<
138 E: Storage + Clock + Metrics,
139 C: Mutable<Item = Operation<K, V>>,
140 K: Key,
141 V: ValueEncoding,
142 I: OrderedIndex<Value = Location>,
143 H: Hasher,
144 const N: usize,
145 > Db<E, C, K, V, I, H, N>
146where
147 Operation<K, V>: Codec,
148 V::Value: Send + Sync,
149{
150 pub async fn key_value_proof(
158 &self,
159 hasher: &mut H,
160 key: K,
161 ) -> Result<KeyValueProof<K, H::Digest, N>, Error> {
162 let op_loc = self.any.get_with_loc(&key).await?;
163 let Some((data, loc)) = op_loc else {
164 return Err(Error::KeyNotFound);
165 };
166 let proof = self.operation_proof(hasher, loc).await?;
167
168 Ok(KeyValueProof {
169 proof,
170 next_key: data.next_key,
171 })
172 }
173
174 pub async fn exclusion_proof(
180 &self,
181 hasher: &mut H,
182 key: &K,
183 ) -> Result<super::ExclusionProof<K, V, H::Digest, N>, Error> {
184 match self.any.get_span(key).await? {
185 Some((loc, key_data)) => {
186 if key_data.key == *key {
187 return Err(Error::KeyExists);
189 }
190 let op_proof = self.operation_proof(hasher, loc).await?;
191 Ok(super::ExclusionProof::KeyValue(op_proof, key_data))
192 }
193 None => {
194 let op = self
198 .any
199 .log
200 .reader()
201 .await
202 .read(*self.any.last_commit_loc)
203 .await?;
204 let Operation::CommitFloor(value, floor) = op else {
205 unreachable!("last_commit_loc should always point to a CommitFloor");
206 };
207 assert_eq!(
208 floor, self.any.last_commit_loc,
209 "inconsistent commit floor: expected last_commit_loc={}, got floor={}",
210 self.any.last_commit_loc, floor
211 );
212 let op_proof = self
213 .operation_proof(hasher, self.any.last_commit_loc)
214 .await?;
215 Ok(super::ExclusionProof::Commit(op_proof, value))
216 }
217 }
218 }
219}
220
221impl<
223 E: Storage + Clock + Metrics,
224 C: Contiguous<Item = Operation<K, V>>,
225 K: Key,
226 V: ValueEncoding,
227 I: OrderedIndex<Value = Location>,
228 H: Hasher,
229 const N: usize,
230 > kv::Gettable for Db<E, C, K, V, I, H, N>
231where
232 Operation<K, V>: Codec,
233 V::Value: Send + Sync,
234{
235 type Key = K;
236 type Value = V::Value;
237 type Error = Error;
238
239 async fn get(&self, key: &Self::Key) -> Result<Option<Self::Value>, Self::Error> {
240 self.get(key).await
241 }
242}