Skip to main content

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