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