hotshot_query_service/availability/
query_data.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::{collections::HashMap, fmt::Debug, hash::Hash};
14
15use committable::{Commitment, Committable};
16use hotshot_types::{
17    data::{Leaf, Leaf2, VidCommitment, VidCommon, VidShare},
18    simple_certificate::QuorumCertificate2,
19    traits::{
20        self,
21        block_contents::{BlockHeader, GENESIS_VID_NUM_STORAGE_NODES},
22        node_implementation::{NodeType, Versions},
23        EncodeBytes,
24    },
25    vid::advz::{advz_scheme, ADVZCommitment, ADVZCommon},
26};
27use jf_advz::VidScheme;
28use serde::{de::DeserializeOwned, Deserialize, Serialize};
29use snafu::{ensure, Snafu};
30
31use crate::{types::HeightIndexed, Header, Metadata, Payload, QuorumCertificate, Transaction};
32
33pub type LeafHash<Types> = Commitment<Leaf2<Types>>;
34pub type LeafHashLegacy<Types> = Commitment<Leaf<Types>>;
35pub type QcHash<Types> = Commitment<QuorumCertificate2<Types>>;
36
37/// A block hash is the hash of the block header.
38///
39/// A block consists of a header and a payload. But the header itself contains a commitment to the
40/// payload, so we can commit to the entire block simply by hashing the header.
41pub type BlockHash<Types> = Commitment<Header<Types>>;
42pub type TransactionHash<Types> = Commitment<Transaction<Types>>;
43pub type TransactionInclusionProof<Types> =
44    <Payload<Types> as QueryablePayload<Types>>::InclusionProof;
45pub type NamespaceIndex<Types> = <Header<Types> as QueryableHeader<Types>>::NamespaceIndex;
46pub type NamespaceId<Types> = <Header<Types> as QueryableHeader<Types>>::NamespaceId;
47
48pub type Timestamp = time::OffsetDateTime;
49
50pub trait QueryableHeader<Types: NodeType>: BlockHeader<Types> {
51    /// Index for looking up a namespace.
52    type NamespaceIndex: Clone + Debug + Hash + PartialEq + Eq + From<i64> + Into<i64> + Send + Sync;
53
54    /// Serialized representation of a namespace.
55    type NamespaceId: Clone
56        + Debug
57        + Serialize
58        + DeserializeOwned
59        + Send
60        + Sync
61        + Hash
62        + PartialEq
63        + Eq
64        + Copy
65        + From<i64>
66        + Into<i64>;
67
68    /// Resolve a namespace index to the serialized identifier for that namespace.
69    fn namespace_id(&self, i: &Self::NamespaceIndex) -> Option<Self::NamespaceId>;
70
71    /// Get the size taken up by the given namespace in the payload.
72    fn namespace_size(&self, i: &Self::NamespaceIndex, payload_size: usize) -> u64;
73}
74
75#[derive(Clone, Debug, PartialEq, Eq)]
76pub struct TransactionIndex<Types: NodeType>
77where
78    Header<Types>: QueryableHeader<Types>,
79{
80    /// Index for looking up the namespace this transaction belongs to.
81    pub ns_index: NamespaceIndex<Types>,
82    /// Index of the transaction within its namespace in its block.
83    pub position: u32,
84}
85
86/// The proof system and the statement which is proved will vary by application, with different
87/// applications proving stronger or weaker statements depending on the trust assumptions at
88/// play. Some may prove a very strong statement (for example, a shared sequencer proving that
89/// the transaction belongs not only to the block but to a section of the block dedicated to a
90/// specific rollup), otherwise may prove something substantially weaker (for example, a trusted
91/// query service may use `()` for the proof).
92pub trait VerifiableInclusion<Types: NodeType>:
93    Clone + Debug + PartialEq + Eq + Serialize + DeserializeOwned + Send + Sync
94{
95    /// Verify the inclusion proof against a payload commitment.
96    /// Returns `None` on error.
97    fn verify(
98        &self,
99        metadata: &Metadata<Types>,
100        tx: &Transaction<Types>,
101        payload_commitment: &VidCommitment,
102        common: &VidCommon,
103    ) -> bool;
104}
105
106/// A block payload whose contents (e.g. individual transactions) can be examined.
107///
108/// Note to implementers: this trait has only a few required methods. The provided methods, for
109/// querying transactions in various ways, are implemented in terms of the required
110/// [`iter`](Self::iter) and [`transaction_proof`](Self::transaction_proof) methods, and
111/// the default implementations may be inefficient (e.g. performing an O(n) search, or computing an
112/// unnecessary inclusion proof). It is good practice to override these default implementations if
113/// your block type supports more efficient implementations (e.g. sublinear indexing by hash).
114pub trait QueryablePayload<Types: NodeType>: traits::BlockPayload<Types>
115where
116    Header<Types>: QueryableHeader<Types>,
117{
118    /// Enumerate the transactions in this block.
119    type Iter<'a>: Iterator<Item = TransactionIndex<Types>>
120    where
121        Self: 'a;
122
123    /// A proof that a certain transaction exists in the block.
124    type InclusionProof: VerifiableInclusion<Types>;
125
126    /// The number of transactions in the block.
127    fn len(&self, meta: &Self::Metadata) -> usize;
128
129    /// Whether this block is empty of transactions.
130    fn is_empty(&self, meta: &Self::Metadata) -> bool {
131        self.len(meta) == 0
132    }
133
134    /// List the transaction indices in the block.
135    fn iter<'a>(&'a self, meta: &'a Self::Metadata) -> Self::Iter<'a>;
136
137    /// Enumerate the transactions in the block with their indices.
138    fn enumerate<'a>(
139        &'a self,
140        meta: &'a Self::Metadata,
141    ) -> Box<dyn 'a + Iterator<Item = (TransactionIndex<Types>, Self::Transaction)>> {
142        Box::new(self.iter(meta).map(|ix| {
143            // `self.transaction` should always return `Some` if we are using an index which was
144            // yielded by `self.iter`.
145            let tx = self.transaction(meta, &ix).unwrap();
146            (ix, tx)
147        }))
148    }
149
150    /// Get a transaction by its block-specific index.
151    fn transaction(
152        &self,
153        meta: &Self::Metadata,
154        index: &TransactionIndex<Types>,
155    ) -> Option<Self::Transaction>;
156
157    /// Get an inclusion proof for the given transaction.
158    ///
159    /// This function may be slow and computationally intensive, especially for large transactions.
160    fn transaction_proof(
161        &self,
162        meta: &Self::Metadata,
163        vid: &VidCommonQueryData<Types>,
164        index: &TransactionIndex<Types>,
165    ) -> Option<Self::InclusionProof>;
166
167    /// Get the index of the `nth` transaction.
168    fn nth(&self, meta: &Self::Metadata, n: usize) -> Option<TransactionIndex<Types>> {
169        self.iter(meta).nth(n)
170    }
171
172    /// Get the `nth` transaction.
173    fn nth_transaction(&self, meta: &Self::Metadata, n: usize) -> Option<Self::Transaction> {
174        self.transaction(meta, &self.nth(meta, n)?)
175    }
176
177    /// Get the index of the transaction with a given hash, if it is in the block.
178    fn by_hash(
179        &self,
180        meta: &Self::Metadata,
181        hash: Commitment<Self::Transaction>,
182    ) -> Option<TransactionIndex<Types>> {
183        self.iter(meta).find(|i| {
184            if let Some(tx) = self.transaction(meta, i) {
185                tx.commit() == hash
186            } else {
187                false
188            }
189        })
190    }
191
192    /// Get the transaction with a given hash, if it is in the block.
193    fn transaction_by_hash(
194        &self,
195        meta: &Self::Metadata,
196        hash: Commitment<Self::Transaction>,
197    ) -> Option<Self::Transaction> {
198        self.transaction(meta, &self.by_hash(meta, hash)?)
199    }
200}
201
202#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
203#[serde(bound = "")]
204pub struct LeafQueryData<Types: NodeType> {
205    pub(crate) leaf: Leaf2<Types>,
206    pub(crate) qc: QuorumCertificate2<Types>,
207}
208
209#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
210#[serde(bound = "")]
211pub struct LeafQueryDataLegacy<Types: NodeType> {
212    pub(crate) leaf: Leaf<Types>,
213    pub(crate) qc: QuorumCertificate<Types>,
214}
215
216impl<Types: NodeType> From<LeafQueryDataLegacy<Types>> for LeafQueryData<Types> {
217    fn from(legacy: LeafQueryDataLegacy<Types>) -> Self {
218        Self {
219            leaf: legacy.leaf.into(),
220            qc: legacy.qc.to_qc2(),
221        }
222    }
223}
224
225#[derive(Clone, Debug, Snafu)]
226#[snafu(display("QC references leaf {qc_leaf}, but expected {leaf}"))]
227pub struct InconsistentLeafError<Types: NodeType> {
228    pub leaf: LeafHash<Types>,
229    pub qc_leaf: LeafHash<Types>,
230}
231
232#[derive(Clone, Debug, Snafu)]
233#[snafu(display("QC references leaf {qc_leaf}, but expected {leaf}"))]
234pub struct InconsistentLeafLegacyError<Types: NodeType> {
235    pub leaf: LeafHashLegacy<Types>,
236    pub qc_leaf: LeafHashLegacy<Types>,
237}
238
239impl<Types: NodeType> LeafQueryDataLegacy<Types> {
240    /// Collect information about a [`Leaf`].
241    ///
242    /// Returns a new [`LeafQueryData`] object populated from `leaf` and `qc`.
243    ///
244    /// # Errors
245    ///
246    /// Fails with an [`InconsistentLeafError`] if `qc` does not reference `leaf`.
247    pub fn new(
248        mut leaf: Leaf<Types>,
249        qc: QuorumCertificate<Types>,
250    ) -> Result<Self, InconsistentLeafLegacyError<Types>> {
251        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
252        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
253        // require updates in callers and relevant types as well.
254        let leaf_commit = <Leaf<Types> as Committable>::commit(&leaf);
255        ensure!(
256            qc.data.leaf_commit == leaf_commit,
257            InconsistentLeafLegacySnafu {
258                leaf: leaf_commit,
259                qc_leaf: qc.data.leaf_commit
260            }
261        );
262
263        // We only want the leaf for the block header and consensus metadata. The payload will be
264        // stored separately.
265        leaf.unfill_block_payload();
266
267        Ok(Self { leaf, qc })
268    }
269
270    pub async fn genesis<HsVer: Versions>(
271        validated_state: &Types::ValidatedState,
272        instance_state: &Types::InstanceState,
273    ) -> Self {
274        Self {
275            leaf: Leaf::genesis::<HsVer>(validated_state, instance_state).await,
276            qc: QuorumCertificate::genesis::<HsVer>(validated_state, instance_state).await,
277        }
278    }
279
280    pub fn leaf(&self) -> &Leaf<Types> {
281        &self.leaf
282    }
283
284    pub fn qc(&self) -> &QuorumCertificate<Types> {
285        &self.qc
286    }
287
288    pub fn header(&self) -> &Header<Types> {
289        self.leaf.block_header()
290    }
291
292    pub fn hash(&self) -> LeafHashLegacy<Types> {
293        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
294        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
295        // require updates in callers and relevant types as well.
296        <Leaf<Types> as Committable>::commit(&self.leaf)
297    }
298
299    pub fn block_hash(&self) -> BlockHash<Types> {
300        self.header().commit()
301    }
302
303    pub fn payload_hash(&self) -> VidCommitment {
304        self.header().payload_commitment()
305    }
306}
307
308impl<Types: NodeType> LeafQueryData<Types> {
309    /// Collect information about a [`Leaf`].
310    ///
311    /// Returns a new [`LeafQueryData`] object populated from `leaf` and `qc`.
312    ///
313    /// # Errors
314    ///
315    /// Fails with an [`InconsistentLeafError`] if `qc` does not reference `leaf`.
316    pub fn new(
317        mut leaf: Leaf2<Types>,
318        qc: QuorumCertificate2<Types>,
319    ) -> Result<Self, InconsistentLeafError<Types>> {
320        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
321        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
322        // require updates in callers and relevant types as well.
323        let leaf_commit = <Leaf2<Types> as Committable>::commit(&leaf);
324        ensure!(
325            qc.data.leaf_commit == leaf_commit,
326            InconsistentLeafSnafu {
327                leaf: leaf_commit,
328                qc_leaf: qc.data.leaf_commit
329            }
330        );
331
332        // We only want the leaf for the block header and consensus metadata. The payload will be
333        // stored separately.
334        leaf.unfill_block_payload();
335
336        Ok(Self { leaf, qc })
337    }
338
339    pub async fn genesis<HsVer: Versions>(
340        validated_state: &Types::ValidatedState,
341        instance_state: &Types::InstanceState,
342    ) -> Self {
343        Self {
344            leaf: Leaf2::genesis::<HsVer>(validated_state, instance_state).await,
345            qc: QuorumCertificate2::genesis::<HsVer>(validated_state, instance_state).await,
346        }
347    }
348
349    pub fn leaf(&self) -> &Leaf2<Types> {
350        &self.leaf
351    }
352
353    pub fn qc(&self) -> &QuorumCertificate2<Types> {
354        &self.qc
355    }
356
357    pub fn header(&self) -> &Header<Types> {
358        self.leaf.block_header()
359    }
360
361    pub fn hash(&self) -> LeafHash<Types> {
362        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
363        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
364        // require updates in callers and relevant types as well.
365        <Leaf2<Types> as Committable>::commit(&self.leaf)
366    }
367
368    pub fn block_hash(&self) -> BlockHash<Types> {
369        self.header().commit()
370    }
371
372    pub fn payload_hash(&self) -> VidCommitment {
373        self.header().payload_commitment()
374    }
375}
376
377impl<Types: NodeType> HeightIndexed for LeafQueryData<Types> {
378    fn height(&self) -> u64 {
379        self.header().block_number()
380    }
381}
382
383impl<Types: NodeType> HeightIndexed for LeafQueryDataLegacy<Types> {
384    fn height(&self) -> u64 {
385        self.header().block_number()
386    }
387}
388
389#[derive(Clone, Debug, Serialize, serde::Deserialize, PartialEq, Eq)]
390#[serde(bound = "")]
391pub struct HeaderQueryData<Types: NodeType> {
392    pub header: Header<Types>,
393}
394
395impl<Types: NodeType> HeaderQueryData<Types> {
396    pub fn new(header: Header<Types>) -> Self {
397        Self { header }
398    }
399
400    pub fn header(&self) -> &Header<Types> {
401        &self.header
402    }
403}
404
405#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
406#[serde(bound = "")]
407pub struct BlockQueryData<Types: NodeType> {
408    pub(crate) header: Header<Types>,
409    pub(crate) payload: Payload<Types>,
410    pub(crate) hash: BlockHash<Types>,
411    pub(crate) size: u64,
412    pub(crate) num_transactions: u64,
413}
414
415impl<Types: NodeType> BlockQueryData<Types> {
416    pub fn new(header: Header<Types>, payload: Payload<Types>) -> Self
417    where
418        Header<Types>: QueryableHeader<Types>,
419        Payload<Types>: QueryablePayload<Types>,
420    {
421        Self {
422            hash: header.commit(),
423            size: payload_size::<Types>(&payload),
424            num_transactions: payload.len(header.metadata()) as u64,
425            header,
426            payload,
427        }
428    }
429
430    pub async fn genesis<HsVer: Versions>(
431        validated_state: &Types::ValidatedState,
432        instance_state: &Types::InstanceState,
433    ) -> Self
434    where
435        Header<Types>: QueryableHeader<Types>,
436        Payload<Types>: QueryablePayload<Types>,
437    {
438        let leaf = Leaf2::<Types>::genesis::<HsVer>(validated_state, instance_state).await;
439        Self::new(leaf.block_header().clone(), leaf.block_payload().unwrap())
440    }
441
442    pub fn header(&self) -> &Header<Types> {
443        &self.header
444    }
445
446    pub fn metadata(&self) -> &Metadata<Types> {
447        self.header.metadata()
448    }
449
450    pub fn payload_hash(&self) -> VidCommitment {
451        self.header.payload_commitment()
452    }
453
454    pub fn payload(&self) -> &Payload<Types> {
455        &self.payload
456    }
457
458    pub fn hash(&self) -> BlockHash<Types> {
459        self.hash
460    }
461
462    pub fn size(&self) -> u64 {
463        self.size
464    }
465
466    pub fn num_transactions(&self) -> u64 {
467        self.num_transactions
468    }
469
470    pub fn namespace_info(&self) -> NamespaceMap<Types>
471    where
472        Header<Types>: QueryableHeader<Types>,
473        Payload<Types>: QueryablePayload<Types>,
474    {
475        let mut map = NamespaceMap::<Types>::new();
476        for tx in self.payload.iter(self.header.metadata()) {
477            let Some(ns_id) = self.header.namespace_id(&tx.ns_index) else {
478                continue;
479            };
480            map.entry(ns_id)
481                .or_insert_with(|| NamespaceInfo {
482                    num_transactions: 0,
483                    size: self.header.namespace_size(&tx.ns_index, self.size as usize),
484                })
485                .num_transactions += 1;
486        }
487        map
488    }
489}
490
491impl<Types: NodeType> BlockQueryData<Types>
492where
493    Header<Types>: QueryableHeader<Types>,
494    Payload<Types>: QueryablePayload<Types>,
495{
496    pub fn transaction(&self, ix: &TransactionIndex<Types>) -> Option<Transaction<Types>> {
497        self.payload().transaction(self.metadata(), ix)
498    }
499
500    pub fn transaction_by_hash(
501        &self,
502        hash: Commitment<Transaction<Types>>,
503    ) -> Option<TransactionIndex<Types>> {
504        self.payload().by_hash(self.metadata(), hash)
505    }
506
507    pub fn transaction_proof(
508        &self,
509        vid_common: &VidCommonQueryData<Types>,
510        ix: &TransactionIndex<Types>,
511    ) -> Option<TransactionInclusionProof<Types>> {
512        self.payload()
513            .transaction_proof(self.metadata(), vid_common, ix)
514    }
515
516    pub fn len(&self) -> usize {
517        self.payload.len(self.metadata())
518    }
519
520    pub fn is_empty(&self) -> bool {
521        self.len() == 0
522    }
523
524    pub fn enumerate(
525        &self,
526    ) -> impl '_ + Iterator<Item = (TransactionIndex<Types>, Transaction<Types>)> {
527        self.payload.enumerate(self.metadata())
528    }
529}
530
531impl<Types: NodeType> HeightIndexed for BlockQueryData<Types> {
532    fn height(&self) -> u64 {
533        self.header.block_number()
534    }
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
538#[serde(bound = "")]
539pub struct ADVZPayloadQueryData<Types: NodeType> {
540    pub(crate) height: u64,
541    pub(crate) block_hash: BlockHash<Types>,
542    pub(crate) hash: ADVZCommitment,
543    pub(crate) size: u64,
544    pub(crate) data: Payload<Types>,
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
548#[serde(bound = "")]
549pub struct PayloadQueryData<Types: NodeType> {
550    pub(crate) height: u64,
551    pub(crate) block_hash: BlockHash<Types>,
552    pub(crate) hash: VidCommitment,
553    pub(crate) size: u64,
554    pub(crate) data: Payload<Types>,
555}
556
557impl<Types: NodeType> From<BlockQueryData<Types>> for PayloadQueryData<Types> {
558    fn from(block: BlockQueryData<Types>) -> Self {
559        Self {
560            height: block.height(),
561            block_hash: block.hash(),
562            hash: block.header.payload_commitment(),
563            size: block.size(),
564            data: block.payload,
565        }
566    }
567}
568
569impl<Types: NodeType> PayloadQueryData<Types> {
570    pub fn to_legacy(&self) -> Option<ADVZPayloadQueryData<Types>> {
571        let VidCommitment::V0(advz_commit) = self.hash else {
572            return None;
573        };
574
575        Some(ADVZPayloadQueryData {
576            height: self.height,
577            block_hash: self.block_hash,
578            hash: advz_commit,
579            size: self.size,
580            data: self.data.clone(),
581        })
582    }
583
584    pub async fn genesis<HsVer: Versions>(
585        validated_state: &Types::ValidatedState,
586        instance_state: &Types::InstanceState,
587    ) -> Self
588    where
589        Header<Types>: QueryableHeader<Types>,
590        Payload<Types>: QueryablePayload<Types>,
591    {
592        BlockQueryData::genesis::<HsVer>(validated_state, instance_state)
593            .await
594            .into()
595    }
596
597    pub fn hash(&self) -> VidCommitment {
598        self.hash
599    }
600
601    pub fn block_hash(&self) -> BlockHash<Types> {
602        self.block_hash
603    }
604
605    pub fn size(&self) -> u64 {
606        self.size
607    }
608
609    pub fn data(&self) -> &Payload<Types> {
610        &self.data
611    }
612}
613
614impl<Types: NodeType> HeightIndexed for PayloadQueryData<Types> {
615    fn height(&self) -> u64 {
616        self.height
617    }
618}
619
620/// The old VidCommonQueryData, associated with ADVZ VID Scheme.
621#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
622#[serde(bound = "")]
623pub struct ADVZCommonQueryData<Types: NodeType> {
624    pub(crate) height: u64,
625    pub(crate) block_hash: BlockHash<Types>,
626    pub(crate) payload_hash: ADVZCommitment,
627    pub(crate) common: ADVZCommon,
628}
629
630impl<Types: NodeType> ADVZCommonQueryData<Types> {
631    pub fn new(header: Header<Types>, common: ADVZCommon) -> anyhow::Result<Self> {
632        let VidCommitment::V0(payload_hash) = header.payload_commitment() else {
633            return Err(anyhow::anyhow!("Inconsistent header type."));
634        };
635        Ok(Self {
636            height: header.block_number(),
637            block_hash: header.commit(),
638            payload_hash,
639            common,
640        })
641    }
642
643    pub async fn genesis<HsVer: Versions>(
644        validated_state: &Types::ValidatedState,
645        instance_state: &Types::InstanceState,
646    ) -> anyhow::Result<Self> {
647        let leaf = Leaf::<Types>::genesis::<HsVer>(validated_state, instance_state).await;
648        let payload = leaf.block_payload().unwrap();
649        let bytes = payload.encode();
650        let disperse = advz_scheme(GENESIS_VID_NUM_STORAGE_NODES)
651            .disperse(bytes)
652            .unwrap();
653
654        Self::new(leaf.block_header().clone(), disperse.common)
655    }
656
657    pub fn block_hash(&self) -> BlockHash<Types> {
658        self.block_hash
659    }
660
661    pub fn payload_hash(&self) -> ADVZCommitment {
662        self.payload_hash
663    }
664
665    pub fn common(&self) -> &ADVZCommon {
666        &self.common
667    }
668}
669
670impl<Types: NodeType> HeightIndexed for ADVZCommonQueryData<Types> {
671    fn height(&self) -> u64 {
672        self.height
673    }
674}
675
676impl<Types: NodeType> HeightIndexed for (ADVZCommonQueryData<Types>, Option<VidShare>) {
677    fn height(&self) -> u64 {
678        self.0.height
679    }
680}
681
682#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
683#[serde(bound = "")]
684pub struct VidCommonQueryData<Types: NodeType> {
685    pub(crate) height: u64,
686    pub(crate) block_hash: BlockHash<Types>,
687    pub(crate) payload_hash: VidCommitment,
688    pub(crate) common: VidCommon,
689}
690
691impl<Types: NodeType> VidCommonQueryData<Types> {
692    pub fn new(header: Header<Types>, common: VidCommon) -> Self {
693        Self {
694            height: header.block_number(),
695            block_hash: header.commit(),
696            payload_hash: header.payload_commitment(),
697            common,
698        }
699    }
700
701    pub async fn genesis<HsVer: Versions>(
702        validated_state: &Types::ValidatedState,
703        instance_state: &Types::InstanceState,
704    ) -> Self {
705        let leaf = Leaf::<Types>::genesis::<HsVer>(validated_state, instance_state).await;
706        let payload = leaf.block_payload().unwrap();
707        let bytes = payload.encode();
708        let disperse = advz_scheme(GENESIS_VID_NUM_STORAGE_NODES)
709            .disperse(bytes)
710            .unwrap();
711
712        Self::new(leaf.block_header().clone(), VidCommon::V0(disperse.common))
713    }
714
715    pub fn block_hash(&self) -> BlockHash<Types> {
716        self.block_hash
717    }
718
719    pub fn payload_hash(&self) -> VidCommitment {
720        self.payload_hash
721    }
722
723    pub fn common(&self) -> &VidCommon {
724        &self.common
725    }
726}
727
728impl<Types: NodeType> HeightIndexed for VidCommonQueryData<Types> {
729    fn height(&self) -> u64 {
730        self.height
731    }
732}
733
734impl<Types: NodeType> HeightIndexed for (VidCommonQueryData<Types>, Option<VidShare>) {
735    fn height(&self) -> u64 {
736        self.0.height
737    }
738}
739
740#[derive(Clone, Debug, PartialEq, Eq)]
741pub struct BlockWithTransaction<Types: NodeType>
742where
743    Header<Types>: QueryableHeader<Types>,
744    Payload<Types>: QueryablePayload<Types>,
745{
746    pub block: BlockQueryData<Types>,
747    pub transaction: TransactionQueryData<Types>,
748    pub index: TransactionIndex<Types>,
749}
750
751impl<Types: NodeType> BlockWithTransaction<Types>
752where
753    Header<Types>: QueryableHeader<Types>,
754    Payload<Types>: QueryablePayload<Types>,
755{
756    pub fn with_hash(block: BlockQueryData<Types>, hash: TransactionHash<Types>) -> Option<Self> {
757        let (tx, i, index) = block.enumerate().enumerate().find_map(|(i, (index, tx))| {
758            if tx.commit() == hash {
759                Some((tx, i as u64, index))
760            } else {
761                None
762            }
763        })?;
764        let transaction = TransactionQueryData::new(tx, &block, &index, i)?;
765
766        Some(BlockWithTransaction {
767            block,
768            transaction,
769            index,
770        })
771    }
772}
773
774#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
775#[serde(bound = "")]
776pub struct TransactionQueryData<Types: NodeType>
777where
778    Header<Types>: QueryableHeader<Types>,
779    Payload<Types>: QueryablePayload<Types>,
780{
781    transaction: Transaction<Types>,
782    hash: TransactionHash<Types>,
783    index: u64,
784    block_hash: BlockHash<Types>,
785    block_height: u64,
786    namespace: NamespaceId<Types>,
787    pos_in_namespace: u32,
788}
789
790#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
791#[serde(bound = "")]
792pub struct TransactionWithProofQueryData<Types: NodeType>
793where
794    Header<Types>: QueryableHeader<Types>,
795    Payload<Types>: QueryablePayload<Types>,
796{
797    // Ideally we should just have a nested `TransactionQueryData` here, with `#[serde(flatten)]`
798    // (for backwards compatibility, the serialization has to keep the fields at the top level of
799    // the response struct). Unfortunately, `#[serde(flatten)]` causes panics when serializing with
800    // bincode, so we have to manually copy in the fields from `TransactionQueryData`.
801    //
802    // Also, for backwards compatibility, the `proof` field has to be in the middle of all the other
803    // fields, which is similarly incompatible with nesting all the other fields.
804    transaction: Transaction<Types>,
805    hash: TransactionHash<Types>,
806    index: u64,
807    proof: TransactionInclusionProof<Types>,
808    block_hash: BlockHash<Types>,
809    block_height: u64,
810    namespace: NamespaceId<Types>,
811    pos_in_namespace: u32,
812}
813
814impl<Types: NodeType> TransactionQueryData<Types>
815where
816    Header<Types>: QueryableHeader<Types>,
817    Payload<Types>: QueryablePayload<Types>,
818{
819    pub fn new(
820        transaction: Transaction<Types>,
821        block: &BlockQueryData<Types>,
822        i: &TransactionIndex<Types>,
823        index: u64,
824    ) -> Option<Self> {
825        Some(Self {
826            hash: transaction.commit(),
827            transaction,
828            index,
829            block_hash: block.hash(),
830            block_height: block.height(),
831            namespace: block.header().namespace_id(&i.ns_index)?,
832            pos_in_namespace: i.position,
833        })
834    }
835
836    /// The underlying transaction data.
837    pub fn transaction(&self) -> &Transaction<Types> {
838        &self.transaction
839    }
840
841    /// The hash of this transaction.
842    pub fn hash(&self) -> TransactionHash<Types> {
843        self.hash
844    }
845
846    /// The (0-based) position of this transaction within its block.
847    pub fn index(&self) -> u64 {
848        self.index
849    }
850
851    /// The height of the block containing this transaction.
852    pub fn block_height(&self) -> u64 {
853        self.block_height
854    }
855
856    /// The hash of the block containing this transaction.
857    pub fn block_hash(&self) -> BlockHash<Types> {
858        self.block_hash
859    }
860}
861
862impl<Types: NodeType> TransactionWithProofQueryData<Types>
863where
864    Header<Types>: QueryableHeader<Types>,
865    Payload<Types>: QueryablePayload<Types>,
866{
867    pub fn new(data: TransactionQueryData<Types>, proof: TransactionInclusionProof<Types>) -> Self {
868        Self {
869            proof,
870            transaction: data.transaction,
871            hash: data.hash,
872            index: data.index,
873            block_hash: data.block_hash,
874            block_height: data.block_height,
875            namespace: data.namespace,
876            pos_in_namespace: data.pos_in_namespace,
877        }
878    }
879
880    /// A proof of inclusion of this transaction in its block.
881    pub fn proof(&self) -> &TransactionInclusionProof<Types> {
882        &self.proof
883    }
884
885    /// The underlying transaction data.
886    pub fn transaction(&self) -> &Transaction<Types> {
887        &self.transaction
888    }
889
890    /// The hash of this transaction.
891    pub fn hash(&self) -> TransactionHash<Types> {
892        self.hash
893    }
894
895    /// The (0-based) position of this transaction within its block.
896    pub fn index(&self) -> u64 {
897        self.index
898    }
899
900    /// The height of the block containing this transaction.
901    pub fn block_height(&self) -> u64 {
902        self.block_height
903    }
904
905    /// The hash of the block containing this transaction.
906    pub fn block_hash(&self) -> BlockHash<Types> {
907        self.block_hash
908    }
909}
910
911pub(crate) fn payload_size<Types: NodeType>(payload: &Payload<Types>) -> u64 {
912    payload.encode().len() as u64
913}
914
915#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
916#[serde(bound = "")]
917pub struct BlockSummaryQueryData<Types: NodeType>
918where
919    Header<Types>: QueryableHeader<Types>,
920{
921    pub(crate) header: Header<Types>,
922    pub(crate) hash: BlockHash<Types>,
923    pub(crate) size: u64,
924    pub(crate) num_transactions: u64,
925    pub(crate) namespaces: NamespaceMap<Types>,
926}
927
928// Add some basic getters to the BlockSummaryQueryData type.
929impl<Types: NodeType> BlockSummaryQueryData<Types>
930where
931    Header<Types>: QueryableHeader<Types>,
932{
933    pub fn header(&self) -> &Header<Types> {
934        &self.header
935    }
936
937    pub fn hash(&self) -> BlockHash<Types> {
938        self.hash
939    }
940
941    pub fn size(&self) -> u64 {
942        self.size
943    }
944
945    pub fn num_transactions(&self) -> u64 {
946        self.num_transactions
947    }
948
949    pub fn namespaces(&self) -> &NamespaceMap<Types> {
950        &self.namespaces
951    }
952}
953
954impl<Types: NodeType> HeightIndexed for BlockSummaryQueryData<Types>
955where
956    Header<Types>: QueryableHeader<Types>,
957{
958    fn height(&self) -> u64 {
959        self.header.block_number()
960    }
961}
962
963#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
964#[serde(bound = "")]
965pub struct TransactionSummaryQueryData<Types: NodeType> {
966    pub(crate) hash: TransactionHash<Types>,
967    pub(crate) header: Header<Types>,
968    // We want a way to determine a summary for each rollup entry, without
969    // the data directly, but rather a summary of the data.
970    // For now, we'll roll with the `Payload` itself.
971    pub(crate) transaction: Transaction<Types>,
972}
973
974// Since BlockSummaryQueryData can be derived entirely from BlockQueryData, we
975// implement the From trait to allow for a seamless conversion using rust
976// contentions.
977impl<Types: NodeType> From<BlockQueryData<Types>> for BlockSummaryQueryData<Types>
978where
979    Header<Types>: QueryableHeader<Types>,
980    Payload<Types>: QueryablePayload<Types>,
981{
982    fn from(value: BlockQueryData<Types>) -> Self {
983        BlockSummaryQueryData {
984            namespaces: value.namespace_info(),
985            header: value.header,
986            hash: value.hash,
987            size: value.size,
988            num_transactions: value.num_transactions,
989        }
990    }
991}
992
993#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
994pub struct NamespaceInfo {
995    pub num_transactions: u64,
996    pub size: u64,
997}
998
999pub type NamespaceMap<Types> = HashMap<NamespaceId<Types>, NamespaceInfo>;
1000
1001/// A summary of a payload without all the data.
1002///
1003/// This type is useful when you only want information about a payload, such as its size or
1004/// transaction count, but you don't want to load the entire payload, which might be very large.
1005#[derive(Clone, Debug, PartialEq, Eq)]
1006pub struct PayloadMetadata<Types>
1007where
1008    Types: NodeType,
1009    Header<Types>: QueryableHeader<Types>,
1010{
1011    pub height: u64,
1012    pub block_hash: BlockHash<Types>,
1013    pub hash: VidCommitment,
1014    pub size: u64,
1015    pub num_transactions: u64,
1016    pub namespaces: NamespaceMap<Types>,
1017}
1018
1019impl<Types> HeightIndexed for PayloadMetadata<Types>
1020where
1021    Types: NodeType,
1022    Header<Types>: QueryableHeader<Types>,
1023{
1024    fn height(&self) -> u64 {
1025        self.height
1026    }
1027}
1028
1029impl<Types> From<BlockQueryData<Types>> for PayloadMetadata<Types>
1030where
1031    Types: NodeType,
1032    Header<Types>: QueryableHeader<Types>,
1033    Payload<Types>: QueryablePayload<Types>,
1034{
1035    fn from(block: BlockQueryData<Types>) -> Self {
1036        Self {
1037            height: block.height(),
1038            block_hash: block.hash(),
1039            hash: block.payload_hash(),
1040            size: block.size(),
1041            num_transactions: block.num_transactions(),
1042            namespaces: block.namespace_info(),
1043        }
1044    }
1045}
1046
1047/// A summary of a VID payload without all the data.
1048///
1049/// This is primarily useful when you want to check if a VID object exists, but not load the whole
1050/// object.
1051#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1052pub struct VidCommonMetadata<Types>
1053where
1054    Types: NodeType,
1055{
1056    pub height: u64,
1057    pub block_hash: BlockHash<Types>,
1058    pub payload_hash: VidCommitment,
1059}
1060
1061impl<Types> HeightIndexed for VidCommonMetadata<Types>
1062where
1063    Types: NodeType,
1064{
1065    fn height(&self) -> u64 {
1066        self.height
1067    }
1068}
1069
1070impl<Types> From<VidCommonQueryData<Types>> for VidCommonMetadata<Types>
1071where
1072    Types: NodeType,
1073{
1074    fn from(common: VidCommonQueryData<Types>) -> Self {
1075        Self {
1076            height: common.height(),
1077            block_hash: common.block_hash(),
1078            payload_hash: common.payload_hash(),
1079        }
1080    }
1081}
1082
1083#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
1084pub struct Limits {
1085    pub small_object_range_limit: usize,
1086    pub large_object_range_limit: usize,
1087}