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