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