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