1mod extension;
30pub mod fetching;
31pub mod fs;
32mod metrics;
33mod notifier;
34pub mod sql;
35pub mod storage;
36mod update;
37
38pub use extension::ExtensibleDataSource;
39pub use fetching::{AvailabilityProvider, FetchingDataSource};
40#[cfg(feature = "file-system-data-source")]
41pub use fs::FileSystemDataSource;
42#[cfg(feature = "metrics-data-source")]
43pub use metrics::MetricsDataSource;
44#[cfg(feature = "sql-data-source")]
45pub use sql::SqlDataSource;
46pub use update::{Transaction, UpdateDataSource, VersionedDataSource};
47
48#[cfg(any(test, feature = "testing"))]
49mod test_helpers {
50 use std::ops::{Bound, RangeBounds};
51
52 use futures::{
53 future,
54 stream::{BoxStream, StreamExt},
55 };
56
57 use crate::{
58 availability::{BlockQueryData, Fetch, LeafQueryData},
59 node::NodeDataSource,
60 testing::{consensus::TestableDataSource, mocks::MockTypes},
61 };
62
63 async fn bound_range<R, D>(ds: &D, range: R) -> impl RangeBounds<usize> + use<R, D>
65 where
66 D: TestableDataSource,
67 R: RangeBounds<usize>,
68 {
69 let start = range.start_bound().cloned();
70 let mut end = range.end_bound().cloned();
71 if end == Bound::Unbounded {
72 end = Bound::Excluded(NodeDataSource::block_height(ds).await.unwrap());
73 }
74 (start, end)
75 }
76
77 pub async fn block_range<R, D>(
79 ds: &D,
80 range: R,
81 ) -> BoxStream<'static, BlockQueryData<MockTypes>>
82 where
83 D: TestableDataSource,
84 R: RangeBounds<usize> + Send + 'static,
85 {
86 ds.get_block_range(bound_range(ds, range).await)
87 .await
88 .then(Fetch::resolve)
89 .boxed()
90 }
91
92 pub async fn leaf_range<R, D>(ds: &D, range: R) -> BoxStream<'static, LeafQueryData<MockTypes>>
94 where
95 D: TestableDataSource,
96 R: RangeBounds<usize> + Send + 'static,
97 {
98 ds.get_leaf_range(bound_range(ds, range).await)
99 .await
100 .then(Fetch::resolve)
101 .boxed()
102 }
103
104 pub async fn get_non_empty_blocks<D>(
105 ds: &D,
106 ) -> Vec<(LeafQueryData<MockTypes>, BlockQueryData<MockTypes>)>
107 where
108 D: TestableDataSource,
109 {
110 leaf_range(ds, 1..)
112 .await
113 .zip(block_range(ds, 1..).await)
114 .filter(|(_, block)| future::ready(!block.is_empty()))
115 .collect()
116 .await
117 }
118}
119
120#[cfg(any(test, feature = "testing"))]
122#[espresso_macros::generic_tests]
123pub mod availability_tests {
124 use std::{
125 collections::HashMap,
126 fmt::Debug,
127 ops::{Bound, RangeBounds},
128 };
129
130 use committable::Committable;
131 use futures::stream::StreamExt;
132 use hotshot::traits::BlockPayload;
133 use hotshot_example_types::{
134 block_types::TestBlockPayload,
135 node_types::{TEST_VERSIONS, TestTypes},
136 };
137 use hotshot_types::{
138 data::{Leaf2, vid_commitment},
139 traits::block_contents::EncodeBytes,
140 vote::HasViewNumber,
141 };
142
143 use super::test_helpers::*;
144 use crate::{
145 availability::{
146 BlockId, BlockInfo, BlockQueryData, LeafQueryData, VidCommonQueryData, payload_size,
147 },
148 data_source::{
149 Transaction,
150 storage::{AvailabilityStorage, NodeStorage, UpdateAvailabilityStorage},
151 },
152 node::NodeDataSource,
153 testing::{
154 consensus::{MockNetwork, TestableDataSource},
155 mocks::{MockTypes, mock_transaction},
156 },
157 types::HeightIndexed,
158 };
159
160 async fn validate<D: TestableDataSource>(ds: &D)
161 where
162 for<'a> D::ReadOnly<'a>: AvailabilityStorage<MockTypes> + NodeStorage<MockTypes>,
163 {
164 let mut seen_payloads = HashMap::new();
167 let mut seen_transactions = HashMap::new();
168 let mut leaves = leaf_range(ds, ..).await.enumerate();
169 while let Some((i, leaf)) = leaves.next().await {
170 assert_eq!(leaf.height(), i as u64);
171 assert_eq!(
172 leaf.hash(),
173 <Leaf2<MockTypes> as Committable>::commit(&leaf.leaf)
174 );
175
176 tracing::info!("looking up leaf {i} various ways");
178 assert_eq!(leaf, ds.get_leaf(i).await.await);
179 assert_eq!(leaf, ds.get_leaf(leaf.hash()).await.await);
180
181 tracing::info!("looking up block {i} various ways");
182 let block = ds.get_block(i).await.await;
183 assert_eq!(leaf.block_hash(), block.hash());
184 assert_eq!(block.height(), i as u64);
185 assert_eq!(block.hash(), block.header().commit());
186 assert_eq!(block.size(), payload_size::<MockTypes>(block.payload()));
187
188 assert_eq!(block, ds.get_block(i).await.await);
190 assert_eq!(ds.get_block(block.hash()).await.await.height(), i as u64);
191 let ix = seen_payloads
201 .entry(block.payload_hash())
202 .or_insert(i as u64);
203 if let Ok(block) = ds
204 .get_block(BlockId::PayloadHash(block.payload_hash()))
205 .await
206 .try_resolve()
207 {
208 assert_eq!(block.height(), *ix);
209 } else {
210 tracing::warn!(
211 "skipping block by payload index check for missing payload {:?}",
212 block.header()
213 );
214 ds.get_block(BlockId::PayloadHash(block.payload_hash()))
216 .await
217 .await;
218 }
219
220 tracing::info!("looking up payload {i} various ways");
222 let expected_payload = block.clone().into();
223 assert_eq!(ds.get_payload(i).await.await, expected_payload);
224 assert_eq!(ds.get_payload(block.hash()).await.await, expected_payload);
225 if let Ok(payload) = ds
228 .get_payload(BlockId::PayloadHash(block.payload_hash()))
229 .await
230 .try_resolve()
231 {
232 if *ix == i as u64 {
233 assert_eq!(payload, expected_payload);
234 }
235 } else {
236 tracing::warn!(
237 "skipping payload index check for missing payload {:?}",
238 block.header()
239 );
240 ds.get_payload(BlockId::PayloadHash(block.payload_hash()))
242 .await
243 .await;
244 }
245
246 tracing::info!("looking up VID common {i} various ways");
248 let common = ds.get_vid_common(block.height() as usize).await.await;
249 assert_eq!(common, ds.get_vid_common(block.hash()).await.await);
250 if let Ok(res) = ds
253 .get_vid_common(BlockId::PayloadHash(block.payload_hash()))
254 .await
255 .try_resolve()
256 {
257 if *ix == i as u64 {
258 assert_eq!(res, common);
259 }
260 } else {
261 tracing::warn!(
262 "skipping VID common index check for missing data {:?}",
263 block.header()
264 );
265 let res = ds
267 .get_vid_common(BlockId::PayloadHash(block.payload_hash()))
268 .await
269 .await;
270 assert_eq!(res.payload_hash(), common.payload_hash());
271 }
272
273 for (j, txn) in block.enumerate() {
274 tracing::info!("looking up transaction {i},{j:?}");
275
276 let ix = seen_transactions
284 .entry(txn.commit())
285 .or_insert((i as u64, j.clone()));
286 if let Ok(tx_data) = ds
287 .get_block_containing_transaction(txn.commit())
288 .await
289 .try_resolve()
290 {
291 assert_eq!(tx_data.transaction.transaction(), &txn);
292 assert_eq!(tx_data.transaction.block_height(), ix.0);
293 assert_eq!(tx_data.transaction.index(), ix.1.position as u64);
294 assert_eq!(tx_data.index, ix.1);
295 assert_eq!(tx_data.block, block);
296 } else {
297 tracing::warn!(
298 "skipping transaction index check for missing transaction {j:?} {txn:?}"
299 );
300 ds.get_block_containing_transaction(txn.commit())
302 .await
303 .await;
304 }
305 }
306 }
307
308 {
310 let mut tx = ds.read().await.unwrap();
311 let block_height = NodeStorage::block_height(&mut tx).await.unwrap();
312 let last_leaf = tx.get_leaf((block_height - 1).into()).await.unwrap();
313
314 if last_leaf.qc().data.epoch.is_some() {
315 tracing::info!(block_height, "checking QC chain");
316 let qc_chain = tx.latest_qc_chain().await.unwrap().unwrap();
317
318 assert_eq!(last_leaf.height(), (block_height - 1) as u64);
319 assert_eq!(qc_chain[0].view_number(), last_leaf.leaf().view_number());
320 assert_eq!(qc_chain[0].leaf_commit(), last_leaf.hash());
321 assert_eq!(qc_chain[1].view_number(), qc_chain[0].view_number() + 1);
322 }
323 }
324 }
325
326 #[test_log::test(tokio::test(flavor = "multi_thread"))]
327 pub async fn test_update<D: TestableDataSource>()
328 where
329 for<'a> D::ReadOnly<'a>: AvailabilityStorage<MockTypes> + NodeStorage<MockTypes>,
330 {
331 let mut network = MockNetwork::<D>::init().await;
332 let ds = network.data_source();
333
334 network.start().await;
335 assert_eq!(get_non_empty_blocks(&ds).await, vec![]);
336
337 let mut blocks = ds.subscribe_blocks(0).await.enumerate();
340 for nonce in 0..3 {
341 let txn = mock_transaction(vec![nonce]);
342 network.submit_transaction(txn).await;
343
344 let (i, block) = loop {
346 tracing::info!("waiting for tx {nonce}");
347 let (i, block) = blocks.next().await.unwrap();
348 if !block.is_empty() {
349 break (i, block);
350 }
351 tracing::info!("block {i} is empty");
352 };
353
354 tracing::info!("got tx {nonce} in block {i}");
355 assert_eq!(ds.get_block(i).await.await, block);
356 validate(&ds).await;
357 }
358
359 {
363 tracing::info!("checking persisted storage");
364 let storage = D::connect(network.storage()).await;
365
366 let block_height = NodeDataSource::block_height(&ds).await.unwrap();
370 assert_eq!(
371 ds.get_block_range(..block_height)
372 .await
373 .map(|fetch| fetch.try_resolve().ok())
374 .collect::<Vec<_>>()
375 .await,
376 storage
377 .get_block_range(..block_height)
378 .await
379 .map(|fetch| fetch.try_resolve().ok())
380 .collect::<Vec<_>>()
381 .await
382 );
383 assert_eq!(
384 ds.get_leaf_range(..block_height)
385 .await
386 .map(|fetch| fetch.try_resolve().ok())
387 .collect::<Vec<_>>()
388 .await,
389 storage
390 .get_leaf_range(..block_height)
391 .await
392 .map(|fetch| fetch.try_resolve().ok())
393 .collect::<Vec<_>>()
394 .await
395 );
396 }
397 }
398
399 #[test_log::test(tokio::test(flavor = "multi_thread"))]
400 pub async fn test_range<D: TestableDataSource>()
401 where
402 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
403 {
404 let mut network = MockNetwork::<D>::init().await;
405 let ds = network.data_source();
406 network.start().await;
407
408 let block_height = loop {
410 let mut tx = ds.read().await.unwrap();
411 let block_height = tx.block_height().await.unwrap();
412 if block_height >= 3 {
413 break block_height as u64;
414 }
415 };
416
417 do_range_test(&ds, 1..=2, 1..3).await; do_range_test(&ds, 1..3, 1..3).await; do_range_test(&ds, 1.., 1..block_height).await; do_range_test(&ds, ..=2, 0..3).await; do_range_test(&ds, ..3, 0..3).await; do_range_test(&ds, .., 0..block_height).await; do_range_test(&ds, ExRange(0..=2), 1..3).await; do_range_test(&ds, ExRange(0..3), 1..3).await; do_range_test(&ds, ExRange(0..), 1..block_height).await; }
429
430 async fn do_range_test<D, R, I>(ds: &D, range: R, expected_indices: I)
431 where
432 D: TestableDataSource,
433 R: RangeBounds<usize> + Clone + Debug + Send + 'static,
434 I: IntoIterator<Item = u64>,
435 {
436 tracing::info!("testing range {range:?}");
437
438 let mut leaves = ds.get_leaf_range(range.clone()).await;
439 let mut blocks = ds.get_block_range(range.clone()).await;
440 let mut payloads = ds.get_payload_range(range.clone()).await;
441 let mut payloads_meta = ds.get_payload_metadata_range(range.clone()).await;
442 let mut vid_common = ds.get_vid_common_range(range.clone()).await;
443 let mut vid_common_meta = ds.get_vid_common_metadata_range(range.clone()).await;
444
445 for i in expected_indices {
446 tracing::info!(i, "check entries");
447 let leaf = leaves.next().await.unwrap().await;
448 let block = blocks.next().await.unwrap().await;
449 let payload = payloads.next().await.unwrap().await;
450 let payload_meta = payloads_meta.next().await.unwrap().await;
451 let common = vid_common.next().await.unwrap().await;
452 let common_meta = vid_common_meta.next().await.unwrap().await;
453 assert_eq!(leaf.height(), i);
454 assert_eq!(block.height(), i);
455 assert_eq!(payload, ds.get_payload(i as usize).await.await);
456 assert_eq!(payload_meta, block.into());
457 assert_eq!(common, ds.get_vid_common(i as usize).await.await);
458 assert_eq!(common_meta, common.into());
459 }
460
461 if range.end_bound() == Bound::Unbounded {
462 loop {
465 let fetch_leaf = leaves.next().await.unwrap();
466 let fetch_block = blocks.next().await.unwrap();
467 let fetch_payload = payloads.next().await.unwrap();
468 let fetch_payload_meta = payloads_meta.next().await.unwrap();
469 let fetch_common = vid_common.next().await.unwrap();
470 let fetch_common_meta = vid_common_meta.next().await.unwrap();
471
472 if fetch_leaf.try_resolve().is_ok()
473 && fetch_block.try_resolve().is_ok()
474 && fetch_payload.try_resolve().is_ok()
475 && fetch_payload_meta.try_resolve().is_ok()
476 && fetch_common.try_resolve().is_ok()
477 && fetch_common_meta.try_resolve().is_ok()
478 {
479 tracing::info!("searching for end of available objects");
480 } else {
481 break;
482 }
483 }
484 } else {
485 assert!(leaves.next().await.is_none());
487 assert!(blocks.next().await.is_none());
488 assert!(payloads.next().await.is_none());
489 assert!(payloads_meta.next().await.is_none());
490 assert!(vid_common.next().await.is_none());
491 assert!(vid_common_meta.next().await.is_none());
492 }
493 }
494
495 #[test_log::test(tokio::test(flavor = "multi_thread"))]
496 pub async fn test_range_rev<D: TestableDataSource>()
497 where
498 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
499 {
500 let mut network = MockNetwork::<D>::init().await;
501 let ds = network.data_source();
502 network.start().await;
503
504 ds.subscribe_leaves(5).await.next().await.unwrap();
506
507 do_range_rev_test(&ds, Bound::Included(1), 5, 1..=5).await;
509 do_range_rev_test(&ds, Bound::Excluded(1), 5, 2..=5).await;
510 do_range_rev_test(&ds, Bound::Unbounded, 5, 0..=5).await;
511 }
512
513 async fn do_range_rev_test<D>(
514 ds: &D,
515 start: Bound<usize>,
516 end: usize,
517 expected_indices: impl DoubleEndedIterator<Item = u64>,
518 ) where
519 D: TestableDataSource,
520 {
521 tracing::info!("testing range {start:?}-{end}");
522
523 let mut leaves = ds.get_leaf_range_rev(start, end).await;
524 let mut blocks = ds.get_block_range_rev(start, end).await;
525 let mut payloads = ds.get_payload_range_rev(start, end).await;
526 let mut payloads_meta = ds.get_payload_metadata_range_rev(start, end).await;
527 let mut vid_common = ds.get_vid_common_range_rev(start, end).await;
528 let mut vid_common_meta = ds.get_vid_common_metadata_range_rev(start, end).await;
529
530 for i in expected_indices.rev() {
531 tracing::info!(i, "check entries");
532 let leaf = leaves.next().await.unwrap().await;
533 let block = blocks.next().await.unwrap().await;
534 let payload = payloads.next().await.unwrap().await;
535 let payload_meta = payloads_meta.next().await.unwrap().await;
536 let common = vid_common.next().await.unwrap().await;
537 let common_meta = vid_common_meta.next().await.unwrap().await;
538 assert_eq!(leaf.height(), i);
539 assert_eq!(block.height(), i);
540 assert_eq!(payload.height(), i);
541 assert_eq!(payload_meta.height(), i);
542 assert_eq!(common, ds.get_vid_common(i as usize).await.await);
543 assert_eq!(
544 common_meta,
545 ds.get_vid_common_metadata(i as usize).await.await
546 );
547 }
548
549 assert!(leaves.next().await.is_none());
551 assert!(blocks.next().await.is_none());
552 assert!(payloads.next().await.is_none());
553 assert!(payloads_meta.next().await.is_none());
554 assert!(vid_common.next().await.is_none());
555 assert!(vid_common_meta.next().await.is_none());
556 }
557
558 #[derive(Clone, Copy, Debug)]
560 struct ExRange<R>(R);
561
562 impl<R: RangeBounds<usize>> RangeBounds<usize> for ExRange<R> {
563 fn start_bound(&self) -> Bound<&usize> {
564 match self.0.start_bound() {
565 Bound::Included(x) => Bound::Excluded(x),
566 Bound::Excluded(x) => Bound::Excluded(x),
567 Bound::Unbounded => Bound::Excluded(&0),
568 }
569 }
570
571 fn end_bound(&self) -> Bound<&usize> {
572 self.0.end_bound()
573 }
574 }
575
576 #[tokio::test]
581 #[test_log::test]
582 pub async fn test_insert_consecutive_identical_blocks<D: TestableDataSource>()
583 where
584 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>,
585 {
586 let storage = D::create(0).await;
587 let ds = D::connect(&storage).await;
588
589 let leaf = LeafQueryData::<MockTypes>::genesis(
590 &Default::default(),
591 &Default::default(),
592 TEST_VERSIONS.test,
593 )
594 .await;
595 let block = BlockQueryData::<MockTypes>::genesis(
596 &Default::default(),
597 &Default::default(),
598 TEST_VERSIONS.test.base,
599 )
600 .await;
601 let vid = VidCommonQueryData::<MockTypes>::genesis(
602 &Default::default(),
603 &Default::default(),
604 TEST_VERSIONS.test.base,
605 )
606 .await;
607
608 let mut leaf2 = leaf.clone();
609 leaf2.leaf.block_header_mut().block_number += 1;
610 let block2 =
611 BlockQueryData::<MockTypes>::new(leaf2.header().clone(), block.payload.clone());
612 let vid2 = VidCommonQueryData::<MockTypes>::new(leaf2.header().clone(), vid.common.clone());
613
614 {
615 let mut tx = ds.write().await.unwrap();
616 tx.insert_leaf_range([&leaf, &leaf2]).await.unwrap();
617 tx.insert_block_range([&block, &block2]).await.unwrap();
618 tx.insert_vid_range([(&vid, None), (&vid2, None)])
619 .await
620 .unwrap();
621 tx.commit().await.unwrap();
622 }
623
624 assert_eq!(ds.get_leaf(0).await.await, leaf);
625 assert_eq!(ds.get_leaf(1).await.await, leaf2);
626 assert_eq!(ds.get_block(0).await.await, block);
627 assert_eq!(ds.get_block(1).await.await, block2);
628 assert_eq!(ds.get_vid_common(0).await.await, vid);
629 assert_eq!(ds.get_vid_common(1).await.await, vid2);
630 }
631
632 #[test_log::test(tokio::test(flavor = "multi_thread"))]
633 pub async fn test_append_payload_verification<D: TestableDataSource>() {
634 let storage = D::create(0).await;
635 let ds = D::connect(&storage).await;
636
637 let leaf = LeafQueryData::<MockTypes>::genesis(
638 &Default::default(),
639 &Default::default(),
640 TEST_VERSIONS.test,
641 )
642 .await;
643 let payload = BlockQueryData::<MockTypes>::genesis(
644 &Default::default(),
645 &Default::default(),
646 TEST_VERSIONS.test.base,
647 )
648 .await
649 .payload;
650
651 ds.append(BlockInfo::new(leaf.clone(), None, None, None))
654 .await
655 .unwrap();
656
657 let mut forked_header = leaf.header().clone();
659 forked_header.payload_commitment =
660 vid_commitment(&[1, 2, 3], &[], 1, TEST_VERSIONS.test.base);
661 let forked_block = BlockQueryData::new(forked_header, payload.clone());
662 ds.append_payload(forked_block).await.unwrap();
663 assert!(ds.get_block(0).await.try_resolve().is_err());
664
665 let block = BlockQueryData::new(leaf.header().clone(), payload.clone());
667 ds.append_payload(block.clone()).await.unwrap();
668 assert_eq!(ds.get_block(0).await.await, block);
669
670 let (payload2, metadata2) =
674 <TestBlockPayload as BlockPayload<TestTypes>>::from_transactions(
675 [mock_transaction(vec![1])],
676 &Default::default(),
677 &Default::default(),
678 )
679 .await
680 .unwrap();
681 let mut leaf2 = leaf.clone();
682 leaf2.leaf.block_header_mut().block_number += 1;
683 leaf2.leaf.block_header_mut().payload_commitment = vid_commitment(
684 &payload2.encode(),
685 &metadata2.encode(),
686 1,
687 TEST_VERSIONS.test.base,
688 );
689 let block2 = BlockQueryData::new(leaf2.header().clone(), payload2);
690 ds.append_payload(block2.clone()).await.unwrap();
691 ds.append(BlockInfo::new(leaf2, None, None, None))
692 .await
693 .unwrap();
694 assert!(ds.get_block(1).await.try_resolve().is_err());
695 ds.append_payload(block2.clone()).await.unwrap();
696 assert_eq!(ds.get_block(1).await.await, block2);
697 }
698}
699
700#[cfg(any(test, feature = "testing"))]
702#[espresso_macros::generic_tests]
703pub mod persistence_tests {
704 use committable::Committable;
705 use hotshot_example_types::{
706 node_types::TEST_VERSIONS,
707 state_types::{TestInstanceState, TestValidatedState},
708 };
709 use hotshot_types::simple_certificate::QuorumCertificate2;
710
711 use crate::{
712 Leaf2,
713 availability::{BlockQueryData, LeafQueryData},
714 data_source::{
715 Transaction,
716 storage::{AvailabilityStorage, NodeStorage, UpdateAvailabilityStorage},
717 },
718 node::NodeDataSource,
719 testing::{
720 consensus::TestableDataSource,
721 mocks::{MockPayload, MockTypes},
722 },
723 types::HeightIndexed,
724 };
725
726 #[test_log::test(tokio::test(flavor = "multi_thread"))]
727 pub async fn test_revert<D: TestableDataSource>()
728 where
729 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>
730 + AvailabilityStorage<MockTypes>
731 + NodeStorage<MockTypes>,
732 {
733 let storage = D::create(0).await;
734 let ds = D::connect(&storage).await;
735
736 let mut qc = QuorumCertificate2::<MockTypes>::genesis(
738 &TestValidatedState::default(),
739 &TestInstanceState::default(),
740 TEST_VERSIONS.test,
741 )
742 .await;
743 let mut leaf = Leaf2::<MockTypes>::genesis(
744 &TestValidatedState::default(),
745 &TestInstanceState::default(),
746 TEST_VERSIONS.test.base,
747 )
748 .await;
749 leaf.block_header_mut().block_number += 1;
752 qc.data.leaf_commit = <Leaf2<MockTypes> as Committable>::commit(&leaf);
753
754 let block = BlockQueryData::new(leaf.block_header().clone(), MockPayload::genesis());
755 let leaf = LeafQueryData::new(leaf, qc).unwrap();
756
757 let mut tx = ds.write().await.unwrap();
759 tx.insert_leaf(&leaf).await.unwrap();
760 tx.insert_block(&block).await.unwrap();
761
762 assert_eq!(tx.block_height().await.unwrap(), 2);
763 assert_eq!(leaf, tx.get_leaf(1.into()).await.unwrap());
764 assert_eq!(block, tx.get_block(1.into()).await.unwrap());
765
766 tx.revert().await;
768 assert_eq!(
769 NodeDataSource::<MockTypes>::block_height(&ds)
770 .await
771 .unwrap(),
772 0
773 );
774 ds.get_leaf(1).await.try_resolve().unwrap_err();
775 ds.get_block(1).await.try_resolve().unwrap_err();
776 }
777
778 #[test_log::test(tokio::test(flavor = "multi_thread"))]
779 pub async fn test_reset<D: TestableDataSource>()
780 where
781 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>,
782 {
783 let storage = D::create(0).await;
784 let ds = D::connect(&storage).await;
785
786 let mut qc = QuorumCertificate2::<MockTypes>::genesis(
788 &TestValidatedState::default(),
789 &TestInstanceState::default(),
790 TEST_VERSIONS.test,
791 )
792 .await;
793 let mut leaf = Leaf2::<MockTypes>::genesis(
794 &TestValidatedState::default(),
795 &TestInstanceState::default(),
796 TEST_VERSIONS.test.base,
797 )
798 .await;
799 leaf.block_header_mut().block_number += 1;
802 qc.data.leaf_commit = <Leaf2<MockTypes> as Committable>::commit(&leaf);
803
804 let block = BlockQueryData::new(leaf.block_header().clone(), MockPayload::genesis());
805 let leaf = LeafQueryData::new(leaf, qc).unwrap();
806
807 let mut tx = ds.write().await.unwrap();
809 tx.insert_leaf(&leaf).await.unwrap();
810 tx.insert_block(&block).await.unwrap();
811 tx.commit().await.unwrap();
812
813 assert_eq!(
814 NodeDataSource::<MockTypes>::block_height(&ds)
815 .await
816 .unwrap(),
817 2
818 );
819 assert_eq!(leaf, ds.get_leaf(1).await.await);
820 assert_eq!(block, ds.get_block(1).await.await);
821
822 drop(ds);
823
824 let ds = D::reset(&storage).await;
826 assert_eq!(
827 NodeDataSource::<MockTypes>::block_height(&ds)
828 .await
829 .unwrap(),
830 0
831 );
832 ds.get_leaf(1).await.try_resolve().unwrap_err();
833 ds.get_block(1).await.try_resolve().unwrap_err();
834 }
835
836 #[test_log::test(tokio::test(flavor = "multi_thread"))]
837 pub async fn test_drop_tx<D: TestableDataSource>()
838 where
839 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>
840 + AvailabilityStorage<MockTypes>
841 + NodeStorage<MockTypes>,
842 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
843 {
844 let storage = D::create(0).await;
845 let ds = D::connect(&storage).await;
846
847 let mut mock_qc = QuorumCertificate2::<MockTypes>::genesis(
849 &TestValidatedState::default(),
850 &TestInstanceState::default(),
851 TEST_VERSIONS.test,
852 )
853 .await;
854 let mut mock_leaf = Leaf2::<MockTypes>::genesis(
855 &TestValidatedState::default(),
856 &TestInstanceState::default(),
857 TEST_VERSIONS.test.base,
858 )
859 .await;
860 mock_leaf.block_header_mut().block_number += 1;
863 mock_qc.data.leaf_commit = <Leaf2<MockTypes> as Committable>::commit(&mock_leaf);
864
865 let block = BlockQueryData::new(mock_leaf.block_header().clone(), MockPayload::genesis());
866 let leaf = LeafQueryData::new(mock_leaf.clone(), mock_qc.clone()).unwrap();
867
868 tracing::info!("write");
870 let mut tx = ds.write().await.unwrap();
871 tx.insert_leaf(&leaf).await.unwrap();
872 tx.insert_block(&block).await.unwrap();
873
874 assert_eq!(tx.block_height().await.unwrap(), 2);
875 assert_eq!(leaf, tx.get_leaf(1.into()).await.unwrap());
876 assert_eq!(block, tx.get_block(1.into()).await.unwrap());
877
878 drop(tx);
880
881 tracing::info!("read");
883 let mut tx = ds.read().await.unwrap();
884 assert_eq!(tx.block_height().await.unwrap(), 0);
885 drop(tx);
886
887 mock_leaf.block_header_mut().block_number += 1;
889 mock_qc.data.leaf_commit = <Leaf2<MockTypes> as Committable>::commit(&mock_leaf);
890 let block = BlockQueryData::new(mock_leaf.block_header().clone(), MockPayload::genesis());
891 let leaf = LeafQueryData::new(mock_leaf, mock_qc).unwrap();
892
893 tracing::info!("write again");
894 let mut tx = ds.write().await.unwrap();
895 tx.insert_leaf(&leaf).await.unwrap();
896 tx.insert_block(&block).await.unwrap();
897 tx.commit().await.unwrap();
898
899 tracing::info!("read again");
902 let height = leaf.height() as usize;
903 assert_eq!(
904 NodeDataSource::<MockTypes>::block_height(&ds)
905 .await
906 .unwrap(),
907 height + 1
908 );
909 assert_eq!(leaf, ds.get_leaf(height).await.await);
910 assert_eq!(block, ds.get_block(height).await.await);
911 ds.get_leaf(height - 1).await.try_resolve().unwrap_err();
912 ds.get_block(height - 1).await.try_resolve().unwrap_err();
913 }
914}
915
916#[cfg(any(test, feature = "testing"))]
918#[espresso_macros::generic_tests]
919pub mod node_tests {
920 use std::time::Duration;
921
922 use committable::Committable;
923 use futures::{future::join_all, stream::StreamExt};
924 use hotshot::traits::BlockPayload;
925 use hotshot_example_types::{
926 block_types::{TestBlockHeader, TestBlockPayload, TestMetadata},
927 node_types::{TEST_VERSIONS, TestTypes},
928 state_types::{TestInstanceState, TestValidatedState},
929 };
930 use hotshot_types::{
931 data::{VidCommitment, VidCommon, VidShare, ViewNumber, vid_commitment},
932 simple_certificate::{CertificatePair, QuorumCertificate2},
933 traits::block_contents::{BlockHeader, EncodeBytes},
934 vid::advz::{ADVZScheme, advz_scheme},
935 };
936 use jf_advz::VidScheme;
937 use pretty_assertions::assert_eq;
938
939 use crate::{
940 Header, Leaf2,
941 availability::{BlockInfo, BlockQueryData, LeafQueryData, VidCommonQueryData},
942 data_source::{
943 storage::{NodeStorage, UpdateAvailabilityStorage},
944 update::Transaction,
945 },
946 node::{
947 BlockId, NodeDataSource, ResourceSyncStatus, SyncStatus, SyncStatusQueryData,
948 SyncStatusRange, TimeWindowQueryData, WindowStart,
949 },
950 testing::{
951 consensus::{MockNetwork, TestableDataSource},
952 mocks::{MockPayload, MockTypes, mock_transaction},
953 sleep,
954 },
955 types::HeightIndexed,
956 };
957
958 fn block_header_timestamp(header: &Header<MockTypes>) -> u64 {
959 <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(header)
960 }
961
962 #[test_log::test(tokio::test(flavor = "multi_thread"))]
963 pub async fn test_sync_status<D: TestableDataSource>()
964 where
965 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>,
966 {
967 let storage = D::create(0).await;
968 let ds = D::build(&storage, |builder| {
969 builder.with_sync_status_ttl(Duration::ZERO)
970 })
971 .await;
972
973 let mut vid = advz_scheme(2);
975
976 let mut leaves = vec![
978 LeafQueryData::<MockTypes>::genesis(
979 &TestValidatedState::default(),
980 &TestInstanceState::default(),
981 TEST_VERSIONS.test,
982 )
983 .await,
984 ];
985 let mut blocks = vec![
986 BlockQueryData::<MockTypes>::genesis(
987 &TestValidatedState::default(),
988 &TestInstanceState::default(),
989 TEST_VERSIONS.test.base,
990 )
991 .await,
992 ];
993 let dispersal = vid.disperse([]).unwrap();
994 let mut vid_commons = vec![VidCommonQueryData::new(
995 leaves[0].header().clone(),
996 VidCommon::V0(dispersal.common.clone()),
997 )];
998 for i in 0..2 {
999 let (payload, metadata) = <MockPayload as BlockPayload<MockTypes>>::from_transactions(
1002 vec![mock_transaction(vec![i as u8])],
1003 &Default::default(),
1004 &Default::default(),
1005 )
1006 .await
1007 .unwrap();
1008 let dispersal = vid.disperse(payload.encode()).unwrap();
1009
1010 let mut leaf = leaves[i].clone();
1011 leaf.leaf.block_header_mut().block_number += 1;
1012 leaf.leaf.block_header_mut().payload_commitment = VidCommitment::V0(dispersal.commit);
1013 leaf.leaf.block_header_mut().metadata = metadata;
1014 let block = BlockQueryData::new(leaf.header().clone(), payload);
1015 let vid_common = VidCommonQueryData::new(
1016 leaf.header().clone(),
1017 VidCommon::V0(dispersal.common.clone()),
1018 );
1019
1020 leaves.push(leaf);
1021 blocks.push(block);
1022 vid_commons.push(vid_common);
1023 }
1024
1025 assert!(ds.sync_status().await.unwrap().is_fully_synced());
1027
1028 ds.append(leaves[0].clone().into()).await.unwrap();
1031 assert_eq!(
1032 ds.sync_status().await.unwrap(),
1033 SyncStatusQueryData {
1034 blocks: ResourceSyncStatus {
1035 missing: 1,
1036 ranges: vec![SyncStatusRange {
1037 start: 0,
1038 end: 1,
1039 status: SyncStatus::Missing,
1040 }]
1041 },
1042 vid_common: ResourceSyncStatus {
1043 missing: 1,
1044 ranges: vec![SyncStatusRange {
1045 start: 0,
1046 end: 1,
1047 status: SyncStatus::Missing,
1048 }]
1049 },
1050 leaves: ResourceSyncStatus {
1051 missing: 0,
1052 ranges: vec![SyncStatusRange {
1053 start: 0,
1054 end: 1,
1055 status: SyncStatus::Present,
1056 }]
1057 },
1058 pruned_height: None,
1059 }
1060 );
1061
1062 ds.append(leaves[2].clone().into()).await.unwrap();
1065 assert_eq!(
1066 ds.sync_status().await.unwrap(),
1067 SyncStatusQueryData {
1068 blocks: ResourceSyncStatus {
1069 missing: 3,
1070 ranges: vec![SyncStatusRange {
1071 start: 0,
1072 end: 3,
1073 status: SyncStatus::Missing,
1074 }]
1075 },
1076 vid_common: ResourceSyncStatus {
1077 missing: 3,
1078 ranges: vec![SyncStatusRange {
1079 start: 0,
1080 end: 3,
1081 status: SyncStatus::Missing,
1082 }]
1083 },
1084 leaves: ResourceSyncStatus {
1085 missing: 1,
1086 ranges: vec![
1087 SyncStatusRange {
1088 start: 0,
1089 end: 1,
1090 status: SyncStatus::Present,
1091 },
1092 SyncStatusRange {
1093 start: 1,
1094 end: 2,
1095 status: SyncStatus::Missing,
1096 },
1097 SyncStatusRange {
1098 start: 2,
1099 end: 3,
1100 status: SyncStatus::Present,
1101 }
1102 ]
1103 },
1104 pruned_height: None,
1105 }
1106 );
1107
1108 {
1110 let mut tx = ds.write().await.unwrap();
1111 tx.insert_vid(&vid_commons[0].clone(), None).await.unwrap();
1112 tx.commit().await.unwrap();
1113 }
1114 assert_eq!(
1115 ds.sync_status().await.unwrap(),
1116 SyncStatusQueryData {
1117 blocks: ResourceSyncStatus {
1118 missing: 3,
1119 ranges: vec![SyncStatusRange {
1120 start: 0,
1121 end: 3,
1122 status: SyncStatus::Missing,
1123 }]
1124 },
1125 vid_common: ResourceSyncStatus {
1126 missing: 2,
1127 ranges: vec![
1128 SyncStatusRange {
1129 start: 0,
1130 end: 1,
1131 status: SyncStatus::Present,
1132 },
1133 SyncStatusRange {
1134 start: 1,
1135 end: 3,
1136 status: SyncStatus::Missing,
1137 },
1138 ]
1139 },
1140 leaves: ResourceSyncStatus {
1141 missing: 1,
1142 ranges: vec![
1143 SyncStatusRange {
1144 start: 0,
1145 end: 1,
1146 status: SyncStatus::Present,
1147 },
1148 SyncStatusRange {
1149 start: 1,
1150 end: 2,
1151 status: SyncStatus::Missing,
1152 },
1153 SyncStatusRange {
1154 start: 2,
1155 end: 3,
1156 status: SyncStatus::Present,
1157 }
1158 ]
1159 },
1160 pruned_height: None,
1161 }
1162 );
1163
1164 {
1166 let mut tx = ds.write().await.unwrap();
1167 tx.insert_block(&blocks[0]).await.unwrap();
1168 tx.insert_vid(&vid_commons[0], None).await.unwrap();
1169 tx.insert_leaf(&leaves[1]).await.unwrap();
1170 tx.insert_block(&blocks[1]).await.unwrap();
1171 tx.insert_vid(&vid_commons[1], None).await.unwrap();
1172 tx.insert_block(&blocks[2]).await.unwrap();
1173 tx.insert_vid(&vid_commons[2], None).await.unwrap();
1174 tx.commit().await.unwrap();
1175 }
1176
1177 let leaves = if ds.get_leaf(1).await.try_resolve().is_err() {
1181 tracing::warn!(
1182 "data source does not support out-of-order filling, allowing one missing leaf"
1183 );
1184 ResourceSyncStatus {
1185 missing: 1,
1186 ranges: vec![
1187 SyncStatusRange {
1188 start: 0,
1189 end: 1,
1190 status: SyncStatus::Present,
1191 },
1192 SyncStatusRange {
1193 start: 1,
1194 end: 2,
1195 status: SyncStatus::Missing,
1196 },
1197 SyncStatusRange {
1198 start: 2,
1199 end: 3,
1200 status: SyncStatus::Present,
1201 },
1202 ],
1203 }
1204 } else {
1205 ResourceSyncStatus {
1206 missing: 0,
1207 ranges: vec![SyncStatusRange {
1208 start: 0,
1209 end: 3,
1210 status: SyncStatus::Present,
1211 }],
1212 }
1213 };
1214 let expected_sync_status = SyncStatusQueryData {
1215 leaves,
1216 blocks: ResourceSyncStatus {
1217 missing: 0,
1218 ranges: vec![SyncStatusRange {
1219 start: 0,
1220 end: 3,
1221 status: SyncStatus::Present,
1222 }],
1223 },
1224 vid_common: ResourceSyncStatus {
1225 missing: 0,
1226 ranges: vec![SyncStatusRange {
1227 start: 0,
1228 end: 3,
1229 status: SyncStatus::Present,
1230 }],
1231 },
1232 pruned_height: None,
1233 };
1234 assert_eq!(ds.sync_status().await.unwrap(), expected_sync_status);
1235 }
1236
1237 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1238 pub async fn test_counters<D: TestableDataSource>() {
1239 let storage = D::create(0).await;
1240 let ds = D::connect(&storage).await;
1241
1242 assert_eq!(ds.count_transactions().await.unwrap(), 0);
1243 assert_eq!(ds.payload_size().await.unwrap(), 0);
1244
1245 let mut total_transactions = 0;
1247 let mut total_size = 0;
1248 'outer: for i in [0, 1, 2] {
1249 let (payload, metadata) =
1254 <TestBlockPayload as BlockPayload<TestTypes>>::from_transactions(
1255 [mock_transaction(vec![i as u8 % 2])],
1256 &TestValidatedState::default(),
1257 &TestInstanceState::default(),
1258 )
1259 .await
1260 .unwrap();
1261 let encoded = payload.encode();
1262 let payload_commitment =
1263 vid_commitment(&encoded, &metadata.encode(), 1, TEST_VERSIONS.test.base);
1264 let header = TestBlockHeader {
1265 block_number: i,
1266 payload_commitment,
1267 timestamp: i,
1268 timestamp_millis: i * 1_000,
1269 builder_commitment:
1270 <TestBlockPayload as BlockPayload<TestTypes>>::builder_commitment(
1271 &payload, &metadata,
1272 ),
1273 metadata: TestMetadata {
1274 num_transactions: 7, },
1276 random: 1, version: TEST_VERSIONS.test.base,
1278 };
1279
1280 let mut leaf = LeafQueryData::<MockTypes>::genesis(
1281 &TestValidatedState::default(),
1282 &TestInstanceState::default(),
1283 TEST_VERSIONS.test,
1284 )
1285 .await;
1286 *leaf.leaf.block_header_mut() = header.clone();
1287 let block = BlockQueryData::new(header, payload);
1288 ds.append(BlockInfo::new(leaf, Some(block.clone()), None, None))
1289 .await
1290 .unwrap();
1291 assert_eq!(
1292 NodeDataSource::<MockTypes>::block_height(&ds)
1293 .await
1294 .unwrap(),
1295 (i + 1) as usize,
1296 );
1297
1298 total_transactions += 1;
1299 total_size += encoded.len();
1300
1301 for retry in 0..5 {
1303 let ds_transactions = ds.count_transactions().await.unwrap();
1304 let ds_payload_size = ds.payload_size().await.unwrap();
1305 if ds_transactions != total_transactions || ds_payload_size != total_size {
1306 tracing::info!(
1307 i,
1308 retry,
1309 total_transactions,
1310 ds_transactions,
1311 total_size,
1312 ds_payload_size,
1313 "waiting for statistics to update"
1314 );
1315 sleep(Duration::from_secs(1)).await;
1316 } else {
1317 continue 'outer;
1318 }
1319 }
1320 panic!("counters did not update in time");
1321 }
1322 }
1323
1324 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1325 pub async fn test_vid_shares<D: TestableDataSource>()
1326 where
1327 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
1328 {
1329 let mut network = MockNetwork::<D>::init().await;
1330 let ds = network.data_source();
1331
1332 network.start().await;
1333
1334 let mut leaves = ds.subscribe_leaves(0).await.take(3);
1336 while let Some(leaf) = leaves.next().await {
1337 tracing::info!("got leaf {}", leaf.height());
1338 let mut tx = ds.read().await.unwrap();
1339 let share = tx.vid_share(leaf.height() as usize).await.unwrap();
1340 assert_eq!(share, tx.vid_share(leaf.block_hash()).await.unwrap());
1341 assert_eq!(
1342 share,
1343 tx.vid_share(BlockId::PayloadHash(leaf.payload_hash()))
1344 .await
1345 .unwrap()
1346 );
1347 }
1348 }
1349
1350 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1351 pub async fn test_vid_monotonicity<D: TestableDataSource>()
1352 where
1353 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>,
1354 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
1355 {
1356 let storage = D::create(0).await;
1357 let ds = D::connect(&storage).await;
1358
1359 let mut vid = advz_scheme(2);
1361 let disperse = vid.disperse([]).unwrap();
1362
1363 let leaf = LeafQueryData::<MockTypes>::genesis(
1365 &TestValidatedState::default(),
1366 &TestInstanceState::default(),
1367 TEST_VERSIONS.test,
1368 )
1369 .await;
1370 let common = VidCommonQueryData::new(leaf.header().clone(), VidCommon::V0(disperse.common));
1371 ds.append(BlockInfo::new(
1372 leaf,
1373 None,
1374 Some(common.clone()),
1375 Some(VidShare::V0(disperse.shares[0].clone())),
1376 ))
1377 .await
1378 .unwrap();
1379
1380 {
1381 assert_eq!(ds.get_vid_common(0).await.await, common);
1382 assert_eq!(
1383 ds.vid_share(0).await.unwrap(),
1384 VidShare::V0(disperse.shares[0].clone())
1385 );
1386 }
1387
1388 {
1391 let mut tx = ds.write().await.unwrap();
1392 tx.insert_vid(&common, None).await.unwrap();
1393 tx.commit().await.unwrap();
1394 }
1395 {
1396 assert_eq!(ds.get_vid_common(0).await.await, common);
1397 assert_eq!(
1398 ds.vid_share(0).await.unwrap(),
1399 VidShare::V0(disperse.shares[0].clone())
1400 );
1401 }
1402 }
1403
1404 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1405 pub async fn test_vid_recovery<D: TestableDataSource>()
1406 where
1407 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
1408 {
1409 let mut network = MockNetwork::<D>::init().await;
1410 let ds = network.data_source();
1411
1412 network.start().await;
1413
1414 let mut blocks = ds.subscribe_blocks(0).await;
1416 let txn = mock_transaction(vec![1, 2, 3]);
1417 network.submit_transaction(txn.clone()).await;
1418
1419 let block = loop {
1421 tracing::info!("waiting for transaction");
1422 let block = blocks.next().await.unwrap();
1423 if !block.is_empty() {
1424 tracing::info!(height = block.height(), "transaction sequenced");
1425 break block;
1426 }
1427 tracing::info!(height = block.height(), "empty block");
1428 };
1429 let height = block.height() as usize;
1430 let commit = if let VidCommitment::V0(commit) = block.payload_hash() {
1431 commit
1432 } else {
1433 panic!("expect ADVZ commitment")
1434 };
1435
1436 let vid = advz_scheme(network.num_nodes());
1438
1439 tracing::info!("fetching common data");
1441 let common = ds.get_vid_common(height).await.await;
1442 let VidCommon::V0(common) = &common.common() else {
1443 panic!("expect ADVZ common");
1444 };
1445 ADVZScheme::is_consistent(&commit, common).unwrap();
1446
1447 tracing::info!("fetching shares");
1449 let network = &network;
1450 let vid = &vid;
1451 let shares: Vec<_> = join_all((0..network.num_nodes()).map(|i| async move {
1452 let ds = network.data_source_index(i);
1453
1454 let mut leaves = ds.subscribe_leaves(height).await;
1457 let leaf = leaves.next().await.unwrap();
1458 assert_eq!(leaf.height(), height as u64);
1459 assert_eq!(leaf.payload_hash(), VidCommitment::V0(commit));
1460
1461 let share = if let VidShare::V0(share) = ds.vid_share(height).await.unwrap() {
1462 share
1463 } else {
1464 panic!("expect ADVZ share")
1465 };
1466 vid.verify_share(&share, common, &commit).unwrap().unwrap();
1467 share
1468 }))
1469 .await;
1470
1471 tracing::info!("recovering payload");
1473 let bytes = vid.recover_payload(&shares, common).unwrap();
1474 let recovered = <MockPayload as BlockPayload<TestTypes>>::from_bytes(
1475 &bytes,
1476 &TestMetadata {
1477 num_transactions: 7, },
1479 );
1480 assert_eq!(recovered, *block.payload());
1481 assert_eq!(recovered.transactions, vec![txn]);
1482 }
1483
1484 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1485 pub async fn test_timestamp_window<D: TestableDataSource>() {
1486 let mut network = MockNetwork::<D>::init().await;
1487 let ds = network.data_source();
1488
1489 network.start().await;
1490
1491 let mut leaves = ds.subscribe_leaves(0).await;
1494 let mut test_blocks: Vec<Vec<Header<MockTypes>>> = vec![];
1497 while test_blocks.len() < 3 {
1498 let leaf = leaves.next().await.unwrap();
1500 let header = leaf.header().clone();
1501 if let Some(last_timestamp) = test_blocks.last_mut() {
1502 if <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(&last_timestamp[0])
1503 == <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(&header)
1504 {
1505 last_timestamp.push(header);
1506 } else {
1507 test_blocks.push(vec![header]);
1508 }
1509 } else {
1510 test_blocks.push(vec![header]);
1511 }
1512 }
1513 tracing::info!("blocks for testing: {test_blocks:#?}");
1514
1515 let check_invariants =
1517 |res: &TimeWindowQueryData<Header<MockTypes>>, start, end, check_prev| {
1518 let mut prev = res.prev.as_ref();
1519 if let Some(prev) = prev {
1520 if check_prev {
1521 assert!(block_header_timestamp(prev) < start);
1522 }
1523 } else {
1524 assert_eq!(res.from().unwrap(), 0);
1527 };
1528 for header in &res.window {
1529 assert!(start <= block_header_timestamp(header));
1530 assert!(block_header_timestamp(header) < end);
1531 if let Some(prev) = prev {
1532 assert!(
1533 <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(prev)
1534 <= <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(header)
1535 );
1536 }
1537 prev = Some(header);
1538 }
1539 if let Some(next) = &res.next {
1540 assert!(<TestBlockHeader as BlockHeader<MockTypes>>::timestamp(next) >= end);
1541 assert!(block_header_timestamp(next) >= block_header_timestamp(prev.unwrap()));
1544 }
1545 };
1546
1547 let get_window = |start, end| {
1548 let ds = ds.clone();
1549 async move {
1550 let window = ds
1551 .get_header_window(WindowStart::Time(start), end, i64::MAX as usize)
1552 .await
1553 .unwrap();
1554 tracing::info!("window for timestamp range {start}-{end}: {window:#?}");
1555 check_invariants(&window, start, end, true);
1556 window
1557 }
1558 };
1559
1560 let start = <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(&test_blocks[1][0]);
1562 let end = start + 1;
1563 let res = get_window(start, end).await;
1564 assert_eq!(res.prev.unwrap(), *test_blocks[0].last().unwrap());
1565 assert_eq!(res.window, test_blocks[1]);
1566 assert_eq!(res.next.unwrap(), test_blocks[2][0]);
1567
1568 let start = 0;
1570 let end = <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(&test_blocks[0][0]) + 1;
1571 let res = get_window(start, end).await;
1572 assert_eq!(res.prev, None);
1573 assert_eq!(res.window, test_blocks[0]);
1574 assert_eq!(res.next.unwrap(), test_blocks[1][0]);
1575
1576 let start = <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(&test_blocks[2][0]);
1578 let end = i64::MAX as u64;
1579 let res = get_window(start, end).await;
1580 assert_eq!(res.prev.unwrap(), *test_blocks[1].last().unwrap());
1581 assert_eq!(res.window[..test_blocks[2].len()], test_blocks[2]);
1584 assert_eq!(res.next, None);
1585 let from = test_blocks.iter().flatten().count() - 1;
1589 let more = ds
1590 .get_header_window(WindowStart::Height(from as u64), end, i64::MAX as usize)
1591 .await
1592 .unwrap();
1593 check_invariants(&more, start, end, false);
1594 assert_eq!(
1595 more.prev.as_ref().unwrap(),
1596 test_blocks.iter().flatten().nth(from - 1).unwrap()
1597 );
1598 assert_eq!(
1599 more.window[..res.window.len() - test_blocks[2].len() + 1],
1600 res.window[test_blocks[2].len() - 1..]
1601 );
1602 assert_eq!(res.next, None);
1603 let more2 = ds
1605 .get_header_window(
1606 test_blocks[2].last().unwrap().commit(),
1607 end,
1608 i64::MAX as usize,
1609 )
1610 .await
1611 .unwrap();
1612 check_invariants(&more2, start, end, false);
1613 assert_eq!(more2.from().unwrap(), more.from().unwrap());
1614 assert_eq!(more2.prev, more.prev);
1615 assert_eq!(more2.next, more.next);
1616 assert_eq!(more2.window[..more.window.len()], more.window);
1617
1618 let start = <TestBlockHeader as BlockHeader<MockTypes>>::timestamp(&test_blocks[1][0]);
1620 let end = start;
1621 let res = get_window(start, end).await;
1622 assert_eq!(res.prev.unwrap(), *test_blocks[0].last().unwrap());
1623 assert_eq!(res.next.unwrap(), test_blocks[1][0]);
1624 assert_eq!(res.window, vec![]);
1625
1626 ds.get_header_window(
1628 WindowStart::Time((i64::MAX - 1) as u64),
1629 i64::MAX as u64,
1630 i64::MAX as usize,
1631 )
1632 .await
1633 .unwrap_err();
1634
1635 let blocks = [test_blocks[0].clone(), test_blocks[1].clone()]
1637 .into_iter()
1638 .flatten()
1639 .collect::<Vec<_>>();
1640 let start = block_header_timestamp(&blocks[0]);
1642 let end = block_header_timestamp(&test_blocks[2][0]);
1643 let res = ds
1644 .get_header_window(WindowStart::Time(start), end, 1)
1645 .await
1646 .unwrap();
1647 assert_eq!(res.prev, None);
1648 assert_eq!(res.window, [blocks[0].clone()]);
1649 assert_eq!(res.next, None);
1650 let res = ds
1652 .get_header_window(WindowStart::Height(blocks[0].height() + 1), end, 1)
1653 .await
1654 .unwrap();
1655 assert_eq!(res.window, [blocks[1].clone()]);
1656 assert_eq!(res.next, None);
1657 let res = ds
1659 .get_header_window(
1660 WindowStart::Height(blocks[1].height() + 1),
1661 end,
1662 blocks.len() - 1,
1663 )
1664 .await
1665 .unwrap();
1666 assert_eq!(res.window, blocks[2..].to_vec());
1667 assert_eq!(res.next, Some(test_blocks[2][0].clone()));
1668 }
1669
1670 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1671 pub async fn test_latest_qc_chain<D: TestableDataSource>()
1672 where
1673 for<'a> D::ReadOnly<'a>: NodeStorage<MockTypes>,
1674 for<'a> D::Transaction<'a>: UpdateAvailabilityStorage<MockTypes>,
1675 {
1676 let storage = D::create(0).await;
1677 let ds = D::connect(&storage).await;
1678
1679 {
1680 let mut tx = ds.read().await.unwrap();
1681 assert_eq!(tx.latest_qc_chain().await.unwrap(), None);
1682 }
1683
1684 async fn leaf_with_qc_chain(
1685 number: u64,
1686 ) -> (LeafQueryData<MockTypes>, [CertificatePair<MockTypes>; 2]) {
1687 let mut leaf = Leaf2::<MockTypes>::genesis(
1688 &Default::default(),
1689 &Default::default(),
1690 TEST_VERSIONS.test.base,
1691 )
1692 .await;
1693 leaf.block_header_mut().block_number = number;
1694
1695 let mut qc1 = QuorumCertificate2::<MockTypes>::genesis(
1696 &Default::default(),
1697 &Default::default(),
1698 TEST_VERSIONS.test,
1699 )
1700 .await;
1701 qc1.view_number = ViewNumber::new(1);
1702 qc1.data.leaf_commit = Committable::commit(&leaf);
1703
1704 let mut qc2 = qc1.clone();
1705 qc2.view_number += 1;
1706
1707 let leaf = LeafQueryData::new(leaf, qc1.clone()).unwrap();
1708 (
1709 leaf,
1710 [
1711 CertificatePair::non_epoch_change(qc1),
1712 CertificatePair::non_epoch_change(qc2),
1713 ],
1714 )
1715 }
1716
1717 {
1719 let (leaf, qcs) = leaf_with_qc_chain(2).await;
1720 let mut tx = ds.write().await.unwrap();
1721 tx.insert_leaf_with_qc_chain(&leaf, Some(qcs.clone()))
1722 .await
1723 .unwrap();
1724 tx.commit().await.unwrap();
1725
1726 assert_eq!(
1727 ds.read().await.unwrap().latest_qc_chain().await.unwrap(),
1728 Some(qcs)
1729 );
1730 }
1731
1732 {
1735 let (leaf, _) = leaf_with_qc_chain(3).await;
1736 let mut tx = ds.write().await.unwrap();
1737 tx.insert_leaf_with_qc_chain(&leaf, None).await.unwrap();
1738 tx.commit().await.unwrap();
1739
1740 assert_eq!(
1741 ds.read().await.unwrap().latest_qc_chain().await.unwrap(),
1742 None
1743 );
1744 }
1745
1746 {
1749 let (leaf, qcs) = leaf_with_qc_chain(1).await;
1750 let mut tx = ds.write().await.unwrap();
1751 tx.insert_leaf_with_qc_chain(&leaf, Some(qcs))
1752 .await
1753 .unwrap();
1754 tx.commit().await.unwrap();
1755
1756 assert_eq!(
1757 ds.read().await.unwrap().latest_qc_chain().await.unwrap(),
1758 None
1759 );
1760 }
1761 }
1762}
1763
1764#[cfg(any(test, feature = "testing"))]
1766#[espresso_macros::generic_tests]
1767pub mod status_tests {
1768 use std::time::Duration;
1769
1770 use crate::{
1771 status::StatusDataSource,
1772 testing::{
1773 consensus::{DataSourceLifeCycle, MockNetwork},
1774 mocks::mock_transaction,
1775 sleep,
1776 },
1777 };
1778
1779 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1780 pub async fn test_metrics<D: DataSourceLifeCycle + StatusDataSource>() {
1781 let mut network = MockNetwork::<D>::init().await;
1782 let ds = network.data_source();
1783
1784 {
1785 assert_eq!(ds.block_height().await.unwrap(), 0);
1787 assert!(ds.success_rate().await.unwrap().is_nan());
1790 assert!(
1793 (ds.elapsed_time_since_last_decide().await.unwrap() as i64
1794 - chrono::Utc::now().timestamp())
1795 .abs()
1796 <= 1,
1797 "time elapsed since last_decided_time is not within 1s"
1798 );
1799 }
1800
1801 let txn = mock_transaction(vec![1, 2, 3]);
1803 network.submit_transaction(txn.clone()).await;
1804
1805 network.start().await;
1807
1808 loop {
1810 let height = ds.block_height().await.unwrap();
1811 if height > 1 {
1812 break;
1813 }
1814 tracing::info!(height, "waiting for a block to be finalized");
1815 sleep(Duration::from_secs(1)).await;
1816 }
1817
1818 {
1819 let success_rate = ds.success_rate().await.unwrap();
1823 assert!(success_rate.is_finite(), "{success_rate}");
1824 assert!(success_rate > 0.0, "{success_rate}");
1825 }
1826
1827 {
1828 network.shut_down().await;
1831 sleep(Duration::from_secs(3)).await;
1832 assert!(ds.elapsed_time_since_last_decide().await.unwrap() >= 3);
1834 }
1835 }
1836}
1837
1838#[macro_export]
1839macro_rules! instantiate_data_source_tests {
1840 ($t:ty) => {
1841 use $crate::data_source::{
1842 availability_tests, node_tests, persistence_tests, status_tests,
1843 };
1844
1845 instantiate_availability_tests!($t);
1846 instantiate_persistence_tests!($t);
1847 instantiate_node_tests!($t);
1848 instantiate_status_tests!($t);
1849 };
1850}