Skip to main content

hotshot_query_service/availability/
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
13use std::ops::{Bound, RangeBounds};
14
15use async_trait::async_trait;
16use futures::{
17    future::Future,
18    stream::{BoxStream, StreamExt},
19};
20pub use hotshot_new_protocol::message::Certificate2;
21pub use hotshot_query_service_types::availability::{BlockId, LeafId};
22use hotshot_types::{
23    data::VidShare, simple_certificate::CertificatePair, traits::node_implementation::NodeType,
24};
25
26use super::{
27    BlockWithTransaction,
28    fetch::Fetch,
29    query_data::{
30        BlockQueryData, LeafQueryData, PayloadMetadata, PayloadQueryData, QueryableHeader,
31        QueryablePayload, TransactionHash, VidCommonMetadata, VidCommonQueryData,
32    },
33};
34use crate::{Header, Payload, QueryResult, types::HeightIndexed};
35
36pub type FetchStream<T> = BoxStream<'static, Fetch<T>>;
37
38/// An interface for querying a HotShot blockchain.
39///
40/// This interface provides expressive queries over all the data which is made available by HotShot
41/// consensus. The data exposed by this interface consists entirely of _normative_ data: data which
42/// every correct HotShot node or light client will agree on, and which is guaranteed by consensus
43/// to be immutable. This immutability has an interesting consequence: all of the methods exposed by
44/// this trait are _pure_: given equivalent inputs, the same method will always return equivalent
45/// outputs[^1].
46///
47/// This purity property has a further consequence: none of the methods defined here can fail! Even
48/// if you query for a block at a position past the end of the current chain -- a block which does
49/// not exist yet -- the query will not fail. It will return an in-progress [`Fetch`] which, when it
50/// finally does resolve, resolves to the unique block at that position in the chain. All subsequent
51/// queries for the same position will eventually resolve to the same block.
52///
53/// In other words, the abstraction is that of an infinitely long chain which exists statically, in
54/// its entirety, at all times. In reality, of course, this chain is being produced incrementally
55/// and has a finite length at any given time. But all this means is that some queries may take a
56/// long time to resolve while others may resolve immediately.
57///
58/// [^1]: The data returned by these methods are wrapped in [`Fetch`], which does not implement
59///       [`PartialEq]`. So to speak of equivalent outputs, we need to define an equivalence
60///       relation on [`Fetch<T>`]. The relation we will use is defined when `T: PartialEq`, and
61///       defines two fetches `f1` and `f2` as equivalent when `f1.await == f2.await`. That is,
62///       depending on when you call a certain method, you may or may not get a response
63///       immediately. But whenever you do get the data you requested, it is unique for that
64///       combination of inputs.
65#[async_trait]
66pub trait AvailabilityDataSource<Types: NodeType>
67where
68    Header<Types>: QueryableHeader<Types>,
69    Payload<Types>: QueryablePayload<Types>,
70{
71    async fn get_leaf<ID>(&self, id: ID) -> Fetch<LeafQueryData<Types>>
72    where
73        ID: Into<LeafId<Types>> + Send + Sync;
74
75    async fn get_header<ID>(&self, id: ID) -> Fetch<Header<Types>>
76    where
77        ID: Into<BlockId<Types>> + Send + Sync;
78
79    async fn get_block<ID>(&self, id: ID) -> Fetch<BlockQueryData<Types>>
80    where
81        ID: Into<BlockId<Types>> + Send + Sync;
82
83    async fn get_payload<ID>(&self, id: ID) -> Fetch<PayloadQueryData<Types>>
84    where
85        ID: Into<BlockId<Types>> + Send + Sync;
86
87    async fn get_payload_metadata<ID>(&self, id: ID) -> Fetch<PayloadMetadata<Types>>
88    where
89        ID: Into<BlockId<Types>> + Send + Sync;
90
91    async fn get_vid_common<ID>(&self, id: ID) -> Fetch<VidCommonQueryData<Types>>
92    where
93        ID: Into<BlockId<Types>> + Send + Sync;
94
95    async fn get_vid_common_metadata<ID>(&self, id: ID) -> Fetch<VidCommonMetadata<Types>>
96    where
97        ID: Into<BlockId<Types>> + Send + Sync;
98
99    async fn get_leaf_range<R>(&self, range: R) -> FetchStream<LeafQueryData<Types>>
100    where
101        R: RangeBounds<usize> + Send + 'static;
102
103    async fn get_header_range<R>(&self, range: R) -> FetchStream<Header<Types>>
104    where
105        R: RangeBounds<usize> + Send + 'static;
106
107    async fn get_block_range<R>(&self, range: R) -> FetchStream<BlockQueryData<Types>>
108    where
109        R: RangeBounds<usize> + Send + 'static;
110
111    async fn get_payload_range<R>(&self, range: R) -> FetchStream<PayloadQueryData<Types>>
112    where
113        R: RangeBounds<usize> + Send + 'static;
114
115    async fn get_payload_metadata_range<R>(&self, range: R) -> FetchStream<PayloadMetadata<Types>>
116    where
117        R: RangeBounds<usize> + Send + 'static;
118
119    async fn get_vid_common_range<R>(&self, range: R) -> FetchStream<VidCommonQueryData<Types>>
120    where
121        R: RangeBounds<usize> + Send + 'static;
122
123    async fn get_vid_common_metadata_range<R>(
124        &self,
125        range: R,
126    ) -> FetchStream<VidCommonMetadata<Types>>
127    where
128        R: RangeBounds<usize> + Send + 'static;
129
130    async fn get_leaf_range_rev(
131        &self,
132        start: Bound<usize>,
133        end: usize,
134    ) -> FetchStream<LeafQueryData<Types>>;
135
136    async fn get_block_range_rev(
137        &self,
138        start: Bound<usize>,
139        end: usize,
140    ) -> FetchStream<BlockQueryData<Types>>;
141
142    async fn get_payload_range_rev(
143        &self,
144        start: Bound<usize>,
145        end: usize,
146    ) -> FetchStream<PayloadQueryData<Types>>;
147
148    async fn get_payload_metadata_range_rev(
149        &self,
150        start: Bound<usize>,
151        end: usize,
152    ) -> FetchStream<PayloadMetadata<Types>>;
153
154    async fn get_vid_common_range_rev(
155        &self,
156        start: Bound<usize>,
157        end: usize,
158    ) -> FetchStream<VidCommonQueryData<Types>>;
159
160    async fn get_vid_common_metadata_range_rev(
161        &self,
162        start: Bound<usize>,
163        end: usize,
164    ) -> FetchStream<VidCommonMetadata<Types>>;
165
166    async fn get_block_containing_transaction(
167        &self,
168        h: TransactionHash<Types>,
169    ) -> Fetch<BlockWithTransaction<Types>>;
170
171    async fn subscribe_blocks(&self, from: usize) -> BoxStream<'static, BlockQueryData<Types>> {
172        self.get_block_range(from..)
173            .await
174            .then(Fetch::resolve)
175            .boxed()
176    }
177
178    async fn subscribe_payloads(&self, from: usize) -> BoxStream<'static, PayloadQueryData<Types>> {
179        self.get_payload_range(from..)
180            .await
181            .then(Fetch::resolve)
182            .boxed()
183    }
184
185    async fn subscribe_payload_metadata(
186        &self,
187        from: usize,
188    ) -> BoxStream<'static, PayloadMetadata<Types>> {
189        self.get_payload_metadata_range(from..)
190            .await
191            .then(Fetch::resolve)
192            .boxed()
193    }
194
195    async fn subscribe_leaves(&self, from: usize) -> BoxStream<'static, LeafQueryData<Types>> {
196        self.get_leaf_range(from..)
197            .await
198            .then(Fetch::resolve)
199            .boxed()
200    }
201
202    async fn subscribe_headers(&self, from: usize) -> BoxStream<'static, Header<Types>> {
203        self.get_header_range(from..)
204            .await
205            .then(Fetch::resolve)
206            .boxed()
207    }
208
209    async fn subscribe_vid_common(
210        &self,
211        from: usize,
212    ) -> BoxStream<'static, VidCommonQueryData<Types>> {
213        self.get_vid_common_range(from..)
214            .await
215            .then(Fetch::resolve)
216            .boxed()
217    }
218
219    async fn subscribe_vid_common_metadata(
220        &self,
221        from: usize,
222    ) -> BoxStream<'static, VidCommonMetadata<Types>> {
223        self.get_vid_common_metadata_range(from..)
224            .await
225            .then(Fetch::resolve)
226            .boxed()
227    }
228
229    async fn get_cert2(&self, _height: u64) -> QueryResult<Option<Certificate2<Types>>> {
230        Ok(None)
231    }
232}
233
234/// Information about a block.
235///
236/// This type encapsulate all the information we might have about a decided HotShot block:
237/// * The leaf, including a header and consensus metadata
238/// * The block itself, which may be missing if this node did not receive a DA proposal for this
239///   block
240/// * VID common and a unique VID share, which may be missing if this node did not receive a VID
241///   share for this block
242#[derive(Clone, Debug)]
243pub struct BlockInfo<Types: NodeType> {
244    pub leaf: LeafQueryData<Types>,
245    pub block: Option<BlockQueryData<Types>>,
246    pub vid_common: Option<VidCommonQueryData<Types>>,
247    pub vid_share: Option<VidShare>,
248    pub qc_chain: Option<[CertificatePair<Types>; 2]>,
249    /// New protocol finality certificate, present at heights finalized by cert2.
250    pub cert2: Option<Certificate2<Types>>,
251}
252
253impl<Types: NodeType> From<LeafQueryData<Types>> for BlockInfo<Types> {
254    fn from(leaf: LeafQueryData<Types>) -> Self {
255        Self::new(leaf, None, None, None)
256    }
257}
258
259impl<Types: NodeType> HeightIndexed for BlockInfo<Types> {
260    fn height(&self) -> u64 {
261        self.leaf.height()
262    }
263}
264
265impl<Types: NodeType> BlockInfo<Types> {
266    pub fn new(
267        leaf: LeafQueryData<Types>,
268        block: Option<BlockQueryData<Types>>,
269        vid_common: Option<VidCommonQueryData<Types>>,
270        vid_share: Option<VidShare>,
271    ) -> Self {
272        Self {
273            leaf,
274            block,
275            vid_common,
276            vid_share,
277            qc_chain: None,
278            cert2: None,
279        }
280    }
281
282    pub fn with_qc_chain(mut self, qc_chain: [CertificatePair<Types>; 2]) -> Self {
283        self.qc_chain = Some(qc_chain);
284        self
285    }
286
287    pub fn with_cert2(mut self, cert2: Certificate2<Types>) -> Self {
288        self.cert2 = Some(cert2);
289        self
290    }
291}
292
293pub trait UpdateAvailabilityData<Types: NodeType> {
294    /// Append information about a new block to the database.
295    fn append(&self, info: BlockInfo<Types>) -> impl Send + Future<Output = anyhow::Result<()>>;
296
297    /// Append a payload for a block whose leaf was already decided without one.
298    ///
299    /// Decide events in the new protocol may arrive before VID reconstruction has produced the
300    /// block payload. When the payload eventually becomes available the data source uses this
301    /// method to fill it in, notifying any pending fetchers. Implementations that don't track
302    /// blocks (e.g. metrics-only) may leave the default no-op.
303    fn append_payload(
304        &self,
305        _block: BlockQueryData<Types>,
306    ) -> impl Send + Future<Output = anyhow::Result<()>> {
307        async { Ok(()) }
308    }
309}