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