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