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