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