Skip to main content

hotshot_query_service/
data_source.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the HotShot Query Service library.
3//
4// This program is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
8// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// General Public License for more details.
10// You should have received a copy of the GNU General Public License along with this program. If not,
11// see <https://www.gnu.org/licenses/>.
12
13//! Persistent storage and sources of data consumed by APIs.
14//!
15//! The APIs provided by this query service are generic over the implementation which actually
16//! retrieves data in answer to queries. We call this implementation a _data source_. This module
17//! defines a data source and provides several pre-built implementations:
18//! * [`FileSystemDataSource`]
19//! * [`SqlDataSource`]
20//! * [`FetchingDataSource`], a generalization of the above
21//! * [`MetricsDataSource`]
22//!
23//! The user can choose which data source to use when initializing the query service.
24//!
25//! We also provide combinators for modularly adding functionality to existing data sources:
26//! * [`ExtensibleDataSource`]
27//!
28
29mod 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    /// Apply an upper bound to a range based on the currently available block height.
64    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    /// Get a stream of blocks, implicitly terminating at the current block height.
78    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    /// Get a stream of leaves, implicitly terminating at the current block height.
93    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        // Ignore the genesis block (start from height 1).
111        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/// Generic tests we can instantiate for all the availability data sources.
121#[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        // Check the consistency of every block/leaf pair. Keep track of payloads and transactions
165        // we've seen so we can detect duplicates.
166        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            // Check indices.
177            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            // Check indices.
189            assert_eq!(block, ds.get_block(i).await.await);
190            assert_eq!(ds.get_block(block.hash()).await.await.height(), i as u64);
191            // We should be able to look up the block by payload hash unless its payload is a
192            // duplicate. For duplicate payloads, this function returns the index of the first
193            // duplicate.
194            //
195            // Note: this ordering is not a strict requirement. It should hold for payloads in local
196            // storage, but we don't have a good way of enforcing it if the payload is missing, in
197            // which case we will return the first matching payload we see, which could happen in
198            // any order. We use `try_resolve` to skip this check if the object isn't available
199            // locally.
200            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                // At least check that _some_ block can be fetched.
215                ds.get_block(BlockId::PayloadHash(block.payload_hash()))
216                    .await
217                    .await;
218            }
219
220            // Check payload lookup.
221            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            // Similar to the above, we can't guarantee which index we will get when passively
226            // fetching this payload, so only check the index if the payload is available locally.
227            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                // At least check that _some_ payload can be fetched.
241                ds.get_payload(BlockId::PayloadHash(block.payload_hash()))
242                    .await
243                    .await;
244            }
245
246            // Look up the common VID data.
247            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            // Similar to the above, we can't guarantee which index we will get when passively
251            // fetching this data, so only check the index if the data is available locally.
252            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                // At least check that _some_ data can be fetched.
266                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                // We should be able to look up the transaction by hash unless it is a duplicate.
277                // For duplicate transactions, this function returns the index of the first
278                // duplicate.
279                //
280                // Similar to the above, we can't guarantee which index we will get when passively
281                // fetching this transaction, so only check the index if the transaction is
282                // available locally.
283                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                    // At least check that _some_ transaction can be fetched.
301                    ds.get_block_containing_transaction(txn.commit())
302                        .await
303                        .await;
304                }
305            }
306        }
307
308        // Validate consistency of latest QC chain (only available after epoch upgrade).
309        {
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        // Submit a few blocks and make sure each one gets reflected in the query service and
338        // preserves the consistency of the data and indices.
339        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            // Wait for the transaction to be finalized.
345            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        // Check that all the updates have been committed to storage, not simply held in memory: we
360        // should be able to read the same data if we connect an entirely new data source to the
361        // underlying storage.
362        {
363            tracing::info!("checking persisted storage");
364            let storage = D::connect(network.storage()).await;
365
366            // Ensure we have the same data in both data sources (if data was missing from the
367            // original it is of course allowed to be missing from persistent storage and thus from
368            // the latter).
369            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        // Wait for there to be at least 3 blocks.
409        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        // Query for a variety of ranges testing all cases of included, excluded, and unbounded
418        // starting and ending bounds
419        do_range_test(&ds, 1..=2, 1..3).await; // (inclusive, inclusive)
420        do_range_test(&ds, 1..3, 1..3).await; // (inclusive, exclusive)
421        do_range_test(&ds, 1.., 1..block_height).await; // (inclusive, unbounded)
422        do_range_test(&ds, ..=2, 0..3).await; // (unbounded, inclusive)
423        do_range_test(&ds, ..3, 0..3).await; // (unbounded, exclusive)
424        do_range_test(&ds, .., 0..block_height).await; // (unbounded, unbounded)
425        do_range_test(&ds, ExRange(0..=2), 1..3).await; // (exclusive, inclusive)
426        do_range_test(&ds, ExRange(0..3), 1..3).await; // (exclusive, exclusive)
427        do_range_test(&ds, ExRange(0..), 1..block_height).await; // (exclusive, unbounded)
428    }
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            // If the range is unbounded, the stream should continue, eventually reaching a point at
463            // which further objects are not yet available, and yielding pending futures from there.
464            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            // If the range is bounded, it should end where expected.
486            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        // Wait for there to be at least 5 blocks.
505        ds.subscribe_leaves(5).await.next().await.unwrap();
506
507        // Test inclusive, exclusive and unbounded lower bound.
508        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        // The range should end where expected.
550        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    // A wrapper around a range that turns the lower bound from inclusive to exclusive.
559    #[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    /// Regression test for a SQL bug.
577    ///
578    /// In PostgreSQL, upserting with multiple conflicting rows in a single statement is not
579    /// allowed.
580    #[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        // The leaf is decided without its payload, as when a decide event precedes VID
652        // reconstruction.
653        ds.append(BlockInfo::new(leaf.clone(), None, None, None))
654            .await
655            .unwrap();
656
657        // A reconstructed payload whose header was not decided at this height is discarded.
658        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        // The payload matching the decided leaf is stored.
666        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        // A payload arriving before its leaf has been ingested is dropped, not held; it can be
671        // stored once the leaf is available. The payload must be distinct from block 0's, or
672        // `append` would back-fill it from storage by payload hash.
673        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/// Generic tests we can instantiate for any data source with reliable, versioned persistent storage.
701#[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        // Mock up some consensus data.
737        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        // Increment the block number, to distinguish this block from the genesis block, which
750        // already exists.
751        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        // Insert, but do not commit, some data and check that we can read it back.
758        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        // Revert the changes.
767        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        // Mock up some consensus data.
787        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        // Increment the block number, to distinguish this block from the genesis block, which
800        // already exists.
801        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        // Insert some data and check that we can read it back.
808        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        // Reset and check that the changes are gone.
825        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        // Mock up some consensus data.
848        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        // Increment the block number, to distinguish this block from the genesis block, which
861        // already exists.
862        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        // Insert, but do not commit, some data and check that we can read it back.
869        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 the transaction, causing a revert.
879        drop(tx);
880
881        // Open a new transaction and check that the changes are reverted.
882        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        // Get a mutable transaction again, insert different data.
888        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        // Read the data back. We should have _only_ the data that was written in the final
900        // transaction.
901        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/// Generic tests we can instantiate for all the node data sources.
917#[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        // Set up a mock VID scheme to use for generating test data.
974        let mut vid = advz_scheme(2);
975
976        // Generate some mock leaves and blocks to insert.
977        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            // Generate a unique payload and VID data, so that missing data is actually missing
1000            // (otherwise it could be borrowed from another block).
1001            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        // At first, the node is fully synced.
1026        assert!(ds.sync_status().await.unwrap().is_fully_synced());
1027
1028        // Insert a leaf without the corresponding block or VID info, make sure we detect that the
1029        // block and VID info are missing.
1030        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        // Insert a leaf whose height is not the successor of the previous leaf. We should now
1063        // detect that the leaf in between is missing (along with all _three_ corresponding blocks).
1064        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        // Insert VID common without a corresponding share.
1109        {
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        // Rectify the missing data.
1165        {
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        // Some data sources (e.g. file system) don't support out-of-order insertion of missing
1178        // data. These would have just ignored the insertion of `leaves[1]`. Detect if this is the
1179        // case; then we allow 1 missing leaf.
1180        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        // Insert some transactions.
1246        let mut total_transactions = 0;
1247        let mut total_size = 0;
1248        'outer: for i in [0, 1, 2] {
1249            // Using `i % 2` as the transaction data ensures we insert a duplicate transaction
1250            // (since we insert more than 2 transactions total). The query service should still
1251            // count these as separate transactions and should include both duplicates when
1252            // computing the total size.
1253            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, // arbitrary
1275                },
1276                random: 1, // arbitrary
1277                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            // Allow some time for the aggregator to update.
1302            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        // Check VID shares for a few blocks.
1335        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        // Generate some test VID data.
1360        let mut vid = advz_scheme(2);
1361        let disperse = vid.disperse([]).unwrap();
1362
1363        // Insert test data with VID common and a share.
1364        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        // Re-insert the common data, without a share. This should not overwrite the share we
1389        // already have.
1390        {
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        // Submit a transaction so we can try to recover a non-empty block.
1415        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        // Wait for the transaction to be finalized.
1420        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        // Set up a test VID scheme.
1437        let vid = advz_scheme(network.num_nodes());
1438
1439        // Get VID common data and verify it.
1440        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        // Collect shares from each node.
1448        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            // Wait until the node has processed up to the desired block; since we have thus far
1455            // only interacted with node 0, it is possible other nodes are slightly behind.
1456            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        // Recover payload.
1472        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, // arbitrary
1478            },
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        // Wait for blocks with at least three different timestamps to be sequenced. This lets us
1492        // test all the edge cases.
1493        let mut leaves = ds.subscribe_leaves(0).await;
1494        // `test_blocks` is a list of lists of headers with the same timestamp. The flattened list
1495        // of headers is contiguous.
1496        let mut test_blocks: Vec<Vec<Header<MockTypes>>> = vec![];
1497        while test_blocks.len() < 3 {
1498            // Wait for the next block to be sequenced.
1499            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        // Define invariants that every response should satisfy.
1516        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                    // `prev` can only be `None` if the first block in the window is the genesis
1525                    // block.
1526                    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                    // If there is a `next`, there must be at least one previous block (either `prev`
1542                    // itself or the last block if the window is nonempty), so we can `unwrap` here.
1543                    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        // Case 0: happy path. All blocks are available, including prev and next.
1561        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        // Case 1: no `prev`, start of window is before genesis.
1569        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        // Case 2: no `next`, end of window is after the most recently sequenced block.
1577        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        // There may have been more blocks sequenced since we grabbed `test_blocks`, so just check
1582        // that the prefix of the window is correct.
1583        assert_eq!(res.window[..test_blocks[2].len()], test_blocks[2]);
1584        assert_eq!(res.next, None);
1585        // Fetch more blocks using the `from` form of the endpoint. Start from the last block we had
1586        // previously (ie fetch a slightly overlapping window) to ensure there is at least one block
1587        // in the new window.
1588        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        // We should get the same result whether we query by block height or hash.
1604        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        // Case 3: the window is empty.
1619        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        // Case 4: no relevant blocks are available yet.
1627        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        // Case 5: limits.
1636        let blocks = [test_blocks[0].clone(), test_blocks[1].clone()]
1637            .into_iter()
1638            .flatten()
1639            .collect::<Vec<_>>();
1640        // Make a query that would return everything, but gets limited.
1641        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        // Query the next page of results, get limited again.
1651        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        // Get the rest of the results.
1658        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        // Insert a leaf with QC chain.
1718        {
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        // Insert a later leaf without a QC chain. This should clear the previously saved QC chain,
1733        // which is no longer up to date.
1734        {
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        // Insert an earlier leaf with a QC chain. This should not be saved since it is not the
1747        // latest leaf.
1748        {
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/// Generic tests we can instantiate for all the status data sources.
1765#[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            // Check that block height is initially zero.
1786            assert_eq!(ds.block_height().await.unwrap(), 0);
1787            // With consensus paused, check that the success rate returns NAN (since the block
1788            // height, the numerator, is 0, and the view number, the denominator, is 0).
1789            assert!(ds.success_rate().await.unwrap().is_nan());
1790            // Since there is no block produced, "last_decided_time" metric is 0.
1791            // Therefore, the elapsed time since the last block should be close to the time elapsed since the Unix epoch.
1792            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        // Submit a transaction
1802        let txn = mock_transaction(vec![1, 2, 3]);
1803        network.submit_transaction(txn.clone()).await;
1804
1805        // Start consensus and wait for the transaction to be finalized.
1806        network.start().await;
1807
1808        // Now wait for at least one non-genesis block to be finalized.
1809        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            // Check that the success rate has been updated. Note that we can only check if success
1820            // rate is positive. We don't know exactly what it is because we can't know how many
1821            // views have elapsed without race conditions.
1822            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            // Shutting down the consensus to halt block production
1829            // Introducing a delay of 3 seconds to ensure that elapsed time since last block is atleast 3seconds
1830            network.shut_down().await;
1831            sleep(Duration::from_secs(3)).await;
1832            // Asserting that the elapsed time since the last block is at least 3 seconds
1833            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}