1use crate::{
2 Context,
3 journal::{authenticated, contiguous::Contiguous},
4 merkle::{Family, Location, MAX_PINNED_NODES, MAX_PROOF_DIGESTS_PER_ELEMENT, Proof},
5 qmdb::{
6 self,
7 operation::Floored,
8 sync::{ServeError, source},
9 },
10};
11use bytes::BufMut;
12use commonware_codec::{
13 Buf, EncodeShared, EncodeSize, Error as CodecError, FixedSize, MaxEncodeSize, Read,
14 ReadExt as _, ReadRangeExt as _, Write, util::max_items_size,
15};
16use commonware_cryptography::{Digest, Hasher};
17use commonware_parallel::Strategy;
18use commonware_utils::{
19 Span,
20 channel::{mpsc, oneshot},
21 sync::{AsyncRwLock, TracedAsyncRwLock},
22};
23use std::{cmp::Ordering, future::Future, num::NonZeroU64, sync::Arc};
24
25pub enum Request<F: Family> {
27 Operations {
29 size: Location<F>,
31 start: Location<F>,
33 max_ops: NonZeroU64,
35 },
36 Boundary {
40 size: Location<F>,
42 start: Location<F>,
44 },
45}
46
47impl<F: Family> Request<F> {
48 pub const fn size(&self) -> Location<F> {
50 match self {
51 Self::Operations { size, .. } | Self::Boundary { size, .. } => *size,
52 }
53 }
54
55 pub const fn start(&self) -> Location<F> {
57 match self {
58 Self::Operations { start, .. } | Self::Boundary { start, .. } => *start,
59 }
60 }
61
62 pub const fn max_ops(&self) -> NonZeroU64 {
64 match self {
65 Self::Operations { max_ops, .. } => *max_ops,
66 Self::Boundary { .. } => NonZeroU64::MIN,
67 }
68 }
69
70 fn order_key(&self) -> (u64, u64, u64, bool) {
72 (
73 *self.size(),
74 *self.start(),
75 self.max_ops().get(),
76 matches!(self, Self::Boundary { .. }),
77 )
78 }
79}
80
81impl<F: Family> Clone for Request<F> {
82 fn clone(&self) -> Self {
83 *self
84 }
85}
86
87impl<F: Family> Copy for Request<F> {}
88
89impl<F: Family> PartialEq for Request<F> {
90 fn eq(&self, other: &Self) -> bool {
91 self.order_key() == other.order_key()
92 }
93}
94
95impl<F: Family> Eq for Request<F> {}
96
97impl<F: Family> PartialOrd for Request<F> {
98 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
99 Some(self.cmp(other))
100 }
101}
102
103impl<F: Family> Ord for Request<F> {
104 fn cmp(&self, other: &Self) -> Ordering {
105 self.order_key().cmp(&other.order_key())
106 }
107}
108
109impl<F: Family> std::hash::Hash for Request<F> {
110 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
111 self.order_key().hash(state);
112 }
113}
114
115impl<F: Family> std::fmt::Debug for Request<F> {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 match self {
118 Self::Operations {
119 size,
120 start,
121 max_ops,
122 } => f
123 .debug_struct("Operations")
124 .field("size", size)
125 .field("start", start)
126 .field("max_ops", max_ops)
127 .finish(),
128 Self::Boundary { size, start } => f
129 .debug_struct("Boundary")
130 .field("size", size)
131 .field("start", start)
132 .finish(),
133 }
134 }
135}
136
137impl<F: Family> std::fmt::Display for Request<F> {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 match self {
140 Self::Operations {
141 size,
142 start,
143 max_ops,
144 } => write!(f, "Operations(size={size}, start={start}, max={max_ops})"),
145 Self::Boundary { size, start } => write!(f, "Boundary(size={size}, start={start})"),
146 }
147 }
148}
149
150impl<F: Family> Write for Request<F> {
151 fn write(&self, buf: &mut impl BufMut) {
152 match self {
153 Self::Operations {
154 size,
155 start,
156 max_ops,
157 } => {
158 0u8.write(buf);
159 size.write(buf);
160 start.write(buf);
161 max_ops.write(buf);
162 }
163 Self::Boundary { size, start } => {
164 1u8.write(buf);
165 size.write(buf);
166 start.write(buf);
167 }
168 }
169 }
170}
171
172impl<F: Family> EncodeSize for Request<F> {
173 fn encode_size(&self) -> usize {
174 1 + match self {
175 Self::Operations {
176 size,
177 start,
178 max_ops,
179 } => size.encode_size() + start.encode_size() + max_ops.encode_size(),
180 Self::Boundary { size, start } => size.encode_size() + start.encode_size(),
181 }
182 }
183}
184
185impl<F: Family> MaxEncodeSize for Request<F> {
186 const MAX_ENCODE_SIZE: usize = u8::SIZE + 2 * Location::<F>::MAX_ENCODE_SIZE + NonZeroU64::SIZE;
188}
189
190impl<F: Family> Read for Request<F> {
191 type Cfg = ();
192
193 fn read_cfg(buf: &mut impl Buf, _: &()) -> std::result::Result<Self, CodecError> {
194 let request = match u8::read(buf)? {
195 0 => Self::Operations {
196 size: Location::<F>::read(buf)?,
197 start: Location::<F>::read(buf)?,
198 max_ops: NonZeroU64::read(buf)?,
199 },
200 1 => Self::Boundary {
201 size: Location::<F>::read(buf)?,
202 start: Location::<F>::read(buf)?,
203 },
204 d => return Err(CodecError::InvalidEnum(d)),
205 };
206 if request.start() >= request.size() {
207 return Err(CodecError::Invalid("Request", "start >= size"));
208 }
209 Ok(request)
210 }
211}
212
213impl<F: Family> Span for Request<F> {}
214
215#[cfg(feature = "arbitrary")]
216impl<F: Family> arbitrary::Arbitrary<'_> for Request<F> {
217 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
218 let size = u.int_in_range(1..=*F::MAX_LEAVES)?;
219 let start = u.int_in_range(0..=size - 1)?;
220 let size = Location::new(size);
221 let start = Location::new(start);
222 Ok(if u.arbitrary()? {
223 Self::Boundary { size, start }
224 } else {
225 Self::Operations {
226 size,
227 start,
228 max_ops: u.arbitrary()?,
229 }
230 })
231 }
232}
233
234pub enum Response<F: Family, Op, D: Digest> {
240 Operations {
242 proof: Proof<F, D>,
244 operations: Vec<Op>,
246 },
247 Boundary {
249 proof: Proof<F, D>,
251 op: Op,
253 pinned_nodes: Vec<D>,
255 },
256}
257
258impl<F: Family, Op, D: Digest> Response<F, Op, D> {
259 pub const fn proof(&self) -> &Proof<F, D> {
261 match self {
262 Self::Operations { proof, .. } | Self::Boundary { proof, .. } => proof,
263 }
264 }
265}
266
267impl<F: Family, Op: Clone, D: Digest> Clone for Response<F, Op, D> {
268 fn clone(&self) -> Self {
269 match self {
270 Self::Operations { proof, operations } => Self::Operations {
271 proof: proof.clone(),
272 operations: operations.clone(),
273 },
274 Self::Boundary {
275 proof,
276 op,
277 pinned_nodes,
278 } => Self::Boundary {
279 proof: proof.clone(),
280 op: op.clone(),
281 pinned_nodes: pinned_nodes.clone(),
282 },
283 }
284 }
285}
286
287impl<F: Family, Op: std::fmt::Debug, D: Digest> std::fmt::Debug for Response<F, Op, D> {
288 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289 match self {
290 Self::Operations { proof, operations } => f
291 .debug_struct("Operations")
292 .field("proof", proof)
293 .field("operations", operations)
294 .finish(),
295 Self::Boundary {
296 proof,
297 op,
298 pinned_nodes,
299 } => f
300 .debug_struct("Boundary")
301 .field("proof", proof)
302 .field("op", op)
303 .field("pinned_nodes", pinned_nodes)
304 .finish(),
305 }
306 }
307}
308
309impl<F: Family, Op: Write, D: Digest> Write for Response<F, Op, D> {
310 fn write(&self, buf: &mut impl BufMut) {
311 match self {
312 Self::Operations { proof, operations } => {
313 0u8.write(buf);
314 proof.write(buf);
315 operations.write(buf);
316 }
317 Self::Boundary {
318 proof,
319 op,
320 pinned_nodes,
321 } => {
322 1u8.write(buf);
323 proof.write(buf);
324 op.write(buf);
325 pinned_nodes.write(buf);
326 }
327 }
328 }
329}
330
331impl<F: Family, Op: EncodeSize, D: Digest> EncodeSize for Response<F, Op, D> {
332 fn encode_size(&self) -> usize {
333 1 + match self {
334 Self::Operations { proof, operations } => {
335 proof.encode_size() + operations.encode_size()
336 }
337 Self::Boundary {
338 proof,
339 op,
340 pinned_nodes,
341 } => proof.encode_size() + op.encode_size() + pinned_nodes.encode_size(),
342 }
343 }
344}
345
346pub fn max_response_size<F: Family, D: Digest>(max_ops: usize, op: usize) -> usize {
353 let operations = Proof::<F, D>::max_encode_size(max_ops * MAX_PROOF_DIGESTS_PER_ELEMENT)
354 + max_items_size(max_ops, op);
355 let boundary = Proof::<F, D>::max_encode_size(MAX_PROOF_DIGESTS_PER_ELEMENT)
356 + op
357 + max_items_size(MAX_PINNED_NODES, D::SIZE);
358 operations.max(boundary) + u8::SIZE
359}
360
361impl<F: Family, Op: Read, D: Digest> Read for Response<F, Op, D> {
362 type Cfg = (usize, Op::Cfg);
364
365 fn read_cfg(
366 buf: &mut impl Buf,
367 (max_ops, op_cfg): &Self::Cfg,
368 ) -> std::result::Result<Self, CodecError> {
369 match u8::read(buf)? {
370 0 => {
371 let max_proof_digests = max_ops.saturating_mul(MAX_PROOF_DIGESTS_PER_ELEMENT);
372 let proof = Proof::<F, D>::read_cfg(buf, &max_proof_digests)?;
373 let operations = Vec::<Op>::read_cfg(buf, &((..=*max_ops).into(), op_cfg.clone()))?;
374 Ok(Self::Operations { proof, operations })
375 }
376 1 => {
377 let proof = Proof::<F, D>::read_cfg(buf, &MAX_PROOF_DIGESTS_PER_ELEMENT)?;
378 let op = Op::read_cfg(buf, op_cfg)?;
379 let pinned_nodes = Vec::<D>::read_range(buf, ..=MAX_PINNED_NODES)?;
380 Ok(Self::Boundary {
381 proof,
382 op,
383 pinned_nodes,
384 })
385 }
386 d => Err(CodecError::InvalidEnum(d)),
387 }
388 }
389}
390
391#[cfg(feature = "arbitrary")]
392impl<F: Family, Op, D: Digest> arbitrary::Arbitrary<'_> for Response<F, Op, D>
393where
394 Op: for<'a> arbitrary::Arbitrary<'a>,
395 D: for<'a> arbitrary::Arbitrary<'a>,
396{
397 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
398 Ok(if u.arbitrary()? {
399 Self::Boundary {
400 proof: u.arbitrary()?,
401 op: u.arbitrary()?,
402 pinned_nodes: u.arbitrary()?,
403 }
404 } else {
405 Self::Operations {
406 proof: u.arbitrary()?,
407 operations: u.arbitrary()?,
408 }
409 })
410 }
411}
412
413pub struct Feedback<R> {
418 sender: oneshot::Sender<bool>,
419 receiver: mpsc::Receiver<(R, oneshot::Sender<bool>)>,
420}
421
422impl<R> Feedback<R> {
423 pub const fn new(
425 sender: oneshot::Sender<bool>,
426 receiver: mpsc::Receiver<(R, oneshot::Sender<bool>)>,
427 ) -> Self {
428 Self { sender, receiver }
429 }
430
431 pub fn accept(self) {
433 let _ = self.sender.send(true);
434 }
435
436 pub async fn reject(self) -> Option<(R, Self)> {
438 let Self {
439 sender,
440 mut receiver,
441 } = self;
442 sender.send(false).ok()?;
443 let (response, sender) = receiver.recv().await?;
444 Some((response, Self { sender, receiver }))
445 }
446}
447
448pub type ResponseOf<S> = Response<<S as Source>::Family, <S as Source>::Op, <S as Source>::Digest>;
450
451pub type Result<S> =
453 std::result::Result<(ResponseOf<S>, Option<Feedback<ResponseOf<S>>>), <S as Source>::Error>;
454
455pub trait Source: Send + Sync {
457 type Family: Family;
459
460 type Digest: Digest;
462
463 type Op;
465
466 type Error: std::error::Error + Send + 'static;
468
469 fn serve(
473 &self,
474 request: Request<Self::Family>,
475 ) -> impl Future<Output = source::Result<Self>> + Send;
476}
477
478impl<T> Source for Arc<T>
479where
480 T: Source + ?Sized,
481{
482 type Family = T::Family;
483 type Digest = T::Digest;
484 type Op = T::Op;
485 type Error = T::Error;
486
487 fn serve(
488 &self,
489 request: Request<Self::Family>,
490 ) -> impl Future<Output = source::Result<Self>> + Send {
491 T::serve(self, request)
492 }
493}
494
495impl<T> Source for Option<T>
496where
497 T: Source,
498 ServeError<T::Family>: From<T::Error>,
499{
500 type Family = T::Family;
501 type Digest = T::Digest;
502 type Op = T::Op;
503 type Error = ServeError<T::Family>;
504
505 async fn serve(&self, request: Request<Self::Family>) -> source::Result<Self> {
506 let source = self.as_ref().ok_or(ServeError::MissingSource)?;
507 Ok(source.serve(request).await?)
508 }
509}
510
511macro_rules! impl_locked_source {
512 ($lock:ident) => {
513 impl<T> Source for $lock<T>
514 where
515 T: Source,
516 {
517 type Family = T::Family;
518 type Digest = T::Digest;
519 type Op = T::Op;
520 type Error = T::Error;
521
522 async fn serve(&self, request: Request<Self::Family>) -> source::Result<Self> {
523 self.read().await.serve(request).await
524 }
525 }
526 };
527}
528
529impl_locked_source!(AsyncRwLock);
530impl_locked_source!(TracedAsyncRwLock);
531
532impl<F, E, C, H, S> Source for authenticated::Journal<F, E, C, H, S>
533where
534 F: Family,
535 E: Context,
536 C: Contiguous<Item: EncodeShared + Floored<F>>,
537 H: Hasher,
538 S: Strategy,
539{
540 type Family = F;
541 type Digest = H::Digest;
542 type Op = C::Item;
543 type Error = qmdb::Error<F>;
544
545 #[tracing::instrument(
546 name = "qmdb.sync.serve",
547 level = "info",
548 skip_all,
549 fields(
550 size = *request.size(),
551 start = *request.start(),
552 max_ops = request.max_ops().get(),
553 ),
554 )]
555 async fn serve(&self, request: Request<F>) -> source::Result<Self> {
556 if request.size() > self.size() {
559 return Err(crate::merkle::Error::RangeOutOfBounds(request.size()).into());
560 }
561 let inactive_peaks = qmdb::inactive_peaks_at::<F, _>(self, request.size()).await?;
562 let response = match request {
563 Request::Operations {
564 size,
565 start,
566 max_ops,
567 } => {
568 let (proof, operations) = self
569 .historical_proof(size, start, max_ops, inactive_peaks)
570 .await?;
571 Response::Operations { proof, operations }
572 }
573 Request::Boundary { size, start } => {
574 let (proof, mut operations) = self
575 .historical_proof(size, start, NonZeroU64::MIN, inactive_peaks)
576 .await?;
577 let op = operations
578 .pop()
579 .ok_or(crate::merkle::Error::RangeOutOfBounds(start))?;
580 let pinned_nodes = self.merkle.pinned_nodes_at(start).await?;
581 Response::Boundary {
582 proof,
583 op,
584 pinned_nodes,
585 }
586 }
587 };
588 Ok((response, None))
589 }
590}
591
592impl<F, E, C, I, H, U, const N: usize, S> Source
593 for crate::qmdb::any::db::Db<F, E, C, I, H, U, N, S>
594where
595 F: Family,
596 E: Context,
597 C: crate::journal::contiguous::Mutable<Item = crate::qmdb::any::operation::Operation<F, U>>,
598 I: crate::index::Unordered<Value = Location<F>>,
599 H: Hasher,
600 U: crate::qmdb::any::operation::update::Update,
601 S: Strategy,
602 crate::qmdb::any::operation::Operation<F, U>: commonware_codec::Codec,
603{
604 type Family = F;
605 type Digest = H::Digest;
606 type Op = crate::qmdb::any::operation::Operation<F, U>;
607 type Error = qmdb::Error<F>;
608
609 async fn serve(&self, request: Request<F>) -> source::Result<Self> {
610 self.log.serve(request).await
611 }
612}
613
614#[cfg(test)]
615pub(crate) mod tests {
616 use super::*;
617 use crate::{
618 merkle::mmr,
619 translator::{OneCap, TwoCap},
620 };
621 use commonware_codec::{Copying, Decode as _, DecodeExt as _, Encode as _};
622 use commonware_cryptography::{Sha256, sha256::Digest as ShaDigest};
623 use commonware_parallel::Rayon;
624 use commonware_runtime::{Runner as _, Spawner as _, Supervisor as _, deterministic};
625 use commonware_utils::{
626 NZU64,
627 sync::{AsyncRwLock, Mutex, TracedAsyncRwLock},
628 };
629 use std::{marker::PhantomData, mem, result::Result, sync::Arc};
630
631 macro_rules! assert_source_variants {
632 ($db:ty) => {
633 assert_serves::<Arc<$db>>();
634 assert_serves::<Arc<AsyncRwLock<$db>>>();
635 assert_serves::<Arc<AsyncRwLock<Option<$db>>>>();
636 assert_serves::<Arc<TracedAsyncRwLock<$db>>>();
637 assert_serves::<Arc<TracedAsyncRwLock<Option<$db>>>>();
638 };
639 }
640
641 fn assert_serves<S: Source>() {}
642
643 #[derive(Clone)]
645 pub struct SequenceSource<F: Family, Op, D: Digest> {
646 responses: Arc<Mutex<Vec<Response<F, Op, D>>>>,
647 verdicts: Arc<Mutex<Vec<oneshot::Receiver<bool>>>>,
648 }
649
650 impl<F: Family, Op, D: Digest> SequenceSource<F, Op, D> {
651 pub fn new(responses: Vec<Response<F, Op, D>>) -> Self {
652 Self {
653 responses: Arc::new(Mutex::new(responses)),
654 verdicts: Arc::new(Mutex::new(Vec::new())),
655 }
656 }
657
658 pub async fn take_verdicts(&self) -> Vec<bool> {
660 let receivers = mem::take(&mut *self.verdicts.lock());
661 let mut verdicts = Vec::with_capacity(receivers.len());
662 for receiver in receivers {
663 if let Ok(verdict) = receiver.await {
664 verdicts.push(verdict);
665 }
666 }
667 verdicts
668 }
669 }
670
671 impl<F, Op, D> Source for SequenceSource<F, Op, D>
672 where
673 F: Family,
674 D: Digest,
675 Op: Send,
676 {
677 type Family = F;
678 type Digest = D;
679 type Op = Op;
680 type Error = qmdb::Error<F>;
681
682 async fn serve(&self, _request: Request<F>) -> source::Result<Self> {
683 let mut responses = mem::take(&mut *self.responses.lock()).into_iter();
684 let response = responses.next().ok_or(qmdb::Error::KeyNotFound)?;
685 let (candidate_tx, candidate_rx) = mpsc::channel(responses.len().max(1));
686 let (verdict_tx, verdict_rx) = oneshot::channel();
687 let mut verdicts = self.verdicts.lock();
688 verdicts.push(verdict_rx);
689 for response in responses {
690 let (verdict_tx, verdict_rx) = oneshot::channel();
691 assert!(candidate_tx.try_send((response, verdict_tx)).is_ok());
692 verdicts.push(verdict_rx);
693 }
694 Ok((response, Some(Feedback::new(verdict_tx, candidate_rx))))
695 }
696 }
697
698 async fn next_candidate<R>(
699 verdict_rx: oneshot::Receiver<bool>,
700 candidate_tx: mpsc::Sender<(R, oneshot::Sender<bool>)>,
701 response: R,
702 ) -> Option<bool> {
703 if verdict_rx.await.ok()? {
704 return Some(true);
705 }
706 let (verdict_tx, next_verdict_rx) = oneshot::channel();
707 candidate_tx.send((response, verdict_tx)).await.ok()?;
708 next_verdict_rx.await.ok()
709 }
710
711 #[test]
712 fn feedback_accepts_and_closes_request() {
713 deterministic::Runner::default().start(|_context| async move {
714 let (candidate_tx, candidate_rx) = mpsc::channel::<(u8, _)>(1);
715 let (verdict_tx, verdict_rx) = oneshot::channel();
716
717 Feedback::new(verdict_tx, candidate_rx).accept();
718
719 assert!(verdict_rx.await.unwrap());
720 let (next_verdict_tx, _) = oneshot::channel();
721 assert!(candidate_tx.send((2, next_verdict_tx)).await.is_err());
722 });
723 }
724
725 #[test]
726 fn feedback_rejects_and_returns_next_candidate() {
727 deterministic::Runner::default().start(|context| async move {
728 let (candidate_tx, candidate_rx) = mpsc::channel(1);
729 let (verdict_tx, verdict_rx) = oneshot::channel();
730 let driver = context
731 .child("driver")
732 .spawn(move |_| async move { next_candidate(verdict_rx, candidate_tx, 2u8).await });
733
734 let (response, feedback) = Feedback::new(verdict_tx, candidate_rx)
735 .reject()
736 .await
737 .unwrap();
738 assert_eq!(response, 2);
739 feedback.accept();
740 assert_eq!(driver.await.unwrap(), Some(true));
741 });
742 }
743
744 #[test]
745 fn dropping_feedback_cancels_request() {
746 deterministic::Runner::default().start(|_context| async move {
747 let (candidate_tx, candidate_rx) = mpsc::channel::<(u8, _)>(1);
748 let (verdict_tx, verdict_rx) = oneshot::channel();
749
750 drop(Feedback::new(verdict_tx, candidate_rx));
751
752 assert!(verdict_rx.await.is_err());
753 let (next_verdict_tx, _) = oneshot::channel();
754 assert!(candidate_tx.send((2, next_verdict_tx)).await.is_err());
755 });
756 }
757
758 #[test]
759 fn dropping_rejection_wait_cancels_request() {
760 deterministic::Runner::default().start(|_context| async move {
761 let (candidate_tx, candidate_rx) = mpsc::channel::<(u8, _)>(1);
762 let (verdict_tx, verdict_rx) = oneshot::channel();
763 let mut reject = Box::pin(Feedback::new(verdict_tx, candidate_rx).reject());
764
765 commonware_macros::select! {
766 _ = reject.as_mut() => panic!("rejection completed without another candidate"),
767 verdict = verdict_rx => assert_eq!(verdict.unwrap(), false),
768 }
769 drop(reject);
770
771 let (next_verdict_tx, _) = oneshot::channel();
772 assert!(candidate_tx.send((2, next_verdict_tx)).await.is_err());
773 });
774 }
775
776 #[test]
777 fn feedback_stops_when_verdict_receiver_is_gone() {
778 deterministic::Runner::default().start(|_context| async move {
779 let (_candidate_tx, candidate_rx) = mpsc::channel::<(u8, _)>(1);
780 let (verdict_tx, verdict_rx) = oneshot::channel();
781 drop(verdict_rx);
782
783 assert!(
784 Feedback::new(verdict_tx, candidate_rx)
785 .reject()
786 .await
787 .is_none()
788 );
789 });
790 }
791
792 #[test]
793 fn feedback_stops_when_source_closes_candidate_stream() {
794 deterministic::Runner::default().start(|_context| async move {
795 let (candidate_tx, candidate_rx) = mpsc::channel::<(u8, _)>(1);
796 let (verdict_tx, verdict_rx) = oneshot::channel();
797 drop(candidate_tx);
798
799 assert!(
800 Feedback::new(verdict_tx, candidate_rx)
801 .reject()
802 .await
803 .is_none()
804 );
805 assert!(!verdict_rx.await.unwrap());
806 });
807 }
808
809 pub async fn fetch_compact_state<R: Source>(
811 source: &R,
812 target: crate::qmdb::sync::CompactTarget<R::Family, R::Digest>,
813 ) -> Result<Response<R::Family, R::Op, R::Digest>, R::Error> {
814 let (response, _feedback) = source
815 .serve(Request::Boundary {
816 size: target.size,
817 start: target.size - 1,
818 })
819 .await?;
820 Ok(response)
821 }
822
823 pub struct FailSource<F: Family, Op, D> {
825 _phantom: PhantomData<(F, Op, D)>,
826 }
827
828 impl<F, Op, D> Source for FailSource<F, Op, D>
829 where
830 F: Family,
831 D: Digest,
832 Op: Send + Sync,
833 {
834 type Family = F;
835 type Digest = D;
836 type Op = Op;
837 type Error = qmdb::Error<F>;
838
839 async fn serve(&self, _request: Request<F>) -> source::Result<Self> {
840 Err(qmdb::Error::KeyNotFound) }
842 }
843
844 impl<F: Family, Op, D> FailSource<F, Op, D> {
845 pub fn new() -> Self {
846 Self {
847 _phantom: PhantomData,
848 }
849 }
850 }
851
852 #[test]
853 fn test_all_qmdb_variants_implement_source() {
854 type AnyOrderedFixed = crate::qmdb::any::ordered::fixed::Db<
855 mmr::Family,
856 deterministic::Context,
857 ShaDigest,
858 ShaDigest,
859 Sha256,
860 OneCap,
861 Rayon,
862 >;
863 type AnyOrderedVariable = crate::qmdb::any::ordered::variable::Db<
864 mmr::Family,
865 deterministic::Context,
866 ShaDigest,
867 Vec<u8>,
868 Sha256,
869 OneCap,
870 Rayon,
871 >;
872 type AnyUnorderedFixed = crate::qmdb::any::unordered::fixed::Db<
873 mmr::Family,
874 deterministic::Context,
875 ShaDigest,
876 ShaDigest,
877 Sha256,
878 TwoCap,
879 Rayon,
880 >;
881 type AnyUnorderedVariable = crate::qmdb::any::unordered::variable::Db<
882 mmr::Family,
883 deterministic::Context,
884 ShaDigest,
885 Vec<u8>,
886 Sha256,
887 TwoCap,
888 Rayon,
889 >;
890 type CurrentOrderedFixed = crate::qmdb::current::ordered::fixed::Db<
891 mmr::Family,
892 deterministic::Context,
893 ShaDigest,
894 ShaDigest,
895 Sha256,
896 OneCap,
897 32,
898 Rayon,
899 >;
900 type CurrentOrderedVariable = crate::qmdb::current::ordered::variable::Db<
901 mmr::Family,
902 deterministic::Context,
903 ShaDigest,
904 Vec<u8>,
905 Sha256,
906 OneCap,
907 32,
908 Rayon,
909 >;
910 type CurrentUnorderedFixed = crate::qmdb::current::unordered::fixed::Db<
911 mmr::Family,
912 deterministic::Context,
913 ShaDigest,
914 ShaDigest,
915 Sha256,
916 TwoCap,
917 32,
918 Rayon,
919 >;
920 type CurrentUnorderedVariable = crate::qmdb::current::unordered::variable::Db<
921 mmr::Family,
922 deterministic::Context,
923 ShaDigest,
924 Vec<u8>,
925 Sha256,
926 TwoCap,
927 32,
928 Rayon,
929 >;
930 type ImmutableFixed = crate::qmdb::immutable::fixed::Db<
931 mmr::Family,
932 deterministic::Context,
933 ShaDigest,
934 ShaDigest,
935 Sha256,
936 TwoCap,
937 Rayon,
938 >;
939 type ImmutableVariable = crate::qmdb::immutable::variable::Db<
940 mmr::Family,
941 deterministic::Context,
942 ShaDigest,
943 Vec<u8>,
944 Sha256,
945 TwoCap,
946 Rayon,
947 >;
948 type KeylessFixed = crate::qmdb::keyless::fixed::Db<
949 mmr::Family,
950 deterministic::Context,
951 ShaDigest,
952 Sha256,
953 Rayon,
954 >;
955 type KeylessVariable = crate::qmdb::keyless::variable::Db<
956 mmr::Family,
957 deterministic::Context,
958 Vec<u8>,
959 Sha256,
960 Rayon,
961 >;
962
963 assert_source_variants!(AnyOrderedFixed);
964 assert_source_variants!(AnyOrderedVariable);
965 assert_source_variants!(AnyUnorderedFixed);
966 assert_source_variants!(AnyUnorderedVariable);
967 assert_source_variants!(CurrentOrderedFixed);
968 assert_source_variants!(CurrentOrderedVariable);
969 assert_source_variants!(CurrentUnorderedFixed);
970 assert_source_variants!(CurrentUnorderedVariable);
971 assert_source_variants!(ImmutableFixed);
972 assert_source_variants!(ImmutableVariable);
973 assert_source_variants!(KeylessFixed);
974 assert_source_variants!(KeylessVariable);
975
976 type KeylessFixedCompactDb = crate::qmdb::keyless::fixed::CompactDb<
977 mmr::Family,
978 deterministic::Context,
979 ShaDigest,
980 Sha256,
981 Rayon,
982 >;
983 type KeylessVariableCompactDb = crate::qmdb::keyless::variable::CompactDb<
984 mmr::Family,
985 deterministic::Context,
986 Vec<u8>,
987 Sha256,
988 Rayon,
989 >;
990 type ImmutableFixedCompactDb = crate::qmdb::immutable::fixed::CompactDb<
991 mmr::Family,
992 deterministic::Context,
993 ShaDigest,
994 ShaDigest,
995 Sha256,
996 Rayon,
997 >;
998 type ImmutableVariableCompactDb = crate::qmdb::immutable::variable::CompactDb<
999 mmr::Family,
1000 deterministic::Context,
1001 ShaDigest,
1002 Vec<u8>,
1003 Sha256,
1004 Rayon,
1005 >;
1006
1007 assert_source_variants!(KeylessFixedCompactDb);
1008 assert_source_variants!(KeylessVariableCompactDb);
1009 assert_source_variants!(ImmutableFixedCompactDb);
1010 assert_source_variants!(ImmutableVariableCompactDb);
1011 }
1012
1013 #[test]
1015 fn test_request_decode_rejects_malformed() {
1016 let valid = Request::<mmr::Family>::Operations {
1017 size: Location::new(10),
1018 start: Location::new(3),
1019 max_ops: NZU64!(2),
1020 };
1021 let decoded = Request::<mmr::Family>::decode(valid.encode()).unwrap();
1022 assert_eq!(decoded, valid);
1023
1024 let mut malformed = Vec::new();
1025 1u8.write(&mut malformed); Location::<mmr::Family>::new(10).write(&mut malformed);
1027 Location::<mmr::Family>::new(10).write(&mut malformed); assert!(Request::<mmr::Family>::decode(malformed).is_err());
1029
1030 let bad_tag = [7u8];
1031 assert!(Request::<mmr::Family>::decode(Copying(&bad_tag)).is_err());
1032 }
1033
1034 #[test]
1037 fn test_request_identity() {
1038 let operations = Request::<mmr::Family>::Operations {
1039 size: Location::new(10),
1040 start: Location::new(3),
1041 max_ops: NZU64!(1),
1042 };
1043 let boundary = Request::<mmr::Family>::Boundary {
1044 size: Location::new(10),
1045 start: Location::new(3),
1046 };
1047 assert_eq!(boundary.max_ops(), NZU64!(1));
1048 assert_ne!(operations, boundary);
1049
1050 let mut set = std::collections::BTreeSet::new();
1051 assert!(set.insert(operations));
1052 assert!(set.insert(boundary));
1053 assert!(!set.insert(operations));
1054 assert_eq!(set.len(), 2);
1055
1056 let smaller_size = Request::<mmr::Family>::Operations {
1058 size: Location::new(9),
1059 start: Location::new(8),
1060 max_ops: NZU64!(5),
1061 };
1062 let smaller_start = Request::<mmr::Family>::Operations {
1063 size: Location::new(10),
1064 start: Location::new(2),
1065 max_ops: NZU64!(5),
1066 };
1067 let fewer_ops = Request::<mmr::Family>::Operations {
1068 size: Location::new(10),
1069 start: Location::new(3),
1070 max_ops: NZU64!(2),
1071 };
1072 let larger_ops = Request::<mmr::Family>::Operations {
1073 size: Location::new(10),
1074 start: Location::new(3),
1075 max_ops: NZU64!(5),
1076 };
1077 assert!(smaller_size < smaller_start);
1078 assert!(smaller_start < fewer_ops);
1079 assert!(fewer_ops < larger_ops);
1080 }
1081
1082 #[test]
1084 fn test_response_decode_rejects_malformed() {
1085 type R = Response<mmr::Family, u64, ShaDigest>;
1086 let digest = ShaDigest::from([7u8; 32]);
1087 let proof = Proof::<mmr::Family, ShaDigest> {
1088 leaves: Location::new(3),
1089 inactive_peaks: 0,
1090 digests: vec![digest],
1091 };
1092
1093 let response = R::Operations {
1095 proof: proof.clone(),
1096 operations: vec![1, 2, 3],
1097 };
1098 assert!(R::decode_cfg(response.encode(), &(3, ())).is_ok());
1099 assert!(R::decode_cfg(response.encode(), &(2, ())).is_err());
1100
1101 let oversized = Proof::<mmr::Family, ShaDigest> {
1103 leaves: Location::new(3),
1104 inactive_peaks: 0,
1105 digests: vec![digest; MAX_PROOF_DIGESTS_PER_ELEMENT + 1],
1106 };
1107 let response = R::Operations {
1108 proof: oversized,
1109 operations: vec![1],
1110 };
1111 assert!(R::decode_cfg(response.encode(), &(1, ())).is_err());
1112
1113 let response = R::Boundary {
1115 proof: proof.clone(),
1116 op: 1,
1117 pinned_nodes: vec![digest; MAX_PINNED_NODES + 1],
1118 };
1119 assert!(R::decode_cfg(response.encode(), &(1, ())).is_err());
1120 let response = R::Boundary {
1121 proof,
1122 op: 1,
1123 pinned_nodes: vec![digest; MAX_PINNED_NODES],
1124 };
1125 assert!(R::decode_cfg(response.encode(), &(1, ())).is_ok());
1126
1127 assert!(R::decode_cfg(Copying(&[9u8]), &(1, ())).is_err());
1129 }
1130
1131 #[test]
1133 fn test_locked_source_reaches_source() {
1134 deterministic::Runner::default().start(|_context| async move {
1135 let lock = AsyncRwLock::new(FailSource::<mmr::Family, u8, ShaDigest>::new());
1136
1137 let request = Request::Operations {
1138 size: Location::new(1),
1139 start: Location::new(0),
1140 max_ops: NZU64!(1),
1141 };
1142 let result = lock.serve(request).await;
1143 assert!(matches!(result, Err(crate::qmdb::Error::KeyNotFound)));
1144 });
1145 }
1146
1147 #[test]
1148 fn fetch_compact_state_leaves_response_unjudged() {
1149 deterministic::Runner::default().start(|_context| async move {
1150 let size = Location::new(1);
1151 let response = Response::<mmr::Family, u64, ShaDigest>::Boundary {
1152 proof: Proof {
1153 leaves: size,
1154 inactive_peaks: 0,
1155 digests: vec![],
1156 },
1157 op: 7,
1158 pinned_nodes: vec![],
1159 };
1160 let expected = response.encode();
1161 let source = SequenceSource::new(vec![response]);
1162 let target = crate::qmdb::sync::CompactTarget {
1163 root: ShaDigest::from([7u8; 32]),
1164 size,
1165 };
1166
1167 let response = fetch_compact_state(&source, target).await.unwrap();
1168 assert_eq!(response.encode(), expected);
1169 assert!(source.take_verdicts().await.is_empty());
1170 });
1171 }
1172
1173 #[test]
1174 fn sequence_source_offers_candidates_until_accepted() {
1175 deterministic::Runner::default().start(|_context| async move {
1176 let response = |op| Response::Operations {
1177 proof: Proof {
1178 leaves: Location::new(1),
1179 inactive_peaks: 0,
1180 digests: vec![],
1181 },
1182 operations: vec![op],
1183 };
1184 let source = SequenceSource::<mmr::Family, _, ShaDigest>::new(vec![
1185 response(1),
1186 response(2),
1187 response(3),
1188 ]);
1189 let request = Request::Operations {
1190 size: Location::new(1),
1191 start: Location::new(0),
1192 max_ops: NZU64!(1),
1193 };
1194
1195 let locked = Arc::new(AsyncRwLock::new(Some(source.clone())));
1196 let (first, feedback) = locked.serve(request).await.unwrap();
1197 assert!(matches!(
1198 first,
1199 Response::Operations { operations, .. } if operations == [1]
1200 ));
1201 let (second, feedback) = feedback.unwrap().reject().await.unwrap();
1202 assert!(matches!(
1203 second,
1204 Response::Operations { operations, .. } if operations == [2]
1205 ));
1206 feedback.accept();
1207 assert_eq!(source.take_verdicts().await, vec![false, true]);
1208 assert!(matches!(
1209 source.serve(request).await,
1210 Err(qmdb::Error::KeyNotFound)
1211 ));
1212 });
1213 }
1214}
1215
1216#[cfg(all(test, feature = "arbitrary"))]
1217mod conformance {
1218 use super::*;
1219 use crate::merkle::{mmb, mmr};
1220 use commonware_codec::conformance::CodecConformance;
1221 use commonware_cryptography::sha256::Digest as Sha256Digest;
1222
1223 commonware_conformance::conformance_tests! {
1224 CodecConformance<Request<mmr::Family>>,
1225 CodecConformance<Request<mmb::Family>>,
1226 CodecConformance<Response<mmr::Family, u64, Sha256Digest>>,
1227 CodecConformance<Response<mmb::Family, u64, Sha256Digest>>,
1228 }
1229}