hotshot_types/
data.rs

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