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 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 < NEW_PROTOCOL_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)?;
1012        let entries = StakeTableEntries::from_iter(stake_table.stake_table()).0;
1013        let threshold = stake_table.success_threshold();
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,
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    /// Calculate the leaf commitment,
1776    /// which is gated on the version to include the block header.
1777    pub fn commit(&self, _upgrade_lock: &UpgradeLock<TYPES>) -> Commitment<Self> {
1778        <Self as Committable>::commit(self)
1779    }
1780}
1781
1782impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
1783    fn eq(&self, other: &Self) -> bool {
1784        self.view_number == other.view_number
1785            && self.justify_qc == other.justify_qc
1786            && self.parent_commitment == other.parent_commitment
1787            && self.block_header == other.block_header
1788    }
1789}
1790
1791impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
1792    fn eq(&self, other: &Self) -> bool {
1793        let Leaf2 {
1794            view_number,
1795            justify_qc,
1796            next_epoch_justify_qc,
1797            parent_commitment,
1798            block_header,
1799            upgrade_certificate,
1800            block_payload: _,
1801            view_change_evidence,
1802            next_drb_result,
1803            with_epoch,
1804        } = self;
1805
1806        *view_number == other.view_number
1807            && *justify_qc == other.justify_qc
1808            && *next_epoch_justify_qc == other.next_epoch_justify_qc
1809            && *parent_commitment == other.parent_commitment
1810            && *block_header == other.block_header
1811            && *upgrade_certificate == other.upgrade_certificate
1812            && *view_change_evidence == other.view_change_evidence
1813            && *next_drb_result == other.next_drb_result
1814            && *with_epoch == other.with_epoch
1815    }
1816}
1817
1818impl<TYPES: NodeType> Hash for Leaf<TYPES> {
1819    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1820        self.view_number.hash(state);
1821        self.justify_qc.hash(state);
1822        self.parent_commitment.hash(state);
1823        self.block_header.hash(state);
1824    }
1825}
1826
1827impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
1828    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1829        self.commit().hash(state);
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> Display for Leaf<TYPES> {
1838    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1839        write!(
1840            f,
1841            "view: {:?}, height: {:?}, justify: {}",
1842            self.view_number,
1843            self.height(),
1844            self.justify_qc
1845        )
1846    }
1847}
1848
1849impl<TYPES: NodeType> QuorumCertificate<TYPES> {
1850    /// Creat the Genesis certificate
1851    #[must_use]
1852    pub async fn genesis(
1853        validated_state: &TYPES::ValidatedState,
1854        instance_state: &TYPES::InstanceState,
1855        upgrade: Upgrade,
1856    ) -> Self {
1857        // since this is genesis, we should never have a decided upgrade certificate.
1858        let upgrade_lock = UpgradeLock::<TYPES>::new(upgrade);
1859
1860        let genesis_view = ViewNumber::genesis();
1861
1862        let data = QuorumData {
1863            leaf_commit: Leaf::genesis(validated_state, instance_state, upgrade.base)
1864                .await
1865                .commit(&upgrade_lock),
1866        };
1867
1868        let versioned_data =
1869            VersionedVoteData::<_, _>::new_infallible(data.clone(), genesis_view, &upgrade_lock);
1870
1871        let bytes: [u8; 32] = versioned_data.commit().into();
1872
1873        Self::new(
1874            data,
1875            Commitment::from_raw(bytes),
1876            genesis_view,
1877            None,
1878            PhantomData,
1879        )
1880    }
1881}
1882
1883impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
1884    /// Create the Genesis certificate
1885    #[must_use]
1886    pub async fn genesis(
1887        validated_state: &TYPES::ValidatedState,
1888        instance_state: &TYPES::InstanceState,
1889        upgrade: Upgrade,
1890    ) -> Self {
1891        // since this is genesis, we should never have a decided upgrade certificate.
1892        let upgrade_lock = UpgradeLock::<TYPES>::new(upgrade);
1893
1894        let genesis_view = ViewNumber::genesis();
1895
1896        let genesis_leaf = Leaf2::genesis(validated_state, instance_state, upgrade.base).await;
1897        let block_number = if upgrade_lock.epochs_enabled(genesis_view) {
1898            Some(genesis_leaf.height())
1899        } else {
1900            None
1901        };
1902        let data = QuorumData2 {
1903            leaf_commit: genesis_leaf.commit(),
1904            epoch: genesis_epoch_from_version(upgrade.base), // #3967 make sure this is enough of a gate for epochs
1905            block_number,
1906        };
1907
1908        let versioned_data =
1909            VersionedVoteData::<_, _>::new_infallible(data, genesis_view, &upgrade_lock);
1910
1911        let bytes: [u8; 32] = versioned_data.commit().into();
1912
1913        Self::new(
1914            data,
1915            Commitment::from_raw(bytes),
1916            genesis_view,
1917            None,
1918            PhantomData,
1919        )
1920    }
1921}
1922
1923impl<TYPES: NodeType> Leaf<TYPES> {
1924    /// Create a new leaf from its components.
1925    ///
1926    /// # Panics
1927    ///
1928    /// Panics if the genesis payload (`TYPES::BlockPayload::genesis()`) is malformed (unable to be
1929    /// interpreted as bytes).
1930    #[must_use]
1931    pub async fn genesis(
1932        validated_state: &TYPES::ValidatedState,
1933        instance_state: &TYPES::InstanceState,
1934        version: Version,
1935    ) -> Self {
1936        let (payload, metadata) =
1937            TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1938                .await
1939                .unwrap();
1940
1941        let genesis_view = ViewNumber::genesis();
1942
1943        let block_header =
1944            TYPES::BlockHeader::genesis(instance_state, payload.clone(), &metadata, version);
1945
1946        let null_quorum_data = QuorumData {
1947            leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
1948        };
1949
1950        let justify_qc = QuorumCertificate::new(
1951            null_quorum_data.clone(),
1952            null_quorum_data.commit(),
1953            genesis_view,
1954            None,
1955            PhantomData,
1956        );
1957
1958        Self {
1959            view_number: genesis_view,
1960            justify_qc,
1961            parent_commitment: null_quorum_data.leaf_commit,
1962            upgrade_certificate: None,
1963            block_header: block_header.clone(),
1964            block_payload: Some(payload),
1965        }
1966    }
1967
1968    /// Time when this leaf was created.
1969    pub fn view_number(&self) -> ViewNumber {
1970        self.view_number
1971    }
1972    /// Height of this leaf in the chain.
1973    ///
1974    /// Equivalently, this is the number of leaves before this one in the chain.
1975    pub fn height(&self) -> u64 {
1976        self.block_header.block_number()
1977    }
1978    /// The QC linking this leaf to its parent in the chain.
1979    pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
1980        self.justify_qc.clone()
1981    }
1982    /// The QC linking this leaf to its parent in the chain.
1983    pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1984        self.upgrade_certificate.clone()
1985    }
1986    /// Commitment to this leaf's parent.
1987    pub fn parent_commitment(&self) -> Commitment<Self> {
1988        self.parent_commitment
1989    }
1990    /// The block header contained in this leaf.
1991    pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1992        &self.block_header
1993    }
1994
1995    /// Get a mutable reference to the block header contained in this leaf.
1996    pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1997        &mut self.block_header
1998    }
1999    /// Fill this leaf with the block payload.
2000    ///
2001    /// # Errors
2002    ///
2003    /// Fails if the payload commitment doesn't match `self.block_header.payload_commitment()`
2004    /// or if the transactions are of invalid length
2005    pub fn fill_block_payload(
2006        &mut self,
2007        block_payload: TYPES::BlockPayload,
2008        num_storage_nodes: usize,
2009        version: Version,
2010    ) -> std::result::Result<(), BlockError> {
2011        let encoded_txns = block_payload.encode();
2012        let commitment = vid_commitment(
2013            &encoded_txns,
2014            &self.block_header.metadata().encode(),
2015            num_storage_nodes,
2016            version,
2017        );
2018        if commitment != self.block_header.payload_commitment() {
2019            return Err(BlockError::InconsistentPayloadCommitment);
2020        }
2021        self.block_payload = Some(block_payload);
2022        Ok(())
2023    }
2024
2025    /// Take the block payload from the leaf and return it if it is present
2026    pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
2027        self.block_payload.take()
2028    }
2029
2030    /// Fill this leaf with the block payload, without checking
2031    /// header and payload consistency
2032    pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
2033        self.block_payload = Some(block_payload);
2034    }
2035
2036    /// Optional block payload.
2037    pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
2038        self.block_payload.clone()
2039    }
2040
2041    /// A commitment to the block payload contained in this leaf.
2042    pub fn payload_commitment(&self) -> VidCommitment {
2043        self.block_header().payload_commitment()
2044    }
2045
2046    /// Validate that a leaf has the right upgrade certificate to be the immediate child of another leaf
2047    ///
2048    /// This may not be a complete function. Please double-check that it performs the checks you expect before substituting validation logic with it.
2049    ///
2050    /// # Errors
2051    /// Returns an error if the certificates are not identical, or that when we no longer see a
2052    /// cert, it's for the right reason.
2053    pub fn extends_upgrade(&self, parent: &Self, upgrade: &UpgradeLock<TYPES>) -> Result<()> {
2054        match (self.upgrade_certificate(), parent.upgrade_certificate()) {
2055            // Easiest cases are:
2056            //   - no upgrade certificate on either: this is the most common case, and is always fine.
2057            //   - 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.
2058            (None | Some(_), None) => {},
2059            // If we no longer see a cert, we have to make sure that we either:
2060            //    - no longer care because we have passed new_version_first_view, or
2061            //    - no longer care because we have passed `decide_by` without deciding the certificate.
2062            (None, Some(parent_cert)) => {
2063                let decided_upgrade_certificate_read = upgrade.decided_upgrade_cert();
2064                ensure!(
2065                    self.view_number() > parent_cert.data.new_version_first_view
2066                        || (self.view_number() > parent_cert.data.decide_by
2067                            && decided_upgrade_certificate_read.is_none()),
2068                    "The new leaf is missing an upgrade certificate that was present in its \
2069                     parent, and should still be live."
2070                );
2071            },
2072            // If we both have a certificate, they should be identical.
2073            // Technically, this prevents us from initiating a new upgrade in the view immediately following an upgrade.
2074            // I think this is a fairly lax restriction.
2075            (Some(cert), Some(parent_cert)) => {
2076                ensure!(
2077                    cert == parent_cert,
2078                    "The new leaf does not extend the parent leaf, because it has attached a \
2079                     different upgrade certificate."
2080                );
2081            },
2082        }
2083
2084        // This check should be added once we sort out the genesis leaf/justify_qc issue.
2085        // ensure!(self.parent_commitment() == parent_leaf.commit(), "The commitment of the parent leaf does not match the specified parent commitment.");
2086
2087        Ok(())
2088    }
2089}
2090
2091impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
2092where
2093    TYPES::ValidatedState: TestableState<TYPES>,
2094    TYPES::BlockPayload: TestableBlock<TYPES>,
2095{
2096    type NodeType = TYPES;
2097
2098    fn create_random_transaction(
2099        &self,
2100        rng: &mut dyn rand::RngCore,
2101        padding: u64,
2102    ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
2103    {
2104        TYPES::ValidatedState::create_random_transaction(None, rng, padding)
2105    }
2106}
2107impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
2108where
2109    TYPES::ValidatedState: TestableState<TYPES>,
2110    TYPES::BlockPayload: TestableBlock<TYPES>,
2111{
2112    type NodeType = TYPES;
2113
2114    fn create_random_transaction(
2115        &self,
2116        rng: &mut dyn rand::RngCore,
2117        padding: u64,
2118    ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
2119    {
2120        TYPES::ValidatedState::create_random_transaction(None, rng, padding)
2121    }
2122}
2123/// Fake the thing a genesis block points to. Needed to avoid infinite recursion
2124#[must_use]
2125pub fn fake_commitment<S: Committable>() -> Commitment<S> {
2126    RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
2127}
2128
2129/// create a random commitment
2130#[must_use]
2131pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
2132    let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
2133    RawCommitmentBuilder::new("Random Commitment")
2134        .constant_str("Random Field")
2135        .var_size_bytes(&random_array)
2136        .finalize()
2137}
2138
2139/// Serialization for the QC assembled signature
2140/// # Panics
2141/// if serialization fails
2142pub fn serialize_signature2<TYPES: NodeType>(
2143    signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
2144) -> Vec<u8> {
2145    let mut signatures_bytes = vec![];
2146    signatures_bytes.extend("Yes".as_bytes());
2147
2148    let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
2149    let proof_bytes = bincode_opts()
2150        .serialize(&proof.as_bitslice())
2151        .expect("This serialization shouldn't be able to fail");
2152    signatures_bytes.extend("bitvec proof".as_bytes());
2153    signatures_bytes.extend(proof_bytes.as_slice());
2154    let sig_bytes = bincode_opts()
2155        .serialize(&sig)
2156        .expect("This serialization shouldn't be able to fail");
2157    signatures_bytes.extend("aggregated signature".as_bytes());
2158    signatures_bytes.extend(sig_bytes.as_slice());
2159    signatures_bytes
2160}
2161
2162impl<TYPES: NodeType> Committable for Leaf<TYPES> {
2163    fn commit(&self) -> committable::Commitment<Self> {
2164        RawCommitmentBuilder::new("leaf commitment")
2165            .u64_field("view number", *self.view_number)
2166            .field("parent leaf commitment", self.parent_commitment)
2167            .field("block header", self.block_header.commit())
2168            .field("justify qc", self.justify_qc.commit())
2169            .optional("upgrade certificate", &self.upgrade_certificate)
2170            .finalize()
2171    }
2172}
2173
2174impl<TYPES: NodeType> Leaf2<TYPES> {
2175    /// Constructs a leaf from a given quorum proposal.
2176    pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
2177        // WARNING: Do NOT change this to a wildcard match, or reference the fields directly in the construction of the leaf.
2178        // The point of this match is that we will get a compile-time error if we add a field without updating this.
2179        let QuorumProposalWrapper {
2180            proposal:
2181                QuorumProposal2 {
2182                    view_number,
2183                    epoch,
2184                    justify_qc,
2185                    next_epoch_justify_qc,
2186                    block_header,
2187                    upgrade_certificate,
2188                    view_change_evidence,
2189                    next_drb_result,
2190                    state_cert: _,
2191                },
2192        } = quorum_proposal;
2193
2194        Self {
2195            view_number: *view_number,
2196            justify_qc: justify_qc.clone(),
2197            next_epoch_justify_qc: next_epoch_justify_qc.clone(),
2198            parent_commitment: justify_qc.data().leaf_commit,
2199            block_header: block_header.clone(),
2200            upgrade_certificate: upgrade_certificate.clone(),
2201            block_payload: None,
2202            view_change_evidence: view_change_evidence.clone(),
2203            next_drb_result: *next_drb_result,
2204            with_epoch: epoch.is_some(),
2205        }
2206    }
2207}
2208
2209impl<TYPES: NodeType> Leaf<TYPES> {
2210    /// Constructs a leaf from a given quorum proposal.
2211    pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
2212        // WARNING: Do NOT change this to a wildcard match, or reference the fields directly in the construction of the leaf.
2213        // The point of this match is that we will get a compile-time error if we add a field without updating this.
2214        let QuorumProposal {
2215            view_number,
2216            justify_qc,
2217            block_header,
2218            upgrade_certificate,
2219            proposal_certificate: _,
2220        } = quorum_proposal;
2221
2222        Self {
2223            view_number: *view_number,
2224            justify_qc: justify_qc.clone(),
2225            parent_commitment: justify_qc.data().leaf_commit,
2226            block_header: block_header.clone(),
2227            upgrade_certificate: upgrade_certificate.clone(),
2228            block_payload: None,
2229        }
2230    }
2231}
2232
2233pub mod null_block {
2234    #![allow(missing_docs)]
2235
2236    use jf_advz::VidScheme;
2237    use vbs::version::Version;
2238    use versions::EPOCH_VERSION;
2239
2240    use crate::{
2241        data::VidCommitment,
2242        traits::{
2243            BlockPayload, block_contents::BuilderFee, node_implementation::NodeType,
2244            signature_key::BuilderSignatureKey,
2245        },
2246        vid::advz::advz_scheme,
2247    };
2248
2249    /// The commitment for a null block payload.
2250    ///
2251    /// Note: the commitment depends on the network (via `num_storage_nodes`),
2252    /// and may change (albeit rarely) during execution.
2253    ///
2254    /// We memoize the result to avoid having to recalculate it.
2255    // TODO(Chengyu): fix it. Empty commitment must be computed at every upgrade.
2256    // #[memoize(SharedCache, Capacity: 10)]
2257    #[must_use]
2258    pub fn commitment(num_storage_nodes: usize) -> Option<VidCommitment> {
2259        let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
2260
2261        match vid_result {
2262            Ok(r) => Some(VidCommitment::V0(r)),
2263            Err(_) => None,
2264        }
2265    }
2266
2267    /// Builder fee data for a null block payload
2268    #[must_use]
2269    pub fn builder_fee<TYPES: NodeType>(
2270        num_storage_nodes: usize,
2271        version: Version,
2272    ) -> Option<BuilderFee<TYPES>> {
2273        /// Arbitrary fee amount, this block doesn't actually come from a builder
2274        const FEE_AMOUNT: u64 = 0;
2275
2276        let (pub_key, priv_key) =
2277            <TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
2278                [0_u8; 32], 0,
2279            );
2280
2281        if version >= EPOCH_VERSION {
2282            let (_null_block, null_block_metadata) =
2283                <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
2284
2285            match TYPES::BuilderSignatureKey::sign_fee(&priv_key, FEE_AMOUNT, &null_block_metadata)
2286            {
2287                Ok(sig) => Some(BuilderFee {
2288                    fee_amount: FEE_AMOUNT,
2289                    fee_account: pub_key,
2290                    fee_signature: sig,
2291                }),
2292                Err(_) => None,
2293            }
2294        } else {
2295            let (_null_block, null_block_metadata) =
2296                <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
2297
2298            match TYPES::BuilderSignatureKey::sign_fee_with_vid_commitment(
2299                &priv_key,
2300                FEE_AMOUNT,
2301                &null_block_metadata,
2302                &commitment(num_storage_nodes)?,
2303            ) {
2304                Ok(sig) => Some(BuilderFee {
2305                    fee_amount: FEE_AMOUNT,
2306                    fee_account: pub_key,
2307                    fee_signature: sig,
2308                }),
2309                Err(_) => None,
2310            }
2311        }
2312    }
2313}
2314
2315/// A packed bundle constructed from a sequence of bundles.
2316#[derive(Debug, Eq, PartialEq, Clone)]
2317pub struct PackedBundle<TYPES: NodeType> {
2318    /// The combined transactions as bytes.
2319    pub encoded_transactions: Arc<[u8]>,
2320
2321    /// The metadata of the block.
2322    pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
2323
2324    /// The view number that this block is associated with.
2325    pub view_number: ViewNumber,
2326
2327    /// The view number that this block is associated with.
2328    pub epoch_number: Option<EpochNumber>,
2329
2330    /// The sequencing fee for submitting bundles.
2331    pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
2332}
2333
2334impl<TYPES: NodeType> PackedBundle<TYPES> {
2335    /// Create a new [`PackedBundle`].
2336    pub fn new(
2337        encoded_transactions: Arc<[u8]>,
2338        metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
2339        view_number: ViewNumber,
2340        epoch_number: Option<EpochNumber>,
2341        sequencing_fees: Vec1<BuilderFee<TYPES>>,
2342    ) -> Self {
2343        Self {
2344            encoded_transactions,
2345            metadata,
2346            view_number,
2347            epoch_number,
2348            sequencing_fees,
2349        }
2350    }
2351}
2352
2353#[cfg(test)]
2354mod test {
2355    use super::*;
2356
2357    #[test]
2358    fn test_vid_commitment_display() {
2359        let vc = VidCommitment::V0(VidCommitment0::default());
2360        assert_eq!(
2361            format!("{vc}"),
2362            "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
2363        );
2364        assert_eq!(
2365            format!("{vc:?}"),
2366            "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
2367        );
2368
2369        let vc = VidCommitment::V1(VidCommitment1::default());
2370        assert_eq!(
2371            format!("{vc}"),
2372            "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
2373        );
2374        assert_eq!(
2375            format!("{vc:?}"),
2376            "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
2377        );
2378
2379        let vc = VidCommitment::V2(VidCommitment2::default());
2380        assert_eq!(
2381            format!("{vc}"),
2382            "AvidmGf2Commit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq"
2383        );
2384        assert_eq!(
2385            format!("{vc:?}"),
2386            "AvidmGf2Commit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq"
2387        );
2388    }
2389}