hotshot_types/
data.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Provides types useful for representing `HotShot`'s data structures
8//!
9//! This module provides types for representing consensus internal state, such as leaves,
10//! `HotShot`'s version of a block, and proposals, messages upon which to reach the consensus.
11
12use std::{
13    fmt::{Debug, Display},
14    hash::Hash,
15    marker::PhantomData,
16    sync::Arc,
17    time::Duration,
18};
19
20use async_lock::RwLock;
21use bincode::Options;
22use committable::{Commitment, CommitmentBoundsArkless, Committable, RawCommitmentBuilder};
23use hotshot_utils::anytrace::*;
24use jf_vid::VidScheme;
25use rand::Rng;
26use serde::{Deserialize, Serialize};
27use tagged_base64::TaggedBase64;
28use thiserror::Error;
29use vbs::version::{StaticVersionType, Version};
30use vec1::Vec1;
31use vid_disperse::{ADVZDisperse, ADVZDisperseShare, AvidMDisperse, VidDisperseShare2};
32
33use crate::{
34    drb::DrbResult,
35    epoch_membership::EpochMembershipCoordinator,
36    impl_has_epoch, impl_has_none_epoch,
37    message::{convert_proposal, Proposal, UpgradeLock},
38    simple_certificate::{
39        LightClientStateUpdateCertificate, NextEpochQuorumCertificate2, QuorumCertificate,
40        QuorumCertificate2, TimeoutCertificate, TimeoutCertificate2, UpgradeCertificate,
41        ViewSyncFinalizeCertificate, ViewSyncFinalizeCertificate2,
42    },
43    simple_vote::{HasEpoch, QuorumData, QuorumData2, UpgradeProposalData, VersionedVoteData},
44    traits::{
45        block_contents::{BlockHeader, BuilderFee, EncodeBytes, TestableBlock},
46        node_implementation::{ConsensusTime, NodeType, Versions},
47        signature_key::SignatureKey,
48        states::TestableState,
49        BlockPayload,
50    },
51    utils::{
52        bincode_opts, genesis_epoch_from_version, option_epoch_from_block_number,
53        EpochTransitionIndicator,
54    },
55    vid::{
56        advz::{advz_scheme, ADVZCommitment, ADVZShare},
57        avidm::{init_avidm_param, AvidMCommitment, AvidMScheme, AvidMShare},
58    },
59    vote::{Certificate, HasViewNumber},
60};
61
62/// Implements `ConsensusTime`, `Display`, `Add`, `AddAssign`, `Deref` and `Sub`
63/// for the given thing wrapper type around u64.
64macro_rules! impl_u64_wrapper {
65    ($t:ty, $genesis_val:expr) => {
66        impl ConsensusTime for $t {
67            /// Create a genesis number
68            fn genesis() -> Self {
69                Self($genesis_val)
70            }
71            /// Create a new number with the given value.
72            fn new(n: u64) -> Self {
73                Self(n)
74            }
75            /// Return the u64 format
76            fn u64(&self) -> u64 {
77                self.0
78            }
79        }
80
81        impl Display for $t {
82            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83                write!(f, "{}", self.0)
84            }
85        }
86
87        impl std::ops::Add<u64> for $t {
88            type Output = $t;
89
90            fn add(self, rhs: u64) -> Self::Output {
91                Self(self.0 + rhs)
92            }
93        }
94
95        impl std::ops::AddAssign<u64> for $t {
96            fn add_assign(&mut self, rhs: u64) {
97                self.0 += rhs;
98            }
99        }
100
101        impl std::ops::Deref for $t {
102            type Target = u64;
103
104            fn deref(&self) -> &Self::Target {
105                &self.0
106            }
107        }
108
109        impl std::ops::Sub<u64> for $t {
110            type Output = $t;
111            fn sub(self, rhs: u64) -> Self::Output {
112                Self(self.0 - rhs)
113            }
114        }
115    };
116}
117
118/// Type-safe wrapper around `u64` so we know the thing we're talking about is a view number.
119#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
120pub struct ViewNumber(u64);
121
122impl Committable for ViewNumber {
123    fn commit(&self) -> Commitment<Self> {
124        let builder = RawCommitmentBuilder::new("View Number Commitment");
125        builder.u64(self.0).finalize()
126    }
127}
128
129impl_u64_wrapper!(ViewNumber, 0u64);
130
131/// Type-safe wrapper around `u64` so we know the thing we're talking about is a epoch number.
132#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
133pub struct EpochNumber(u64);
134
135impl Committable for EpochNumber {
136    fn commit(&self) -> Commitment<Self> {
137        let builder = RawCommitmentBuilder::new("Epoch Number Commitment");
138        builder.u64(self.0).finalize()
139    }
140}
141
142impl_u64_wrapper!(EpochNumber, 1u64);
143
144/// A proposal to start providing data availability for a block.
145#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
146#[serde(bound = "TYPES: NodeType")]
147pub struct DaProposal<TYPES: NodeType> {
148    /// Encoded transactions in the block to be applied.
149    pub encoded_transactions: Arc<[u8]>,
150    /// Metadata of the block to be applied.
151    pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
152    /// View this proposal applies to
153    pub view_number: TYPES::View,
154}
155
156/// A proposal to start providing data availability for a block.
157#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
158#[serde(bound = "TYPES: NodeType")]
159pub struct DaProposal2<TYPES: NodeType> {
160    /// Encoded transactions in the block to be applied.
161    pub encoded_transactions: Arc<[u8]>,
162    /// Metadata of the block to be applied.
163    pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
164    /// View this proposal applies to
165    pub view_number: TYPES::View,
166    /// Epoch this proposal applies to
167    pub epoch: Option<TYPES::Epoch>,
168    /// Indicates whether we are in epoch transition
169    /// In epoch transition the next epoch payload commit should be calculated additionally
170    pub epoch_transition_indicator: EpochTransitionIndicator,
171}
172
173impl<TYPES: NodeType> From<DaProposal<TYPES>> for DaProposal2<TYPES> {
174    fn from(da_proposal: DaProposal<TYPES>) -> Self {
175        Self {
176            encoded_transactions: da_proposal.encoded_transactions,
177            metadata: da_proposal.metadata,
178            view_number: da_proposal.view_number,
179            epoch: None,
180            epoch_transition_indicator: EpochTransitionIndicator::NotInTransition,
181        }
182    }
183}
184
185impl<TYPES: NodeType> From<DaProposal2<TYPES>> for DaProposal<TYPES> {
186    fn from(da_proposal2: DaProposal2<TYPES>) -> Self {
187        Self {
188            encoded_transactions: da_proposal2.encoded_transactions,
189            metadata: da_proposal2.metadata,
190            view_number: da_proposal2.view_number,
191        }
192    }
193}
194
195/// A proposal to upgrade the network
196#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
197#[serde(bound = "TYPES: NodeType")]
198pub struct UpgradeProposal<TYPES>
199where
200    TYPES: NodeType,
201{
202    /// The information about which version we are upgrading to.
203    pub upgrade_proposal: UpgradeProposalData<TYPES>,
204    /// View this proposal applies to
205    pub view_number: TYPES::View,
206}
207
208/// VID Commitment type
209#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
210#[serde(
211    try_from = "tagged_base64::TaggedBase64",
212    into = "tagged_base64::TaggedBase64"
213)]
214pub enum VidCommitment {
215    V0(ADVZCommitment),
216    V1(AvidMCommitment),
217}
218
219impl Default for VidCommitment {
220    fn default() -> Self {
221        Self::V0(Default::default())
222    }
223}
224
225impl Display for VidCommitment {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        std::write!(f, "{}", TaggedBase64::from(self))
228    }
229}
230
231impl Debug for VidCommitment {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        std::fmt::Display::fmt(self, f)
234    }
235}
236
237impl From<VidCommitment> for TaggedBase64 {
238    fn from(val: VidCommitment) -> Self {
239        match val {
240            VidCommitment::V0(comm) => comm.into(),
241            VidCommitment::V1(comm) => comm.into(),
242        }
243    }
244}
245
246impl From<&VidCommitment> for TaggedBase64 {
247    fn from(val: &VidCommitment) -> Self {
248        match val {
249            VidCommitment::V0(comm) => comm.into(),
250            VidCommitment::V1(comm) => comm.into(),
251        }
252    }
253}
254
255impl TryFrom<TaggedBase64> for VidCommitment {
256    type Error = tagged_base64::Tb64Error;
257
258    fn try_from(value: TaggedBase64) -> std::result::Result<Self, Self::Error> {
259        ADVZCommitment::try_from(&value)
260            .map(Self::V0)
261            .or(AvidMCommitment::try_from(value).map(Self::V1))
262    }
263}
264
265impl<'a> TryFrom<&'a TaggedBase64> for VidCommitment {
266    type Error = tagged_base64::Tb64Error;
267
268    fn try_from(value: &'a TaggedBase64) -> std::result::Result<Self, Self::Error> {
269        ADVZCommitment::try_from(value)
270            .map(Self::V0)
271            .or(AvidMCommitment::try_from(value).map(Self::V1))
272    }
273}
274
275impl std::str::FromStr for VidCommitment {
276    type Err = tagged_base64::Tb64Error;
277    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
278        use core::convert::TryFrom;
279        Self::try_from(TaggedBase64::from_str(s)?)
280            .map_err(|_| tagged_base64::Tb64Error::InvalidData)
281    }
282}
283
284// TODO(Chengyu): cannot have this because of `impl<H> From<Output<H>> for HasherNode<H>`.
285// impl From<ADVZCommitment> for VidCommitment {
286//     fn from(comm: ADVZCommitment) -> Self {
287//         Self::V0(comm)
288//     }
289// }
290
291impl From<AvidMCommitment> for VidCommitment {
292    fn from(comm: AvidMCommitment) -> Self {
293        Self::V1(comm)
294    }
295}
296
297impl AsRef<[u8]> for VidCommitment {
298    fn as_ref(&self) -> &[u8] {
299        match self {
300            Self::V0(comm) => comm.as_ref(),
301            Self::V1(comm) => comm.as_ref(),
302        }
303    }
304}
305
306impl AsRef<[u8; 32]> for VidCommitment {
307    fn as_ref(&self) -> &[u8; 32] {
308        match self {
309            Self::V0(comm) => comm.as_ref().as_ref(),
310            Self::V1(comm) => comm.as_ref(),
311        }
312    }
313}
314
315impl VidCommitment {
316    /// Unwrap an ADVZCommitment. Panic if incorrect version.
317    pub fn unwrap_v0(self) -> ADVZCommitment {
318        match self {
319            VidCommitment::V0(comm) => comm,
320            _ => panic!("Unexpected version for this commitment"),
321        }
322    }
323    /// Unwrap an AvidMCommitment. Panic if incorrect version.
324    pub fn unwrap_v1(self) -> AvidMCommitment {
325        match self {
326            VidCommitment::V1(comm) => comm,
327            _ => panic!("Unexpected version for this commitment"),
328        }
329    }
330}
331
332/// Compute the VID payload commitment.
333/// TODO(Gus) delete this function?
334/// # Panics
335/// If the VID computation fails.
336#[must_use]
337#[allow(clippy::panic)]
338pub fn vid_commitment<V: Versions>(
339    encoded_transactions: &[u8],
340    metadata: &[u8],
341    total_weight: usize,
342    version: Version,
343) -> VidCommitment {
344    if version < V::Epochs::VERSION {
345        let encoded_tx_len = encoded_transactions.len();
346        advz_scheme(total_weight)
347            .commit_only(encoded_transactions)
348            .map(VidCommitment::V0)
349            .unwrap_or_else(|err| {
350                panic!(
351                    "VidScheme::commit_only \
352                     failure:(total_weight,payload_byte_len)=({total_weight},{encoded_tx_len}) \
353                     error: {err}"
354                )
355            })
356    } else {
357        let param = init_avidm_param(total_weight).unwrap();
358        let encoded_tx_len = encoded_transactions.len();
359        AvidMScheme::commit(
360            &param,
361            encoded_transactions,
362            ns_table::parse_ns_table(encoded_tx_len, metadata),
363        )
364        .map(VidCommitment::V1)
365        .unwrap()
366    }
367}
368
369/// VID share type
370#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
371pub enum VidShare {
372    V0(ADVZShare),
373    V1(AvidMShare),
374}
375
376// TODO(Chengyu): cannot have this
377// impl From<ADVZShare> for VidShare {
378//     fn from(share: ADVZShare) -> Self {
379//         Self::V0(share)
380//     }
381// }
382
383impl From<AvidMShare> for VidShare {
384    fn from(share: AvidMShare) -> Self {
385        Self::V1(share)
386    }
387}
388
389pub mod ns_table;
390pub mod vid_disperse;
391
392/// A helper struct to hold the disperse data and the time it took to calculate the disperse
393pub struct VidDisperseAndDuration<TYPES: NodeType> {
394    /// The disperse data
395    pub disperse: VidDisperse<TYPES>,
396    /// The time it took to calculate the disperse
397    pub duration: Duration,
398}
399
400/// VID dispersal data
401///
402/// Like [`DaProposal`].
403///
404/// TODO move to vid.rs?
405#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
406#[serde(bound = "TYPES: NodeType")]
407pub enum VidDisperse<TYPES: NodeType> {
408    /// Disperse type for first VID version
409    V0(vid_disperse::ADVZDisperse<TYPES>),
410    /// Place holder for VID upgrade
411    V1(vid_disperse::AvidMDisperse<TYPES>),
412}
413
414impl<TYPES: NodeType> From<vid_disperse::ADVZDisperse<TYPES>> for VidDisperse<TYPES> {
415    fn from(disperse: vid_disperse::ADVZDisperse<TYPES>) -> Self {
416        Self::V0(disperse)
417    }
418}
419
420impl<TYPES: NodeType> From<vid_disperse::AvidMDisperse<TYPES>> for VidDisperse<TYPES> {
421    fn from(disperse: vid_disperse::AvidMDisperse<TYPES>) -> Self {
422        Self::V1(disperse)
423    }
424}
425
426impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperse<TYPES> {
427    fn view_number(&self) -> TYPES::View {
428        match self {
429            Self::V0(disperse) => disperse.view_number(),
430            Self::V1(disperse) => disperse.view_number(),
431        }
432    }
433}
434
435impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperse<TYPES> {
436    fn epoch(&self) -> Option<TYPES::Epoch> {
437        match self {
438            Self::V0(disperse) => disperse.epoch(),
439            Self::V1(disperse) => disperse.epoch(),
440        }
441    }
442}
443
444impl<TYPES: NodeType> VidDisperse<TYPES> {
445    /// Calculate the vid disperse information from the payload given a view, epoch and membership,
446    /// If the sender epoch is missing, it means it's the same as the target epoch.
447    ///
448    /// # Errors
449    /// Returns an error if the disperse or commitment calculation fails
450    #[allow(clippy::panic)]
451    pub async fn calculate_vid_disperse<V: Versions>(
452        payload: &TYPES::BlockPayload,
453        membership: &EpochMembershipCoordinator<TYPES>,
454        view: TYPES::View,
455        target_epoch: Option<TYPES::Epoch>,
456        data_epoch: Option<TYPES::Epoch>,
457        metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
458        upgrade_lock: &UpgradeLock<TYPES, V>,
459    ) -> Result<VidDisperseAndDuration<TYPES>> {
460        let version = upgrade_lock.version_infallible(view).await;
461        if version < V::Epochs::VERSION {
462            ADVZDisperse::calculate_vid_disperse(
463                payload,
464                membership,
465                view,
466                target_epoch,
467                data_epoch,
468            )
469            .await
470            .map(|(disperse, duration)| VidDisperseAndDuration {
471                disperse: Self::V0(disperse),
472                duration,
473            })
474        } else {
475            AvidMDisperse::calculate_vid_disperse(
476                payload,
477                membership,
478                view,
479                target_epoch,
480                data_epoch,
481                metadata,
482            )
483            .await
484            .map(|(disperse, duration)| VidDisperseAndDuration {
485                disperse: Self::V1(disperse),
486                duration,
487            })
488        }
489    }
490
491    /// Return the internal payload commitment
492    pub fn payload_commitment(&self) -> VidCommitment {
493        match self {
494            Self::V0(disperse) => VidCommitment::V0(disperse.payload_commitment),
495            Self::V1(disperse) => disperse.payload_commitment.into(),
496        }
497    }
498
499    /// Return a slice reference to the payload commitment. Should be used for signature.
500    pub fn payload_commitment_ref(&self) -> &[u8] {
501        match self {
502            Self::V0(disperse) => disperse.payload_commitment.as_ref(),
503            Self::V1(disperse) => disperse.payload_commitment.as_ref(),
504        }
505    }
506
507    /// Set the view number
508    pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
509        match self {
510            Self::V0(share) => share.view_number = view_number,
511            Self::V1(share) => share.view_number = view_number,
512        }
513    }
514}
515
516/// VID share and associated metadata for a single node
517#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
518#[serde(bound = "TYPES: NodeType")]
519pub enum VidDisperseShare<TYPES: NodeType> {
520    /// VID disperse share type for first version VID
521    V0(vid_disperse::ADVZDisperseShare<TYPES>),
522    /// VID disperse share type after epoch upgrade and VID upgrade
523    V1(vid_disperse::VidDisperseShare2<TYPES>),
524}
525
526impl<TYPES: NodeType> VidDisperseShare<TYPES> {
527    /// Create a vector of `VidDisperseShare` from `VidDisperse`
528    pub fn from_vid_disperse(vid_disperse: VidDisperse<TYPES>) -> Vec<Self> {
529        match vid_disperse {
530            VidDisperse::V0(vid_disperse) => {
531                ADVZDisperseShare::<TYPES>::from_advz_disperse(vid_disperse)
532                    .into_iter()
533                    .map(|share| Self::V0(share))
534                    .collect()
535            },
536            VidDisperse::V1(vid_disperse) => {
537                VidDisperseShare2::<TYPES>::from_vid_disperse(vid_disperse)
538                    .into_iter()
539                    .map(|share| Self::V1(share))
540                    .collect()
541            },
542        }
543    }
544
545    /// Consume `self` and return a `Proposal`
546    pub fn to_proposal(
547        self,
548        private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
549    ) -> Option<Proposal<TYPES, Self>> {
550        let payload_commitment_ref: &[u8] = match &self {
551            Self::V0(share) => share.payload_commitment.as_ref(),
552            Self::V1(share) => share.payload_commitment.as_ref(),
553        };
554        let Ok(signature) = TYPES::SignatureKey::sign(private_key, payload_commitment_ref) else {
555            tracing::error!("VID: failed to sign dispersal share payload");
556            return None;
557        };
558        Some(Proposal {
559            signature,
560            _pd: PhantomData,
561            data: self,
562        })
563    }
564
565    /// Split a VID share proposal into a proposal for each recipient.
566    pub fn to_vid_share_proposals(
567        vid_disperse_proposal: Proposal<TYPES, VidDisperse<TYPES>>,
568    ) -> Vec<Proposal<TYPES, Self>> {
569        match vid_disperse_proposal.data {
570            VidDisperse::V0(disperse) => ADVZDisperseShare::to_vid_share_proposals(
571                disperse,
572                &vid_disperse_proposal.signature,
573            )
574            .into_iter()
575            .map(|proposal| convert_proposal(proposal))
576            .collect(),
577            VidDisperse::V1(disperse) => VidDisperseShare2::to_vid_share_proposals(
578                disperse,
579                &vid_disperse_proposal.signature,
580            )
581            .into_iter()
582            .map(|proposal| convert_proposal(proposal))
583            .collect(),
584        }
585    }
586
587    /// Return the internal `recipient_key`
588    pub fn recipient_key(&self) -> &TYPES::SignatureKey {
589        match self {
590            Self::V0(share) => &share.recipient_key,
591            Self::V1(share) => &share.recipient_key,
592        }
593    }
594
595    /// Return the payload length in bytes.
596    pub fn payload_byte_len(&self) -> u32 {
597        match self {
598            Self::V0(share) => share.payload_byte_len(),
599            Self::V1(share) => share.payload_byte_len(),
600        }
601    }
602
603    /// Return a reference to the internal payload VID commitment
604    pub fn payload_commitment_ref(&self) -> &[u8] {
605        match self {
606            Self::V0(share) => share.payload_commitment.as_ref(),
607            Self::V1(share) => share.payload_commitment.as_ref(),
608        }
609    }
610
611    /// Return the internal payload VID commitment
612    pub fn payload_commitment(&self) -> VidCommitment {
613        match self {
614            Self::V0(share) => VidCommitment::V0(share.payload_commitment),
615            Self::V1(share) => share.payload_commitment.into(),
616        }
617    }
618
619    /// Return the target epoch
620    pub fn target_epoch(&self) -> Option<<TYPES as NodeType>::Epoch> {
621        match self {
622            Self::V0(_) => None,
623            Self::V1(share) => share.target_epoch,
624        }
625    }
626
627    /// Internally verify the share given necessary information
628    ///
629    /// # Errors
630    #[allow(clippy::result_unit_err)]
631    pub fn verify_share(&self, total_nodes: usize) -> std::result::Result<(), ()> {
632        match self {
633            Self::V0(share) => share.verify_share(total_nodes),
634            Self::V1(share) => share.verify_share(total_nodes),
635        }
636    }
637
638    /// Set the view number
639    pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
640        match self {
641            Self::V0(share) => share.view_number = view_number,
642            Self::V1(share) => share.view_number = view_number,
643        }
644    }
645}
646
647impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperseShare<TYPES> {
648    fn view_number(&self) -> TYPES::View {
649        match self {
650            Self::V0(disperse) => disperse.view_number(),
651            Self::V1(disperse) => disperse.view_number(),
652        }
653    }
654}
655
656impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperseShare<TYPES> {
657    fn epoch(&self) -> Option<TYPES::Epoch> {
658        match self {
659            Self::V0(_) => None,
660            Self::V1(share) => share.epoch(),
661        }
662    }
663}
664
665impl<TYPES: NodeType> From<vid_disperse::ADVZDisperseShare<TYPES>> for VidDisperseShare<TYPES> {
666    fn from(share: vid_disperse::ADVZDisperseShare<TYPES>) -> Self {
667        Self::V0(share)
668    }
669}
670
671impl<TYPES: NodeType> From<vid_disperse::VidDisperseShare2<TYPES>> for VidDisperseShare<TYPES> {
672    fn from(share: vid_disperse::VidDisperseShare2<TYPES>) -> Self {
673        Self::V1(share)
674    }
675}
676
677/// Helper type to encapsulate the various ways that proposal certificates can be captured and
678/// stored.
679#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
680#[serde(bound(deserialize = ""))]
681pub enum ViewChangeEvidence<TYPES: NodeType> {
682    /// Holds a timeout certificate.
683    Timeout(TimeoutCertificate<TYPES>),
684    /// Holds a view sync finalized certificate.
685    ViewSync(ViewSyncFinalizeCertificate<TYPES>),
686}
687
688impl<TYPES: NodeType> ViewChangeEvidence<TYPES> {
689    /// Check that the given ViewChangeEvidence is relevant to the current view.
690    pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
691        match self {
692            ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
693            ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
694        }
695    }
696
697    /// Convert to ViewChangeEvidence2
698    pub fn to_evidence2(self) -> ViewChangeEvidence2<TYPES> {
699        match self {
700            ViewChangeEvidence::Timeout(timeout_cert) => {
701                ViewChangeEvidence2::Timeout(timeout_cert.to_tc2())
702            },
703            ViewChangeEvidence::ViewSync(view_sync_cert) => {
704                ViewChangeEvidence2::ViewSync(view_sync_cert.to_vsc2())
705            },
706        }
707    }
708}
709
710/// Helper type to encapsulate the various ways that proposal certificates can be captured and
711/// stored.
712#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
713#[serde(bound(deserialize = ""))]
714pub enum ViewChangeEvidence2<TYPES: NodeType> {
715    /// Holds a timeout certificate.
716    Timeout(TimeoutCertificate2<TYPES>),
717    /// Holds a view sync finalized certificate.
718    ViewSync(ViewSyncFinalizeCertificate2<TYPES>),
719}
720
721impl<TYPES: NodeType> ViewChangeEvidence2<TYPES> {
722    /// Check that the given ViewChangeEvidence2 is relevant to the current view.
723    pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
724        match self {
725            ViewChangeEvidence2::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
726            ViewChangeEvidence2::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
727        }
728    }
729
730    /// Convert to ViewChangeEvidence
731    pub fn to_evidence(self) -> ViewChangeEvidence<TYPES> {
732        match self {
733            ViewChangeEvidence2::Timeout(timeout_cert) => {
734                ViewChangeEvidence::Timeout(timeout_cert.to_tc())
735            },
736            ViewChangeEvidence2::ViewSync(view_sync_cert) => {
737                ViewChangeEvidence::ViewSync(view_sync_cert.to_vsc())
738            },
739        }
740    }
741}
742
743/// Proposal to append a block.
744#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
745#[serde(bound(deserialize = ""))]
746pub struct QuorumProposal<TYPES: NodeType> {
747    /// The block header to append
748    pub block_header: TYPES::BlockHeader,
749
750    /// CurView from leader when proposing leaf
751    pub view_number: TYPES::View,
752
753    /// Per spec, justification
754    pub justify_qc: QuorumCertificate<TYPES>,
755
756    /// Possible upgrade certificate, which the leader may optionally attach.
757    pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
758
759    /// Possible timeout or view sync certificate.
760    /// - A timeout certificate is only present if the justify_qc is not for the preceding view
761    /// - A view sync certificate is only present if the justify_qc and timeout_cert are not
762    ///   present.
763    pub proposal_certificate: Option<ViewChangeEvidence<TYPES>>,
764}
765
766/// Proposal to append a block.
767#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
768#[serde(bound(deserialize = ""))]
769pub struct QuorumProposal2<TYPES: NodeType> {
770    /// The block header to append
771    pub block_header: TYPES::BlockHeader,
772
773    /// view number for the proposal
774    pub view_number: TYPES::View,
775
776    /// The epoch number corresponding to the block number. Can be `None` for pre-epoch version.
777    pub epoch: Option<TYPES::Epoch>,
778
779    /// certificate that the proposal is chaining from
780    pub justify_qc: QuorumCertificate2<TYPES>,
781
782    /// certificate that the proposal is chaining from formed by the next epoch nodes
783    pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
784
785    /// Possible upgrade certificate, which the leader may optionally attach.
786    pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
787
788    /// Possible timeout or view sync certificate. If the `justify_qc` is not for a proposal in the immediately preceding view, then either a timeout or view sync certificate must be attached.
789    pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
790
791    /// The DRB result for the next epoch.
792    ///
793    /// This is required only for the last block of the epoch. Nodes will verify that it's
794    /// consistent with the result from their computations.
795    #[serde(with = "serde_bytes")]
796    pub next_drb_result: Option<DrbResult>,
797
798    /// The light client state update certificate for the next epoch.
799    /// This is required for the epoch root.
800    pub state_cert: Option<LightClientStateUpdateCertificate<TYPES>>,
801}
802
803/// Wrapper around a proposal to append a block
804#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
805#[serde(bound(deserialize = ""))]
806pub struct QuorumProposalWrapper<TYPES: NodeType> {
807    /// The wrapped proposal
808    pub proposal: QuorumProposal2<TYPES>,
809}
810
811impl<TYPES: NodeType> QuorumProposal2<TYPES> {
812    /// Validates whether the epoch is consistent with the version and the block number
813    /// # Errors
814    /// Returns an error if the epoch is inconsistent with the version or the block number
815    pub async fn validate_epoch<V: Versions>(
816        &self,
817        upgrade_lock: &UpgradeLock<TYPES, V>,
818        epoch_height: u64,
819    ) -> Result<()> {
820        let calculated_epoch = option_epoch_from_block_number::<TYPES>(
821            upgrade_lock.epochs_enabled(self.view_number()).await,
822            self.block_header.block_number(),
823            epoch_height,
824        );
825        ensure!(
826            calculated_epoch == self.epoch(),
827            "Quorum proposal invalid: inconsistent epoch."
828        );
829        Ok(())
830    }
831}
832
833impl<TYPES: NodeType> QuorumProposalWrapper<TYPES> {
834    /// Helper function to get the proposal's block_header
835    pub fn block_header(&self) -> &TYPES::BlockHeader {
836        &self.proposal.block_header
837    }
838
839    /// Helper function to get the proposal's view_number
840    pub fn view_number(&self) -> TYPES::View {
841        self.proposal.view_number
842    }
843
844    /// Helper function to get the proposal's justify_qc
845    pub fn justify_qc(&self) -> &QuorumCertificate2<TYPES> {
846        &self.proposal.justify_qc
847    }
848
849    /// Helper function to get the proposal's next_epoch_justify_qc
850    pub fn next_epoch_justify_qc(&self) -> &Option<NextEpochQuorumCertificate2<TYPES>> {
851        &self.proposal.next_epoch_justify_qc
852    }
853
854    /// Helper function to get the proposal's upgrade_certificate
855    pub fn upgrade_certificate(&self) -> &Option<UpgradeCertificate<TYPES>> {
856        &self.proposal.upgrade_certificate
857    }
858
859    /// Helper function to get the proposal's view_change_evidence
860    pub fn view_change_evidence(&self) -> &Option<ViewChangeEvidence2<TYPES>> {
861        &self.proposal.view_change_evidence
862    }
863
864    /// Helper function to get the proposal's next_drb_result
865    pub fn next_drb_result(&self) -> &Option<DrbResult> {
866        &self.proposal.next_drb_result
867    }
868
869    /// Validates whether the epoch is consistent with the version and the block number
870    /// # Errors
871    /// Returns an error if the epoch is inconsistent with the version or the block number
872    pub async fn validate_epoch<V: Versions>(
873        &self,
874        upgrade_lock: &UpgradeLock<TYPES, V>,
875        epoch_height: u64,
876    ) -> Result<()> {
877        self.proposal
878            .validate_epoch(upgrade_lock, epoch_height)
879            .await
880    }
881
882    /// Helper function to get the proposal's light client state update certificate
883    pub fn state_cert(&self) -> &Option<LightClientStateUpdateCertificate<TYPES>> {
884        &self.proposal.state_cert
885    }
886}
887
888impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposalWrapper<TYPES> {
889    fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
890        Self {
891            proposal: quorum_proposal.into(),
892        }
893    }
894}
895
896impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposalWrapper<TYPES> {
897    fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
898        Self {
899            proposal: quorum_proposal2,
900        }
901    }
902}
903
904impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal<TYPES> {
905    fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
906        quorum_proposal_wrapper.proposal.into()
907    }
908}
909
910impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2<TYPES> {
911    fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
912        quorum_proposal_wrapper.proposal
913    }
914}
915
916impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposal2<TYPES> {
917    fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
918        Self {
919            block_header: quorum_proposal.block_header,
920            view_number: quorum_proposal.view_number,
921            epoch: None,
922            justify_qc: quorum_proposal.justify_qc.to_qc2(),
923            next_epoch_justify_qc: None,
924            upgrade_certificate: quorum_proposal.upgrade_certificate,
925            view_change_evidence: quorum_proposal
926                .proposal_certificate
927                .map(ViewChangeEvidence::to_evidence2),
928            next_drb_result: None,
929            state_cert: None,
930        }
931    }
932}
933
934impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal<TYPES> {
935    fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
936        Self {
937            block_header: quorum_proposal2.block_header,
938            view_number: quorum_proposal2.view_number,
939            justify_qc: quorum_proposal2.justify_qc.to_qc(),
940            upgrade_certificate: quorum_proposal2.upgrade_certificate,
941            proposal_certificate: quorum_proposal2
942                .view_change_evidence
943                .map(ViewChangeEvidence2::to_evidence),
944        }
945    }
946}
947
948impl<TYPES: NodeType> From<Leaf<TYPES>> for Leaf2<TYPES> {
949    fn from(leaf: Leaf<TYPES>) -> Self {
950        let bytes: [u8; 32] = leaf.parent_commitment.into();
951
952        Self {
953            view_number: leaf.view_number,
954            justify_qc: leaf.justify_qc.to_qc2(),
955            next_epoch_justify_qc: None,
956            parent_commitment: Commitment::from_raw(bytes),
957            block_header: leaf.block_header,
958            upgrade_certificate: leaf.upgrade_certificate,
959            block_payload: leaf.block_payload,
960            view_change_evidence: None,
961            next_drb_result: None,
962            with_epoch: false,
963        }
964    }
965}
966
967impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal<TYPES> {
968    fn view_number(&self) -> TYPES::View {
969        self.view_number
970    }
971}
972
973impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal2<TYPES> {
974    fn view_number(&self) -> TYPES::View {
975        self.view_number
976    }
977}
978
979impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal<TYPES> {
980    fn view_number(&self) -> TYPES::View {
981        self.view_number
982    }
983}
984
985impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal2<TYPES> {
986    fn view_number(&self) -> TYPES::View {
987        self.view_number
988    }
989}
990
991impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposalWrapper<TYPES> {
992    fn view_number(&self) -> TYPES::View {
993        self.proposal.view_number
994    }
995}
996
997impl<TYPES: NodeType> HasViewNumber<TYPES> for UpgradeProposal<TYPES> {
998    fn view_number(&self) -> TYPES::View {
999        self.view_number
1000    }
1001}
1002
1003impl_has_epoch!(QuorumProposal2<TYPES>, DaProposal2<TYPES>);
1004
1005impl_has_none_epoch!(
1006    QuorumProposal<TYPES>,
1007    DaProposal<TYPES>,
1008    UpgradeProposal<TYPES>,
1009    ADVZDisperseShare<TYPES>
1010);
1011
1012impl<TYPES: NodeType> HasEpoch<TYPES> for QuorumProposalWrapper<TYPES> {
1013    /// Return an underlying proposal's epoch
1014    #[allow(clippy::panic)]
1015    fn epoch(&self) -> Option<TYPES::Epoch> {
1016        self.proposal.epoch()
1017    }
1018}
1019
1020/// The error type for block and its transactions.
1021#[derive(Error, Debug, Serialize, Deserialize)]
1022pub enum BlockError {
1023    /// The block header is invalid
1024    #[error("Invalid block header: {0}")]
1025    InvalidBlockHeader(String),
1026
1027    /// The payload commitment does not match the block header's payload commitment
1028    #[error("Inconsistent payload commitment")]
1029    InconsistentPayloadCommitment,
1030
1031    /// The block header apply failed
1032    #[error("Failed to apply block header: {0}")]
1033    FailedHeaderApply(String),
1034}
1035
1036/// Additional functions required to use a [`Leaf`] with hotshot-testing.
1037pub trait TestableLeaf {
1038    /// Type of nodes participating in the network.
1039    type NodeType: NodeType;
1040
1041    /// Create a transaction that can be added to the block contained in this leaf.
1042    fn create_random_transaction(
1043        &self,
1044        rng: &mut dyn rand::RngCore,
1045        padding: u64,
1046    ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction;
1047}
1048
1049/// This is the consensus-internal analogous concept to a block, and it contains the block proper,
1050/// as well as the hash of its parent `Leaf`.
1051/// NOTE: `State` is constrained to implementing `BlockContents`, is `TypeMap::BlockPayload`
1052#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1053#[serde(bound(deserialize = ""))]
1054pub struct Leaf<TYPES: NodeType> {
1055    /// CurView from leader when proposing leaf
1056    view_number: TYPES::View,
1057
1058    /// Per spec, justification
1059    justify_qc: QuorumCertificate<TYPES>,
1060
1061    /// The hash of the parent `Leaf`
1062    /// So we can ask if it extends
1063    parent_commitment: Commitment<Self>,
1064
1065    /// Block header.
1066    block_header: TYPES::BlockHeader,
1067
1068    /// Optional upgrade certificate, if one was attached to the quorum proposal for this view.
1069    upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1070
1071    /// Optional block payload.
1072    ///
1073    /// It may be empty for nodes not in the DA committee.
1074    block_payload: Option<TYPES::BlockPayload>,
1075}
1076
1077/// This is the consensus-internal analogous concept to a block, and it contains the block proper,
1078/// as well as the hash of its parent `Leaf`.
1079#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1080#[serde(bound(deserialize = ""))]
1081pub struct Leaf2<TYPES: NodeType> {
1082    /// CurView from leader when proposing leaf
1083    view_number: TYPES::View,
1084
1085    /// Per spec, justification
1086    justify_qc: QuorumCertificate2<TYPES>,
1087
1088    /// certificate that the proposal is chaining from formed by the next epoch nodes
1089    next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1090
1091    /// The hash of the parent `Leaf`
1092    /// So we can ask if it extends
1093    parent_commitment: Commitment<Self>,
1094
1095    /// Block header.
1096    block_header: TYPES::BlockHeader,
1097
1098    /// Optional upgrade certificate, if one was attached to the quorum proposal for this view.
1099    upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1100
1101    /// Optional block payload.
1102    ///
1103    /// It may be empty for nodes not in the DA committee.
1104    block_payload: Option<TYPES::BlockPayload>,
1105
1106    /// Possible timeout or view sync certificate. If the `justify_qc` is not for a proposal in the immediately preceding view, then either a timeout or view sync certificate must be attached.
1107    pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1108
1109    /// The DRB result for the next epoch.
1110    ///
1111    /// This is required only for the last block of the epoch. Nodes will verify that it's
1112    /// consistent with the result from their computations.
1113    #[serde(with = "serde_bytes")]
1114    pub next_drb_result: Option<DrbResult>,
1115
1116    /// Indicates whether or not epochs were enabled.
1117    pub with_epoch: bool,
1118}
1119
1120impl<TYPES: NodeType> Leaf2<TYPES> {
1121    /// Create a new leaf from its components.
1122    ///
1123    /// # Panics
1124    ///
1125    /// Panics if the genesis payload (`TYPES::BlockPayload::genesis()`) is malformed (unable to be
1126    /// interpreted as bytes).
1127    #[must_use]
1128    pub async fn genesis<V: Versions>(
1129        validated_state: &TYPES::ValidatedState,
1130        instance_state: &TYPES::InstanceState,
1131    ) -> Self {
1132        let epoch = genesis_epoch_from_version::<V, TYPES>();
1133
1134        let (payload, metadata) =
1135            TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1136                .await
1137                .unwrap();
1138
1139        let genesis_view = TYPES::View::genesis();
1140
1141        let block_header =
1142            TYPES::BlockHeader::genesis::<V>(instance_state, payload.clone(), &metadata);
1143
1144        let block_number = if V::Base::VERSION < V::Epochs::VERSION {
1145            None
1146        } else {
1147            Some(0u64)
1148        };
1149
1150        let null_quorum_data = QuorumData2 {
1151            leaf_commit: Commitment::<Leaf2<TYPES>>::default_commitment_no_preimage(),
1152            epoch,
1153            block_number,
1154        };
1155
1156        let justify_qc = QuorumCertificate2::new(
1157            null_quorum_data.clone(),
1158            null_quorum_data.commit(),
1159            genesis_view,
1160            None,
1161            PhantomData,
1162        );
1163
1164        Self {
1165            view_number: genesis_view,
1166            justify_qc,
1167            next_epoch_justify_qc: None,
1168            parent_commitment: null_quorum_data.leaf_commit,
1169            upgrade_certificate: None,
1170            block_header: block_header.clone(),
1171            block_payload: Some(payload),
1172            view_change_evidence: None,
1173            next_drb_result: None,
1174            with_epoch: epoch.is_some(),
1175        }
1176    }
1177    /// Time when this leaf was created.
1178    pub fn view_number(&self) -> TYPES::View {
1179        self.view_number
1180    }
1181    /// Epoch in which this leaf was created.
1182    pub fn epoch(&self, epoch_height: u64) -> Option<TYPES::Epoch> {
1183        option_epoch_from_block_number::<TYPES>(
1184            self.with_epoch,
1185            self.block_header.block_number(),
1186            epoch_height,
1187        )
1188    }
1189    /// Height of this leaf in the chain.
1190    ///
1191    /// Equivalently, this is the number of leaves before this one in the chain.
1192    pub fn height(&self) -> u64 {
1193        self.block_header.block_number()
1194    }
1195    /// The QC linking this leaf to its parent in the chain.
1196    pub fn justify_qc(&self) -> QuorumCertificate2<TYPES> {
1197        self.justify_qc.clone()
1198    }
1199    /// The QC linking this leaf to its parent in the chain.
1200    pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1201        self.upgrade_certificate.clone()
1202    }
1203    /// Commitment to this leaf's parent.
1204    pub fn parent_commitment(&self) -> Commitment<Self> {
1205        self.parent_commitment
1206    }
1207    /// The block header contained in this leaf.
1208    pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1209        &self.block_header
1210    }
1211
1212    /// Get a mutable reference to the block header contained in this leaf.
1213    pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1214        &mut self.block_header
1215    }
1216    /// Fill this leaf with the block payload.
1217    ///
1218    /// # Errors
1219    ///
1220    /// Fails if the payload commitment doesn't match `self.block_header.payload_commitment()`
1221    /// or if the transactions are of invalid length
1222    pub fn fill_block_payload<V: Versions>(
1223        &mut self,
1224        block_payload: TYPES::BlockPayload,
1225        num_storage_nodes: usize,
1226        version: Version,
1227    ) -> std::result::Result<(), BlockError> {
1228        let encoded_txns = block_payload.encode();
1229        let commitment = vid_commitment::<V>(
1230            &encoded_txns,
1231            &self.block_header.metadata().encode(),
1232            num_storage_nodes,
1233            version,
1234        );
1235        if commitment != self.block_header.payload_commitment() {
1236            return Err(BlockError::InconsistentPayloadCommitment);
1237        }
1238        self.block_payload = Some(block_payload);
1239        Ok(())
1240    }
1241
1242    /// Take the block payload from the leaf and return it if it is present
1243    pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1244        self.block_payload.take()
1245    }
1246
1247    /// Fill this leaf with the block payload, without checking
1248    /// header and payload consistency
1249    pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1250        self.block_payload = Some(block_payload);
1251    }
1252
1253    /// Optional block payload.
1254    pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1255        self.block_payload.clone()
1256    }
1257
1258    /// A commitment to the block payload contained in this leaf.
1259    pub fn payload_commitment(&self) -> VidCommitment {
1260        self.block_header().payload_commitment()
1261    }
1262
1263    /// Validate that a leaf has the right upgrade certificate to be the immediate child of another leaf
1264    ///
1265    /// This may not be a complete function. Please double-check that it performs the checks you expect before substituting validation logic with it.
1266    ///
1267    /// # Errors
1268    /// Returns an error if the certificates are not identical, or that when we no longer see a
1269    /// cert, it's for the right reason.
1270    pub async fn extends_upgrade(
1271        &self,
1272        parent: &Self,
1273        decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1274    ) -> Result<()> {
1275        match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1276            // Easiest cases are:
1277            //   - no upgrade certificate on either: this is the most common case, and is always fine.
1278            //   - if the parent didn't have a certificate, but we see one now, it just means that we have begun an upgrade: again, this is always fine.
1279            (None | Some(_), None) => {},
1280            // If we no longer see a cert, we have to make sure that we either:
1281            //    - no longer care because we have passed new_version_first_view, or
1282            //    - no longer care because we have passed `decide_by` without deciding the certificate.
1283            (None, Some(parent_cert)) => {
1284                let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1285                ensure!(
1286                    self.view_number() > parent_cert.data.new_version_first_view
1287                        || (self.view_number() > parent_cert.data.decide_by
1288                            && decided_upgrade_certificate_read.is_none()),
1289                    "The new leaf is missing an upgrade certificate that was present in its \
1290                     parent, and should still be live."
1291                );
1292            },
1293            // If we both have a certificate, they should be identical.
1294            // Technically, this prevents us from initiating a new upgrade in the view immediately following an upgrade.
1295            // I think this is a fairly lax restriction.
1296            (Some(cert), Some(parent_cert)) => {
1297                ensure!(
1298                    cert == parent_cert,
1299                    "The new leaf does not extend the parent leaf, because it has attached a \
1300                     different upgrade certificate."
1301                );
1302            },
1303        }
1304
1305        // This check should be added once we sort out the genesis leaf/justify_qc issue.
1306        // ensure!(self.parent_commitment() == parent_leaf.commit(), "The commitment of the parent leaf does not match the specified parent commitment.");
1307
1308        Ok(())
1309    }
1310
1311    /// Converts a `Leaf2` to a `Leaf`. This operation is fundamentally unsafe and should not be used.
1312    pub fn to_leaf_unsafe(self) -> Leaf<TYPES> {
1313        let bytes: [u8; 32] = self.parent_commitment.into();
1314
1315        Leaf {
1316            view_number: self.view_number,
1317            justify_qc: self.justify_qc.to_qc(),
1318            parent_commitment: Commitment::from_raw(bytes),
1319            block_header: self.block_header,
1320            upgrade_certificate: self.upgrade_certificate,
1321            block_payload: self.block_payload,
1322        }
1323    }
1324}
1325
1326impl<TYPES: NodeType> Committable for Leaf2<TYPES> {
1327    fn commit(&self) -> committable::Commitment<Self> {
1328        let Leaf2 {
1329            view_number,
1330            justify_qc,
1331            next_epoch_justify_qc,
1332            parent_commitment,
1333            block_header,
1334            upgrade_certificate,
1335            block_payload: _,
1336            view_change_evidence,
1337            next_drb_result,
1338            with_epoch,
1339        } = self;
1340
1341        let mut cb = RawCommitmentBuilder::new("leaf commitment")
1342            .u64_field("view number", **view_number)
1343            .field("parent leaf commitment", *parent_commitment)
1344            .field("block header", block_header.commit())
1345            .field("justify qc", justify_qc.commit())
1346            .optional("upgrade certificate", upgrade_certificate);
1347
1348        if *with_epoch {
1349            cb = cb
1350                .constant_str("with_epoch")
1351                .optional("next_epoch_justify_qc", next_epoch_justify_qc);
1352
1353            if let Some(next_drb_result) = next_drb_result {
1354                cb = cb
1355                    .constant_str("next_drb_result")
1356                    .fixed_size_bytes(next_drb_result);
1357            }
1358
1359            match view_change_evidence {
1360                Some(ViewChangeEvidence2::Timeout(cert)) => {
1361                    cb = cb.field("timeout cert", cert.commit());
1362                },
1363                Some(ViewChangeEvidence2::ViewSync(cert)) => {
1364                    cb = cb.field("viewsync cert", cert.commit());
1365                },
1366                None => {},
1367            }
1368        }
1369
1370        cb.finalize()
1371    }
1372}
1373
1374impl<TYPES: NodeType> Leaf<TYPES> {
1375    #[allow(clippy::unused_async)]
1376    /// Calculate the leaf commitment,
1377    /// which is gated on the version to include the block header.
1378    pub async fn commit<V: Versions>(
1379        &self,
1380        _upgrade_lock: &UpgradeLock<TYPES, V>,
1381    ) -> Commitment<Self> {
1382        <Self as Committable>::commit(self)
1383    }
1384}
1385
1386impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
1387    fn eq(&self, other: &Self) -> bool {
1388        self.view_number == other.view_number
1389            && self.justify_qc == other.justify_qc
1390            && self.parent_commitment == other.parent_commitment
1391            && self.block_header == other.block_header
1392    }
1393}
1394
1395impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
1396    fn eq(&self, other: &Self) -> bool {
1397        let Leaf2 {
1398            view_number,
1399            justify_qc,
1400            next_epoch_justify_qc,
1401            parent_commitment,
1402            block_header,
1403            upgrade_certificate,
1404            block_payload: _,
1405            view_change_evidence,
1406            next_drb_result,
1407            with_epoch,
1408        } = self;
1409
1410        *view_number == other.view_number
1411            && *justify_qc == other.justify_qc
1412            && *next_epoch_justify_qc == other.next_epoch_justify_qc
1413            && *parent_commitment == other.parent_commitment
1414            && *block_header == other.block_header
1415            && *upgrade_certificate == other.upgrade_certificate
1416            && *view_change_evidence == other.view_change_evidence
1417            && *next_drb_result == other.next_drb_result
1418            && *with_epoch == other.with_epoch
1419    }
1420}
1421
1422impl<TYPES: NodeType> Hash for Leaf<TYPES> {
1423    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1424        self.view_number.hash(state);
1425        self.justify_qc.hash(state);
1426        self.parent_commitment.hash(state);
1427        self.block_header.hash(state);
1428    }
1429}
1430
1431impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
1432    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1433        self.commit().hash(state);
1434        self.view_number.hash(state);
1435        self.justify_qc.hash(state);
1436        self.parent_commitment.hash(state);
1437        self.block_header.hash(state);
1438    }
1439}
1440
1441impl<TYPES: NodeType> Display for Leaf<TYPES> {
1442    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1443        write!(
1444            f,
1445            "view: {:?}, height: {:?}, justify: {}",
1446            self.view_number,
1447            self.height(),
1448            self.justify_qc
1449        )
1450    }
1451}
1452
1453impl<TYPES: NodeType> QuorumCertificate<TYPES> {
1454    #[must_use]
1455    /// Creat the Genesis certificate
1456    pub async fn genesis<V: Versions>(
1457        validated_state: &TYPES::ValidatedState,
1458        instance_state: &TYPES::InstanceState,
1459    ) -> Self {
1460        // since this is genesis, we should never have a decided upgrade certificate.
1461        let upgrade_lock = UpgradeLock::<TYPES, V>::new();
1462
1463        let genesis_view = <TYPES::View as ConsensusTime>::genesis();
1464
1465        let data = QuorumData {
1466            leaf_commit: Leaf::genesis::<V>(validated_state, instance_state)
1467                .await
1468                .commit(&upgrade_lock)
1469                .await,
1470        };
1471
1472        let versioned_data =
1473            VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1474                .await;
1475
1476        let bytes: [u8; 32] = versioned_data.commit().into();
1477
1478        Self::new(
1479            data,
1480            Commitment::from_raw(bytes),
1481            genesis_view,
1482            None,
1483            PhantomData,
1484        )
1485    }
1486}
1487
1488impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
1489    #[must_use]
1490    /// Create the Genesis certificate
1491    pub async fn genesis<V: Versions>(
1492        validated_state: &TYPES::ValidatedState,
1493        instance_state: &TYPES::InstanceState,
1494    ) -> Self {
1495        // since this is genesis, we should never have a decided upgrade certificate.
1496        let upgrade_lock = UpgradeLock::<TYPES, V>::new();
1497
1498        let genesis_view = <TYPES::View as ConsensusTime>::genesis();
1499
1500        let genesis_leaf = Leaf2::genesis::<V>(validated_state, instance_state).await;
1501        let block_number = if upgrade_lock.epochs_enabled(genesis_view).await {
1502            Some(genesis_leaf.height())
1503        } else {
1504            None
1505        };
1506        let data = QuorumData2 {
1507            leaf_commit: genesis_leaf.commit(),
1508            epoch: genesis_epoch_from_version::<V, TYPES>(), // #3967 make sure this is enough of a gate for epochs
1509            block_number,
1510        };
1511
1512        let versioned_data =
1513            VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1514                .await;
1515
1516        let bytes: [u8; 32] = versioned_data.commit().into();
1517
1518        Self::new(
1519            data,
1520            Commitment::from_raw(bytes),
1521            genesis_view,
1522            None,
1523            PhantomData,
1524        )
1525    }
1526}
1527
1528impl<TYPES: NodeType> Leaf<TYPES> {
1529    /// Create a new leaf from its components.
1530    ///
1531    /// # Panics
1532    ///
1533    /// Panics if the genesis payload (`TYPES::BlockPayload::genesis()`) is malformed (unable to be
1534    /// interpreted as bytes).
1535    #[must_use]
1536    pub async fn genesis<V: Versions>(
1537        validated_state: &TYPES::ValidatedState,
1538        instance_state: &TYPES::InstanceState,
1539    ) -> Self {
1540        let (payload, metadata) =
1541            TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1542                .await
1543                .unwrap();
1544
1545        let genesis_view = TYPES::View::genesis();
1546
1547        let block_header =
1548            TYPES::BlockHeader::genesis::<V>(instance_state, payload.clone(), &metadata);
1549
1550        let null_quorum_data = QuorumData {
1551            leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
1552        };
1553
1554        let justify_qc = QuorumCertificate::new(
1555            null_quorum_data.clone(),
1556            null_quorum_data.commit(),
1557            genesis_view,
1558            None,
1559            PhantomData,
1560        );
1561
1562        Self {
1563            view_number: genesis_view,
1564            justify_qc,
1565            parent_commitment: null_quorum_data.leaf_commit,
1566            upgrade_certificate: None,
1567            block_header: block_header.clone(),
1568            block_payload: Some(payload),
1569        }
1570    }
1571
1572    /// Time when this leaf was created.
1573    pub fn view_number(&self) -> TYPES::View {
1574        self.view_number
1575    }
1576    /// Height of this leaf in the chain.
1577    ///
1578    /// Equivalently, this is the number of leaves before this one in the chain.
1579    pub fn height(&self) -> u64 {
1580        self.block_header.block_number()
1581    }
1582    /// The QC linking this leaf to its parent in the chain.
1583    pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
1584        self.justify_qc.clone()
1585    }
1586    /// The QC linking this leaf to its parent in the chain.
1587    pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1588        self.upgrade_certificate.clone()
1589    }
1590    /// Commitment to this leaf's parent.
1591    pub fn parent_commitment(&self) -> Commitment<Self> {
1592        self.parent_commitment
1593    }
1594    /// The block header contained in this leaf.
1595    pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1596        &self.block_header
1597    }
1598
1599    /// Get a mutable reference to the block header contained in this leaf.
1600    pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1601        &mut self.block_header
1602    }
1603    /// Fill this leaf with the block payload.
1604    ///
1605    /// # Errors
1606    ///
1607    /// Fails if the payload commitment doesn't match `self.block_header.payload_commitment()`
1608    /// or if the transactions are of invalid length
1609    pub fn fill_block_payload<V: Versions>(
1610        &mut self,
1611        block_payload: TYPES::BlockPayload,
1612        num_storage_nodes: usize,
1613        version: Version,
1614    ) -> std::result::Result<(), BlockError> {
1615        let encoded_txns = block_payload.encode();
1616        let commitment = vid_commitment::<V>(
1617            &encoded_txns,
1618            &self.block_header.metadata().encode(),
1619            num_storage_nodes,
1620            version,
1621        );
1622        if commitment != self.block_header.payload_commitment() {
1623            return Err(BlockError::InconsistentPayloadCommitment);
1624        }
1625        self.block_payload = Some(block_payload);
1626        Ok(())
1627    }
1628
1629    /// Take the block payload from the leaf and return it if it is present
1630    pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1631        self.block_payload.take()
1632    }
1633
1634    /// Fill this leaf with the block payload, without checking
1635    /// header and payload consistency
1636    pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1637        self.block_payload = Some(block_payload);
1638    }
1639
1640    /// Optional block payload.
1641    pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1642        self.block_payload.clone()
1643    }
1644
1645    /// A commitment to the block payload contained in this leaf.
1646    pub fn payload_commitment(&self) -> VidCommitment {
1647        self.block_header().payload_commitment()
1648    }
1649
1650    /// Validate that a leaf has the right upgrade certificate to be the immediate child of another leaf
1651    ///
1652    /// This may not be a complete function. Please double-check that it performs the checks you expect before substituting validation logic with it.
1653    ///
1654    /// # Errors
1655    /// Returns an error if the certificates are not identical, or that when we no longer see a
1656    /// cert, it's for the right reason.
1657    pub async fn extends_upgrade(
1658        &self,
1659        parent: &Self,
1660        decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1661    ) -> Result<()> {
1662        match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1663            // Easiest cases are:
1664            //   - no upgrade certificate on either: this is the most common case, and is always fine.
1665            //   - if the parent didn't have a certificate, but we see one now, it just means that we have begun an upgrade: again, this is always fine.
1666            (None | Some(_), None) => {},
1667            // If we no longer see a cert, we have to make sure that we either:
1668            //    - no longer care because we have passed new_version_first_view, or
1669            //    - no longer care because we have passed `decide_by` without deciding the certificate.
1670            (None, Some(parent_cert)) => {
1671                let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1672                ensure!(
1673                    self.view_number() > parent_cert.data.new_version_first_view
1674                        || (self.view_number() > parent_cert.data.decide_by
1675                            && decided_upgrade_certificate_read.is_none()),
1676                    "The new leaf is missing an upgrade certificate that was present in its \
1677                     parent, and should still be live."
1678                );
1679            },
1680            // If we both have a certificate, they should be identical.
1681            // Technically, this prevents us from initiating a new upgrade in the view immediately following an upgrade.
1682            // I think this is a fairly lax restriction.
1683            (Some(cert), Some(parent_cert)) => {
1684                ensure!(
1685                    cert == parent_cert,
1686                    "The new leaf does not extend the parent leaf, because it has attached a \
1687                     different upgrade certificate."
1688                );
1689            },
1690        }
1691
1692        // This check should be added once we sort out the genesis leaf/justify_qc issue.
1693        // ensure!(self.parent_commitment() == parent_leaf.commit(), "The commitment of the parent leaf does not match the specified parent commitment.");
1694
1695        Ok(())
1696    }
1697}
1698
1699impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
1700where
1701    TYPES::ValidatedState: TestableState<TYPES>,
1702    TYPES::BlockPayload: TestableBlock<TYPES>,
1703{
1704    type NodeType = TYPES;
1705
1706    fn create_random_transaction(
1707        &self,
1708        rng: &mut dyn rand::RngCore,
1709        padding: u64,
1710    ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
1711    {
1712        TYPES::ValidatedState::create_random_transaction(None, rng, padding)
1713    }
1714}
1715impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
1716where
1717    TYPES::ValidatedState: TestableState<TYPES>,
1718    TYPES::BlockPayload: TestableBlock<TYPES>,
1719{
1720    type NodeType = TYPES;
1721
1722    fn create_random_transaction(
1723        &self,
1724        rng: &mut dyn rand::RngCore,
1725        padding: u64,
1726    ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
1727    {
1728        TYPES::ValidatedState::create_random_transaction(None, rng, padding)
1729    }
1730}
1731/// Fake the thing a genesis block points to. Needed to avoid infinite recursion
1732#[must_use]
1733pub fn fake_commitment<S: Committable>() -> Commitment<S> {
1734    RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
1735}
1736
1737/// create a random commitment
1738#[must_use]
1739pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
1740    let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
1741    RawCommitmentBuilder::new("Random Commitment")
1742        .constant_str("Random Field")
1743        .var_size_bytes(&random_array)
1744        .finalize()
1745}
1746
1747/// Serialization for the QC assembled signature
1748/// # Panics
1749/// if serialization fails
1750pub fn serialize_signature2<TYPES: NodeType>(
1751    signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
1752) -> Vec<u8> {
1753    let mut signatures_bytes = vec![];
1754    signatures_bytes.extend("Yes".as_bytes());
1755
1756    let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
1757    let proof_bytes = bincode_opts()
1758        .serialize(&proof.as_bitslice())
1759        .expect("This serialization shouldn't be able to fail");
1760    signatures_bytes.extend("bitvec proof".as_bytes());
1761    signatures_bytes.extend(proof_bytes.as_slice());
1762    let sig_bytes = bincode_opts()
1763        .serialize(&sig)
1764        .expect("This serialization shouldn't be able to fail");
1765    signatures_bytes.extend("aggregated signature".as_bytes());
1766    signatures_bytes.extend(sig_bytes.as_slice());
1767    signatures_bytes
1768}
1769
1770impl<TYPES: NodeType> Committable for Leaf<TYPES> {
1771    fn commit(&self) -> committable::Commitment<Self> {
1772        RawCommitmentBuilder::new("leaf commitment")
1773            .u64_field("view number", *self.view_number)
1774            .field("parent leaf commitment", self.parent_commitment)
1775            .field("block header", self.block_header.commit())
1776            .field("justify qc", self.justify_qc.commit())
1777            .optional("upgrade certificate", &self.upgrade_certificate)
1778            .finalize()
1779    }
1780}
1781
1782impl<TYPES: NodeType> Leaf2<TYPES> {
1783    /// Constructs a leaf from a given quorum proposal.
1784    pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
1785        // WARNING: Do NOT change this to a wildcard match, or reference the fields directly in the construction of the leaf.
1786        // The point of this match is that we will get a compile-time error if we add a field without updating this.
1787        let QuorumProposalWrapper {
1788            proposal:
1789                QuorumProposal2 {
1790                    view_number,
1791                    epoch,
1792                    justify_qc,
1793                    next_epoch_justify_qc,
1794                    block_header,
1795                    upgrade_certificate,
1796                    view_change_evidence,
1797                    next_drb_result,
1798                    state_cert: _,
1799                },
1800        } = quorum_proposal;
1801
1802        Self {
1803            view_number: *view_number,
1804            justify_qc: justify_qc.clone(),
1805            next_epoch_justify_qc: next_epoch_justify_qc.clone(),
1806            parent_commitment: justify_qc.data().leaf_commit,
1807            block_header: block_header.clone(),
1808            upgrade_certificate: upgrade_certificate.clone(),
1809            block_payload: None,
1810            view_change_evidence: view_change_evidence.clone(),
1811            next_drb_result: *next_drb_result,
1812            with_epoch: epoch.is_some(),
1813        }
1814    }
1815}
1816
1817impl<TYPES: NodeType> Leaf<TYPES> {
1818    /// Constructs a leaf from a given quorum proposal.
1819    pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
1820        // WARNING: Do NOT change this to a wildcard match, or reference the fields directly in the construction of the leaf.
1821        // The point of this match is that we will get a compile-time error if we add a field without updating this.
1822        let QuorumProposal {
1823            view_number,
1824            justify_qc,
1825            block_header,
1826            upgrade_certificate,
1827            proposal_certificate: _,
1828        } = quorum_proposal;
1829
1830        Self {
1831            view_number: *view_number,
1832            justify_qc: justify_qc.clone(),
1833            parent_commitment: justify_qc.data().leaf_commit,
1834            block_header: block_header.clone(),
1835            upgrade_certificate: upgrade_certificate.clone(),
1836            block_payload: None,
1837        }
1838    }
1839}
1840
1841pub mod null_block {
1842    #![allow(missing_docs)]
1843
1844    use jf_vid::VidScheme;
1845    use vbs::version::StaticVersionType;
1846
1847    use crate::{
1848        data::VidCommitment,
1849        traits::{
1850            block_contents::BuilderFee,
1851            node_implementation::{NodeType, Versions},
1852            signature_key::BuilderSignatureKey,
1853            BlockPayload,
1854        },
1855        vid::advz::advz_scheme,
1856    };
1857
1858    /// The commitment for a null block payload.
1859    ///
1860    /// Note: the commitment depends on the network (via `num_storage_nodes`),
1861    /// and may change (albeit rarely) during execution.
1862    ///
1863    /// We memoize the result to avoid having to recalculate it.
1864    // TODO(Chengyu): fix it. Empty commitment must be computed at every upgrade.
1865    // #[memoize(SharedCache, Capacity: 10)]
1866    #[must_use]
1867    pub fn commitment<V: Versions>(num_storage_nodes: usize) -> Option<VidCommitment> {
1868        let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
1869
1870        match vid_result {
1871            Ok(r) => Some(VidCommitment::V0(r)),
1872            Err(_) => None,
1873        }
1874    }
1875
1876    /// Builder fee data for a null block payload
1877    #[must_use]
1878    pub fn builder_fee<TYPES: NodeType, V: Versions>(
1879        num_storage_nodes: usize,
1880        version: vbs::version::Version,
1881    ) -> Option<BuilderFee<TYPES>> {
1882        /// Arbitrary fee amount, this block doesn't actually come from a builder
1883        const FEE_AMOUNT: u64 = 0;
1884
1885        let (pub_key, priv_key) =
1886            <TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
1887                [0_u8; 32], 0,
1888            );
1889
1890        if version >= V::Epochs::VERSION {
1891            let (_null_block, null_block_metadata) =
1892                <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
1893
1894            match TYPES::BuilderSignatureKey::sign_fee(&priv_key, FEE_AMOUNT, &null_block_metadata)
1895            {
1896                Ok(sig) => Some(BuilderFee {
1897                    fee_amount: FEE_AMOUNT,
1898                    fee_account: pub_key,
1899                    fee_signature: sig,
1900                }),
1901                Err(_) => None,
1902            }
1903        } else {
1904            let (_null_block, null_block_metadata) =
1905                <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
1906
1907            match TYPES::BuilderSignatureKey::sign_fee_with_vid_commitment(
1908                &priv_key,
1909                FEE_AMOUNT,
1910                &null_block_metadata,
1911                &commitment::<V>(num_storage_nodes)?,
1912            ) {
1913                Ok(sig) => Some(BuilderFee {
1914                    fee_amount: FEE_AMOUNT,
1915                    fee_account: pub_key,
1916                    fee_signature: sig,
1917                }),
1918                Err(_) => None,
1919            }
1920        }
1921    }
1922}
1923
1924/// A packed bundle constructed from a sequence of bundles.
1925#[derive(Debug, Eq, PartialEq, Clone)]
1926pub struct PackedBundle<TYPES: NodeType> {
1927    /// The combined transactions as bytes.
1928    pub encoded_transactions: Arc<[u8]>,
1929
1930    /// The metadata of the block.
1931    pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
1932
1933    /// The view number that this block is associated with.
1934    pub view_number: TYPES::View,
1935
1936    /// The view number that this block is associated with.
1937    pub epoch_number: Option<TYPES::Epoch>,
1938
1939    /// The sequencing fee for submitting bundles.
1940    pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
1941}
1942
1943impl<TYPES: NodeType> PackedBundle<TYPES> {
1944    /// Create a new [`PackedBundle`].
1945    pub fn new(
1946        encoded_transactions: Arc<[u8]>,
1947        metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
1948        view_number: TYPES::View,
1949        epoch_number: Option<TYPES::Epoch>,
1950        sequencing_fees: Vec1<BuilderFee<TYPES>>,
1951    ) -> Self {
1952        Self {
1953            encoded_transactions,
1954            metadata,
1955            view_number,
1956            epoch_number,
1957            sequencing_fees,
1958        }
1959    }
1960}
1961
1962#[cfg(test)]
1963mod test {
1964    use super::*;
1965
1966    #[test]
1967    fn test_vid_commitment_display() {
1968        let vc = VidCommitment::V0(ADVZCommitment::default());
1969        assert_eq!(
1970            format!("{vc}"),
1971            "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
1972        );
1973        assert_eq!(
1974            format!("{vc:?}"),
1975            "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
1976        );
1977
1978        let vc = VidCommitment::V1(AvidMCommitment {
1979            commit: Default::default(),
1980        });
1981        assert_eq!(
1982            format!("{vc}"),
1983            "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
1984        );
1985        assert_eq!(
1986            format!("{vc:?}"),
1987            "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
1988        );
1989    }
1990}