1use std::{collections::HashMap, fmt::Display, sync::Arc, time::Duration};
2
3use anyhow::Result;
4use committable::Committable;
5use disco_types::status::StatusCode;
6use espresso_types::{BlockMerkleTree, Header, NsIndex, NsProof, SeqTypes};
7use futures::{TryStreamExt, future::try_join, stream::StreamExt};
8use hotshot_query_service::{
9 Error,
10 availability::{AvailabilityDataSource, LeafQueryData, PayloadQueryData, VidCommonQueryData},
11 data_source::{VersionedDataSource, storage::NodeStorage},
12 merklized_state::{MerklizedStateDataSource, Snapshot},
13 node::BlockId,
14 types::HeightIndexed,
15};
16use hotshot_types::simple_certificate::QuorumCertificate2;
17use itertools::izip;
18use jf_merkle_tree_compat::MerkleTreeScheme;
19use light_client::{
20 client::NAMESPACES_PARAM_TAG,
21 consensus::{header::HeaderProof, leaf::LeafProof, namespace::NamespaceProof},
22};
23use tagged_base64::TaggedBase64;
24use versions::NEW_PROTOCOL_VERSION;
25
26pub(crate) async fn get_leaf_proof<State>(
31 state: &State,
32 requested_leaf: LeafQueryData<SeqTypes>,
33 finalized: Option<usize>,
34 fetch_timeout: Duration,
35 chain_limit: usize,
36) -> Result<LeafProof, Error>
37where
38 State: AvailabilityDataSource<SeqTypes> + VersionedDataSource,
39 for<'a> State::ReadOnly<'a>: NodeStorage<SeqTypes>,
40{
41 let requested = requested_leaf.height() as usize;
42 let new_protocol = requested_leaf.header().version() >= NEW_PROTOCOL_VERSION;
43
44 let mut hint = finalized.filter(|finalized| finalized.saturating_sub(requested) <= chain_limit);
45
46 if let Some(finalized) = hint
48 && new_protocol
49 && let Some(cert2_height) = earliest_cert2_height(state, requested as u64).await
50 && cert2_height + 1 < finalized as u64
51 {
52 hint = None;
53 }
54
55 if let Some(finalized) = hint {
56 get_leaf_proof_with_finalized_assumption(
57 state,
58 requested_leaf,
59 finalized,
60 fetch_timeout,
61 chain_limit,
62 )
63 .await
64 } else if new_protocol {
65 get_leaf_proof_with_cert2(state, requested_leaf, fetch_timeout, chain_limit).await
66 } else {
67 get_leaf_proof_with_qc_chain(state, requested_leaf, fetch_timeout, chain_limit).await
68 }
69}
70
71async fn earliest_cert2_height<State>(state: &State, height: u64) -> Option<u64>
74where
75 State: VersionedDataSource,
76 for<'a> State::ReadOnly<'a>: NodeStorage<SeqTypes>,
77{
78 let cert2 = state
79 .read()
80 .await
81 .ok()?
82 .load_earliest_cert2(height)
83 .await
84 .ok()??;
85 Some(cert2.data.block_number)
86}
87
88pub(crate) async fn get_leaf_proof_with_qc_chain<State>(
89 state: &State,
90 requested_leaf: LeafQueryData<SeqTypes>,
91 fetch_timeout: Duration,
92 chain_limit: usize,
93) -> Result<LeafProof, Error>
94where
95 State: AvailabilityDataSource<SeqTypes> + VersionedDataSource,
96 for<'a> State::ReadOnly<'a>: NodeStorage<SeqTypes>,
97{
98 let requested = requested_leaf.height() as usize;
99 let mut tx = state.read().await.map_err(internal)?;
103 let latest_height = NodeStorage::block_height(&mut tx).await.map_err(internal)?;
104 let qc_chain = tx.latest_qc_chain().await.map_err(internal)?;
105 drop(tx);
106
107 let mut leaves = state.get_leaf_range(requested + 1..latest_height).await;
108 let mut proof = LeafProof::default();
109 let requested_leaf_qc = requested_leaf.qc().clone();
110 proof.push(requested_leaf);
111
112 let mut remaining = chain_limit;
113 while let Some(leaf) = leaves.next().await {
114 let leaf = leaf
115 .with_timeout(fetch_timeout)
116 .await
117 .ok_or_else(|| not_found("missing leaves"))?;
118
119 remaining = remaining
120 .checked_sub(1)
121 .ok_or_else(|| chain_too_long(requested, chain_limit))?;
122
123 if leaf.header().version() >= NEW_PROTOCOL_VERSION {
125 complete_proof_with_cert2(
126 state,
127 &mut proof,
128 leaf,
129 requested_leaf_qc.clone(),
130 fetch_timeout,
131 remaining,
132 )
133 .await?;
134 return Ok(proof);
135 }
136
137 if proof.push(leaf) {
138 return Ok(proof);
139 }
140 }
141
142 let Some([committing_qc, deciding_qc]) = qc_chain else {
146 return Err(not_found("missing QC 2-chain to prove finality"));
147 };
148 proof.add_qc_chain(Arc::new(committing_qc), Arc::new(deciding_qc));
149
150 Ok(proof)
151}
152
153pub(crate) async fn get_leaf_proof_with_cert2<State>(
161 state: &State,
162 requested_leaf: LeafQueryData<SeqTypes>,
163 fetch_timeout: Duration,
164 chain_limit: usize,
165) -> Result<LeafProof, Error>
166where
167 State: AvailabilityDataSource<SeqTypes> + VersionedDataSource,
168 for<'a> State::ReadOnly<'a>: NodeStorage<SeqTypes>,
169{
170 let mut proof = LeafProof::default();
171 let requested_leaf_qc = requested_leaf.qc().clone();
172 complete_proof_with_cert2(
173 state,
174 &mut proof,
175 requested_leaf,
176 requested_leaf_qc,
177 fetch_timeout,
178 chain_limit,
179 )
180 .await?;
181 Ok(proof)
182}
183
184async fn complete_proof_with_cert2<State>(
187 state: &State,
188 proof: &mut LeafProof,
189 leaf: LeafQueryData<SeqTypes>,
190 requested_leaf_qc: QuorumCertificate2<SeqTypes>,
191 fetch_timeout: Duration,
192 chain_limit: usize,
193) -> Result<(), Error>
194where
195 State: AvailabilityDataSource<SeqTypes> + VersionedDataSource,
196 for<'a> State::ReadOnly<'a>: NodeStorage<SeqTypes>,
197{
198 let start_height = leaf.height();
199 let start_commit = leaf.leaf().commit();
200
201 if proof.push(leaf) {
203 return Ok(());
204 }
205
206 let cert2 = state
207 .read()
208 .await
209 .map_err(internal)?
210 .load_earliest_cert2(start_height)
211 .await
212 .map_err(internal)?
213 .ok_or_else(|| {
214 not_found(format!(
215 "no cert2 finality proof available at or after height {start_height}"
216 ))
217 })?;
218
219 let cert2_height = cert2.data.block_number;
220 if cert2_height < start_height {
221 return Err(not_found(
222 "cert2 finality proof is older than requested leaf",
223 ));
224 }
225 if cert2_height - start_height > chain_limit as u64 {
226 return Err(not_found(format!(
227 "earliest cert2 finality proof (height {cert2_height}) is more than {chain_limit} \
228 leaves past height {start_height}"
229 )));
230 }
231
232 if cert2_height == start_height {
233 if start_commit != cert2.data.leaf_commit {
234 return Err(internal("stored cert2 does not finalize the expected leaf"));
235 }
236 proof.add_certificate(Arc::new(cert2), requested_leaf_qc);
237 return Ok(());
238 }
239
240 let mut leaves = state
241 .get_leaf_range(start_height as usize + 1..cert2_height as usize + 1)
242 .await;
243 while let Some(leaf) = leaves.next().await {
247 let leaf = leaf
248 .with_timeout(fetch_timeout)
249 .await
250 .ok_or_else(|| not_found("missing leaves"))?;
251
252 if leaf.height() == cert2_height {
253 if leaf.leaf().commit() != cert2.data.leaf_commit {
254 return Err(internal("stored cert2 does not finalize the expected leaf"));
255 }
256 if !proof.push(leaf) {
257 proof.add_certificate(Arc::new(cert2), requested_leaf_qc);
258 }
259 return Ok(());
260 }
261 if proof.push(leaf) {
262 return Ok(());
263 }
264 }
265
266 Err(not_found("missing cert2 leaf"))
267}
268
269pub(crate) async fn get_leaf_proof_with_finalized_assumption<State>(
270 state: &State,
271 requested_leaf: LeafQueryData<SeqTypes>,
272 finalized: usize,
273 fetch_timeout: Duration,
274 chain_limit: usize,
275) -> Result<LeafProof, Error>
276where
277 State: AvailabilityDataSource<SeqTypes>,
278{
279 let requested = requested_leaf.height() as usize;
280 if finalized <= requested {
284 return Err(Error::Custom {
285 message: format!(
286 "finalized leaf height ({finalized}) must be greater than requested ({requested})"
287 ),
288 status: StatusCode::BAD_REQUEST,
289 });
290 }
291 if finalized - requested > chain_limit {
292 return Err(Error::Custom {
293 message: format!(
294 "finalized leaf height ({finalized}) is more than {chain_limit} blocks past the \
295 requested leaf ({requested}); request a proof without the finalized parameter \
296 instead"
297 ),
298 status: StatusCode::BAD_REQUEST,
299 });
300 }
301
302 let mut leaves = state.get_leaf_range(requested + 1..finalized).await;
303 let mut proof = LeafProof::default();
304 proof.push(requested_leaf);
305
306 while let Some(leaf) = leaves.next().await {
307 let leaf = leaf
308 .with_timeout(fetch_timeout)
309 .await
310 .ok_or_else(|| not_found("missing leaves"))?;
311
312 if proof.push(leaf) {
313 return Ok(proof);
314 }
315 }
316
317 Ok(proof)
318}
319
320pub(crate) async fn get_header_proof<State>(
321 state: &State,
322 root: u64,
323 requested: BlockId<SeqTypes>,
324 fetch_timeout: Duration,
325) -> Result<HeaderProof, Error>
326where
327 State: AvailabilityDataSource<SeqTypes>
328 + MerklizedStateDataSource<SeqTypes, BlockMerkleTree, { BlockMerkleTree::ARITY }>
329 + VersionedDataSource,
330{
331 let header = state
332 .get_header(requested)
333 .await
334 .with_timeout(fetch_timeout)
335 .await
336 .ok_or_else(|| not_found(format!("unknown header {requested}")))?;
337 if header.height() >= root {
338 return Err(Error::Custom {
339 message: format!(
340 "height ({}) must be less than root ({root})",
341 header.height()
342 ),
343 status: StatusCode::BAD_REQUEST,
344 });
345 }
346 let path = MerklizedStateDataSource::<SeqTypes, BlockMerkleTree, _>::get_path(
347 state,
348 Snapshot::Index(root),
349 header.height(),
350 )
351 .await
352 .map_err(|source| Error::MerklizedState {
353 source: source.into(),
354 })?;
355
356 Ok(HeaderProof::new(header, path))
357}
358
359pub(crate) async fn fetch_block_data_range<State>(
360 state: &State,
361 start: usize,
362 end: usize,
363 fetch_timeout: Duration,
364 large_object_range_limit: usize,
365) -> Result<
366 Vec<(
367 Header,
368 PayloadQueryData<SeqTypes>,
369 VidCommonQueryData<SeqTypes>,
370 )>,
371 Error,
372>
373where
374 State: AvailabilityDataSource<SeqTypes>,
375{
376 if end <= start {
377 return Err(Error::Custom {
378 message: format!("requested empty interval [{start}, {end})"),
379 status: StatusCode::BAD_REQUEST,
380 });
381 }
382 if end - start > large_object_range_limit {
383 return Err(Error::Custom {
384 message: format!(
385 "requested range [{start}, {end}) exceeds maximum size {large_object_range_limit}"
386 ),
387 status: StatusCode::BAD_REQUEST,
388 });
389 }
390
391 let fetch_headers = async move {
392 state
393 .get_header_range(start..end)
394 .await
395 .enumerate()
396 .then(|(i, fetch)| async move {
397 fetch
398 .with_timeout(fetch_timeout)
399 .await
400 .ok_or_else(|| Error::Custom {
401 message: format!("missing header {}", start + i),
402 status: StatusCode::NOT_FOUND,
403 })
404 })
405 .try_collect::<Vec<_>>()
406 .await
407 };
408 let fetch_payloads = async move {
409 state
410 .get_payload_range(start..end)
411 .await
412 .enumerate()
413 .then(|(i, fetch)| async move {
414 fetch
415 .with_timeout(fetch_timeout)
416 .await
417 .ok_or_else(|| Error::Custom {
418 message: format!("missing payload {}", start + i),
419 status: StatusCode::NOT_FOUND,
420 })
421 })
422 .try_collect::<Vec<_>>()
423 .await
424 };
425 let fetch_vid_commons = async move {
426 state
427 .get_vid_common_range(start..end)
428 .await
429 .enumerate()
430 .then(|(i, fetch)| async move {
431 fetch
432 .with_timeout(fetch_timeout)
433 .await
434 .ok_or_else(|| Error::Custom {
435 message: format!("missing VID common {}", start + i),
436 status: StatusCode::NOT_FOUND,
437 })
438 })
439 .try_collect::<Vec<_>>()
440 .await
441 };
442 let (headers, (payloads, vid_commons)) =
443 try_join(fetch_headers, try_join(fetch_payloads, fetch_vid_commons)).await?;
444
445 Ok(izip!(headers, payloads, vid_commons).collect())
446}
447
448pub(crate) async fn get_namespace_proof_range<State>(
450 state: &State,
451 start: usize,
452 end: usize,
453 namespace: u64,
454 fetch_timeout: Duration,
455 large_object_range_limit: usize,
456) -> Result<Vec<NamespaceProof>, Error>
457where
458 State: AvailabilityDataSource<SeqTypes>,
459{
460 let blocks = get_namespaces_proof_range(
461 state,
462 start,
463 end,
464 &[namespace],
465 fetch_timeout,
466 large_object_range_limit,
467 )
468 .await?;
469 Ok(blocks
470 .into_iter()
471 .map(|mut block| {
472 block
473 .remove(&namespace)
474 .unwrap_or_else(NamespaceProof::not_present)
475 })
476 .collect())
477}
478
479pub(crate) async fn get_namespaces_proof_range<State>(
480 state: &State,
481 start: usize,
482 end: usize,
483 namespaces: &[u64],
484 fetch_timeout: Duration,
485 large_object_range_limit: usize,
486) -> Result<Vec<HashMap<u64, NamespaceProof>>, Error>
487where
488 State: AvailabilityDataSource<SeqTypes>,
489{
490 fetch_block_data_range(state, start, end, fetch_timeout, large_object_range_limit)
491 .await?
492 .into_iter()
493 .map(|(header, payload, vid_common)| {
494 namespaces
495 .iter()
496 .filter_map(|&namespace| {
497 let ns_index = header.ns_table().find_ns_id(&namespace.into())?;
498 Some(
499 build_namespace_proof(&payload, &ns_index, &vid_common)
500 .map(|proof| (namespace, proof)),
501 )
502 })
503 .collect::<Result<HashMap<_, _>, _>>()
504 })
505 .collect()
506}
507
508pub(crate) fn parse_namespaces_str(encoded: &str) -> anyhow::Result<Vec<u64>> {
511 let encoded: TaggedBase64 = encoded
512 .parse()
513 .map_err(|err| anyhow::anyhow!("invalid namespaces parameter: {err}"))?;
514 if encoded.tag() != NAMESPACES_PARAM_TAG {
515 anyhow::bail!(
516 "invalid namespaces parameter tag: expected {NAMESPACES_PARAM_TAG}, got {}",
517 encoded.tag()
518 );
519 }
520 serde_json::from_slice(&encoded.value())
521 .map_err(|err| anyhow::anyhow!("invalid namespaces parameter: {err}"))
522}
523
524fn build_namespace_proof(
526 payload: &PayloadQueryData<SeqTypes>,
527 ns_index: &NsIndex,
528 vid_common: &VidCommonQueryData<SeqTypes>,
529) -> Result<NamespaceProof, Error> {
530 let ns_proof =
531 NsProof::new(payload.data(), ns_index, vid_common.common()).ok_or_else(|| {
532 Error::Custom {
533 message: "failed to construct namespace proof".into(),
534 status: StatusCode::INTERNAL_SERVER_ERROR,
535 }
536 })?;
537 Ok(NamespaceProof::new(ns_proof, vid_common.common().clone()))
538}
539
540fn internal(err: impl Display) -> Error {
541 Error::Custom {
542 message: err.to_string(),
543 status: StatusCode::INTERNAL_SERVER_ERROR,
544 }
545}
546
547fn not_found(msg: impl Into<String>) -> Error {
548 Error::Custom {
549 message: msg.into(),
550 status: StatusCode::NOT_FOUND,
551 }
552}
553
554fn chain_too_long(requested: usize, chain_limit: usize) -> Error {
555 not_found(format!(
556 "no finality proof found within {chain_limit} leaves of requested leaf {requested}"
557 ))
558}
559
560#[cfg(test)]
561mod test {
562 use std::marker::PhantomData;
563
564 use committable::Committable;
565 use disco_types::error::Error;
566 use espresso_types::BLOCK_MERKLE_TREE_HEIGHT;
567 use futures::future::join_all;
568 use hotshot_query_service::{
569 availability::{BlockQueryData, TransactionIndex, VidCommonQueryData},
570 data_source::{Transaction, storage::UpdateAvailabilityStorage},
571 merklized_state::UpdateStateData,
572 };
573 use hotshot_types::{
574 data::ViewNumber, simple_certificate::CertificatePair, simple_vote::Vote2Data,
575 };
576 use jf_merkle_tree_compat::{AppendableMerkleTreeScheme, ToTraversalPath};
577 use light_client::{
578 consensus::leaf::{FinalityProof, LeafProofHint},
579 testing::{
580 AlwaysTrueQuorum, ENABLE_EPOCHS, LEGACY_VERSION, TestClient, VersionCheckQuorum,
581 custom_leaf_chain_with_upgrade, leaf_chain, leaf_chain_with_upgrade,
582 },
583 };
584 use versions::{DRB_AND_HEADER_UPGRADE_VERSION, EPOCH_VERSION, NEW_PROTOCOL_VERSION, Upgrade};
585
586 use super::*;
587 use crate::api::{
588 data_source::{SequencerDataSource, testing::TestableSequencerDataSource},
589 sql::DataSource,
590 };
591
592 const CHAIN_LIMIT: usize = 500;
593
594 fn cert2_for_leaf(leaf: &LeafQueryData<SeqTypes>) -> espresso_types::Certificate2<SeqTypes> {
595 let data = Vote2Data {
596 leaf_commit: leaf.leaf().commit(),
597 epoch: leaf.qc().data.epoch.unwrap(),
598 block_number: leaf.height(),
599 };
600 espresso_types::Certificate2::new(
601 data.clone(),
602 data.commit(),
603 leaf.leaf().view_number(),
604 None,
605 PhantomData,
606 )
607 }
608
609 #[test_log::test(tokio::test(flavor = "multi_thread"))]
610 async fn test_two_chain() {
611 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
612 let ds = DataSource::create(
613 DataSource::persistence_options(&storage),
614 Default::default(),
615 false,
616 )
617 .await
618 .unwrap();
619
620 let leaves = leaf_chain(1..=3, EPOCH_VERSION).await;
622 {
623 let mut tx = ds.write().await.unwrap();
624 tx.insert_leaf(&leaves[0]).await.unwrap();
625 tx.insert_leaf(&leaves[1]).await.unwrap();
626 tx.insert_leaf(&leaves[2]).await.unwrap();
627 tx.commit().await.unwrap();
628 }
629
630 let proof =
632 get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
633 .await
634 .unwrap();
635 assert_eq!(
636 proof
637 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
638 .await
639 .unwrap(),
640 leaves[0]
641 );
642 }
643
644 #[test_log::test(tokio::test(flavor = "multi_thread"))]
645 async fn test_finalized() {
646 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
647 let ds = DataSource::create(
648 DataSource::persistence_options(&storage),
649 Default::default(),
650 false,
651 )
652 .await
653 .unwrap();
654
655 let leaves = leaf_chain(1..=2, EPOCH_VERSION).await;
658 {
659 let mut tx = ds.write().await.unwrap();
660 tx.insert_leaf(&leaves[0]).await.unwrap();
661 tx.commit().await.unwrap();
662 }
663
664 let proof = get_leaf_proof_with_finalized_assumption(
665 &ds,
666 leaves[0].clone(),
667 2,
668 Duration::MAX,
669 CHAIN_LIMIT,
670 )
671 .await
672 .unwrap();
673 assert_eq!(
674 proof
675 .verify(LeafProofHint::assumption(leaves[1].leaf()))
676 .await
677 .unwrap(),
678 leaves[0]
679 );
680 }
681
682 #[test_log::test(tokio::test(flavor = "multi_thread"))]
683 async fn test_new_protocol_finalized_assumption() {
684 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
685 let ds = DataSource::create(
686 DataSource::persistence_options(&storage),
687 Default::default(),
688 false,
689 )
690 .await
691 .unwrap();
692
693 let leaves = leaf_chain(1..=2, NEW_PROTOCOL_VERSION).await;
694 {
695 let mut tx = ds.write().await.unwrap();
696 tx.insert_leaf(&leaves[0]).await.unwrap();
697 tx.commit().await.unwrap();
698 }
699
700 let proof = get_leaf_proof_with_finalized_assumption(
701 &ds,
702 leaves[0].clone(),
703 2,
704 Duration::MAX,
705 CHAIN_LIMIT,
706 )
707 .await
708 .unwrap();
709 assert!(matches!(proof.proof(), FinalityProof::Assumption));
710 assert_eq!(
711 proof
712 .verify(LeafProofHint::assumption(leaves[1].leaf()))
713 .await
714 .unwrap(),
715 leaves[0]
716 );
717 }
718
719 #[test_log::test(tokio::test(flavor = "multi_thread"))]
720 async fn test_new_protocol_cert2() {
721 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
722 let ds = DataSource::create(
723 DataSource::persistence_options(&storage),
724 Default::default(),
725 false,
726 )
727 .await
728 .unwrap();
729
730 let leaves = leaf_chain(1..=2, NEW_PROTOCOL_VERSION).await;
731 let cert2_leaf = &leaves[1];
732 let cert2 = cert2_for_leaf(cert2_leaf);
733
734 {
735 let mut tx = ds.write().await.unwrap();
736 tx.insert_leaf(&leaves[0]).await.unwrap();
737 tx.insert_leaf(cert2_leaf).await.unwrap();
738 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
739 tx.commit().await.unwrap();
740 }
741
742 let proof = get_leaf_proof_with_cert2(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
743 .await
744 .unwrap();
745 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
746 assert_eq!(
747 proof
748 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
749 .await
750 .unwrap(),
751 leaves[0]
752 );
753 }
754
755 #[test_log::test(tokio::test(flavor = "multi_thread"))]
756 async fn test_new_protocol_cert2_chain_limit() {
757 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
758 let ds = DataSource::create(
759 DataSource::persistence_options(&storage),
760 Default::default(),
761 false,
762 )
763 .await
764 .unwrap();
765
766 let leaves = leaf_chain(1..=4, NEW_PROTOCOL_VERSION).await;
768 let cert2_leaf = &leaves[3];
769 let cert2 = cert2_for_leaf(cert2_leaf);
770
771 {
772 let mut tx = ds.write().await.unwrap();
773 for leaf in &leaves {
774 tx.insert_leaf(leaf).await.unwrap();
775 }
776 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
777 tx.commit().await.unwrap();
778 }
779
780 let err = get_leaf_proof_with_cert2(&ds, leaves[0].clone(), Duration::MAX, 2)
781 .await
782 .unwrap_err();
783 assert_eq!(err.status(), StatusCode::NOT_FOUND);
784
785 let proof = get_leaf_proof_with_cert2(&ds, leaves[0].clone(), Duration::MAX, 3)
786 .await
787 .unwrap();
788 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
789 assert_eq!(
790 proof
791 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
792 .await
793 .unwrap(),
794 leaves[0]
795 );
796 }
797
798 #[test_log::test(tokio::test(flavor = "multi_thread"))]
799 async fn test_new_protocol_hint_vs_cert2() {
800 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
801 let ds = DataSource::create(
802 DataSource::persistence_options(&storage),
803 Default::default(),
804 false,
805 )
806 .await
807 .unwrap();
808
809 let leaves = leaf_chain(1..=5, NEW_PROTOCOL_VERSION).await;
810 let cert2_leaf = &leaves[1];
811 let cert2 = cert2_for_leaf(cert2_leaf);
812
813 {
814 let mut tx = ds.write().await.unwrap();
815 for leaf in &leaves {
816 tx.insert_leaf(leaf).await.unwrap();
817 }
818 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
819 tx.commit().await.unwrap();
820 }
821
822 let proof = get_leaf_proof(&ds, leaves[0].clone(), Some(5), Duration::MAX, CHAIN_LIMIT)
824 .await
825 .unwrap();
826 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
827 assert_eq!(
828 proof
829 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
830 .await
831 .unwrap(),
832 leaves[0]
833 );
834
835 let proof = get_leaf_proof(&ds, leaves[0].clone(), Some(3), Duration::MAX, CHAIN_LIMIT)
837 .await
838 .unwrap();
839 assert!(matches!(proof.proof(), FinalityProof::Assumption));
840 assert_eq!(
841 proof
842 .verify(LeafProofHint::assumption(leaves[2].leaf()))
843 .await
844 .unwrap(),
845 leaves[0]
846 );
847
848 let proof = get_leaf_proof(&ds, leaves[2].clone(), Some(5), Duration::MAX, CHAIN_LIMIT)
850 .await
851 .unwrap();
852 assert!(matches!(proof.proof(), FinalityProof::Assumption));
853 assert_eq!(
854 proof
855 .verify(LeafProofHint::assumption(leaves[4].leaf()))
856 .await
857 .unwrap(),
858 leaves[2]
859 );
860 }
861
862 #[test_log::test(tokio::test(flavor = "multi_thread"))]
863 async fn test_distant_hint_falls_through() {
864 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
865 let ds = DataSource::create(
866 DataSource::persistence_options(&storage),
867 Default::default(),
868 false,
869 )
870 .await
871 .unwrap();
872
873 let leaves = leaf_chain_with_upgrade(
876 1..=5,
877 4,
878 Upgrade::new(DRB_AND_HEADER_UPGRADE_VERSION, NEW_PROTOCOL_VERSION),
879 )
880 .await;
881 let cert2_leaf = &leaves[4];
882 let cert2 = cert2_for_leaf(cert2_leaf);
883
884 {
885 let mut tx = ds.write().await.unwrap();
886 for leaf in &leaves {
887 tx.insert_leaf(leaf).await.unwrap();
888 }
889 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
890 tx.commit().await.unwrap();
891 }
892
893 let proof = get_leaf_proof(
895 &ds,
896 leaves[0].clone(),
897 Some(1000),
898 Duration::MAX,
899 CHAIN_LIMIT,
900 )
901 .await
902 .unwrap();
903 assert!(matches!(proof.proof(), FinalityProof::HotStuff2 { .. }));
904 assert_eq!(
905 proof
906 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
907 .await
908 .unwrap(),
909 leaves[0]
910 );
911
912 let proof = get_leaf_proof(
914 &ds,
915 leaves[3].clone(),
916 Some(1000),
917 Duration::MAX,
918 CHAIN_LIMIT,
919 )
920 .await
921 .unwrap();
922 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
923 assert_eq!(
924 proof
925 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
926 .await
927 .unwrap(),
928 leaves[3]
929 );
930 }
931
932 #[test_log::test(tokio::test(flavor = "multi_thread"))]
933 async fn test_qc_chain_chain_limit() {
934 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
935 let ds = DataSource::create(
936 DataSource::persistence_options(&storage),
937 Default::default(),
938 false,
939 )
940 .await
941 .unwrap();
942
943 let leaves = leaf_chain(1..=3, EPOCH_VERSION).await;
945 {
946 let mut tx = ds.write().await.unwrap();
947 for leaf in &leaves {
948 tx.insert_leaf(leaf).await.unwrap();
949 }
950 tx.commit().await.unwrap();
951 }
952
953 let err = get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, 1)
954 .await
955 .unwrap_err();
956 assert_eq!(err.status(), StatusCode::NOT_FOUND);
957
958 let proof = get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, 2)
959 .await
960 .unwrap();
961 assert_eq!(
962 proof
963 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
964 .await
965 .unwrap(),
966 leaves[0]
967 );
968 }
969
970 #[test_log::test(tokio::test(flavor = "multi_thread"))]
971 async fn test_finalized_hint_too_far() {
972 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
973 let ds = DataSource::create(
974 DataSource::persistence_options(&storage),
975 Default::default(),
976 false,
977 )
978 .await
979 .unwrap();
980
981 let leaves = leaf_chain(1..=2, EPOCH_VERSION).await;
982 {
983 let mut tx = ds.write().await.unwrap();
984 tx.insert_leaf(&leaves[0]).await.unwrap();
985 tx.commit().await.unwrap();
986 }
987
988 let err = get_leaf_proof_with_finalized_assumption(
989 &ds,
990 leaves[0].clone(),
991 1000,
992 Duration::MAX,
993 10,
994 )
995 .await
996 .unwrap_err();
997 assert_eq!(err.status(), StatusCode::BAD_REQUEST);
998 }
999
1000 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1001 async fn test_qc_chain_new_protocol_cutover() {
1002 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1003 let ds = DataSource::create(
1004 DataSource::persistence_options(&storage),
1005 Default::default(),
1006 false,
1007 )
1008 .await
1009 .unwrap();
1010
1011 let leaves = custom_leaf_chain_with_upgrade(
1014 1..=4,
1015 2,
1016 Upgrade::new(DRB_AND_HEADER_UPGRADE_VERSION, NEW_PROTOCOL_VERSION),
1017 |proposal| {
1018 proposal.view_number = ViewNumber::new(proposal.block_header.height() * 2);
1019 },
1020 )
1021 .await;
1022 assert_eq!(leaves[0].header().version(), DRB_AND_HEADER_UPGRADE_VERSION);
1023 assert_eq!(leaves[1].header().version(), NEW_PROTOCOL_VERSION);
1024 let cert2_leaf = &leaves[3];
1025 let cert2 = cert2_for_leaf(cert2_leaf);
1026
1027 {
1028 let mut tx = ds.write().await.unwrap();
1029 for leaf in &leaves {
1030 tx.insert_leaf(leaf).await.unwrap();
1031 }
1032 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
1033 tx.commit().await.unwrap();
1034 }
1035
1036 let proof =
1037 get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
1038 .await
1039 .unwrap();
1040 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
1041 assert_eq!(
1042 proof
1043 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
1044 .await
1045 .unwrap(),
1046 leaves[0]
1047 );
1048 }
1049
1050 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1051 async fn test_qc_chain_new_protocol_cutover_two_chain() {
1052 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1053 let ds = DataSource::create(
1054 DataSource::persistence_options(&storage),
1055 Default::default(),
1056 false,
1057 )
1058 .await
1059 .unwrap();
1060
1061 let leaves = leaf_chain_with_upgrade(
1064 1..=3,
1065 3,
1066 Upgrade::new(DRB_AND_HEADER_UPGRADE_VERSION, NEW_PROTOCOL_VERSION),
1067 )
1068 .await;
1069 assert_eq!(leaves[1].header().version(), DRB_AND_HEADER_UPGRADE_VERSION);
1070 assert_eq!(leaves[2].header().version(), NEW_PROTOCOL_VERSION);
1071
1072 {
1073 let mut tx = ds.write().await.unwrap();
1074 for leaf in &leaves {
1075 tx.insert_leaf(leaf).await.unwrap();
1076 }
1077 tx.commit().await.unwrap();
1078 }
1079
1080 let proof =
1081 get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
1082 .await
1083 .unwrap();
1084 assert!(matches!(proof.proof(), FinalityProof::HotStuff2 { .. }));
1085 assert_eq!(
1086 proof
1087 .verify(LeafProofHint::Quorum(&VersionCheckQuorum::new(
1088 leaves.iter().map(|leaf| leaf.leaf().clone())
1089 )))
1090 .await
1091 .unwrap(),
1092 leaves[0]
1093 );
1094 }
1095
1096 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1097 async fn test_qc_chain_last_legacy_leaf_uses_cert2() {
1098 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1099 let ds = DataSource::create(
1100 DataSource::persistence_options(&storage),
1101 Default::default(),
1102 false,
1103 )
1104 .await
1105 .unwrap();
1106
1107 let leaves = leaf_chain_with_upgrade(
1110 1..=4,
1111 2,
1112 Upgrade::new(DRB_AND_HEADER_UPGRADE_VERSION, NEW_PROTOCOL_VERSION),
1113 )
1114 .await;
1115 assert_eq!(leaves[0].header().version(), DRB_AND_HEADER_UPGRADE_VERSION);
1116 assert_eq!(leaves[1].header().version(), NEW_PROTOCOL_VERSION);
1117
1118 let cert2_leaf = &leaves[2];
1119 let cert2 = cert2_for_leaf(cert2_leaf);
1120
1121 {
1122 let mut tx = ds.write().await.unwrap();
1123 for leaf in &leaves {
1124 tx.insert_leaf(leaf).await.unwrap();
1125 }
1126 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
1127 tx.commit().await.unwrap();
1128 }
1129
1130 let proof =
1131 get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
1132 .await
1133 .unwrap();
1134 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
1135 assert_eq!(
1136 proof
1137 .verify(LeafProofHint::Quorum(&VersionCheckQuorum::new(
1138 leaves.iter().map(|leaf| leaf.leaf().clone())
1139 )))
1140 .await
1141 .unwrap(),
1142 leaves[0]
1143 );
1144 }
1145
1146 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1151 async fn test_leaf_before_cutover_uses_hotstuff2() {
1152 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1153 let ds = DataSource::create(
1154 DataSource::persistence_options(&storage),
1155 Default::default(),
1156 false,
1157 )
1158 .await
1159 .unwrap();
1160
1161 let leaves = leaf_chain_with_upgrade(
1163 1..=5,
1164 4,
1165 Upgrade::new(DRB_AND_HEADER_UPGRADE_VERSION, NEW_PROTOCOL_VERSION),
1166 )
1167 .await;
1168 assert!(leaves[1].header().version() < NEW_PROTOCOL_VERSION);
1169 assert!(leaves[3].header().version() >= NEW_PROTOCOL_VERSION);
1170
1171 {
1172 let mut tx = ds.write().await.unwrap();
1173 for leaf in &leaves {
1174 tx.insert_leaf(leaf).await.unwrap();
1175 }
1176 tx.commit().await.unwrap();
1177 }
1178
1179 let proof =
1180 get_leaf_proof_with_qc_chain(&ds, leaves[1].clone(), Duration::MAX, CHAIN_LIMIT)
1181 .await
1182 .unwrap();
1183 assert!(matches!(proof.proof(), FinalityProof::HotStuff2 { .. }));
1184 assert_eq!(
1185 proof
1186 .verify(LeafProofHint::Quorum(&VersionCheckQuorum::new(
1187 leaves.iter().map(|leaf| leaf.leaf().clone())
1188 )))
1189 .await
1190 .unwrap(),
1191 leaves[1]
1192 );
1193 }
1194
1195 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1201 async fn test_leaf_before_cutover_uses_cert2() {
1202 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1203 let ds = DataSource::create(
1204 DataSource::persistence_options(&storage),
1205 Default::default(),
1206 false,
1207 )
1208 .await
1209 .unwrap();
1210
1211 let leaves = custom_leaf_chain_with_upgrade(
1214 1..=5,
1215 4,
1216 Upgrade::new(DRB_AND_HEADER_UPGRADE_VERSION, NEW_PROTOCOL_VERSION),
1217 |proposal| {
1218 proposal.view_number = ViewNumber::new(proposal.block_header.height() * 2);
1219 },
1220 )
1221 .await;
1222 assert!(leaves[1].header().version() < NEW_PROTOCOL_VERSION);
1223 assert!(leaves[3].header().version() >= NEW_PROTOCOL_VERSION);
1224 let cert2_leaf = &leaves[3];
1225 let cert2 = cert2_for_leaf(cert2_leaf);
1226
1227 {
1228 let mut tx = ds.write().await.unwrap();
1229 for leaf in &leaves {
1230 tx.insert_leaf(leaf).await.unwrap();
1231 }
1232 tx.insert_cert2(cert2_leaf.height(), cert2).await.unwrap();
1233 tx.commit().await.unwrap();
1234 }
1235
1236 let proof =
1237 get_leaf_proof_with_qc_chain(&ds, leaves[1].clone(), Duration::MAX, CHAIN_LIMIT)
1238 .await
1239 .unwrap();
1240 assert!(matches!(proof.proof(), FinalityProof::NewProtocol { .. }));
1241 assert_eq!(
1242 proof
1243 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
1244 .await
1245 .unwrap(),
1246 leaves[1]
1247 );
1248 }
1249
1250 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1251 async fn test_bad_finalized() {
1252 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1253 let ds = DataSource::create(
1254 DataSource::persistence_options(&storage),
1255 Default::default(),
1256 false,
1257 )
1258 .await
1259 .unwrap();
1260
1261 let leaves = leaf_chain(1..2, EPOCH_VERSION).await;
1264 {
1265 let mut tx = ds.write().await.unwrap();
1266 tx.insert_leaf(&leaves[0]).await.unwrap();
1267 tx.commit().await.unwrap();
1268 }
1269
1270 let err = get_leaf_proof_with_finalized_assumption(
1271 &ds,
1272 leaves[0].clone(),
1273 0,
1274 Duration::MAX,
1275 CHAIN_LIMIT,
1276 )
1277 .await
1278 .unwrap_err();
1279 assert_eq!(err.status(), StatusCode::BAD_REQUEST);
1280 }
1281
1282 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1283 async fn test_no_chain() {
1284 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1285 let ds = DataSource::create(
1286 DataSource::persistence_options(&storage),
1287 Default::default(),
1288 false,
1289 )
1290 .await
1291 .unwrap();
1292
1293 let leaves = leaf_chain(1..=4, EPOCH_VERSION).await;
1296 {
1297 let mut tx = ds.write().await.unwrap();
1298 tx.insert_leaf(&leaves[0]).await.unwrap();
1299 tx.insert_leaf(&leaves[2]).await.unwrap();
1300 tx.insert_leaf(&leaves[3]).await.unwrap();
1301 tx.commit().await.unwrap();
1302 }
1303
1304 let err = get_leaf_proof_with_qc_chain(
1305 &ds,
1306 leaves[0].clone(),
1307 Duration::from_secs(1),
1308 CHAIN_LIMIT,
1309 )
1310 .await
1311 .unwrap_err();
1312 assert_eq!(err.status(), StatusCode::NOT_FOUND);
1313
1314 let err = get_leaf_proof_with_finalized_assumption(
1318 &ds,
1319 leaves[0].clone(),
1320 4,
1321 Duration::from_secs(1),
1322 CHAIN_LIMIT,
1323 )
1324 .await
1325 .unwrap_err();
1326 assert_eq!(err.status(), StatusCode::NOT_FOUND);
1327 }
1328
1329 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1330 async fn test_final_qcs() {
1331 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1332 let ds = DataSource::create(
1333 DataSource::persistence_options(&storage),
1334 Default::default(),
1335 false,
1336 )
1337 .await
1338 .unwrap();
1339
1340 let leaves = leaf_chain(1..=3, EPOCH_VERSION).await;
1342 let qcs = [
1343 CertificatePair::for_parent(leaves[1].leaf()),
1344 CertificatePair::for_parent(leaves[2].leaf()),
1345 ];
1346 {
1347 let mut tx = ds.write().await.unwrap();
1348 tx.insert_leaf_with_qc_chain(&leaves[0], Some(qcs.clone()))
1349 .await
1350 .unwrap();
1351 tx.commit().await.unwrap();
1352 }
1353
1354 let proof =
1355 get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
1356 .await
1357 .unwrap();
1358 assert_eq!(
1359 proof
1360 .verify(LeafProofHint::Quorum(&AlwaysTrueQuorum))
1361 .await
1362 .unwrap(),
1363 leaves[0]
1364 );
1365 }
1366
1367 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1368 async fn test_upgrade_to_epochs() {
1369 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1370 let ds = DataSource::create(
1371 DataSource::persistence_options(&storage),
1372 Default::default(),
1373 false,
1374 )
1375 .await
1376 .unwrap();
1377
1378 let leaves = leaf_chain_with_upgrade(1..=4, 2, ENABLE_EPOCHS).await;
1382 assert_eq!(leaves[0].header().version(), LEGACY_VERSION);
1383 assert_eq!(leaves[1].header().version(), DRB_AND_HEADER_UPGRADE_VERSION);
1384 let qcs = [
1385 CertificatePair::for_parent(leaves[2].leaf()),
1386 CertificatePair::for_parent(leaves[3].leaf()),
1387 ];
1388 {
1389 let mut tx = ds.write().await.unwrap();
1390 tx.insert_leaf(&leaves[0]).await.unwrap();
1391 tx.insert_leaf_with_qc_chain(&leaves[1], Some(qcs.clone()))
1392 .await
1393 .unwrap();
1394 tx.commit().await.unwrap();
1395 }
1396
1397 let proof =
1398 get_leaf_proof_with_qc_chain(&ds, leaves[0].clone(), Duration::MAX, CHAIN_LIMIT)
1399 .await
1400 .unwrap();
1401 assert_eq!(
1402 proof
1403 .verify(LeafProofHint::Quorum(&VersionCheckQuorum::new(
1404 leaves.iter().map(|leaf| leaf.leaf().clone())
1405 )))
1406 .await
1407 .unwrap(),
1408 leaves[0]
1409 );
1410 assert!(matches!(proof.proof(), FinalityProof::HotStuff2 { .. }))
1411 }
1412
1413 #[tokio::test]
1414 #[test_log::test]
1415 async fn test_header_proof() {
1416 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1417 let ds = DataSource::create(
1418 DataSource::persistence_options(&storage),
1419 Default::default(),
1420 false,
1421 )
1422 .await
1423 .unwrap();
1424
1425 let leaves = leaf_chain(0..=2, EPOCH_VERSION).await;
1427 let mts = leaves
1428 .iter()
1429 .scan(
1430 BlockMerkleTree::new(BLOCK_MERKLE_TREE_HEIGHT),
1431 |mt, leaf| {
1432 assert_eq!(mt.commitment(), leaf.header().block_merkle_tree_root());
1433 let item = mt.clone();
1434 mt.push(leaf.block_hash()).unwrap();
1435 Some(item)
1436 },
1437 )
1438 .collect::<Vec<_>>();
1439
1440 {
1442 let mut tx = ds.write().await.unwrap();
1443 for (leaf, mt) in leaves.iter().zip(&mts) {
1444 tx.insert_leaf(leaf).await.unwrap();
1445
1446 if leaf.height() > 0 {
1447 let merkle_path = mt.lookup(leaf.height() - 1).expect_ok().unwrap().1;
1448 UpdateStateData::<SeqTypes, BlockMerkleTree, _>::insert_merkle_nodes(
1449 &mut tx,
1450 merkle_path,
1451 ToTraversalPath::<{ BlockMerkleTree::ARITY }>::to_traversal_path(
1452 &(leaf.height() - 1),
1453 BLOCK_MERKLE_TREE_HEIGHT,
1454 ),
1455 leaf.height(),
1456 )
1457 .await
1458 .unwrap();
1459 UpdateStateData::<SeqTypes, BlockMerkleTree, _>::set_last_state_height(
1460 &mut tx,
1461 leaf.height() as usize,
1462 )
1463 .await
1464 .unwrap();
1465 }
1466 }
1467 tx.commit().await.unwrap();
1468 }
1469
1470 for (root, mt) in mts.iter().enumerate().skip(1) {
1472 for (height, leaf) in leaves.iter().enumerate().take(root) {
1473 tracing::info!(root, height, "test happy path");
1474 let proof =
1475 get_header_proof(&ds, root as u64, BlockId::Number(height), Duration::MAX)
1476 .await
1477 .unwrap();
1478 assert_eq!(proof.verify_ref(mt.commitment()).unwrap(), leaf.header());
1479 }
1480 }
1481
1482 let err = get_header_proof(&ds, 5, BlockId::Number(4), Duration::from_secs(1))
1484 .await
1485 .unwrap_err();
1486 assert_eq!(err.status(), StatusCode::NOT_FOUND);
1487
1488 let err = get_header_proof(&ds, 1, BlockId::Number(1), Duration::MAX)
1490 .await
1491 .unwrap_err();
1492 assert_eq!(err.status(), StatusCode::BAD_REQUEST);
1493 }
1494
1495 #[tokio::test]
1496 #[test_log::test]
1497 async fn test_namespace_proof() {
1498 let storage = <DataSource as TestableSequencerDataSource>::create_storage().await;
1499 let ds = DataSource::create(
1500 DataSource::persistence_options(&storage),
1501 Default::default(),
1502 false,
1503 )
1504 .await
1505 .unwrap();
1506
1507 let client = TestClient::default();
1509 let leaves = join_all((0..=2).map(|i| client.leaf(i))).await;
1510 let payloads = join_all((0..=2).map(|i| client.payload(i))).await;
1511 let vid_commons = join_all((0..=2).map(|i| client.vid_common(i))).await;
1512
1513 {
1515 let mut tx = ds.write().await.unwrap();
1516 for (leaf, payload, vid_common) in izip!(&leaves, &payloads, &vid_commons) {
1517 tx.insert_leaf(leaf).await.unwrap();
1518 tx.insert_block(&BlockQueryData::<SeqTypes>::new(
1519 leaf.header().clone(),
1520 payload.clone(),
1521 ))
1522 .await
1523 .unwrap();
1524 tx.insert_vid(
1525 &VidCommonQueryData::<SeqTypes>::new(leaf.header().clone(), vid_common.clone()),
1526 None,
1527 )
1528 .await
1529 .unwrap();
1530 }
1531 tx.commit().await.unwrap();
1532 }
1533
1534 let ns = payloads[0]
1536 .transaction(&TransactionIndex {
1537 ns_index: 0.into(),
1538 position: 0,
1539 })
1540 .unwrap()
1541 .namespace();
1542 let proofs = get_namespace_proof_range(&ds, 0, 3, ns.into(), Duration::MAX, 100)
1543 .await
1544 .unwrap();
1545 assert_eq!(proofs.len(), 3);
1546 for (leaf, proof) in leaves.iter().zip(proofs) {
1547 proof.verify(leaf.header(), ns).unwrap();
1548 }
1549
1550 let tx = payloads[1]
1552 .transaction(&TransactionIndex {
1553 ns_index: 0.into(),
1554 position: 0,
1555 })
1556 .unwrap();
1557 let ns = tx.namespace();
1558 let proofs = get_namespace_proof_range(&ds, 1, 2, ns.into(), Duration::MAX, 100)
1559 .await
1560 .unwrap();
1561 assert_eq!(proofs.len(), 1);
1562 assert_eq!(proofs[0].verify(leaves[1].header(), ns).unwrap(), [tx]);
1563
1564 let err = get_namespace_proof_range(&ds, 0, 4, ns.into(), Duration::from_secs(1), 100)
1566 .await
1567 .unwrap_err();
1568 assert_eq!(err.status(), StatusCode::NOT_FOUND);
1569
1570 let err = get_namespace_proof_range(&ds, 1, 0, ns.into(), Duration::from_secs(1), 100)
1572 .await
1573 .unwrap_err();
1574 assert_eq!(err.status(), StatusCode::BAD_REQUEST);
1575 assert!(
1576 err.to_string().contains("requested empty interval"),
1577 "{err:#}"
1578 );
1579
1580 let err = get_namespace_proof_range(&ds, 0, 10_000, ns.into(), Duration::from_secs(1), 100)
1582 .await
1583 .unwrap_err();
1584 assert_eq!(err.status(), StatusCode::BAD_REQUEST);
1585 assert!(err.to_string().contains("exceeds maximum size"), "{err:#}");
1586 }
1587}