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> {
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 = "")]
208pub struct LeafQueryData<Types: NodeType> {
209    pub(crate) leaf: Leaf2<Types>,
210    pub(crate) qc: QuorumCertificate2<Types>,
211}
212
213#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
214#[serde(bound = "")]
215pub struct LeafQueryDataLegacy<Types: NodeType> {
216    pub(crate) leaf: Leaf<Types>,
217    pub(crate) qc: QuorumCertificate<Types>,
218}
219
220impl<Types: NodeType> From<LeafQueryDataLegacy<Types>> for LeafQueryData<Types> {
221    fn from(legacy: LeafQueryDataLegacy<Types>) -> Self {
222        Self {
223            leaf: legacy.leaf.into(),
224            qc: legacy.qc.to_qc2(),
225        }
226    }
227}
228
229#[derive(Clone, Debug, Snafu)]
230#[snafu(display("QC references leaf {qc_leaf}, but expected {leaf}"))]
231pub struct InconsistentLeafError<Types: NodeType> {
232    pub leaf: LeafHash<Types>,
233    pub qc_leaf: LeafHash<Types>,
234}
235
236#[derive(Clone, Debug, Snafu)]
237#[snafu(display("QC references leaf {qc_leaf}, but expected {leaf}"))]
238pub struct InconsistentLeafLegacyError<Types: NodeType> {
239    pub leaf: LeafHashLegacy<Types>,
240    pub qc_leaf: LeafHashLegacy<Types>,
241}
242
243impl<Types: NodeType> LeafQueryDataLegacy<Types> {
244    /// Collect information about a [`Leaf`].
245    ///
246    /// Returns a new [`LeafQueryData`] object populated from `leaf` and `qc`.
247    ///
248    /// # Errors
249    ///
250    /// Fails with an [`InconsistentLeafError`] if `qc` does not reference `leaf`.
251    pub fn new(
252        mut leaf: Leaf<Types>,
253        qc: QuorumCertificate<Types>,
254    ) -> Result<Self, InconsistentLeafLegacyError<Types>> {
255        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
256        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
257        // require updates in callers and relevant types as well.
258        let leaf_commit = <Leaf<Types> as Committable>::commit(&leaf);
259        ensure!(
260            qc.data.leaf_commit == leaf_commit,
261            InconsistentLeafLegacySnafu {
262                leaf: leaf_commit,
263                qc_leaf: qc.data.leaf_commit
264            }
265        );
266
267        // We only want the leaf for the block header and consensus metadata. The payload will be
268        // stored separately.
269        leaf.unfill_block_payload();
270
271        Ok(Self { leaf, qc })
272    }
273
274    pub async fn genesis(
275        validated_state: &Types::ValidatedState,
276        instance_state: &Types::InstanceState,
277        upgrade: Upgrade,
278    ) -> Self {
279        Self {
280            leaf: Leaf::genesis(validated_state, instance_state, upgrade.base).await,
281            qc: QuorumCertificate::genesis(validated_state, instance_state, upgrade).await,
282        }
283    }
284
285    pub fn leaf(&self) -> &Leaf<Types> {
286        &self.leaf
287    }
288
289    pub fn qc(&self) -> &QuorumCertificate<Types> {
290        &self.qc
291    }
292
293    pub fn header(&self) -> &Header<Types> {
294        self.leaf.block_header()
295    }
296
297    pub fn hash(&self) -> LeafHashLegacy<Types> {
298        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
299        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
300        // require updates in callers and relevant types as well.
301        <Leaf<Types> as Committable>::commit(&self.leaf)
302    }
303
304    pub fn block_hash(&self) -> BlockHash<Types> {
305        self.header().commit()
306    }
307
308    pub fn payload_hash(&self) -> VidCommitment {
309        self.header().payload_commitment()
310    }
311}
312
313impl<Types: NodeType> LeafQueryData<Types> {
314    /// Collect information about a [`Leaf`].
315    ///
316    /// Returns a new [`LeafQueryData`] object populated from `leaf` and `qc`.
317    ///
318    /// # Errors
319    ///
320    /// Fails with an [`InconsistentLeafError`] if `qc` does not reference `leaf`.
321    pub fn new(
322        mut leaf: Leaf2<Types>,
323        qc: QuorumCertificate2<Types>,
324    ) -> Result<Self, InconsistentLeafError<Types>> {
325        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
326        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
327        // require updates in callers and relevant types as well.
328        let leaf_commit = <Leaf2<Types> as Committable>::commit(&leaf);
329        ensure!(
330            qc.data.leaf_commit == leaf_commit,
331            InconsistentLeafSnafu {
332                leaf: leaf_commit,
333                qc_leaf: qc.data.leaf_commit
334            }
335        );
336
337        // We only want the leaf for the block header and consensus metadata. The payload will be
338        // stored separately.
339        leaf.unfill_block_payload();
340
341        Ok(Self { leaf, qc })
342    }
343
344    pub async fn genesis(
345        validated_state: &Types::ValidatedState,
346        instance_state: &Types::InstanceState,
347        upgrade: Upgrade,
348    ) -> Self {
349        Self {
350            leaf: Leaf2::genesis(validated_state, instance_state, upgrade.base).await,
351            qc: QuorumCertificate2::genesis(validated_state, instance_state, upgrade).await,
352        }
353    }
354
355    pub fn leaf(&self) -> &Leaf2<Types> {
356        &self.leaf
357    }
358
359    pub fn qc(&self) -> &QuorumCertificate2<Types> {
360        &self.qc
361    }
362
363    pub fn header(&self) -> &Header<Types> {
364        self.leaf.block_header()
365    }
366
367    pub fn hash(&self) -> LeafHash<Types> {
368        // TODO: Replace with the new `commit` function in HotShot. Add an `upgrade_lock` parameter
369        // and a `HsVer: Versions` bound, then call `leaf.commit(upgrade_lock).await`. This will
370        // require updates in callers and relevant types as well.
371        <Leaf2<Types> as Committable>::commit(&self.leaf)
372    }
373
374    pub fn block_hash(&self) -> BlockHash<Types> {
375        self.header().commit()
376    }
377
378    pub fn payload_hash(&self) -> VidCommitment {
379        self.header().payload_commitment()
380    }
381}
382
383impl<Types: NodeType> HeightIndexed for LeafQueryData<Types> {
384    fn height(&self) -> u64 {
385        self.header().block_number()
386    }
387}
388
389impl<Types: NodeType> HeightIndexed for LeafQueryDataLegacy<Types> {
390    fn height(&self) -> u64 {
391        self.header().block_number()
392    }
393}
394
395#[derive(Clone, Debug, Serialize, serde::Deserialize, PartialEq, Eq)]
396#[serde(bound = "")]
397pub struct HeaderQueryData<Types: NodeType> {
398    pub header: Header<Types>,
399}
400
401impl<Types: NodeType> HeaderQueryData<Types> {
402    pub fn new(header: Header<Types>) -> Self {
403        Self { header }
404    }
405
406    pub fn header(&self) -> &Header<Types> {
407        &self.header
408    }
409}
410
411#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
412#[serde(bound = "")]
413pub struct BlockQueryData<Types: NodeType> {
414    pub(crate) header: Header<Types>,
415    pub(crate) payload: Payload<Types>,
416    pub(crate) hash: BlockHash<Types>,
417    pub(crate) size: u64,
418    pub(crate) num_transactions: u64,
419}
420
421impl<Types: NodeType> BlockQueryData<Types> {
422    pub fn new(header: Header<Types>, payload: Payload<Types>) -> Self
423    where
424        Header<Types>: QueryableHeader<Types>,
425        Payload<Types>: QueryablePayload<Types>,
426    {
427        Self {
428            hash: header.commit(),
429            size: payload_size::<Types>(&payload),
430            num_transactions: payload.len(header.metadata()) as u64,
431            header,
432            payload,
433        }
434    }
435
436    pub async fn genesis(
437        validated_state: &Types::ValidatedState,
438        instance_state: &Types::InstanceState,
439        base: Version,
440    ) -> Self
441    where
442        Header<Types>: QueryableHeader<Types>,
443        Payload<Types>: QueryablePayload<Types>,
444    {
445        let leaf = Leaf2::<Types>::genesis(validated_state, instance_state, base).await;
446        Self::new(leaf.block_header().clone(), leaf.block_payload().unwrap())
447    }
448
449    pub fn header(&self) -> &Header<Types> {
450        &self.header
451    }
452
453    pub fn metadata(&self) -> &Metadata<Types> {
454        self.header.metadata()
455    }
456
457    pub fn payload_hash(&self) -> VidCommitment {
458        self.header.payload_commitment()
459    }
460
461    pub fn payload(&self) -> &Payload<Types> {
462        &self.payload
463    }
464
465    pub fn hash(&self) -> BlockHash<Types> {
466        self.hash
467    }
468
469    pub fn size(&self) -> u64 {
470        self.size
471    }
472
473    pub fn num_transactions(&self) -> u64 {
474        self.num_transactions
475    }
476
477    pub fn namespace_info(&self) -> NamespaceMap<Types>
478    where
479        Header<Types>: QueryableHeader<Types>,
480        Payload<Types>: QueryablePayload<Types>,
481    {
482        let mut map = NamespaceMap::<Types>::new();
483        for tx in self.payload.iter(self.header.metadata()) {
484            let Some(ns_id) = self.header.namespace_id(&tx.ns_index) else {
485                continue;
486            };
487            map.entry(ns_id)
488                .or_insert_with(|| NamespaceInfo {
489                    num_transactions: 0,
490                    size: self.header.namespace_size(&tx.ns_index, self.size as usize),
491                })
492                .num_transactions += 1;
493        }
494        map
495    }
496}
497
498impl<Types: NodeType> BlockQueryData<Types>
499where
500    Header<Types>: QueryableHeader<Types>,
501    Payload<Types>: QueryablePayload<Types>,
502{
503    pub fn transaction(&self, ix: &TransactionIndex<Types>) -> Option<Transaction<Types>> {
504        self.payload().transaction(self.metadata(), ix)
505    }
506
507    pub fn transaction_by_hash(
508        &self,
509        hash: Commitment<Transaction<Types>>,
510    ) -> Option<TransactionIndex<Types>> {
511        self.payload().by_hash(self.metadata(), hash)
512    }
513
514    pub fn transaction_proof(
515        &self,
516        vid_common: &VidCommonQueryData<Types>,
517        ix: &TransactionIndex<Types>,
518    ) -> Option<TransactionInclusionProof<Types>> {
519        self.payload()
520            .transaction_proof(self.metadata(), vid_common, ix)
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(
592        validated_state: &Types::ValidatedState,
593        instance_state: &Types::InstanceState,
594        base: Version,
595    ) -> Self
596    where
597        Header<Types>: QueryableHeader<Types>,
598        Payload<Types>: QueryablePayload<Types>,
599    {
600        BlockQueryData::genesis(validated_state, instance_state, base)
601            .await
602            .into()
603    }
604
605    pub fn hash(&self) -> VidCommitment {
606        self.hash
607    }
608
609    pub fn block_hash(&self) -> BlockHash<Types> {
610        self.block_hash
611    }
612
613    pub fn size(&self) -> u64 {
614        self.size
615    }
616
617    pub fn data(&self) -> &Payload<Types> {
618        &self.data
619    }
620}
621
622impl<Types: NodeType> HeightIndexed for PayloadQueryData<Types> {
623    fn height(&self) -> u64 {
624        self.height
625    }
626}
627
628/// The old VidCommonQueryData, associated with ADVZ VID Scheme.
629#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
630#[serde(bound = "")]
631pub struct ADVZCommonQueryData<Types: NodeType> {
632    pub(crate) height: u64,
633    pub(crate) block_hash: BlockHash<Types>,
634    pub(crate) payload_hash: ADVZCommitment,
635    pub(crate) common: ADVZCommon,
636}
637
638impl<Types: NodeType> ADVZCommonQueryData<Types> {
639    pub fn new(header: Header<Types>, common: ADVZCommon) -> anyhow::Result<Self> {
640        let VidCommitment::V0(payload_hash) = header.payload_commitment() else {
641            return Err(anyhow::anyhow!("Inconsistent header type."));
642        };
643        Ok(Self {
644            height: header.block_number(),
645            block_hash: header.commit(),
646            payload_hash,
647            common,
648        })
649    }
650
651    pub async fn genesis(
652        validated_state: &Types::ValidatedState,
653        instance_state: &Types::InstanceState,
654        base: Version,
655    ) -> anyhow::Result<Self> {
656        let leaf = Leaf::<Types>::genesis(validated_state, instance_state, base).await;
657        let payload = leaf.block_payload().unwrap();
658        let bytes = payload.encode();
659        let disperse = advz_scheme(GENESIS_VID_NUM_STORAGE_NODES)
660            .disperse(bytes)
661            .unwrap();
662
663        Self::new(leaf.block_header().clone(), disperse.common)
664    }
665
666    pub fn block_hash(&self) -> BlockHash<Types> {
667        self.block_hash
668    }
669
670    pub fn payload_hash(&self) -> ADVZCommitment {
671        self.payload_hash
672    }
673
674    pub fn common(&self) -> &ADVZCommon {
675        &self.common
676    }
677}
678
679impl<Types: NodeType> HeightIndexed for ADVZCommonQueryData<Types> {
680    fn height(&self) -> u64 {
681        self.height
682    }
683}
684
685impl<Types: NodeType> HeightIndexed for (ADVZCommonQueryData<Types>, Option<VidShare>) {
686    fn height(&self) -> u64 {
687        self.0.height
688    }
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
692#[serde(bound = "")]
693pub struct VidCommonQueryData<Types: NodeType> {
694    pub(crate) height: u64,
695    pub(crate) block_hash: BlockHash<Types>,
696    pub(crate) payload_hash: VidCommitment,
697    pub(crate) common: VidCommon,
698}
699
700impl<Types: NodeType> VidCommonQueryData<Types> {
701    pub fn new(header: Header<Types>, common: VidCommon) -> Self {
702        Self {
703            height: header.block_number(),
704            block_hash: header.commit(),
705            payload_hash: header.payload_commitment(),
706            common,
707        }
708    }
709
710    pub async fn genesis(
711        validated_state: &Types::ValidatedState,
712        instance_state: &Types::InstanceState,
713        base: Version,
714    ) -> Self {
715        let leaf = Leaf::<Types>::genesis(validated_state, instance_state, base).await;
716        let payload = leaf.block_payload().unwrap();
717        let bytes = payload.encode();
718        let disperse = advz_scheme(GENESIS_VID_NUM_STORAGE_NODES)
719            .disperse(bytes)
720            .unwrap();
721
722        Self::new(leaf.block_header().clone(), VidCommon::V0(disperse.common))
723    }
724
725    pub fn block_hash(&self) -> BlockHash<Types> {
726        self.block_hash
727    }
728
729    pub fn payload_hash(&self) -> VidCommitment {
730        self.payload_hash
731    }
732
733    pub fn common(&self) -> &VidCommon {
734        &self.common
735    }
736}
737
738impl<Types: NodeType> HeightIndexed for VidCommonQueryData<Types> {
739    fn height(&self) -> u64 {
740        self.height
741    }
742}
743
744impl<Types: NodeType> HeightIndexed for (VidCommonQueryData<Types>, Option<VidShare>) {
745    fn height(&self) -> u64 {
746        self.0.height
747    }
748}
749
750#[derive(Clone, Debug, PartialEq, Eq)]
751pub struct BlockWithTransaction<Types: NodeType>
752where
753    Header<Types>: QueryableHeader<Types>,
754    Payload<Types>: QueryablePayload<Types>,
755{
756    pub block: BlockQueryData<Types>,
757    pub transaction: TransactionQueryData<Types>,
758    pub index: TransactionIndex<Types>,
759}
760
761impl<Types: NodeType> BlockWithTransaction<Types>
762where
763    Header<Types>: QueryableHeader<Types>,
764    Payload<Types>: QueryablePayload<Types>,
765{
766    pub fn with_hash(block: BlockQueryData<Types>, hash: TransactionHash<Types>) -> Option<Self> {
767        let (tx, i, index) = block.enumerate().enumerate().find_map(|(i, (index, tx))| {
768            if tx.commit() == hash {
769                Some((tx, i as u64, index))
770            } else {
771                None
772            }
773        })?;
774        let transaction = TransactionQueryData::new(tx, &block, &index, i)?;
775
776        Some(BlockWithTransaction {
777            block,
778            transaction,
779            index,
780        })
781    }
782}
783
784#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
785#[serde(bound = "")]
786pub struct TransactionQueryData<Types: NodeType>
787where
788    Header<Types>: QueryableHeader<Types>,
789    Payload<Types>: QueryablePayload<Types>,
790{
791    transaction: Transaction<Types>,
792    hash: TransactionHash<Types>,
793    index: u64,
794    block_hash: BlockHash<Types>,
795    block_height: u64,
796    namespace: NamespaceId<Types>,
797    pos_in_namespace: u32,
798}
799
800#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
801#[serde(bound = "")]
802pub struct TransactionWithProofQueryData<Types: NodeType>
803where
804    Header<Types>: QueryableHeader<Types>,
805    Payload<Types>: QueryablePayload<Types>,
806{
807    // Ideally we should just have a nested `TransactionQueryData` here, with `#[serde(flatten)]`
808    // (for backwards compatibility, the serialization has to keep the fields at the top level of
809    // the response struct). Unfortunately, `#[serde(flatten)]` causes panics when serializing with
810    // bincode, so we have to manually copy in the fields from `TransactionQueryData`.
811    //
812    // Also, for backwards compatibility, the `proof` field has to be in the middle of all the other
813    // fields, which is similarly incompatible with nesting all the other fields.
814    transaction: Transaction<Types>,
815    hash: TransactionHash<Types>,
816    index: u64,
817    proof: TransactionInclusionProof<Types>,
818    block_hash: BlockHash<Types>,
819    block_height: u64,
820    namespace: NamespaceId<Types>,
821    pos_in_namespace: u32,
822}
823
824impl<Types: NodeType> TransactionQueryData<Types>
825where
826    Header<Types>: QueryableHeader<Types>,
827    Payload<Types>: QueryablePayload<Types>,
828{
829    pub fn new(
830        transaction: Transaction<Types>,
831        block: &BlockQueryData<Types>,
832        i: &TransactionIndex<Types>,
833        index: u64,
834    ) -> Option<Self> {
835        Some(Self {
836            hash: transaction.commit(),
837            transaction,
838            index,
839            block_hash: block.hash(),
840            block_height: block.height(),
841            namespace: block.header().namespace_id(&i.ns_index)?,
842            pos_in_namespace: i.position,
843        })
844    }
845
846    /// The underlying transaction data.
847    pub fn transaction(&self) -> &Transaction<Types> {
848        &self.transaction
849    }
850
851    /// The hash of this transaction.
852    pub fn hash(&self) -> TransactionHash<Types> {
853        self.hash
854    }
855
856    /// The (0-based) position of this transaction within its block.
857    pub fn index(&self) -> u64 {
858        self.index
859    }
860
861    /// The height of the block containing this transaction.
862    pub fn block_height(&self) -> u64 {
863        self.block_height
864    }
865
866    /// The hash of the block containing this transaction.
867    pub fn block_hash(&self) -> BlockHash<Types> {
868        self.block_hash
869    }
870}
871
872impl<Types: NodeType> TransactionWithProofQueryData<Types>
873where
874    Header<Types>: QueryableHeader<Types>,
875    Payload<Types>: QueryablePayload<Types>,
876{
877    pub fn new(data: TransactionQueryData<Types>, proof: TransactionInclusionProof<Types>) -> Self {
878        Self {
879            proof,
880            transaction: data.transaction,
881            hash: data.hash,
882            index: data.index,
883            block_hash: data.block_hash,
884            block_height: data.block_height,
885            namespace: data.namespace,
886            pos_in_namespace: data.pos_in_namespace,
887        }
888    }
889
890    /// A proof of inclusion of this transaction in its block.
891    pub fn proof(&self) -> &TransactionInclusionProof<Types> {
892        &self.proof
893    }
894
895    /// The underlying transaction data.
896    pub fn transaction(&self) -> &Transaction<Types> {
897        &self.transaction
898    }
899
900    /// The hash of this transaction.
901    pub fn hash(&self) -> TransactionHash<Types> {
902        self.hash
903    }
904
905    /// The (0-based) position of this transaction within its block.
906    pub fn index(&self) -> u64 {
907        self.index
908    }
909
910    /// The height of the block containing this transaction.
911    pub fn block_height(&self) -> u64 {
912        self.block_height
913    }
914
915    /// The hash of the block containing this transaction.
916    pub fn block_hash(&self) -> BlockHash<Types> {
917        self.block_hash
918    }
919}
920
921pub(crate) fn payload_size<Types: NodeType>(payload: &Payload<Types>) -> u64 {
922    payload.encode().len() as u64
923}
924
925#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
926#[serde(bound = "")]
927pub struct BlockSummaryQueryData<Types: NodeType>
928where
929    Header<Types>: QueryableHeader<Types>,
930{
931    pub(crate) header: Header<Types>,
932    pub(crate) hash: BlockHash<Types>,
933    pub(crate) size: u64,
934    pub(crate) num_transactions: u64,
935    pub(crate) namespaces: NamespaceMap<Types>,
936}
937
938// Add some basic getters to the BlockSummaryQueryData type.
939impl<Types: NodeType> BlockSummaryQueryData<Types>
940where
941    Header<Types>: QueryableHeader<Types>,
942{
943    pub fn header(&self) -> &Header<Types> {
944        &self.header
945    }
946
947    pub fn hash(&self) -> BlockHash<Types> {
948        self.hash
949    }
950
951    pub fn size(&self) -> u64 {
952        self.size
953    }
954
955    pub fn num_transactions(&self) -> u64 {
956        self.num_transactions
957    }
958
959    pub fn namespaces(&self) -> &NamespaceMap<Types> {
960        &self.namespaces
961    }
962}
963
964impl<Types: NodeType> HeightIndexed for BlockSummaryQueryData<Types>
965where
966    Header<Types>: QueryableHeader<Types>,
967{
968    fn height(&self) -> u64 {
969        self.header.block_number()
970    }
971}
972
973#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
974#[serde(bound = "")]
975pub struct TransactionSummaryQueryData<Types: NodeType> {
976    pub(crate) hash: TransactionHash<Types>,
977    pub(crate) header: Header<Types>,
978    // We want a way to determine a summary for each rollup entry, without
979    // the data directly, but rather a summary of the data.
980    // For now, we'll roll with the `Payload` itself.
981    pub(crate) transaction: Transaction<Types>,
982}
983
984// Since BlockSummaryQueryData can be derived entirely from BlockQueryData, we
985// implement the From trait to allow for a seamless conversion using rust
986// contentions.
987impl<Types: NodeType> From<BlockQueryData<Types>> for BlockSummaryQueryData<Types>
988where
989    Header<Types>: QueryableHeader<Types>,
990    Payload<Types>: QueryablePayload<Types>,
991{
992    fn from(value: BlockQueryData<Types>) -> Self {
993        BlockSummaryQueryData {
994            namespaces: value.namespace_info(),
995            header: value.header,
996            hash: value.hash,
997            size: value.size,
998            num_transactions: value.num_transactions,
999        }
1000    }
1001}
1002
1003#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1004pub struct NamespaceInfo {
1005    pub num_transactions: u64,
1006    pub size: u64,
1007}
1008
1009pub type NamespaceMap<Types> = HashMap<NamespaceId<Types>, NamespaceInfo>;
1010
1011/// A summary of a payload without all the data.
1012///
1013/// This type is useful when you only want information about a payload, such as its size or
1014/// transaction count, but you don't want to load the entire payload, which might be very large.
1015#[derive(Clone, Debug, PartialEq, Eq)]
1016pub struct PayloadMetadata<Types>
1017where
1018    Types: NodeType,
1019    Header<Types>: QueryableHeader<Types>,
1020{
1021    pub height: u64,
1022    pub block_hash: BlockHash<Types>,
1023    pub hash: VidCommitment,
1024    pub size: u64,
1025    pub num_transactions: u64,
1026    pub namespaces: NamespaceMap<Types>,
1027}
1028
1029impl<Types> HeightIndexed for PayloadMetadata<Types>
1030where
1031    Types: NodeType,
1032    Header<Types>: QueryableHeader<Types>,
1033{
1034    fn height(&self) -> u64 {
1035        self.height
1036    }
1037}
1038
1039impl<Types> From<BlockQueryData<Types>> for PayloadMetadata<Types>
1040where
1041    Types: NodeType,
1042    Header<Types>: QueryableHeader<Types>,
1043    Payload<Types>: QueryablePayload<Types>,
1044{
1045    fn from(block: BlockQueryData<Types>) -> Self {
1046        Self {
1047            height: block.height(),
1048            block_hash: block.hash(),
1049            hash: block.payload_hash(),
1050            size: block.size(),
1051            num_transactions: block.num_transactions(),
1052            namespaces: block.namespace_info(),
1053        }
1054    }
1055}
1056
1057/// A summary of a VID payload without all the data.
1058///
1059/// This is primarily useful when you want to check if a VID object exists, but not load the whole
1060/// object.
1061#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1062pub struct VidCommonMetadata<Types>
1063where
1064    Types: NodeType,
1065{
1066    pub height: u64,
1067    pub block_hash: BlockHash<Types>,
1068    pub payload_hash: VidCommitment,
1069}
1070
1071impl<Types> HeightIndexed for VidCommonMetadata<Types>
1072where
1073    Types: NodeType,
1074{
1075    fn height(&self) -> u64 {
1076        self.height
1077    }
1078}
1079
1080impl<Types> From<VidCommonQueryData<Types>> for VidCommonMetadata<Types>
1081where
1082    Types: NodeType,
1083{
1084    fn from(common: VidCommonQueryData<Types>) -> Self {
1085        Self {
1086            height: common.height(),
1087            block_hash: common.block_hash(),
1088            payload_hash: common.payload_hash(),
1089        }
1090    }
1091}
1092
1093#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
1094pub struct Limits {
1095    pub small_object_range_limit: usize,
1096    pub large_object_range_limit: usize,
1097}