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