Skip to main content

hotshot_query_service/data_source/
extension.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
13use std::{
14    ops::{Bound, RangeBounds},
15    sync::Arc,
16};
17
18use async_trait::async_trait;
19use futures::stream::BoxStream;
20use hotshot::types::Event;
21use hotshot_events_service::events_source::{EventFilterSet, EventsSource, StartupInfo};
22use hotshot_types::{data::VidShare, event::LegacyEvent, traits::node_implementation::NodeType};
23use jf_merkle_tree_compat::prelude::MerkleProof;
24use tagged_base64::TaggedBase64;
25
26use super::VersionedDataSource;
27use crate::{
28    Header, Payload, QueryResult, Transaction,
29    availability::{
30        AvailabilityDataSource, BlockId, BlockInfo, BlockQueryData, BlockWithTransaction,
31        Certificate2, Fetch, FetchStream, LeafId, LeafQueryData, NamespaceId, PayloadMetadata,
32        PayloadQueryData, QueryableHeader, QueryablePayload, TransactionHash,
33        UpdateAvailabilityData, VidCommonMetadata, VidCommonQueryData,
34    },
35    data_source::storage::pruning::PrunedHeightDataSource,
36    explorer::{self, ExplorerDataSource, ExplorerHeader, ExplorerTransaction},
37    merklized_state::{
38        MerklizedState, MerklizedStateDataSource, MerklizedStateHeightPersistence, Snapshot,
39        UpdateStateData,
40    },
41    metrics::PrometheusMetrics,
42    node::{NodeDataSource, SyncStatusQueryData, TimeWindowQueryData, WindowStart},
43    status::{HasMetrics, StatusDataSource},
44};
45/// Wrapper to add extensibility to an existing data source.
46///
47/// [`ExtensibleDataSource`] adds app-specific data to any existing data source. It implements all
48/// the data source traits defined in this crate as long as the underlying data source does so,
49/// which means it can be used as state for instantiating the APIs defined in this crate. At the
50/// same time, it provides access to an application-defined state type, which means it can also be
51/// used to implement application-specific endpoints.
52///
53/// [`ExtensibleDataSource`] implements `AsRef<U>` and `AsMut<U>` for some user-defined type `U`, so
54/// your API extensions can always access application-specific state from [`ExtensibleDataSource`].
55/// We can use this to complete the [UTXO example](crate#extension) by extending our data source
56/// with an index to look up transactions by the UTXOs they contain:
57///
58/// ```
59/// # use async_trait::async_trait;
60/// # use hotshot_query_service::availability::{AvailabilityDataSource, TransactionIndex};
61/// # use hotshot_query_service::data_source::ExtensibleDataSource;
62/// # use hotshot_query_service::testing::mocks::MockTypes as AppTypes;
63/// # use std::collections::HashMap;
64/// # #[async_trait]
65/// # trait UtxoDataSource: AvailabilityDataSource<AppTypes> {
66/// #   async fn find_utxo(&self, utxo: u64) -> Option<(usize, TransactionIndex<AppTypes>, usize)>;
67/// # }
68/// type UtxoIndex = HashMap<u64, (usize, TransactionIndex<AppTypes>, usize)>;
69///
70/// #[async_trait]
71/// impl<UnderlyingDataSource> UtxoDataSource for
72///     ExtensibleDataSource<UnderlyingDataSource, UtxoIndex>
73/// where
74///     UnderlyingDataSource: AvailabilityDataSource<AppTypes> + Send + Sync,
75/// {
76///     async fn find_utxo(&self, utxo: u64) -> Option<(usize, TransactionIndex<AppTypes>, usize)> {
77///         self.as_ref().get(&utxo).cloned()
78///     }
79/// }
80/// ```
81#[derive(Clone, Copy, Debug)]
82pub struct ExtensibleDataSource<D, U> {
83    data_source: D,
84    user_data: U,
85}
86
87impl<D, U> ExtensibleDataSource<D, U> {
88    pub fn new(data_source: D, user_data: U) -> Self {
89        Self {
90            data_source,
91            user_data,
92        }
93    }
94
95    /// Access the underlying data source.
96    ///
97    /// This functionality is provided as an inherent method rather than an implementation of the
98    /// [`AsRef`] trait so that `self.as_ref()` unambiguously returns `&U`, helping with type
99    /// inference.
100    pub fn inner(&self) -> &D {
101        &self.data_source
102    }
103
104    /// Mutably access the underlying data source.
105    ///
106    /// This functionality is provided as an inherent method rather than an implementation of the
107    /// [`AsMut`] trait so that `self.as_mut()` unambiguously returns `&U`, helping with type
108    /// inference.
109    pub fn inner_mut(&mut self) -> &mut D {
110        &mut self.data_source
111    }
112}
113
114impl<D, U> AsRef<U> for ExtensibleDataSource<D, U> {
115    fn as_ref(&self) -> &U {
116        &self.user_data
117    }
118}
119
120impl<D, U> AsMut<U> for ExtensibleDataSource<D, U> {
121    fn as_mut(&mut self) -> &mut U {
122        &mut self.user_data
123    }
124}
125
126impl<D, U> VersionedDataSource for ExtensibleDataSource<D, U>
127where
128    D: VersionedDataSource + Send,
129    U: Send + Sync,
130{
131    type Transaction<'a>
132        = D::Transaction<'a>
133    where
134        Self: 'a;
135
136    type ReadOnly<'a>
137        = D::ReadOnly<'a>
138    where
139        Self: 'a;
140
141    async fn write(&self) -> anyhow::Result<Self::Transaction<'_>> {
142        self.data_source.write().await
143    }
144
145    async fn read(&self) -> anyhow::Result<Self::ReadOnly<'_>> {
146        self.data_source.read().await
147    }
148}
149
150#[async_trait]
151impl<D, U> PrunedHeightDataSource for ExtensibleDataSource<D, U>
152where
153    D: PrunedHeightDataSource + Send + Sync,
154    U: Send + Sync,
155{
156    async fn load_pruned_height(&self) -> anyhow::Result<Option<u64>> {
157        self.data_source.load_pruned_height().await
158    }
159
160    async fn load_state_pruned_height(&self) -> anyhow::Result<Option<u64>> {
161        self.data_source.load_state_pruned_height().await
162    }
163}
164
165#[async_trait]
166impl<D, U, Types> AvailabilityDataSource<Types> for ExtensibleDataSource<D, U>
167where
168    D: AvailabilityDataSource<Types> + Send + Sync,
169    U: Send + Sync,
170    Types: NodeType,
171    Header<Types>: QueryableHeader<Types>,
172    Payload<Types>: QueryablePayload<Types>,
173{
174    async fn get_leaf<ID>(&self, id: ID) -> Fetch<LeafQueryData<Types>>
175    where
176        ID: Into<LeafId<Types>> + Send + Sync,
177    {
178        self.data_source.get_leaf(id).await
179    }
180
181    async fn get_header<ID>(&self, id: ID) -> Fetch<Header<Types>>
182    where
183        ID: Into<BlockId<Types>> + Send + Sync,
184    {
185        self.data_source.get_header(id).await
186    }
187
188    async fn get_block<ID>(&self, id: ID) -> Fetch<BlockQueryData<Types>>
189    where
190        ID: Into<BlockId<Types>> + Send + Sync,
191    {
192        self.data_source.get_block(id).await
193    }
194    async fn get_payload<ID>(&self, id: ID) -> Fetch<PayloadQueryData<Types>>
195    where
196        ID: Into<BlockId<Types>> + Send + Sync,
197    {
198        self.data_source.get_payload(id).await
199    }
200    async fn get_payload_metadata<ID>(&self, id: ID) -> Fetch<PayloadMetadata<Types>>
201    where
202        ID: Into<BlockId<Types>> + Send + Sync,
203    {
204        self.data_source.get_payload_metadata(id).await
205    }
206    async fn get_vid_common<ID>(&self, id: ID) -> Fetch<VidCommonQueryData<Types>>
207    where
208        ID: Into<BlockId<Types>> + Send + Sync,
209    {
210        self.data_source.get_vid_common(id).await
211    }
212    async fn get_vid_common_metadata<ID>(&self, id: ID) -> Fetch<VidCommonMetadata<Types>>
213    where
214        ID: Into<BlockId<Types>> + Send + Sync,
215    {
216        self.data_source.get_vid_common_metadata(id).await
217    }
218    async fn get_leaf_range<R>(&self, range: R) -> FetchStream<LeafQueryData<Types>>
219    where
220        R: RangeBounds<usize> + Send + 'static,
221    {
222        self.data_source.get_leaf_range(range).await
223    }
224    async fn get_block_range<R>(&self, range: R) -> FetchStream<BlockQueryData<Types>>
225    where
226        R: RangeBounds<usize> + Send + 'static,
227    {
228        self.data_source.get_block_range(range).await
229    }
230
231    async fn get_header_range<R>(&self, range: R) -> FetchStream<Header<Types>>
232    where
233        R: RangeBounds<usize> + Send + 'static,
234    {
235        self.data_source.get_header_range(range).await
236    }
237    async fn get_payload_range<R>(&self, range: R) -> FetchStream<PayloadQueryData<Types>>
238    where
239        R: RangeBounds<usize> + Send + 'static,
240    {
241        self.data_source.get_payload_range(range).await
242    }
243    async fn get_payload_metadata_range<R>(&self, range: R) -> FetchStream<PayloadMetadata<Types>>
244    where
245        R: RangeBounds<usize> + Send + 'static,
246    {
247        self.data_source.get_payload_metadata_range(range).await
248    }
249    async fn get_vid_common_range<R>(&self, range: R) -> FetchStream<VidCommonQueryData<Types>>
250    where
251        R: RangeBounds<usize> + Send + 'static,
252    {
253        self.data_source.get_vid_common_range(range).await
254    }
255    async fn get_vid_common_metadata_range<R>(
256        &self,
257        range: R,
258    ) -> FetchStream<VidCommonMetadata<Types>>
259    where
260        R: RangeBounds<usize> + Send + 'static,
261    {
262        self.data_source.get_vid_common_metadata_range(range).await
263    }
264
265    async fn get_leaf_range_rev(
266        &self,
267        start: Bound<usize>,
268        end: usize,
269    ) -> FetchStream<LeafQueryData<Types>> {
270        self.data_source.get_leaf_range_rev(start, end).await
271    }
272    async fn get_block_range_rev(
273        &self,
274        start: Bound<usize>,
275        end: usize,
276    ) -> FetchStream<BlockQueryData<Types>> {
277        self.data_source.get_block_range_rev(start, end).await
278    }
279    async fn get_payload_range_rev(
280        &self,
281        start: Bound<usize>,
282        end: usize,
283    ) -> FetchStream<PayloadQueryData<Types>> {
284        self.data_source.get_payload_range_rev(start, end).await
285    }
286    async fn get_payload_metadata_range_rev(
287        &self,
288        start: Bound<usize>,
289        end: usize,
290    ) -> FetchStream<PayloadMetadata<Types>> {
291        self.data_source
292            .get_payload_metadata_range_rev(start, end)
293            .await
294    }
295    async fn get_vid_common_range_rev(
296        &self,
297        start: Bound<usize>,
298        end: usize,
299    ) -> FetchStream<VidCommonQueryData<Types>> {
300        self.data_source.get_vid_common_range_rev(start, end).await
301    }
302    async fn get_vid_common_metadata_range_rev(
303        &self,
304        start: Bound<usize>,
305        end: usize,
306    ) -> FetchStream<VidCommonMetadata<Types>> {
307        self.data_source
308            .get_vid_common_metadata_range_rev(start, end)
309            .await
310    }
311    async fn get_block_containing_transaction(
312        &self,
313        h: TransactionHash<Types>,
314    ) -> Fetch<BlockWithTransaction<Types>> {
315        self.data_source.get_block_containing_transaction(h).await
316    }
317
318    async fn get_cert2(&self, height: u64) -> QueryResult<Option<Certificate2<Types>>> {
319        self.data_source.get_cert2(height).await
320    }
321}
322
323impl<D, U, Types> UpdateAvailabilityData<Types> for ExtensibleDataSource<D, U>
324where
325    D: UpdateAvailabilityData<Types> + Send + Sync,
326    U: Send + Sync,
327    Types: NodeType,
328{
329    async fn append(&self, info: BlockInfo<Types>) -> anyhow::Result<()> {
330        self.data_source.append(info).await
331    }
332
333    async fn append_payload(&self, block: BlockQueryData<Types>) -> anyhow::Result<()> {
334        self.data_source.append_payload(block).await
335    }
336}
337
338#[async_trait]
339impl<D, U, Types> NodeDataSource<Types> for ExtensibleDataSource<D, U>
340where
341    D: NodeDataSource<Types> + Send + Sync,
342    U: Send + Sync,
343    Types: NodeType,
344    Header<Types>: QueryableHeader<Types>,
345{
346    async fn block_height(&self) -> QueryResult<usize> {
347        self.data_source.block_height().await
348    }
349    async fn count_transactions_in_range(
350        &self,
351        range: impl RangeBounds<usize> + Send + Sync + Clone,
352        namespace: Option<NamespaceId<Types>>,
353    ) -> QueryResult<usize> {
354        self.data_source
355            .count_transactions_in_range(range, namespace)
356            .await
357    }
358    async fn payload_size_in_range(
359        &self,
360        range: impl RangeBounds<usize> + Send + Sync + Clone,
361        namespace: Option<NamespaceId<Types>>,
362    ) -> QueryResult<usize> {
363        self.data_source
364            .payload_size_in_range(range, namespace)
365            .await
366    }
367    async fn vid_share<ID>(&self, id: ID) -> QueryResult<VidShare>
368    where
369        ID: Into<BlockId<Types>> + Send + Sync,
370    {
371        self.data_source.vid_share(id).await
372    }
373    async fn sync_status(&self) -> QueryResult<SyncStatusQueryData> {
374        self.data_source.sync_status().await
375    }
376    async fn get_header_window(
377        &self,
378        start: impl Into<WindowStart<Types>> + Send + Sync,
379        end: u64,
380        limit: usize,
381    ) -> QueryResult<TimeWindowQueryData<Header<Types>>> {
382        self.data_source.get_header_window(start, end, limit).await
383    }
384}
385
386impl<D, U> HasMetrics for ExtensibleDataSource<D, U>
387where
388    D: HasMetrics,
389{
390    fn metrics(&self) -> &PrometheusMetrics {
391        self.data_source.metrics()
392    }
393}
394
395#[async_trait]
396impl<D, U> StatusDataSource for ExtensibleDataSource<D, U>
397where
398    D: StatusDataSource + Send + Sync,
399    U: Send + Sync,
400{
401    async fn block_height(&self) -> QueryResult<usize> {
402        self.data_source.block_height().await
403    }
404}
405
406#[async_trait]
407impl<D, U, Types, State, const ARITY: usize> MerklizedStateDataSource<Types, State, ARITY>
408    for ExtensibleDataSource<D, U>
409where
410    D: MerklizedStateDataSource<Types, State, ARITY> + Sync,
411    U: Send + Sync,
412    Types: NodeType,
413    State: MerklizedState<Types, ARITY>,
414{
415    async fn get_path(
416        &self,
417        snapshot: Snapshot<Types, State, ARITY>,
418        key: State::Key,
419    ) -> QueryResult<MerkleProof<State::Entry, State::Key, State::T, ARITY>> {
420        self.data_source.get_path(snapshot, key).await
421    }
422}
423
424#[async_trait]
425impl<D, U> MerklizedStateHeightPersistence for ExtensibleDataSource<D, U>
426where
427    D: MerklizedStateHeightPersistence + Sync,
428    U: Send + Sync,
429{
430    async fn get_last_state_height(&self) -> QueryResult<usize> {
431        self.data_source.get_last_state_height().await
432    }
433}
434
435#[async_trait]
436impl<D, U, Types, State, const ARITY: usize> UpdateStateData<Types, State, ARITY>
437    for ExtensibleDataSource<D, U>
438where
439    D: UpdateStateData<Types, State, ARITY> + Send + Sync,
440    U: Send + Sync,
441    State: MerklizedState<Types, ARITY>,
442    Types: NodeType,
443{
444    async fn set_last_state_height(&mut self, height: usize) -> anyhow::Result<()> {
445        self.data_source.set_last_state_height(height).await
446    }
447
448    async fn insert_merkle_nodes(
449        &mut self,
450        path: MerkleProof<State::Entry, State::Key, State::T, ARITY>,
451        traversal_path: Vec<usize>,
452        block_number: u64,
453    ) -> anyhow::Result<()> {
454        self.data_source
455            .insert_merkle_nodes(path, traversal_path, block_number)
456            .await
457    }
458
459    async fn insert_merkle_nodes_batch(
460        &mut self,
461        proofs: Vec<(
462            MerkleProof<State::Entry, State::Key, State::T, ARITY>,
463            Vec<usize>,
464        )>,
465        block_number: u64,
466    ) -> anyhow::Result<()> {
467        self.data_source
468            .insert_merkle_nodes_batch(proofs, block_number)
469            .await
470    }
471}
472
473#[async_trait]
474impl<D, U, Types> ExplorerDataSource<Types> for ExtensibleDataSource<D, U>
475where
476    D: ExplorerDataSource<Types> + Sync,
477    U: Send + Sync,
478    Types: NodeType,
479    Payload<Types>: QueryablePayload<Types>,
480    Header<Types>: ExplorerHeader<Types> + QueryableHeader<Types>,
481    Transaction<Types>: ExplorerTransaction<Types>,
482{
483    async fn get_block_detail(
484        &self,
485        request: explorer::query_data::BlockIdentifier<Types>,
486    ) -> Result<explorer::query_data::BlockDetail<Types>, explorer::query_data::GetBlockDetailError>
487    {
488        self.data_source.get_block_detail(request).await
489    }
490
491    async fn get_block_summaries(
492        &self,
493        request: explorer::query_data::GetBlockSummariesRequest<Types>,
494    ) -> Result<
495        Vec<explorer::query_data::BlockSummary<Types>>,
496        explorer::query_data::GetBlockSummariesError,
497    > {
498        self.data_source.get_block_summaries(request).await
499    }
500
501    async fn get_transaction_detail(
502        &self,
503        request: explorer::query_data::TransactionIdentifier<Types>,
504    ) -> Result<
505        explorer::query_data::TransactionDetailResponse<Types>,
506        explorer::query_data::GetTransactionDetailError,
507    > {
508        self.data_source.get_transaction_detail(request).await
509    }
510
511    async fn get_transaction_summaries(
512        &self,
513        request: explorer::query_data::GetTransactionSummariesRequest<Types>,
514    ) -> Result<
515        Vec<explorer::query_data::TransactionSummary<Types>>,
516        explorer::query_data::GetTransactionSummariesError,
517    > {
518        self.data_source.get_transaction_summaries(request).await
519    }
520
521    async fn get_explorer_summary(
522        &self,
523    ) -> Result<
524        explorer::query_data::ExplorerSummary<Types>,
525        explorer::query_data::GetExplorerSummaryError,
526    > {
527        self.data_source.get_explorer_summary().await
528    }
529
530    async fn get_search_results(
531        &self,
532        query: TaggedBase64,
533    ) -> Result<
534        explorer::query_data::SearchResult<Types>,
535        explorer::query_data::GetSearchResultsError,
536    > {
537        self.data_source.get_search_results(query).await
538    }
539}
540
541/// Where the user data type supports it, derive `EventsSource` for the extensible data
542/// source.
543#[async_trait]
544impl<D, U, Types> EventsSource<Types> for ExtensibleDataSource<D, U>
545where
546    U: EventsSource<Types> + Sync,
547    D: Send + Sync,
548    Types: NodeType,
549{
550    type EventStream = BoxStream<'static, Arc<Event<Types>>>;
551    type LegacyEventStream = BoxStream<'static, Arc<LegacyEvent<Types>>>;
552
553    async fn get_event_stream(&self, filter: Option<EventFilterSet<Types>>) -> Self::EventStream {
554        Box::pin(self.user_data.get_event_stream(filter).await)
555    }
556
557    async fn get_legacy_event_stream(
558        &self,
559        filter: Option<EventFilterSet<Types>>,
560    ) -> Self::LegacyEventStream {
561        Box::pin(self.user_data.get_legacy_event_stream(filter).await)
562    }
563
564    async fn get_startup_info(&self) -> StartupInfo<Types> {
565        self.user_data.get_startup_info().await
566    }
567}
568
569#[cfg(any(test, feature = "testing"))]
570mod impl_testable_data_source {
571    use hotshot::types::Event;
572    use hotshot_types::new_protocol::CoordinatorEvent;
573
574    use super::*;
575    use crate::{
576        data_source::{UpdateDataSource, fetching::Builder},
577        testing::{
578            consensus::{DataSourceLifeCycle, TestableDataSource},
579            mocks::MockTypes,
580        },
581    };
582
583    #[async_trait]
584    impl<D, U> DataSourceLifeCycle for ExtensibleDataSource<D, U>
585    where
586        D: TestableDataSource + UpdateDataSource<MockTypes>,
587        U: Clone + Default + Send + Sync + 'static,
588    {
589        type Storage = D::Storage;
590        type S = D::S;
591        type P = D::P;
592
593        async fn create(node_id: usize) -> Self::Storage {
594            D::create(node_id).await
595        }
596
597        async fn build(
598            storage: &Self::Storage,
599            opt: impl Send
600            + FnOnce(
601                Builder<MockTypes, Self::S, Self::P>,
602            ) -> Builder<MockTypes, Self::S, Self::P>,
603        ) -> Self {
604            Self::new(D::build(storage, opt).await, Default::default())
605        }
606
607        async fn reset(storage: &Self::Storage) -> Self {
608            Self::new(D::reset(storage).await, Default::default())
609        }
610
611        async fn handle_event(&self, event: &Event<MockTypes>) {
612            let event = CoordinatorEvent::LegacyEvent(event.clone());
613            self.update(&event).await.unwrap();
614        }
615    }
616}
617
618#[cfg(test)]
619mod test {
620    use super::ExtensibleDataSource;
621    use crate::testing::consensus::MockDataSource;
622    // For some reason this is the only way to import the macro defined in another module of this
623    // crate.
624    use crate::*;
625
626    instantiate_data_source_tests!(ExtensibleDataSource<MockDataSource, ()>);
627}