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