1use std::{
13 fmt::{Debug, Display},
14 hash::Hash,
15 marker::PhantomData,
16 sync::Arc,
17 time::Duration,
18};
19
20use bincode::Options;
21use committable::{Commitment, CommitmentBoundsArkless, Committable, RawCommitmentBuilder};
22use hotshot_utils::anytrace::*;
23use jf_advz::VidScheme;
24use rand::Rng;
25use serde::{Deserialize, Serialize};
26use tagged_base64::{TaggedBase64, Tb64Error};
27use thiserror::Error;
28use vbs::version::Version;
29use vec1::Vec1;
30use versions::{EPOCH_VERSION, NEW_PROTOCOL_VERSION, Upgrade};
31
32use crate::{
33 drb::DrbResult,
34 epoch_membership::EpochMembershipCoordinator,
35 message::{Proposal, UpgradeLock, convert_proposal},
36 simple_certificate::{
37 LightClientStateUpdateCertificateV1, LightClientStateUpdateCertificateV2,
38 NextEpochQuorumCertificate2, QuorumCertificate, QuorumCertificate2, TimeoutCertificate,
39 TimeoutCertificate2, UpgradeCertificate, ViewSyncFinalizeCertificate,
40 ViewSyncFinalizeCertificate2,
41 },
42 simple_vote::{HasEpoch, QuorumData, QuorumData2, UpgradeProposalData, VersionedVoteData},
43 stake_table::StakeTableEntries,
44 traits::{
45 BlockPayload,
46 block_contents::{BlockHeader, BuilderFee, EncodeBytes, TestableBlock},
47 node_implementation::NodeType,
48 signature_key::SignatureKey,
49 states::TestableState,
50 },
51 utils::{
52 EpochTransitionIndicator, bincode_opts, genesis_epoch_from_version,
53 option_epoch_from_block_number,
54 },
55 vid::{
56 advz::{ADVZScheme, advz_scheme},
57 avidm::{AvidMScheme, init_avidm_param},
58 avidm_gf2::{AvidmGf2Scheme, init_avidm_gf2_param},
59 },
60 vote::{Certificate, HasViewNumber},
61};
62
63macro_rules! impl_u64_wrapper {
66 ($t:ty, $genesis_val:expr) => {
67 impl $t {
68 pub const fn genesis() -> Self {
70 Self($genesis_val)
71 }
72 pub const fn new(n: u64) -> Self {
74 Self(n)
75 }
76 pub const 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#[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#[derive(
146 Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
147)]
148#[cfg_attr(
149 feature = "rlp",
150 derive(alloy_rlp::RlpEncodableWrapper, alloy_rlp::RlpDecodableWrapper)
151)]
152pub struct EpochNumber(u64);
153
154impl Committable for EpochNumber {
155 fn commit(&self) -> Commitment<Self> {
156 let builder = RawCommitmentBuilder::new("Epoch Number Commitment");
157 builder.u64(self.0).finalize()
158 }
159}
160
161impl_u64_wrapper!(EpochNumber, 1u64);
162
163#[derive(
164 Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
165)]
166#[serde(transparent)]
167pub struct BlockNumber(u64);
168
169impl Committable for BlockNumber {
170 fn commit(&self) -> Commitment<Self> {
171 let builder = RawCommitmentBuilder::new("BlockNumber Commitment");
172 builder.u64(self.0).finalize()
173 }
174}
175
176impl_u64_wrapper!(BlockNumber, 0u64);
177
178#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
180#[serde(bound = "TYPES: NodeType")]
181pub struct DaProposal<TYPES: NodeType> {
182 pub encoded_transactions: Arc<[u8]>,
184 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
186 pub view_number: ViewNumber,
188}
189
190#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
192#[serde(bound = "TYPES: NodeType")]
193pub struct DaProposal2<TYPES: NodeType> {
194 pub encoded_transactions: Arc<[u8]>,
196 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
198 pub view_number: ViewNumber,
200 pub epoch: Option<EpochNumber>,
202 pub epoch_transition_indicator: EpochTransitionIndicator,
205}
206
207impl<TYPES: NodeType> From<DaProposal<TYPES>> for DaProposal2<TYPES> {
208 fn from(da_proposal: DaProposal<TYPES>) -> Self {
209 Self {
210 encoded_transactions: da_proposal.encoded_transactions,
211 metadata: da_proposal.metadata,
212 view_number: da_proposal.view_number,
213 epoch: None,
214 epoch_transition_indicator: EpochTransitionIndicator::NotInTransition,
215 }
216 }
217}
218
219impl<TYPES: NodeType> From<DaProposal2<TYPES>> for DaProposal<TYPES> {
220 fn from(da_proposal2: DaProposal2<TYPES>) -> Self {
221 Self {
222 encoded_transactions: da_proposal2.encoded_transactions,
223 metadata: da_proposal2.metadata,
224 view_number: da_proposal2.view_number,
225 }
226 }
227}
228
229#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
231pub struct UpgradeProposal {
232 pub upgrade_proposal: UpgradeProposalData,
234 pub view_number: ViewNumber,
236}
237
238pub type VidCommitment0 = crate::vid::advz::ADVZCommitment;
240pub type VidCommitment1 = crate::vid::avidm::AvidMCommitment;
241pub type VidCommitment2 = crate::vid::avidm_gf2::AvidmGf2Commitment;
242
243#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
245#[serde(
246 try_from = "tagged_base64::TaggedBase64",
247 into = "tagged_base64::TaggedBase64"
248)]
249pub enum VidCommitment {
250 V0(VidCommitment0),
251 V1(VidCommitment1),
252 V2(VidCommitment2),
253}
254
255impl Default for VidCommitment {
256 fn default() -> Self {
257 Self::V0(Default::default())
258 }
259}
260
261impl Display for VidCommitment {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 std::write!(f, "{}", TaggedBase64::from(self))
264 }
265}
266
267impl Debug for VidCommitment {
268 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
269 std::fmt::Display::fmt(self, f)
270 }
271}
272
273impl From<VidCommitment> for TaggedBase64 {
274 fn from(val: VidCommitment) -> Self {
275 match val {
276 VidCommitment::V0(comm) => comm.into(),
277 VidCommitment::V1(comm) => comm.into(),
278 VidCommitment::V2(comm) => comm.into(),
279 }
280 }
281}
282
283impl From<&VidCommitment> for TaggedBase64 {
284 fn from(val: &VidCommitment) -> Self {
285 match val {
286 VidCommitment::V0(comm) => comm.into(),
287 VidCommitment::V1(comm) => comm.into(),
288 VidCommitment::V2(comm) => comm.into(),
289 }
290 }
291}
292
293impl TryFrom<TaggedBase64> for VidCommitment {
294 type Error = tagged_base64::Tb64Error;
295
296 fn try_from(value: TaggedBase64) -> std::result::Result<Self, Self::Error> {
297 match value.tag().as_str() {
298 "HASH" => VidCommitment0::try_from(value).map(Self::V0),
299 "AvidMCommit" => VidCommitment1::try_from(value).map(Self::V1),
300 "AvidmGf2Commit" => VidCommitment2::try_from(value).map(Self::V2),
301 _ => Err(Tb64Error::InvalidTag),
302 }
303 }
304}
305
306impl<'a> TryFrom<&'a TaggedBase64> for VidCommitment {
307 type Error = tagged_base64::Tb64Error;
308
309 fn try_from(value: &'a TaggedBase64) -> std::result::Result<Self, Self::Error> {
310 match value.tag().as_str() {
311 "HASH" => VidCommitment0::try_from(value).map(Self::V0),
312 "AvidMCommit" => VidCommitment1::try_from(value).map(Self::V1),
313 "AvidmGf2Commit" => VidCommitment2::try_from(value).map(Self::V2),
314 _ => Err(Tb64Error::InvalidTag),
315 }
316 }
317}
318
319impl std::str::FromStr for VidCommitment {
320 type Err = tagged_base64::Tb64Error;
321 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
322 use core::convert::TryFrom;
323 Self::try_from(TaggedBase64::from_str(s)?)
324 .map_err(|_| tagged_base64::Tb64Error::InvalidData)
325 }
326}
327
328impl From<VidCommitment1> for VidCommitment {
336 fn from(comm: VidCommitment1) -> Self {
337 Self::V1(comm)
338 }
339}
340
341impl From<VidCommitment2> for VidCommitment {
342 fn from(comm: VidCommitment2) -> Self {
343 Self::V2(comm)
344 }
345}
346
347impl AsRef<[u8]> for VidCommitment {
348 fn as_ref(&self) -> &[u8] {
349 match self {
350 Self::V0(comm) => comm.as_ref(),
351 Self::V1(comm) => comm.as_ref(),
352 Self::V2(comm) => comm.as_ref(),
353 }
354 }
355}
356
357impl AsRef<[u8; 32]> for VidCommitment {
358 fn as_ref(&self) -> &[u8; 32] {
359 match self {
360 Self::V0(comm) => comm.as_ref().as_ref(),
361 Self::V1(comm) => comm.as_ref(),
362 Self::V2(comm) => comm.as_ref(),
363 }
364 }
365}
366
367#[must_use]
372#[allow(clippy::panic)]
373pub fn vid_commitment(
374 encoded_transactions: &[u8],
375 metadata: &[u8],
376 total_weight: usize,
377 version: Version,
378) -> VidCommitment {
379 if version < EPOCH_VERSION {
380 let encoded_tx_len = encoded_transactions.len();
381 advz_scheme(total_weight)
382 .commit_only(encoded_transactions)
383 .map(VidCommitment::V0)
384 .unwrap_or_else(|err| {
385 panic!(
386 "VidScheme::commit_only \
387 failure:(total_weight,payload_byte_len)=({total_weight},{encoded_tx_len}) \
388 error: {err}"
389 )
390 })
391 } else if version < NEW_PROTOCOL_VERSION {
392 let param = init_avidm_param(total_weight).unwrap();
393 let encoded_tx_len = encoded_transactions.len();
394 AvidMScheme::commit(
395 ¶m,
396 encoded_transactions,
397 ns_table::parse_ns_table(encoded_tx_len, metadata),
398 )
399 .map(VidCommitment::V1)
400 .unwrap()
401 } else {
402 let param = init_avidm_gf2_param(total_weight).unwrap();
403 let encoded_tx_len = encoded_transactions.len();
404 AvidmGf2Scheme::commit(
405 ¶m,
406 encoded_transactions,
407 ns_table::parse_ns_table(encoded_tx_len, metadata),
408 )
409 .map(|(comm, _)| VidCommitment::V2(comm))
410 .unwrap()
411 }
412}
413
414pub type VidCommon0 = crate::vid::advz::ADVZCommon;
416pub type VidCommon1 = crate::vid::avidm::AvidMCommon;
417pub type VidCommon2 = crate::vid::avidm_gf2::AvidmGf2Common;
418
419#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
421pub enum VidCommon {
422 V0(VidCommon0),
423 V1(VidCommon1),
424 V2(VidCommon2),
425}
426
427impl From<VidCommon1> for VidCommon {
428 fn from(comm: VidCommon1) -> Self {
429 Self::V1(comm)
430 }
431}
432
433impl From<VidCommon2> for VidCommon {
434 fn from(comm: VidCommon2) -> Self {
435 Self::V2(comm)
436 }
437}
438
439#[derive(Clone, Copy, Debug, Eq, PartialEq)]
441pub enum VidCommonRef<'a> {
442 V0(&'a VidCommon0),
443 V1(&'a VidCommon1),
444 V2(&'a VidCommon2),
445}
446
447impl<'a> VidCommonRef<'a> {
448 pub fn is_consistent(&self, comm: &VidCommitment) -> bool {
449 match (self, comm) {
450 (Self::V0(common), VidCommitment::V0(comm)) => {
451 ADVZScheme::is_consistent(comm, common).is_ok()
452 },
453 (Self::V1(_), VidCommitment::V1(_)) => true,
457 (Self::V2(common), VidCommitment::V2(comm)) => {
458 AvidmGf2Scheme::is_consistent(comm, common)
459 },
460 _ => false,
461 }
462 }
463}
464
465impl VidCommon {
466 pub fn as_ref(&self) -> VidCommonRef<'_> {
467 match self {
468 Self::V0(c) => VidCommonRef::V0(c),
469 Self::V1(c) => VidCommonRef::V1(c),
470 Self::V2(c) => VidCommonRef::V2(c),
471 }
472 }
473
474 pub fn is_consistent(&self, comm: &VidCommitment) -> bool {
475 self.as_ref().is_consistent(comm)
476 }
477}
478
479pub type VidShare0 = crate::vid::advz::ADVZShare;
481pub type VidShare1 = crate::vid::avidm::AvidMShare;
482pub type VidShare2 = crate::vid::avidm_gf2::AvidmGf2Share;
483
484#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
486pub enum VidShare {
487 V0(VidShare0),
488 V1(VidShare1),
489 V2(VidShare2),
490}
491
492impl From<VidShare1> for VidShare {
500 fn from(share: VidShare1) -> Self {
501 Self::V1(share)
502 }
503}
504
505impl From<VidShare2> for VidShare {
506 fn from(share: VidShare2) -> Self {
507 Self::V2(share)
508 }
509}
510
511pub mod ns_table;
512pub mod vid_disperse;
513
514pub struct VidDisperseAndDuration<TYPES: NodeType> {
516 pub disperse: VidDisperse<TYPES>,
518 pub duration: Duration,
520}
521
522pub type VidDisperse0<TYPES> = vid_disperse::ADVZDisperse<TYPES>;
524pub type VidDisperse1<TYPES> = vid_disperse::AvidMDisperse<TYPES>;
525pub type VidDisperse2<TYPES> = vid_disperse::AvidmGf2Disperse<TYPES>;
526
527#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
533#[serde(bound = "TYPES: NodeType")]
534pub enum VidDisperse<TYPES: NodeType> {
535 V0(VidDisperse0<TYPES>),
537 V1(VidDisperse1<TYPES>),
539 V2(VidDisperse2<TYPES>),
541}
542
543impl<TYPES: NodeType> From<VidDisperse0<TYPES>> for VidDisperse<TYPES> {
544 fn from(disperse: VidDisperse0<TYPES>) -> Self {
545 Self::V0(disperse)
546 }
547}
548
549impl<TYPES: NodeType> From<VidDisperse1<TYPES>> for VidDisperse<TYPES> {
550 fn from(disperse: VidDisperse1<TYPES>) -> Self {
551 Self::V1(disperse)
552 }
553}
554
555impl<TYPES: NodeType> From<VidDisperse2<TYPES>> for VidDisperse<TYPES> {
556 fn from(disperse: VidDisperse2<TYPES>) -> Self {
557 Self::V2(disperse)
558 }
559}
560
561impl<TYPES: NodeType> HasViewNumber for VidDisperse<TYPES> {
562 fn view_number(&self) -> ViewNumber {
563 match self {
564 Self::V0(disperse) => disperse.view_number(),
565 Self::V1(disperse) => disperse.view_number(),
566 Self::V2(disperse) => disperse.view_number(),
567 }
568 }
569}
570
571impl<TYPES: NodeType> HasEpoch for VidDisperse<TYPES> {
572 fn epoch(&self) -> Option<EpochNumber> {
573 match self {
574 Self::V0(disperse) => disperse.epoch(),
575 Self::V1(disperse) => disperse.epoch(),
576 Self::V2(disperse) => disperse.epoch(),
577 }
578 }
579}
580
581impl<TYPES: NodeType> VidDisperse<TYPES> {
582 #[allow(clippy::panic)]
588 pub async fn calculate_vid_disperse(
589 payload: &TYPES::BlockPayload,
590 membership: &EpochMembershipCoordinator<TYPES>,
591 view: ViewNumber,
592 target_epoch: Option<EpochNumber>,
593 data_epoch: Option<EpochNumber>,
594 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
595 upgrade_lock: &UpgradeLock<TYPES>,
596 ) -> Result<VidDisperseAndDuration<TYPES>> {
597 let epochs_enabled = upgrade_lock.epochs_enabled(view);
598 let upgraded_vid2 = upgrade_lock.upgraded_vid2(view);
599 if upgraded_vid2 {
600 VidDisperse2::calculate_vid_disperse(
601 payload,
602 membership,
603 view,
604 target_epoch,
605 data_epoch,
606 metadata,
607 )
608 .await
609 .map(|(disperse, duration)| VidDisperseAndDuration {
610 disperse: Self::V2(disperse),
611 duration,
612 })
613 } else if epochs_enabled {
614 VidDisperse1::calculate_vid_disperse(
615 payload,
616 membership,
617 view,
618 target_epoch,
619 data_epoch,
620 metadata,
621 )
622 .await
623 .map(|(disperse, duration)| VidDisperseAndDuration {
624 disperse: Self::V1(disperse),
625 duration,
626 })
627 } else {
628 VidDisperse0::calculate_vid_disperse(
629 payload,
630 membership,
631 view,
632 target_epoch,
633 data_epoch,
634 )
635 .await
636 .map(|(disperse, duration)| VidDisperseAndDuration {
637 disperse: Self::V0(disperse),
638 duration,
639 })
640 }
641 }
642
643 pub fn payload_commitment(&self) -> VidCommitment {
645 match self {
646 Self::V0(disperse) => VidCommitment::V0(disperse.payload_commitment),
647 Self::V1(disperse) => disperse.payload_commitment.into(),
648 Self::V2(disperse) => disperse.payload_commitment.into(),
649 }
650 }
651
652 pub fn payload_commitment_ref(&self) -> &[u8] {
654 match self {
655 Self::V0(disperse) => disperse.payload_commitment.as_ref(),
656 Self::V1(disperse) => disperse.payload_commitment.as_ref(),
657 Self::V2(disperse) => disperse.payload_commitment.as_ref(),
658 }
659 }
660
661 pub fn set_view_number(&mut self, view_number: ViewNumber) {
663 match self {
664 Self::V0(share) => share.view_number = view_number,
665 Self::V1(share) => share.view_number = view_number,
666 Self::V2(share) => share.view_number = view_number,
667 }
668 }
669
670 pub fn to_shares(self) -> Vec<VidDisperseShare<TYPES>> {
671 match self {
672 VidDisperse::V0(disperse) => disperse
673 .to_shares()
674 .into_iter()
675 .map(|share| VidDisperseShare::V0(share))
676 .collect(),
677 VidDisperse::V1(disperse) => disperse
678 .to_shares()
679 .into_iter()
680 .map(|share| VidDisperseShare::V1(share))
681 .collect(),
682 VidDisperse::V2(disperse) => disperse
683 .to_shares()
684 .into_iter()
685 .map(|share| VidDisperseShare::V2(share))
686 .collect(),
687 }
688 }
689
690 pub fn to_share_proposals(
692 proposal: Proposal<TYPES, Self>,
693 ) -> Vec<Proposal<TYPES, VidDisperseShare<TYPES>>> {
694 match proposal.data {
695 VidDisperse::V0(disperse) => disperse
696 .to_share_proposals(&proposal.signature)
697 .into_iter()
698 .map(|proposal| convert_proposal(proposal))
699 .collect(),
700 VidDisperse::V1(disperse) => disperse
701 .to_share_proposals(&proposal.signature)
702 .into_iter()
703 .map(|proposal| convert_proposal(proposal))
704 .collect(),
705 VidDisperse::V2(disperse) => disperse
706 .to_share_proposals(&proposal.signature)
707 .into_iter()
708 .map(|proposal| convert_proposal(proposal))
709 .collect(),
710 }
711 }
712}
713
714pub type VidDisperseShare0<TYPES> = vid_disperse::ADVZDisperseShare<TYPES>;
716pub type VidDisperseShare1<TYPES> = vid_disperse::AvidMDisperseShare<TYPES>;
717pub type VidDisperseShare2<TYPES> = vid_disperse::AvidmGf2DisperseShare<TYPES>;
718
719#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
721#[serde(bound = "TYPES: NodeType")]
722pub enum VidDisperseShare<TYPES: NodeType> {
723 V0(VidDisperseShare0<TYPES>),
725 V1(VidDisperseShare1<TYPES>),
727 V2(VidDisperseShare2<TYPES>),
729}
730
731impl<TYPES: NodeType> From<VidDisperseShare0<TYPES>> for VidDisperseShare<TYPES> {
732 fn from(share: VidDisperseShare0<TYPES>) -> Self {
733 Self::V0(share)
734 }
735}
736
737impl<TYPES: NodeType> From<VidDisperseShare1<TYPES>> for VidDisperseShare<TYPES> {
738 fn from(share: VidDisperseShare1<TYPES>) -> Self {
739 Self::V1(share)
740 }
741}
742
743impl<TYPES: NodeType> From<VidDisperseShare2<TYPES>> for VidDisperseShare<TYPES> {
744 fn from(share: VidDisperseShare2<TYPES>) -> Self {
745 Self::V2(share)
746 }
747}
748
749impl<TYPES: NodeType> VidDisperseShare<TYPES> {
750 pub fn to_proposal(
752 self,
753 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
754 ) -> Option<Proposal<TYPES, Self>> {
755 let payload_commitment_ref: &[u8] = match &self {
756 Self::V0(share) => share.payload_commitment.as_ref(),
757 Self::V1(share) => share.payload_commitment.as_ref(),
758 Self::V2(share) => share.payload_commitment.as_ref(),
759 };
760 let Ok(signature) = TYPES::SignatureKey::sign(private_key, payload_commitment_ref) else {
761 tracing::error!("VID: failed to sign dispersal share payload");
762 return None;
763 };
764 Some(Proposal {
765 signature,
766 _pd: PhantomData,
767 data: self,
768 })
769 }
770
771 pub fn recipient_key(&self) -> &TYPES::SignatureKey {
773 match self {
774 Self::V0(share) => &share.recipient_key,
775 Self::V1(share) => &share.recipient_key,
776 Self::V2(share) => &share.recipient_key,
777 }
778 }
779
780 pub fn payload_byte_len(&self) -> u32 {
782 match self {
783 Self::V0(share) => share.payload_byte_len(),
784 Self::V1(share) => share.payload_byte_len(),
785 Self::V2(share) => share.payload_byte_len(),
786 }
787 }
788
789 pub fn payload_commitment_ref(&self) -> &[u8] {
791 match self {
792 Self::V0(share) => share.payload_commitment.as_ref(),
793 Self::V1(share) => share.payload_commitment.as_ref(),
794 Self::V2(share) => share.payload_commitment.as_ref(),
795 }
796 }
797
798 pub fn payload_commitment(&self) -> VidCommitment {
800 match self {
801 Self::V0(share) => VidCommitment::V0(share.payload_commitment),
802 Self::V1(share) => share.payload_commitment.into(),
803 Self::V2(share) => share.payload_commitment.into(),
804 }
805 }
806
807 pub fn target_epoch(&self) -> Option<EpochNumber> {
809 match self {
810 Self::V0(_) => None,
811 Self::V1(share) => share.target_epoch,
812 Self::V2(share) => share.target_epoch,
813 }
814 }
815
816 pub fn common(&self) -> VidCommonRef<'_> {
818 match self {
819 Self::V0(share) => VidCommonRef::V0(&share.common),
820 Self::V1(share) => VidCommonRef::V1(&share.common),
821 Self::V2(share) => VidCommonRef::V2(&share.common),
822 }
823 }
824
825 pub fn is_consistent(&self) -> bool {
827 match self {
828 Self::V0(share) => share.is_consistent(),
829 Self::V1(share) => share.is_consistent(),
830 Self::V2(share) => share.is_consistent(),
831 }
832 }
833
834 pub fn verify_with_verified_common(&self) -> bool {
837 match self {
838 Self::V0(share) => share.verify_with_verified_common(),
839 Self::V1(share) => share.verify_with_verified_common(),
840 Self::V2(share) => share.verify_with_verified_common(),
841 }
842 }
843
844 pub fn verify(&self, total_nodes: usize) -> bool {
846 match self {
847 Self::V0(share) => share.verify(total_nodes),
848 Self::V1(share) => share.verify(total_nodes),
849 Self::V2(share) => share.verify(total_nodes),
850 }
851 }
852
853 pub fn set_view_number(&mut self, view_number: ViewNumber) {
855 match self {
856 Self::V0(share) => share.view_number = view_number,
857 Self::V1(share) => share.view_number = view_number,
858 Self::V2(share) => share.view_number = view_number,
859 }
860 }
861}
862
863impl<TYPES: NodeType> HasViewNumber for VidDisperseShare<TYPES> {
864 fn view_number(&self) -> ViewNumber {
865 match self {
866 Self::V0(disperse) => disperse.view_number(),
867 Self::V1(disperse) => disperse.view_number(),
868 Self::V2(disperse) => disperse.view_number(),
869 }
870 }
871}
872
873impl<TYPES: NodeType> HasEpoch for VidDisperseShare<TYPES> {
874 fn epoch(&self) -> Option<EpochNumber> {
875 match self {
876 Self::V0(_) => None,
877 Self::V1(share) => share.epoch(),
878 Self::V2(share) => share.epoch(),
879 }
880 }
881}
882
883#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
886#[serde(bound(deserialize = ""))]
887pub enum ViewChangeEvidence<TYPES: NodeType> {
888 Timeout(TimeoutCertificate<TYPES>),
890 ViewSync(ViewSyncFinalizeCertificate<TYPES>),
892}
893
894impl<TYPES: NodeType> ViewChangeEvidence<TYPES> {
895 pub fn is_valid_for_view(&self, view: &ViewNumber) -> bool {
897 match self {
898 ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
899 ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
900 }
901 }
902
903 pub fn to_evidence2(self) -> ViewChangeEvidence2<TYPES> {
905 match self {
906 ViewChangeEvidence::Timeout(timeout_cert) => {
907 ViewChangeEvidence2::Timeout(timeout_cert.to_tc2())
908 },
909 ViewChangeEvidence::ViewSync(view_sync_cert) => {
910 ViewChangeEvidence2::ViewSync(view_sync_cert.to_vsc2())
911 },
912 }
913 }
914}
915
916#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
919#[serde(bound(deserialize = ""))]
920pub enum ViewChangeEvidence2<TYPES: NodeType> {
921 Timeout(TimeoutCertificate2<TYPES>),
923 ViewSync(ViewSyncFinalizeCertificate2<TYPES>),
925}
926
927impl<TYPES: NodeType> ViewChangeEvidence2<TYPES> {
928 pub fn is_valid_for_view(&self, view: &ViewNumber) -> bool {
930 match self {
931 ViewChangeEvidence2::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
932 ViewChangeEvidence2::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
933 }
934 }
935
936 pub fn to_evidence(self) -> ViewChangeEvidence<TYPES> {
938 match self {
939 ViewChangeEvidence2::Timeout(timeout_cert) => {
940 ViewChangeEvidence::Timeout(timeout_cert.to_tc())
941 },
942 ViewChangeEvidence2::ViewSync(view_sync_cert) => {
943 ViewChangeEvidence::ViewSync(view_sync_cert.to_vsc())
944 },
945 }
946 }
947}
948
949#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
951#[serde(bound(deserialize = ""))]
952pub struct QuorumProposal<TYPES: NodeType> {
953 pub block_header: TYPES::BlockHeader,
955
956 pub view_number: ViewNumber,
958
959 pub justify_qc: QuorumCertificate<TYPES>,
961
962 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
964
965 pub proposal_certificate: Option<ViewChangeEvidence<TYPES>>,
970}
971
972#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
974#[serde(bound(deserialize = ""))]
975pub struct QuorumProposal2<TYPES: NodeType> {
976 pub block_header: TYPES::BlockHeader,
978
979 pub view_number: ViewNumber,
981
982 pub epoch: Option<EpochNumber>,
984
985 pub justify_qc: QuorumCertificate2<TYPES>,
987
988 pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
990
991 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
993
994 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
996
997 #[serde(with = "serde_bytes")]
1002 pub next_drb_result: Option<DrbResult>,
1003
1004 pub state_cert: Option<LightClientStateUpdateCertificateV2<TYPES>>,
1007}
1008
1009impl<TYPES: NodeType> QuorumProposal2<TYPES> {
1010 pub async fn validate_certs(
1011 &self,
1012 membership: EpochMembershipCoordinator<TYPES>,
1013 upgrade_lock: &UpgradeLock<TYPES>,
1014 ) -> Result<()> {
1015 let stake_table = membership.membership_for_epoch(self.epoch)?;
1016 let entries = StakeTableEntries::from_iter(stake_table.stake_table()).0;
1017 let threshold = stake_table.success_threshold();
1018 self.justify_qc
1019 .is_valid_cert(&entries, threshold, upgrade_lock)?;
1020 let view_change_view = match &self.view_change_evidence {
1021 Some(ViewChangeEvidence2::Timeout(timeout_cert)) => {
1022 timeout_cert.is_valid_cert(&entries, threshold, upgrade_lock)?;
1023 Some(timeout_cert.view_number() + 1)
1024 },
1025 Some(ViewChangeEvidence2::ViewSync(view_sync_cert)) => {
1026 view_sync_cert.is_valid_cert(&entries, threshold, upgrade_lock)?;
1027 Some(view_sync_cert.view_number())
1028 },
1029 _ => None,
1030 };
1031 if !(self.justify_qc.view_number() + 1 == self.view_number()
1032 || view_change_view == Some(self.view_number()))
1033 {
1034 bail!("Invalid view change evidence");
1035 }
1036 Ok(())
1037 }
1038 pub fn is_validate_block_height(&self) -> bool {
1039 self.justify_qc
1040 .data()
1041 .block_number
1042 .is_some_and(|bn| bn + 1 == self.block_header.block_number())
1043 }
1044}
1045
1046#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
1055#[serde(bound(deserialize = ""))]
1056pub struct QuorumProposal2Legacy<TYPES: NodeType> {
1057 pub block_header: TYPES::BlockHeader,
1059
1060 pub view_number: ViewNumber,
1062
1063 pub epoch: Option<EpochNumber>,
1065
1066 pub justify_qc: QuorumCertificate2<TYPES>,
1068
1069 pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1071
1072 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1074
1075 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1077
1078 #[serde(with = "serde_bytes")]
1083 pub next_drb_result: Option<DrbResult>,
1084
1085 pub state_cert: Option<LightClientStateUpdateCertificateV1<TYPES>>,
1089}
1090
1091impl<TYPES: NodeType> From<QuorumProposal2Legacy<TYPES>> for QuorumProposal2<TYPES> {
1092 fn from(quorum_proposal2: QuorumProposal2Legacy<TYPES>) -> Self {
1093 Self {
1094 block_header: quorum_proposal2.block_header,
1095 view_number: quorum_proposal2.view_number,
1096 epoch: quorum_proposal2.epoch,
1097 justify_qc: quorum_proposal2.justify_qc,
1098 next_epoch_justify_qc: quorum_proposal2.next_epoch_justify_qc,
1099 upgrade_certificate: quorum_proposal2.upgrade_certificate,
1100 view_change_evidence: quorum_proposal2.view_change_evidence,
1101 next_drb_result: quorum_proposal2.next_drb_result,
1102 state_cert: quorum_proposal2.state_cert.map(Into::into),
1103 }
1104 }
1105}
1106
1107impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal2Legacy<TYPES> {
1108 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
1109 Self {
1110 block_header: quorum_proposal2.block_header,
1111 view_number: quorum_proposal2.view_number,
1112 epoch: quorum_proposal2.epoch,
1113 justify_qc: quorum_proposal2.justify_qc,
1114 next_epoch_justify_qc: quorum_proposal2.next_epoch_justify_qc,
1115 upgrade_certificate: quorum_proposal2.upgrade_certificate,
1116 view_change_evidence: quorum_proposal2.view_change_evidence,
1117 next_drb_result: quorum_proposal2.next_drb_result,
1118 state_cert: quorum_proposal2.state_cert.map(Into::into),
1119 }
1120 }
1121}
1122
1123#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
1129#[serde(bound(deserialize = ""))]
1130pub struct QuorumProposalWrapperLegacy<TYPES: NodeType> {
1131 pub proposal: QuorumProposal2Legacy<TYPES>,
1133}
1134
1135#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
1137#[serde(bound(deserialize = ""))]
1138pub struct QuorumProposalWrapper<TYPES: NodeType> {
1139 pub proposal: QuorumProposal2<TYPES>,
1141}
1142
1143impl<TYPES: NodeType> From<QuorumProposalWrapperLegacy<TYPES>> for QuorumProposalWrapper<TYPES> {
1144 fn from(v3: QuorumProposalWrapperLegacy<TYPES>) -> Self {
1145 Self {
1146 proposal: v3.proposal.into(),
1147 }
1148 }
1149}
1150
1151impl<TYPES: NodeType> QuorumProposal2<TYPES> {
1152 pub async fn validate_epoch(
1156 &self,
1157 upgrade_lock: &UpgradeLock<TYPES>,
1158 epoch_height: u64,
1159 ) -> Result<()> {
1160 let calculated_epoch = option_epoch_from_block_number(
1161 upgrade_lock.epochs_enabled(self.view_number()),
1162 self.block_header.block_number(),
1163 epoch_height,
1164 );
1165 ensure!(
1166 calculated_epoch == self.epoch(),
1167 "Quorum proposal invalid: inconsistent epoch."
1168 );
1169 Ok(())
1170 }
1171}
1172
1173impl<TYPES: NodeType> QuorumProposalWrapper<TYPES> {
1174 pub fn block_header(&self) -> &TYPES::BlockHeader {
1176 &self.proposal.block_header
1177 }
1178
1179 pub fn view_number(&self) -> ViewNumber {
1181 self.proposal.view_number
1182 }
1183
1184 pub fn justify_qc(&self) -> &QuorumCertificate2<TYPES> {
1186 &self.proposal.justify_qc
1187 }
1188
1189 pub fn next_epoch_justify_qc(&self) -> &Option<NextEpochQuorumCertificate2<TYPES>> {
1191 &self.proposal.next_epoch_justify_qc
1192 }
1193
1194 pub fn upgrade_certificate(&self) -> &Option<UpgradeCertificate<TYPES>> {
1196 &self.proposal.upgrade_certificate
1197 }
1198
1199 pub fn view_change_evidence(&self) -> &Option<ViewChangeEvidence2<TYPES>> {
1201 &self.proposal.view_change_evidence
1202 }
1203
1204 pub fn next_drb_result(&self) -> &Option<DrbResult> {
1206 &self.proposal.next_drb_result
1207 }
1208
1209 pub async fn validate_epoch(
1213 &self,
1214 upgrade_lock: &UpgradeLock<TYPES>,
1215 epoch_height: u64,
1216 ) -> Result<()> {
1217 self.proposal
1218 .validate_epoch(upgrade_lock, epoch_height)
1219 .await
1220 }
1221
1222 pub fn state_cert(&self) -> &Option<LightClientStateUpdateCertificateV2<TYPES>> {
1224 &self.proposal.state_cert
1225 }
1226}
1227
1228impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposalWrapper<TYPES> {
1229 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
1230 Self {
1231 proposal: quorum_proposal.into(),
1232 }
1233 }
1234}
1235
1236impl<TYPES: NodeType> From<QuorumProposal2Legacy<TYPES>> for QuorumProposalWrapper<TYPES> {
1237 fn from(quorum_proposal: QuorumProposal2Legacy<TYPES>) -> Self {
1238 Self {
1239 proposal: quorum_proposal.into(),
1240 }
1241 }
1242}
1243
1244impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposalWrapper<TYPES> {
1245 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
1246 Self {
1247 proposal: quorum_proposal2,
1248 }
1249 }
1250}
1251
1252impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal<TYPES> {
1253 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
1254 quorum_proposal_wrapper.proposal.into()
1255 }
1256}
1257
1258impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2Legacy<TYPES> {
1259 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
1260 quorum_proposal_wrapper.proposal.into()
1261 }
1262}
1263
1264impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2<TYPES> {
1265 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
1266 quorum_proposal_wrapper.proposal
1267 }
1268}
1269
1270impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposal2<TYPES> {
1271 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
1272 Self {
1273 block_header: quorum_proposal.block_header,
1274 view_number: quorum_proposal.view_number,
1275 epoch: None,
1276 justify_qc: quorum_proposal.justify_qc.to_qc2(),
1277 next_epoch_justify_qc: None,
1278 upgrade_certificate: quorum_proposal.upgrade_certificate,
1279 view_change_evidence: quorum_proposal
1280 .proposal_certificate
1281 .map(ViewChangeEvidence::to_evidence2),
1282 next_drb_result: None,
1283 state_cert: None,
1284 }
1285 }
1286}
1287
1288impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal<TYPES> {
1289 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
1290 Self {
1291 block_header: quorum_proposal2.block_header,
1292 view_number: quorum_proposal2.view_number,
1293 justify_qc: quorum_proposal2.justify_qc.to_qc(),
1294 upgrade_certificate: quorum_proposal2.upgrade_certificate,
1295 proposal_certificate: quorum_proposal2
1296 .view_change_evidence
1297 .map(ViewChangeEvidence2::to_evidence),
1298 }
1299 }
1300}
1301
1302impl<TYPES: NodeType> From<Leaf<TYPES>> for Leaf2<TYPES> {
1303 fn from(leaf: Leaf<TYPES>) -> Self {
1304 let bytes: [u8; 32] = leaf.parent_commitment.into();
1305
1306 Self {
1307 view_number: leaf.view_number,
1308 justify_qc: leaf.justify_qc.to_qc2(),
1309 next_epoch_justify_qc: None,
1310 parent_commitment: Commitment::from_raw(bytes),
1311 block_header: leaf.block_header,
1312 upgrade_certificate: leaf.upgrade_certificate,
1313 block_payload: leaf.block_payload,
1314 view_change_evidence: None,
1315 next_drb_result: None,
1316 with_epoch: false,
1317 }
1318 }
1319}
1320
1321impl<TYPES: NodeType> HasViewNumber for DaProposal<TYPES> {
1322 fn view_number(&self) -> ViewNumber {
1323 self.view_number
1324 }
1325}
1326
1327impl<TYPES: NodeType> HasViewNumber for DaProposal2<TYPES> {
1328 fn view_number(&self) -> ViewNumber {
1329 self.view_number
1330 }
1331}
1332
1333impl<TYPES: NodeType> HasViewNumber for QuorumProposal<TYPES> {
1334 fn view_number(&self) -> ViewNumber {
1335 self.view_number
1336 }
1337}
1338
1339impl<TYPES: NodeType> HasViewNumber for QuorumProposal2<TYPES> {
1340 fn view_number(&self) -> ViewNumber {
1341 self.view_number
1342 }
1343}
1344
1345impl<TYPES: NodeType> HasViewNumber for QuorumProposal2Legacy<TYPES> {
1346 fn view_number(&self) -> ViewNumber {
1347 self.view_number
1348 }
1349}
1350
1351impl<TYPES: NodeType> HasViewNumber for QuorumProposalWrapper<TYPES> {
1352 fn view_number(&self) -> ViewNumber {
1353 self.proposal.view_number
1354 }
1355}
1356
1357impl<TYPES: NodeType> HasViewNumber for QuorumProposalWrapperLegacy<TYPES> {
1358 fn view_number(&self) -> ViewNumber {
1359 self.proposal.view_number
1360 }
1361}
1362
1363impl HasViewNumber for UpgradeProposal {
1364 fn view_number(&self) -> ViewNumber {
1365 self.view_number
1366 }
1367}
1368
1369impl<NODE: NodeType> HasEpoch for QuorumProposal2<NODE> {
1370 fn epoch(&self) -> Option<EpochNumber> {
1371 self.epoch
1372 }
1373}
1374
1375impl<NODE: NodeType> HasEpoch for DaProposal2<NODE> {
1376 fn epoch(&self) -> Option<EpochNumber> {
1377 self.epoch
1378 }
1379}
1380
1381impl<NODE: NodeType> HasEpoch for QuorumProposal2Legacy<NODE> {
1382 fn epoch(&self) -> Option<EpochNumber> {
1383 self.epoch
1384 }
1385}
1386
1387impl HasEpoch for UpgradeProposal {
1388 fn epoch(&self) -> Option<EpochNumber> {
1389 None
1390 }
1391}
1392
1393impl<NODE: NodeType> HasEpoch for QuorumProposal<NODE> {
1394 fn epoch(&self) -> Option<EpochNumber> {
1395 None
1396 }
1397}
1398
1399impl<NODE: NodeType> HasEpoch for DaProposal<NODE> {
1400 fn epoch(&self) -> Option<EpochNumber> {
1401 None
1402 }
1403}
1404
1405impl<NODE: NodeType> HasEpoch for QuorumProposalWrapper<NODE> {
1406 #[allow(clippy::panic)]
1408 fn epoch(&self) -> Option<EpochNumber> {
1409 self.proposal.epoch()
1410 }
1411}
1412
1413impl<TYPES: NodeType> HasEpoch for QuorumProposalWrapperLegacy<TYPES> {
1414 #[allow(clippy::panic)]
1416 fn epoch(&self) -> Option<EpochNumber> {
1417 self.proposal.epoch()
1418 }
1419}
1420
1421#[derive(Error, Debug, Serialize, Deserialize)]
1423pub enum BlockError {
1424 #[error("Invalid block header: {0}")]
1426 InvalidBlockHeader(String),
1427
1428 #[error("Inconsistent payload commitment")]
1430 InconsistentPayloadCommitment,
1431
1432 #[error("Failed to apply block header: {0}")]
1434 FailedHeaderApply(String),
1435}
1436
1437pub trait TestableLeaf {
1439 type NodeType: NodeType;
1441
1442 fn create_random_transaction(
1444 &self,
1445 rng: &mut dyn rand::RngCore,
1446 padding: u64,
1447 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction;
1448}
1449
1450#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1454#[serde(bound(deserialize = ""))]
1455pub struct Leaf<TYPES: NodeType> {
1456 view_number: ViewNumber,
1458
1459 justify_qc: QuorumCertificate<TYPES>,
1461
1462 parent_commitment: Commitment<Self>,
1465
1466 block_header: TYPES::BlockHeader,
1468
1469 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1471
1472 block_payload: Option<TYPES::BlockPayload>,
1476}
1477
1478#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1481#[serde(bound(deserialize = ""))]
1482pub struct Leaf2<TYPES: NodeType> {
1483 view_number: ViewNumber,
1485
1486 justify_qc: QuorumCertificate2<TYPES>,
1488
1489 next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1491
1492 parent_commitment: Commitment<Self>,
1495
1496 block_header: TYPES::BlockHeader,
1498
1499 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1501
1502 block_payload: Option<TYPES::BlockPayload>,
1506
1507 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1509
1510 #[serde(with = "serde_bytes")]
1515 pub next_drb_result: Option<DrbResult>,
1516
1517 pub with_epoch: bool,
1519}
1520
1521impl<TYPES: NodeType> Leaf2<TYPES> {
1522 #[must_use]
1529 pub async fn genesis(
1530 validated_state: &TYPES::ValidatedState,
1531 instance_state: &TYPES::InstanceState,
1532 version: Version,
1533 ) -> Self {
1534 let epoch = genesis_epoch_from_version(version);
1535
1536 let (payload, metadata) =
1537 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1538 .await
1539 .unwrap();
1540
1541 let genesis_view = ViewNumber::genesis();
1542
1543 let block_header =
1544 TYPES::BlockHeader::genesis(instance_state, payload.clone(), &metadata, version);
1545
1546 let block_number = if version < EPOCH_VERSION {
1547 None
1548 } else {
1549 Some(0u64)
1550 };
1551
1552 let null_quorum_data = QuorumData2 {
1553 leaf_commit: Commitment::<Leaf2<TYPES>>::default_commitment_no_preimage(),
1554 epoch,
1555 block_number,
1556 };
1557
1558 let justify_qc = QuorumCertificate2::new(
1559 null_quorum_data,
1560 null_quorum_data.commit(),
1561 genesis_view,
1562 None,
1563 PhantomData,
1564 );
1565
1566 Self {
1567 view_number: genesis_view,
1568 justify_qc,
1569 next_epoch_justify_qc: None,
1570 parent_commitment: null_quorum_data.leaf_commit,
1571 upgrade_certificate: None,
1572 block_header: block_header.clone(),
1573 block_payload: Some(payload),
1574 view_change_evidence: None,
1575 next_drb_result: None,
1576 with_epoch: epoch.is_some(),
1577 }
1578 }
1579 pub fn view_number(&self) -> ViewNumber {
1581 self.view_number
1582 }
1583 pub fn epoch(&self, epoch_height: u64) -> Option<EpochNumber> {
1585 option_epoch_from_block_number(
1586 self.with_epoch,
1587 self.block_header.block_number(),
1588 epoch_height,
1589 )
1590 }
1591 pub fn height(&self) -> u64 {
1595 self.block_header.block_number()
1596 }
1597 pub fn justify_qc(&self) -> QuorumCertificate2<TYPES> {
1599 self.justify_qc.clone()
1600 }
1601 pub fn next_epoch_justify_qc(&self) -> Option<NextEpochQuorumCertificate2<TYPES>> {
1605 self.next_epoch_justify_qc.clone()
1606 }
1607 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1609 self.upgrade_certificate.clone()
1610 }
1611 pub fn parent_commitment(&self) -> Commitment<Self> {
1613 self.parent_commitment
1614 }
1615 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1617 &self.block_header
1618 }
1619
1620 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1622 &mut self.block_header
1623 }
1624 pub fn fill_block_payload(
1631 &mut self,
1632 block_payload: TYPES::BlockPayload,
1633 num_storage_nodes: usize,
1634 version: Version,
1635 ) -> std::result::Result<(), BlockError> {
1636 let encoded_txns = block_payload.encode();
1637 let commitment = vid_commitment(
1638 &encoded_txns,
1639 &self.block_header.metadata().encode(),
1640 num_storage_nodes,
1641 version,
1642 );
1643 if commitment != self.block_header.payload_commitment() {
1644 return Err(BlockError::InconsistentPayloadCommitment);
1645 }
1646 self.block_payload = Some(block_payload);
1647 Ok(())
1648 }
1649
1650 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1652 self.block_payload.take()
1653 }
1654
1655 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1658 self.block_payload = Some(block_payload);
1659 }
1660
1661 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1663 self.block_payload.clone()
1664 }
1665
1666 pub fn block_payload_ref(&self) -> Option<&TYPES::BlockPayload> {
1667 self.block_payload.as_ref()
1668 }
1669
1670 pub fn payload_commitment(&self) -> VidCommitment {
1672 self.block_header().payload_commitment()
1673 }
1674
1675 pub fn extends_upgrade(&self, parent: &Self, upgrade: &UpgradeLock<TYPES>) -> Result<()> {
1683 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1684 (None | Some(_), None) => {},
1688 (None, Some(parent_cert)) => {
1692 let decided_upgrade_certificate_read = upgrade.decided_upgrade_cert();
1693 ensure!(
1694 self.view_number() > parent_cert.data.new_version_first_view
1695 || (self.view_number() > parent_cert.data.decide_by
1696 && decided_upgrade_certificate_read.is_none()),
1697 "The new leaf is missing an upgrade certificate that was present in its \
1698 parent, and should still be live."
1699 );
1700 },
1701 (Some(cert), Some(parent_cert)) => {
1705 ensure!(
1706 cert == parent_cert,
1707 "The new leaf does not extend the parent leaf, because it has attached a \
1708 different upgrade certificate."
1709 );
1710 },
1711 }
1712
1713 Ok(())
1717 }
1718
1719 pub fn to_leaf_unsafe(self) -> Leaf<TYPES> {
1721 let bytes: [u8; 32] = self.parent_commitment.into();
1722
1723 Leaf {
1724 view_number: self.view_number,
1725 justify_qc: self.justify_qc.to_qc(),
1726 parent_commitment: Commitment::from_raw(bytes),
1727 block_header: self.block_header,
1728 upgrade_certificate: self.upgrade_certificate,
1729 block_payload: self.block_payload,
1730 }
1731 }
1732}
1733
1734impl<TYPES: NodeType> Committable for Leaf2<TYPES> {
1735 fn commit(&self) -> committable::Commitment<Self> {
1736 let Leaf2 {
1737 view_number,
1738 justify_qc,
1739 next_epoch_justify_qc,
1740 parent_commitment,
1741 block_header,
1742 upgrade_certificate,
1743 block_payload: _,
1744 view_change_evidence,
1745 next_drb_result,
1746 with_epoch,
1747 } = self;
1748
1749 let mut cb = RawCommitmentBuilder::new("leaf commitment")
1750 .u64_field("view number", **view_number)
1751 .field("parent leaf commitment", *parent_commitment)
1752 .field("block header", block_header.commit())
1753 .field("justify qc", justify_qc.commit())
1754 .optional("upgrade certificate", upgrade_certificate);
1755
1756 if *with_epoch {
1757 cb = cb
1758 .constant_str("with_epoch")
1759 .optional("next_epoch_justify_qc", next_epoch_justify_qc);
1760
1761 if let Some(next_drb_result) = next_drb_result {
1762 cb = cb
1763 .constant_str("next_drb_result")
1764 .fixed_size_bytes(next_drb_result);
1765 }
1766
1767 match view_change_evidence {
1768 Some(ViewChangeEvidence2::Timeout(cert)) => {
1769 cb = cb.field("timeout cert", cert.commit());
1770 },
1771 Some(ViewChangeEvidence2::ViewSync(cert)) => {
1772 cb = cb.field("viewsync cert", cert.commit());
1773 },
1774 None => {},
1775 }
1776 }
1777
1778 cb.finalize()
1779 }
1780}
1781
1782impl<TYPES: NodeType> Leaf<TYPES> {
1783 pub fn commit(&self, _upgrade_lock: &UpgradeLock<TYPES>) -> Commitment<Self> {
1786 <Self as Committable>::commit(self)
1787 }
1788}
1789
1790impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
1791 fn eq(&self, other: &Self) -> bool {
1792 self.view_number == other.view_number
1793 && self.justify_qc == other.justify_qc
1794 && self.parent_commitment == other.parent_commitment
1795 && self.block_header == other.block_header
1796 }
1797}
1798
1799impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
1800 fn eq(&self, other: &Self) -> bool {
1801 let Leaf2 {
1802 view_number,
1803 justify_qc,
1804 next_epoch_justify_qc,
1805 parent_commitment,
1806 block_header,
1807 upgrade_certificate,
1808 block_payload: _,
1809 view_change_evidence,
1810 next_drb_result,
1811 with_epoch,
1812 } = self;
1813
1814 *view_number == other.view_number
1815 && *justify_qc == other.justify_qc
1816 && *next_epoch_justify_qc == other.next_epoch_justify_qc
1817 && *parent_commitment == other.parent_commitment
1818 && *block_header == other.block_header
1819 && *upgrade_certificate == other.upgrade_certificate
1820 && *view_change_evidence == other.view_change_evidence
1821 && *next_drb_result == other.next_drb_result
1822 && *with_epoch == other.with_epoch
1823 }
1824}
1825
1826impl<TYPES: NodeType> Hash for Leaf<TYPES> {
1827 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1828 self.view_number.hash(state);
1829 self.justify_qc.hash(state);
1830 self.parent_commitment.hash(state);
1831 self.block_header.hash(state);
1832 }
1833}
1834
1835impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
1836 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1837 self.commit().hash(state);
1838 self.view_number.hash(state);
1839 self.justify_qc.hash(state);
1840 self.parent_commitment.hash(state);
1841 self.block_header.hash(state);
1842 }
1843}
1844
1845impl<TYPES: NodeType> Display for Leaf<TYPES> {
1846 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1847 write!(
1848 f,
1849 "view: {:?}, height: {:?}, justify: {}",
1850 self.view_number,
1851 self.height(),
1852 self.justify_qc
1853 )
1854 }
1855}
1856
1857impl<TYPES: NodeType> QuorumCertificate<TYPES> {
1858 #[must_use]
1860 pub async fn genesis(
1861 validated_state: &TYPES::ValidatedState,
1862 instance_state: &TYPES::InstanceState,
1863 upgrade: Upgrade,
1864 ) -> Self {
1865 let upgrade_lock = UpgradeLock::<TYPES>::new(upgrade);
1867
1868 let genesis_view = ViewNumber::genesis();
1869
1870 let data = QuorumData {
1871 leaf_commit: Leaf::genesis(validated_state, instance_state, upgrade.base)
1872 .await
1873 .commit(&upgrade_lock),
1874 };
1875
1876 let versioned_data =
1877 VersionedVoteData::<_, _>::new_infallible(data.clone(), genesis_view, &upgrade_lock);
1878
1879 let bytes: [u8; 32] = versioned_data.commit().into();
1880
1881 Self::new(
1882 data,
1883 Commitment::from_raw(bytes),
1884 genesis_view,
1885 None,
1886 PhantomData,
1887 )
1888 }
1889}
1890
1891impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
1892 #[must_use]
1894 pub async fn genesis(
1895 validated_state: &TYPES::ValidatedState,
1896 instance_state: &TYPES::InstanceState,
1897 upgrade: Upgrade,
1898 ) -> Self {
1899 let upgrade_lock = UpgradeLock::<TYPES>::new(upgrade);
1901
1902 let genesis_view = ViewNumber::genesis();
1903
1904 let genesis_leaf = Leaf2::genesis(validated_state, instance_state, upgrade.base).await;
1905 let block_number = if upgrade_lock.epochs_enabled(genesis_view) {
1906 Some(genesis_leaf.height())
1907 } else {
1908 None
1909 };
1910 let data = QuorumData2 {
1911 leaf_commit: genesis_leaf.commit(),
1912 epoch: genesis_epoch_from_version(upgrade.base), block_number,
1914 };
1915
1916 let versioned_data =
1917 VersionedVoteData::<_, _>::new_infallible(data, genesis_view, &upgrade_lock);
1918
1919 let bytes: [u8; 32] = versioned_data.commit().into();
1920
1921 Self::new(
1922 data,
1923 Commitment::from_raw(bytes),
1924 genesis_view,
1925 None,
1926 PhantomData,
1927 )
1928 }
1929}
1930
1931impl<TYPES: NodeType> Leaf<TYPES> {
1932 #[must_use]
1939 pub async fn genesis(
1940 validated_state: &TYPES::ValidatedState,
1941 instance_state: &TYPES::InstanceState,
1942 version: Version,
1943 ) -> Self {
1944 let (payload, metadata) =
1945 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1946 .await
1947 .unwrap();
1948
1949 let genesis_view = ViewNumber::genesis();
1950
1951 let block_header =
1952 TYPES::BlockHeader::genesis(instance_state, payload.clone(), &metadata, version);
1953
1954 let null_quorum_data = QuorumData {
1955 leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
1956 };
1957
1958 let justify_qc = QuorumCertificate::new(
1959 null_quorum_data.clone(),
1960 null_quorum_data.commit(),
1961 genesis_view,
1962 None,
1963 PhantomData,
1964 );
1965
1966 Self {
1967 view_number: genesis_view,
1968 justify_qc,
1969 parent_commitment: null_quorum_data.leaf_commit,
1970 upgrade_certificate: None,
1971 block_header: block_header.clone(),
1972 block_payload: Some(payload),
1973 }
1974 }
1975
1976 pub fn view_number(&self) -> ViewNumber {
1978 self.view_number
1979 }
1980 pub fn height(&self) -> u64 {
1984 self.block_header.block_number()
1985 }
1986 pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
1988 self.justify_qc.clone()
1989 }
1990 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1992 self.upgrade_certificate.clone()
1993 }
1994 pub fn parent_commitment(&self) -> Commitment<Self> {
1996 self.parent_commitment
1997 }
1998 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
2000 &self.block_header
2001 }
2002
2003 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
2005 &mut self.block_header
2006 }
2007 pub fn fill_block_payload(
2014 &mut self,
2015 block_payload: TYPES::BlockPayload,
2016 num_storage_nodes: usize,
2017 version: Version,
2018 ) -> std::result::Result<(), BlockError> {
2019 let encoded_txns = block_payload.encode();
2020 let commitment = vid_commitment(
2021 &encoded_txns,
2022 &self.block_header.metadata().encode(),
2023 num_storage_nodes,
2024 version,
2025 );
2026 if commitment != self.block_header.payload_commitment() {
2027 return Err(BlockError::InconsistentPayloadCommitment);
2028 }
2029 self.block_payload = Some(block_payload);
2030 Ok(())
2031 }
2032
2033 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
2035 self.block_payload.take()
2036 }
2037
2038 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
2041 self.block_payload = Some(block_payload);
2042 }
2043
2044 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
2046 self.block_payload.clone()
2047 }
2048
2049 pub fn payload_commitment(&self) -> VidCommitment {
2051 self.block_header().payload_commitment()
2052 }
2053
2054 pub fn extends_upgrade(&self, parent: &Self, upgrade: &UpgradeLock<TYPES>) -> Result<()> {
2062 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
2063 (None | Some(_), None) => {},
2067 (None, Some(parent_cert)) => {
2071 let decided_upgrade_certificate_read = upgrade.decided_upgrade_cert();
2072 ensure!(
2073 self.view_number() > parent_cert.data.new_version_first_view
2074 || (self.view_number() > parent_cert.data.decide_by
2075 && decided_upgrade_certificate_read.is_none()),
2076 "The new leaf is missing an upgrade certificate that was present in its \
2077 parent, and should still be live."
2078 );
2079 },
2080 (Some(cert), Some(parent_cert)) => {
2084 ensure!(
2085 cert == parent_cert,
2086 "The new leaf does not extend the parent leaf, because it has attached a \
2087 different upgrade certificate."
2088 );
2089 },
2090 }
2091
2092 Ok(())
2096 }
2097}
2098
2099impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
2100where
2101 TYPES::ValidatedState: TestableState<TYPES>,
2102 TYPES::BlockPayload: TestableBlock<TYPES>,
2103{
2104 type NodeType = TYPES;
2105
2106 fn create_random_transaction(
2107 &self,
2108 rng: &mut dyn rand::RngCore,
2109 padding: u64,
2110 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
2111 {
2112 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
2113 }
2114}
2115impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
2116where
2117 TYPES::ValidatedState: TestableState<TYPES>,
2118 TYPES::BlockPayload: TestableBlock<TYPES>,
2119{
2120 type NodeType = TYPES;
2121
2122 fn create_random_transaction(
2123 &self,
2124 rng: &mut dyn rand::RngCore,
2125 padding: u64,
2126 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
2127 {
2128 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
2129 }
2130}
2131#[must_use]
2133pub fn fake_commitment<S: Committable>() -> Commitment<S> {
2134 RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
2135}
2136
2137#[must_use]
2139pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
2140 let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
2141 RawCommitmentBuilder::new("Random Commitment")
2142 .constant_str("Random Field")
2143 .var_size_bytes(&random_array)
2144 .finalize()
2145}
2146
2147pub fn serialize_signature2<TYPES: NodeType>(
2151 signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
2152) -> Vec<u8> {
2153 let mut signatures_bytes = vec![];
2154 signatures_bytes.extend("Yes".as_bytes());
2155
2156 let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
2157 let proof_bytes = bincode_opts()
2158 .serialize(&proof.as_bitslice())
2159 .expect("This serialization shouldn't be able to fail");
2160 signatures_bytes.extend("bitvec proof".as_bytes());
2161 signatures_bytes.extend(proof_bytes.as_slice());
2162 let sig_bytes = bincode_opts()
2163 .serialize(&sig)
2164 .expect("This serialization shouldn't be able to fail");
2165 signatures_bytes.extend("aggregated signature".as_bytes());
2166 signatures_bytes.extend(sig_bytes.as_slice());
2167 signatures_bytes
2168}
2169
2170impl<TYPES: NodeType> Committable for Leaf<TYPES> {
2171 fn commit(&self) -> committable::Commitment<Self> {
2172 RawCommitmentBuilder::new("leaf commitment")
2173 .u64_field("view number", *self.view_number)
2174 .field("parent leaf commitment", self.parent_commitment)
2175 .field("block header", self.block_header.commit())
2176 .field("justify qc", self.justify_qc.commit())
2177 .optional("upgrade certificate", &self.upgrade_certificate)
2178 .finalize()
2179 }
2180}
2181
2182impl<TYPES: NodeType> Leaf2<TYPES> {
2183 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
2185 let QuorumProposalWrapper {
2188 proposal:
2189 QuorumProposal2 {
2190 view_number,
2191 epoch,
2192 justify_qc,
2193 next_epoch_justify_qc,
2194 block_header,
2195 upgrade_certificate,
2196 view_change_evidence,
2197 next_drb_result,
2198 state_cert: _,
2199 },
2200 } = quorum_proposal;
2201
2202 Self {
2203 view_number: *view_number,
2204 justify_qc: justify_qc.clone(),
2205 next_epoch_justify_qc: next_epoch_justify_qc.clone(),
2206 parent_commitment: justify_qc.data().leaf_commit,
2207 block_header: block_header.clone(),
2208 upgrade_certificate: upgrade_certificate.clone(),
2209 block_payload: None,
2210 view_change_evidence: view_change_evidence.clone(),
2211 next_drb_result: *next_drb_result,
2212 with_epoch: epoch.is_some(),
2213 }
2214 }
2215}
2216
2217impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for Leaf2<TYPES> {
2218 fn from(value: QuorumProposalWrapper<TYPES>) -> Self {
2219 let QuorumProposalWrapper {
2220 proposal:
2221 QuorumProposal2 {
2222 view_number,
2223 epoch,
2224 justify_qc,
2225 next_epoch_justify_qc,
2226 block_header,
2227 upgrade_certificate,
2228 view_change_evidence,
2229 next_drb_result,
2230 state_cert: _,
2231 },
2232 } = value;
2233
2234 let parent_commitment = justify_qc.data().leaf_commit;
2235
2236 Self {
2237 view_number,
2238 justify_qc,
2239 next_epoch_justify_qc,
2240 parent_commitment,
2241 block_header,
2242 upgrade_certificate,
2243 block_payload: None,
2244 view_change_evidence,
2245 next_drb_result,
2246 with_epoch: epoch.is_some(),
2247 }
2248 }
2249}
2250
2251impl<TYPES: NodeType> Leaf<TYPES> {
2252 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
2254 let QuorumProposal {
2257 view_number,
2258 justify_qc,
2259 block_header,
2260 upgrade_certificate,
2261 proposal_certificate: _,
2262 } = quorum_proposal;
2263
2264 Self {
2265 view_number: *view_number,
2266 justify_qc: justify_qc.clone(),
2267 parent_commitment: justify_qc.data().leaf_commit,
2268 block_header: block_header.clone(),
2269 upgrade_certificate: upgrade_certificate.clone(),
2270 block_payload: None,
2271 }
2272 }
2273}
2274
2275pub mod null_block {
2276 #![allow(missing_docs)]
2277
2278 use jf_advz::VidScheme;
2279 use vbs::version::Version;
2280 use versions::EPOCH_VERSION;
2281
2282 use crate::{
2283 data::VidCommitment,
2284 traits::{
2285 BlockPayload, block_contents::BuilderFee, node_implementation::NodeType,
2286 signature_key::BuilderSignatureKey,
2287 },
2288 vid::advz::advz_scheme,
2289 };
2290
2291 #[must_use]
2300 pub fn commitment(num_storage_nodes: usize) -> Option<VidCommitment> {
2301 let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
2302
2303 match vid_result {
2304 Ok(r) => Some(VidCommitment::V0(r)),
2305 Err(_) => None,
2306 }
2307 }
2308
2309 #[must_use]
2311 pub fn builder_fee<TYPES: NodeType>(
2312 num_storage_nodes: usize,
2313 version: Version,
2314 ) -> Option<BuilderFee<TYPES>> {
2315 const FEE_AMOUNT: u64 = 0;
2317
2318 let (pub_key, priv_key) =
2319 <TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
2320 [0_u8; 32], 0,
2321 );
2322
2323 if version >= EPOCH_VERSION {
2324 let (_null_block, null_block_metadata) =
2325 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
2326
2327 match TYPES::BuilderSignatureKey::sign_fee(&priv_key, FEE_AMOUNT, &null_block_metadata)
2328 {
2329 Ok(sig) => Some(BuilderFee {
2330 fee_amount: FEE_AMOUNT,
2331 fee_account: pub_key,
2332 fee_signature: sig,
2333 }),
2334 Err(_) => None,
2335 }
2336 } else {
2337 let (_null_block, null_block_metadata) =
2338 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
2339
2340 match TYPES::BuilderSignatureKey::sign_fee_with_vid_commitment(
2341 &priv_key,
2342 FEE_AMOUNT,
2343 &null_block_metadata,
2344 &commitment(num_storage_nodes)?,
2345 ) {
2346 Ok(sig) => Some(BuilderFee {
2347 fee_amount: FEE_AMOUNT,
2348 fee_account: pub_key,
2349 fee_signature: sig,
2350 }),
2351 Err(_) => None,
2352 }
2353 }
2354 }
2355}
2356
2357#[derive(Debug, Eq, PartialEq, Clone)]
2359pub struct PackedBundle<TYPES: NodeType> {
2360 pub encoded_transactions: Arc<[u8]>,
2362
2363 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
2365
2366 pub view_number: ViewNumber,
2368
2369 pub epoch_number: Option<EpochNumber>,
2371
2372 pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
2374}
2375
2376impl<TYPES: NodeType> PackedBundle<TYPES> {
2377 pub fn new(
2379 encoded_transactions: Arc<[u8]>,
2380 metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
2381 view_number: ViewNumber,
2382 epoch_number: Option<EpochNumber>,
2383 sequencing_fees: Vec1<BuilderFee<TYPES>>,
2384 ) -> Self {
2385 Self {
2386 encoded_transactions,
2387 metadata,
2388 view_number,
2389 epoch_number,
2390 sequencing_fees,
2391 }
2392 }
2393}
2394
2395#[cfg(test)]
2396mod test {
2397 use super::*;
2398
2399 #[test]
2400 fn test_vid_commitment_display() {
2401 let vc = VidCommitment::V0(VidCommitment0::default());
2402 assert_eq!(
2403 format!("{vc}"),
2404 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
2405 );
2406 assert_eq!(
2407 format!("{vc:?}"),
2408 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
2409 );
2410
2411 let vc = VidCommitment::V1(VidCommitment1::default());
2412 assert_eq!(
2413 format!("{vc}"),
2414 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
2415 );
2416 assert_eq!(
2417 format!("{vc:?}"),
2418 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
2419 );
2420
2421 let vc = VidCommitment::V2(VidCommitment2::default());
2422 assert_eq!(
2423 format!("{vc}"),
2424 "AvidmGf2Commit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq"
2425 );
2426 assert_eq!(
2427 format!("{vc:?}"),
2428 "AvidmGf2Commit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq"
2429 );
2430 }
2431}