1use 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_vid::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 LightClientStateUpdateCertificate, NextEpochQuorumCertificate2, QuorumCertificate,
40 QuorumCertificate2, TimeoutCertificate, TimeoutCertificate2, UpgradeCertificate,
41 ViewSyncFinalizeCertificate, ViewSyncFinalizeCertificate2,
42 },
43 simple_vote::{HasEpoch, QuorumData, QuorumData2, UpgradeProposalData, VersionedVoteData},
44 traits::{
45 block_contents::{BlockHeader, BuilderFee, EncodeBytes, TestableBlock},
46 node_implementation::{ConsensusTime, NodeType, Versions},
47 signature_key::SignatureKey,
48 states::TestableState,
49 BlockPayload,
50 },
51 utils::{
52 bincode_opts, genesis_epoch_from_version, option_epoch_from_block_number,
53 EpochTransitionIndicator,
54 },
55 vid::{
56 advz::{advz_scheme, ADVZCommitment, ADVZShare},
57 avidm::{init_avidm_param, AvidMCommitment, AvidMScheme, AvidMShare},
58 },
59 vote::{Certificate, HasViewNumber},
60};
61
62macro_rules! impl_u64_wrapper {
65 ($t:ty, $genesis_val:expr) => {
66 impl ConsensusTime for $t {
67 fn genesis() -> Self {
69 Self($genesis_val)
70 }
71 fn new(n: u64) -> Self {
73 Self(n)
74 }
75 fn u64(&self) -> u64 {
77 self.0
78 }
79 }
80
81 impl Display for $t {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{}", self.0)
84 }
85 }
86
87 impl std::ops::Add<u64> for $t {
88 type Output = $t;
89
90 fn add(self, rhs: u64) -> Self::Output {
91 Self(self.0 + rhs)
92 }
93 }
94
95 impl std::ops::AddAssign<u64> for $t {
96 fn add_assign(&mut self, rhs: u64) {
97 self.0 += rhs;
98 }
99 }
100
101 impl std::ops::Deref for $t {
102 type Target = u64;
103
104 fn deref(&self) -> &Self::Target {
105 &self.0
106 }
107 }
108
109 impl std::ops::Sub<u64> for $t {
110 type Output = $t;
111 fn sub(self, rhs: u64) -> Self::Output {
112 Self(self.0 - rhs)
113 }
114 }
115 };
116}
117
118#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
120pub struct ViewNumber(u64);
121
122impl Committable for ViewNumber {
123 fn commit(&self) -> Commitment<Self> {
124 let builder = RawCommitmentBuilder::new("View Number Commitment");
125 builder.u64(self.0).finalize()
126 }
127}
128
129impl_u64_wrapper!(ViewNumber, 0u64);
130
131#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
133pub struct EpochNumber(u64);
134
135impl Committable for EpochNumber {
136 fn commit(&self) -> Commitment<Self> {
137 let builder = RawCommitmentBuilder::new("Epoch Number Commitment");
138 builder.u64(self.0).finalize()
139 }
140}
141
142impl_u64_wrapper!(EpochNumber, 1u64);
143
144#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
146#[serde(bound = "TYPES: NodeType")]
147pub struct DaProposal<TYPES: NodeType> {
148 pub encoded_transactions: Arc<[u8]>,
150 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
152 pub view_number: TYPES::View,
154}
155
156#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
158#[serde(bound = "TYPES: NodeType")]
159pub struct DaProposal2<TYPES: NodeType> {
160 pub encoded_transactions: Arc<[u8]>,
162 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
164 pub view_number: TYPES::View,
166 pub epoch: Option<TYPES::Epoch>,
168 pub epoch_transition_indicator: EpochTransitionIndicator,
171}
172
173impl<TYPES: NodeType> From<DaProposal<TYPES>> for DaProposal2<TYPES> {
174 fn from(da_proposal: DaProposal<TYPES>) -> Self {
175 Self {
176 encoded_transactions: da_proposal.encoded_transactions,
177 metadata: da_proposal.metadata,
178 view_number: da_proposal.view_number,
179 epoch: None,
180 epoch_transition_indicator: EpochTransitionIndicator::NotInTransition,
181 }
182 }
183}
184
185impl<TYPES: NodeType> From<DaProposal2<TYPES>> for DaProposal<TYPES> {
186 fn from(da_proposal2: DaProposal2<TYPES>) -> Self {
187 Self {
188 encoded_transactions: da_proposal2.encoded_transactions,
189 metadata: da_proposal2.metadata,
190 view_number: da_proposal2.view_number,
191 }
192 }
193}
194
195#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
197#[serde(bound = "TYPES: NodeType")]
198pub struct UpgradeProposal<TYPES>
199where
200 TYPES: NodeType,
201{
202 pub upgrade_proposal: UpgradeProposalData<TYPES>,
204 pub view_number: TYPES::View,
206}
207
208#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
210#[serde(
211 try_from = "tagged_base64::TaggedBase64",
212 into = "tagged_base64::TaggedBase64"
213)]
214pub enum VidCommitment {
215 V0(ADVZCommitment),
216 V1(AvidMCommitment),
217}
218
219impl Default for VidCommitment {
220 fn default() -> Self {
221 Self::V0(Default::default())
222 }
223}
224
225impl Display for VidCommitment {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 std::write!(f, "{}", TaggedBase64::from(self))
228 }
229}
230
231impl Debug for VidCommitment {
232 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233 std::fmt::Display::fmt(self, f)
234 }
235}
236
237impl From<VidCommitment> for TaggedBase64 {
238 fn from(val: VidCommitment) -> Self {
239 match val {
240 VidCommitment::V0(comm) => comm.into(),
241 VidCommitment::V1(comm) => comm.into(),
242 }
243 }
244}
245
246impl From<&VidCommitment> for TaggedBase64 {
247 fn from(val: &VidCommitment) -> Self {
248 match val {
249 VidCommitment::V0(comm) => comm.into(),
250 VidCommitment::V1(comm) => comm.into(),
251 }
252 }
253}
254
255impl TryFrom<TaggedBase64> for VidCommitment {
256 type Error = tagged_base64::Tb64Error;
257
258 fn try_from(value: TaggedBase64) -> std::result::Result<Self, Self::Error> {
259 ADVZCommitment::try_from(&value)
260 .map(Self::V0)
261 .or(AvidMCommitment::try_from(value).map(Self::V1))
262 }
263}
264
265impl<'a> TryFrom<&'a TaggedBase64> for VidCommitment {
266 type Error = tagged_base64::Tb64Error;
267
268 fn try_from(value: &'a TaggedBase64) -> std::result::Result<Self, Self::Error> {
269 ADVZCommitment::try_from(value)
270 .map(Self::V0)
271 .or(AvidMCommitment::try_from(value).map(Self::V1))
272 }
273}
274
275impl std::str::FromStr for VidCommitment {
276 type Err = tagged_base64::Tb64Error;
277 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
278 use core::convert::TryFrom;
279 Self::try_from(TaggedBase64::from_str(s)?)
280 .map_err(|_| tagged_base64::Tb64Error::InvalidData)
281 }
282}
283
284impl From<AvidMCommitment> for VidCommitment {
292 fn from(comm: AvidMCommitment) -> Self {
293 Self::V1(comm)
294 }
295}
296
297impl AsRef<[u8]> for VidCommitment {
298 fn as_ref(&self) -> &[u8] {
299 match self {
300 Self::V0(comm) => comm.as_ref(),
301 Self::V1(comm) => comm.as_ref(),
302 }
303 }
304}
305
306impl AsRef<[u8; 32]> for VidCommitment {
307 fn as_ref(&self) -> &[u8; 32] {
308 match self {
309 Self::V0(comm) => comm.as_ref().as_ref(),
310 Self::V1(comm) => comm.as_ref(),
311 }
312 }
313}
314
315impl VidCommitment {
316 pub fn unwrap_v0(self) -> ADVZCommitment {
318 match self {
319 VidCommitment::V0(comm) => comm,
320 _ => panic!("Unexpected version for this commitment"),
321 }
322 }
323 pub fn unwrap_v1(self) -> AvidMCommitment {
325 match self {
326 VidCommitment::V1(comm) => comm,
327 _ => panic!("Unexpected version for this commitment"),
328 }
329 }
330}
331
332#[must_use]
337#[allow(clippy::panic)]
338pub fn vid_commitment<V: Versions>(
339 encoded_transactions: &[u8],
340 metadata: &[u8],
341 total_weight: usize,
342 version: Version,
343) -> VidCommitment {
344 if version < V::Epochs::VERSION {
345 let encoded_tx_len = encoded_transactions.len();
346 advz_scheme(total_weight).commit_only(encoded_transactions).map(VidCommitment::V0).unwrap_or_else(|err| panic!("VidScheme::commit_only failure:(total_weight,payload_byte_len)=({total_weight},{encoded_tx_len}) error: {err}"))
347 } else {
348 let param = init_avidm_param(total_weight).unwrap();
349 let encoded_tx_len = encoded_transactions.len();
350 AvidMScheme::commit(
351 ¶m,
352 encoded_transactions,
353 ns_table::parse_ns_table(encoded_tx_len, metadata),
354 )
355 .map(VidCommitment::V1)
356 .unwrap()
357 }
358}
359
360#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
362pub enum VidShare {
363 V0(ADVZShare),
364 V1(AvidMShare),
365}
366
367impl From<AvidMShare> for VidShare {
375 fn from(share: AvidMShare) -> Self {
376 Self::V1(share)
377 }
378}
379
380pub mod ns_table;
381pub mod vid_disperse;
382
383pub struct VidDisperseAndDuration<TYPES: NodeType> {
385 pub disperse: VidDisperse<TYPES>,
387 pub duration: Duration,
389}
390
391#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
397#[serde(bound = "TYPES: NodeType")]
398pub enum VidDisperse<TYPES: NodeType> {
399 V0(vid_disperse::ADVZDisperse<TYPES>),
401 V1(vid_disperse::AvidMDisperse<TYPES>),
403}
404
405impl<TYPES: NodeType> From<vid_disperse::ADVZDisperse<TYPES>> for VidDisperse<TYPES> {
406 fn from(disperse: vid_disperse::ADVZDisperse<TYPES>) -> Self {
407 Self::V0(disperse)
408 }
409}
410
411impl<TYPES: NodeType> From<vid_disperse::AvidMDisperse<TYPES>> for VidDisperse<TYPES> {
412 fn from(disperse: vid_disperse::AvidMDisperse<TYPES>) -> Self {
413 Self::V1(disperse)
414 }
415}
416
417impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperse<TYPES> {
418 fn view_number(&self) -> TYPES::View {
419 match self {
420 Self::V0(disperse) => disperse.view_number(),
421 Self::V1(disperse) => disperse.view_number(),
422 }
423 }
424}
425
426impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperse<TYPES> {
427 fn epoch(&self) -> Option<TYPES::Epoch> {
428 match self {
429 Self::V0(disperse) => disperse.epoch(),
430 Self::V1(disperse) => disperse.epoch(),
431 }
432 }
433}
434
435impl<TYPES: NodeType> VidDisperse<TYPES> {
436 #[allow(clippy::panic)]
442 pub async fn calculate_vid_disperse<V: Versions>(
443 payload: &TYPES::BlockPayload,
444 membership: &EpochMembershipCoordinator<TYPES>,
445 view: TYPES::View,
446 target_epoch: Option<TYPES::Epoch>,
447 data_epoch: Option<TYPES::Epoch>,
448 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
449 upgrade_lock: &UpgradeLock<TYPES, V>,
450 ) -> Result<VidDisperseAndDuration<TYPES>> {
451 let version = upgrade_lock.version_infallible(view).await;
452 if version < V::Epochs::VERSION {
453 ADVZDisperse::calculate_vid_disperse(
454 payload,
455 membership,
456 view,
457 target_epoch,
458 data_epoch,
459 )
460 .await
461 .map(|(disperse, duration)| VidDisperseAndDuration {
462 disperse: Self::V0(disperse),
463 duration,
464 })
465 } else {
466 AvidMDisperse::calculate_vid_disperse(
467 payload,
468 membership,
469 view,
470 target_epoch,
471 data_epoch,
472 metadata,
473 )
474 .await
475 .map(|(disperse, duration)| VidDisperseAndDuration {
476 disperse: Self::V1(disperse),
477 duration,
478 })
479 }
480 }
481
482 pub fn payload_commitment(&self) -> VidCommitment {
484 match self {
485 Self::V0(disperse) => VidCommitment::V0(disperse.payload_commitment),
486 Self::V1(disperse) => disperse.payload_commitment.into(),
487 }
488 }
489
490 pub fn payload_commitment_ref(&self) -> &[u8] {
492 match self {
493 Self::V0(disperse) => disperse.payload_commitment.as_ref(),
494 Self::V1(disperse) => disperse.payload_commitment.as_ref(),
495 }
496 }
497
498 pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
500 match self {
501 Self::V0(share) => share.view_number = view_number,
502 Self::V1(share) => share.view_number = view_number,
503 }
504 }
505}
506
507#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
509#[serde(bound = "TYPES: NodeType")]
510pub enum VidDisperseShare<TYPES: NodeType> {
511 V0(vid_disperse::ADVZDisperseShare<TYPES>),
513 V1(vid_disperse::VidDisperseShare2<TYPES>),
515}
516
517impl<TYPES: NodeType> VidDisperseShare<TYPES> {
518 pub fn from_vid_disperse(vid_disperse: VidDisperse<TYPES>) -> Vec<Self> {
520 match vid_disperse {
521 VidDisperse::V0(vid_disperse) => {
522 ADVZDisperseShare::<TYPES>::from_advz_disperse(vid_disperse)
523 .into_iter()
524 .map(|share| Self::V0(share))
525 .collect()
526 },
527 VidDisperse::V1(vid_disperse) => {
528 VidDisperseShare2::<TYPES>::from_vid_disperse(vid_disperse)
529 .into_iter()
530 .map(|share| Self::V1(share))
531 .collect()
532 },
533 }
534 }
535
536 pub fn to_proposal(
538 self,
539 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
540 ) -> Option<Proposal<TYPES, Self>> {
541 let payload_commitment_ref: &[u8] = match &self {
542 Self::V0(share) => share.payload_commitment.as_ref(),
543 Self::V1(share) => share.payload_commitment.as_ref(),
544 };
545 let Ok(signature) = TYPES::SignatureKey::sign(private_key, payload_commitment_ref) else {
546 tracing::error!("VID: failed to sign dispersal share payload");
547 return None;
548 };
549 Some(Proposal {
550 signature,
551 _pd: PhantomData,
552 data: self,
553 })
554 }
555
556 pub fn to_vid_share_proposals(
558 vid_disperse_proposal: Proposal<TYPES, VidDisperse<TYPES>>,
559 ) -> Vec<Proposal<TYPES, Self>> {
560 match vid_disperse_proposal.data {
561 VidDisperse::V0(disperse) => ADVZDisperseShare::to_vid_share_proposals(
562 disperse,
563 &vid_disperse_proposal.signature,
564 )
565 .into_iter()
566 .map(|proposal| convert_proposal(proposal))
567 .collect(),
568 VidDisperse::V1(disperse) => VidDisperseShare2::to_vid_share_proposals(
569 disperse,
570 &vid_disperse_proposal.signature,
571 )
572 .into_iter()
573 .map(|proposal| convert_proposal(proposal))
574 .collect(),
575 }
576 }
577
578 pub fn recipient_key(&self) -> &TYPES::SignatureKey {
580 match self {
581 Self::V0(share) => &share.recipient_key,
582 Self::V1(share) => &share.recipient_key,
583 }
584 }
585
586 pub fn payload_byte_len(&self) -> u32 {
588 match self {
589 Self::V0(share) => share.payload_byte_len(),
590 Self::V1(share) => share.payload_byte_len(),
591 }
592 }
593
594 pub fn payload_commitment_ref(&self) -> &[u8] {
596 match self {
597 Self::V0(share) => share.payload_commitment.as_ref(),
598 Self::V1(share) => share.payload_commitment.as_ref(),
599 }
600 }
601
602 pub fn payload_commitment(&self) -> VidCommitment {
604 match self {
605 Self::V0(share) => VidCommitment::V0(share.payload_commitment),
606 Self::V1(share) => share.payload_commitment.into(),
607 }
608 }
609
610 pub fn target_epoch(&self) -> Option<<TYPES as NodeType>::Epoch> {
612 match self {
613 Self::V0(_) => None,
614 Self::V1(share) => share.target_epoch,
615 }
616 }
617
618 #[allow(clippy::result_unit_err)]
622 pub fn verify_share(&self, total_nodes: usize) -> std::result::Result<(), ()> {
623 match self {
624 Self::V0(share) => share.verify_share(total_nodes),
625 Self::V1(share) => share.verify_share(total_nodes),
626 }
627 }
628
629 pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
631 match self {
632 Self::V0(share) => share.view_number = view_number,
633 Self::V1(share) => share.view_number = view_number,
634 }
635 }
636}
637
638impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperseShare<TYPES> {
639 fn view_number(&self) -> TYPES::View {
640 match self {
641 Self::V0(disperse) => disperse.view_number(),
642 Self::V1(disperse) => disperse.view_number(),
643 }
644 }
645}
646
647impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperseShare<TYPES> {
648 fn epoch(&self) -> Option<TYPES::Epoch> {
649 match self {
650 Self::V0(_) => None,
651 Self::V1(share) => share.epoch(),
652 }
653 }
654}
655
656impl<TYPES: NodeType> From<vid_disperse::ADVZDisperseShare<TYPES>> for VidDisperseShare<TYPES> {
657 fn from(share: vid_disperse::ADVZDisperseShare<TYPES>) -> Self {
658 Self::V0(share)
659 }
660}
661
662impl<TYPES: NodeType> From<vid_disperse::VidDisperseShare2<TYPES>> for VidDisperseShare<TYPES> {
663 fn from(share: vid_disperse::VidDisperseShare2<TYPES>) -> Self {
664 Self::V1(share)
665 }
666}
667
668#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
671#[serde(bound(deserialize = ""))]
672pub enum ViewChangeEvidence<TYPES: NodeType> {
673 Timeout(TimeoutCertificate<TYPES>),
675 ViewSync(ViewSyncFinalizeCertificate<TYPES>),
677}
678
679impl<TYPES: NodeType> ViewChangeEvidence<TYPES> {
680 pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
682 match self {
683 ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
684 ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
685 }
686 }
687
688 pub fn to_evidence2(self) -> ViewChangeEvidence2<TYPES> {
690 match self {
691 ViewChangeEvidence::Timeout(timeout_cert) => {
692 ViewChangeEvidence2::Timeout(timeout_cert.to_tc2())
693 },
694 ViewChangeEvidence::ViewSync(view_sync_cert) => {
695 ViewChangeEvidence2::ViewSync(view_sync_cert.to_vsc2())
696 },
697 }
698 }
699}
700
701#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
704#[serde(bound(deserialize = ""))]
705pub enum ViewChangeEvidence2<TYPES: NodeType> {
706 Timeout(TimeoutCertificate2<TYPES>),
708 ViewSync(ViewSyncFinalizeCertificate2<TYPES>),
710}
711
712impl<TYPES: NodeType> ViewChangeEvidence2<TYPES> {
713 pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
715 match self {
716 ViewChangeEvidence2::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
717 ViewChangeEvidence2::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
718 }
719 }
720
721 pub fn to_evidence(self) -> ViewChangeEvidence<TYPES> {
723 match self {
724 ViewChangeEvidence2::Timeout(timeout_cert) => {
725 ViewChangeEvidence::Timeout(timeout_cert.to_tc())
726 },
727 ViewChangeEvidence2::ViewSync(view_sync_cert) => {
728 ViewChangeEvidence::ViewSync(view_sync_cert.to_vsc())
729 },
730 }
731 }
732}
733
734#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
736#[serde(bound(deserialize = ""))]
737pub struct QuorumProposal<TYPES: NodeType> {
738 pub block_header: TYPES::BlockHeader,
740
741 pub view_number: TYPES::View,
743
744 pub justify_qc: QuorumCertificate<TYPES>,
746
747 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
749
750 pub proposal_certificate: Option<ViewChangeEvidence<TYPES>>,
755}
756
757#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
759#[serde(bound(deserialize = ""))]
760pub struct QuorumProposal2<TYPES: NodeType> {
761 pub block_header: TYPES::BlockHeader,
763
764 pub view_number: TYPES::View,
766
767 pub epoch: Option<TYPES::Epoch>,
769
770 pub justify_qc: QuorumCertificate2<TYPES>,
772
773 pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
775
776 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
778
779 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
781
782 #[serde(with = "serde_bytes")]
787 pub next_drb_result: Option<DrbResult>,
788
789 pub state_cert: Option<LightClientStateUpdateCertificate<TYPES>>,
792}
793
794#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
796#[serde(bound(deserialize = ""))]
797pub struct QuorumProposalWrapper<TYPES: NodeType> {
798 pub proposal: QuorumProposal2<TYPES>,
800}
801
802impl<TYPES: NodeType> QuorumProposal2<TYPES> {
803 pub async fn validate_epoch<V: Versions>(
807 &self,
808 upgrade_lock: &UpgradeLock<TYPES, V>,
809 epoch_height: u64,
810 ) -> Result<()> {
811 let calculated_epoch = option_epoch_from_block_number::<TYPES>(
812 upgrade_lock.epochs_enabled(self.view_number()).await,
813 self.block_header.block_number(),
814 epoch_height,
815 );
816 ensure!(
817 calculated_epoch == self.epoch(),
818 "Quorum proposal invalid: inconsistent epoch."
819 );
820 Ok(())
821 }
822}
823
824impl<TYPES: NodeType> QuorumProposalWrapper<TYPES> {
825 pub fn block_header(&self) -> &TYPES::BlockHeader {
827 &self.proposal.block_header
828 }
829
830 pub fn view_number(&self) -> TYPES::View {
832 self.proposal.view_number
833 }
834
835 pub fn justify_qc(&self) -> &QuorumCertificate2<TYPES> {
837 &self.proposal.justify_qc
838 }
839
840 pub fn next_epoch_justify_qc(&self) -> &Option<NextEpochQuorumCertificate2<TYPES>> {
842 &self.proposal.next_epoch_justify_qc
843 }
844
845 pub fn upgrade_certificate(&self) -> &Option<UpgradeCertificate<TYPES>> {
847 &self.proposal.upgrade_certificate
848 }
849
850 pub fn view_change_evidence(&self) -> &Option<ViewChangeEvidence2<TYPES>> {
852 &self.proposal.view_change_evidence
853 }
854
855 pub fn next_drb_result(&self) -> &Option<DrbResult> {
857 &self.proposal.next_drb_result
858 }
859
860 pub async fn validate_epoch<V: Versions>(
864 &self,
865 upgrade_lock: &UpgradeLock<TYPES, V>,
866 epoch_height: u64,
867 ) -> Result<()> {
868 self.proposal
869 .validate_epoch(upgrade_lock, epoch_height)
870 .await
871 }
872
873 pub fn state_cert(&self) -> &Option<LightClientStateUpdateCertificate<TYPES>> {
875 &self.proposal.state_cert
876 }
877}
878
879impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposalWrapper<TYPES> {
880 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
881 Self {
882 proposal: quorum_proposal.into(),
883 }
884 }
885}
886
887impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposalWrapper<TYPES> {
888 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
889 Self {
890 proposal: quorum_proposal2,
891 }
892 }
893}
894
895impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal<TYPES> {
896 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
897 quorum_proposal_wrapper.proposal.into()
898 }
899}
900
901impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2<TYPES> {
902 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
903 quorum_proposal_wrapper.proposal
904 }
905}
906
907impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposal2<TYPES> {
908 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
909 Self {
910 block_header: quorum_proposal.block_header,
911 view_number: quorum_proposal.view_number,
912 epoch: None,
913 justify_qc: quorum_proposal.justify_qc.to_qc2(),
914 next_epoch_justify_qc: None,
915 upgrade_certificate: quorum_proposal.upgrade_certificate,
916 view_change_evidence: quorum_proposal
917 .proposal_certificate
918 .map(ViewChangeEvidence::to_evidence2),
919 next_drb_result: None,
920 state_cert: None,
921 }
922 }
923}
924
925impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal<TYPES> {
926 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
927 Self {
928 block_header: quorum_proposal2.block_header,
929 view_number: quorum_proposal2.view_number,
930 justify_qc: quorum_proposal2.justify_qc.to_qc(),
931 upgrade_certificate: quorum_proposal2.upgrade_certificate,
932 proposal_certificate: quorum_proposal2
933 .view_change_evidence
934 .map(ViewChangeEvidence2::to_evidence),
935 }
936 }
937}
938
939impl<TYPES: NodeType> From<Leaf<TYPES>> for Leaf2<TYPES> {
940 fn from(leaf: Leaf<TYPES>) -> Self {
941 let bytes: [u8; 32] = leaf.parent_commitment.into();
942
943 Self {
944 view_number: leaf.view_number,
945 justify_qc: leaf.justify_qc.to_qc2(),
946 next_epoch_justify_qc: None,
947 parent_commitment: Commitment::from_raw(bytes),
948 block_header: leaf.block_header,
949 upgrade_certificate: leaf.upgrade_certificate,
950 block_payload: leaf.block_payload,
951 view_change_evidence: None,
952 next_drb_result: None,
953 with_epoch: false,
954 }
955 }
956}
957
958impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal<TYPES> {
959 fn view_number(&self) -> TYPES::View {
960 self.view_number
961 }
962}
963
964impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal2<TYPES> {
965 fn view_number(&self) -> TYPES::View {
966 self.view_number
967 }
968}
969
970impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal<TYPES> {
971 fn view_number(&self) -> TYPES::View {
972 self.view_number
973 }
974}
975
976impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal2<TYPES> {
977 fn view_number(&self) -> TYPES::View {
978 self.view_number
979 }
980}
981
982impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposalWrapper<TYPES> {
983 fn view_number(&self) -> TYPES::View {
984 self.proposal.view_number
985 }
986}
987
988impl<TYPES: NodeType> HasViewNumber<TYPES> for UpgradeProposal<TYPES> {
989 fn view_number(&self) -> TYPES::View {
990 self.view_number
991 }
992}
993
994impl_has_epoch!(QuorumProposal2<TYPES>, DaProposal2<TYPES>);
995
996impl_has_none_epoch!(
997 QuorumProposal<TYPES>,
998 DaProposal<TYPES>,
999 UpgradeProposal<TYPES>,
1000 ADVZDisperseShare<TYPES>
1001);
1002
1003impl<TYPES: NodeType> HasEpoch<TYPES> for QuorumProposalWrapper<TYPES> {
1004 #[allow(clippy::panic)]
1006 fn epoch(&self) -> Option<TYPES::Epoch> {
1007 self.proposal.epoch()
1008 }
1009}
1010
1011#[derive(Error, Debug, Serialize, Deserialize)]
1013pub enum BlockError {
1014 #[error("Invalid block header: {0}")]
1016 InvalidBlockHeader(String),
1017
1018 #[error("Inconsistent payload commitment")]
1020 InconsistentPayloadCommitment,
1021
1022 #[error("Failed to apply block header: {0}")]
1024 FailedHeaderApply(String),
1025}
1026
1027pub trait TestableLeaf {
1029 type NodeType: NodeType;
1031
1032 fn create_random_transaction(
1034 &self,
1035 rng: &mut dyn rand::RngCore,
1036 padding: u64,
1037 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction;
1038}
1039
1040#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1044#[serde(bound(deserialize = ""))]
1045pub struct Leaf<TYPES: NodeType> {
1046 view_number: TYPES::View,
1048
1049 justify_qc: QuorumCertificate<TYPES>,
1051
1052 parent_commitment: Commitment<Self>,
1055
1056 block_header: TYPES::BlockHeader,
1058
1059 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1061
1062 block_payload: Option<TYPES::BlockPayload>,
1066}
1067
1068#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1071#[serde(bound(deserialize = ""))]
1072pub struct Leaf2<TYPES: NodeType> {
1073 view_number: TYPES::View,
1075
1076 justify_qc: QuorumCertificate2<TYPES>,
1078
1079 next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1081
1082 parent_commitment: Commitment<Self>,
1085
1086 block_header: TYPES::BlockHeader,
1088
1089 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1091
1092 block_payload: Option<TYPES::BlockPayload>,
1096
1097 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1099
1100 #[serde(with = "serde_bytes")]
1105 pub next_drb_result: Option<DrbResult>,
1106
1107 pub with_epoch: bool,
1109}
1110
1111impl<TYPES: NodeType> Leaf2<TYPES> {
1112 #[must_use]
1119 pub async fn genesis<V: Versions>(
1120 validated_state: &TYPES::ValidatedState,
1121 instance_state: &TYPES::InstanceState,
1122 ) -> Self {
1123 let epoch = genesis_epoch_from_version::<V, TYPES>();
1124
1125 let (payload, metadata) =
1126 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1127 .await
1128 .unwrap();
1129
1130 let genesis_view = TYPES::View::genesis();
1131
1132 let block_header =
1133 TYPES::BlockHeader::genesis::<V>(instance_state, payload.clone(), &metadata);
1134
1135 let block_number = if V::Base::VERSION < V::Epochs::VERSION {
1136 None
1137 } else {
1138 Some(0u64)
1139 };
1140
1141 let null_quorum_data = QuorumData2 {
1142 leaf_commit: Commitment::<Leaf2<TYPES>>::default_commitment_no_preimage(),
1143 epoch,
1144 block_number,
1145 };
1146
1147 let justify_qc = QuorumCertificate2::new(
1148 null_quorum_data.clone(),
1149 null_quorum_data.commit(),
1150 genesis_view,
1151 None,
1152 PhantomData,
1153 );
1154
1155 Self {
1156 view_number: genesis_view,
1157 justify_qc,
1158 next_epoch_justify_qc: None,
1159 parent_commitment: null_quorum_data.leaf_commit,
1160 upgrade_certificate: None,
1161 block_header: block_header.clone(),
1162 block_payload: Some(payload),
1163 view_change_evidence: None,
1164 next_drb_result: None,
1165 with_epoch: epoch.is_some(),
1166 }
1167 }
1168 pub fn view_number(&self) -> TYPES::View {
1170 self.view_number
1171 }
1172 pub fn epoch(&self, epoch_height: u64) -> Option<TYPES::Epoch> {
1174 option_epoch_from_block_number::<TYPES>(
1175 self.with_epoch,
1176 self.block_header.block_number(),
1177 epoch_height,
1178 )
1179 }
1180 pub fn height(&self) -> u64 {
1184 self.block_header.block_number()
1185 }
1186 pub fn justify_qc(&self) -> QuorumCertificate2<TYPES> {
1188 self.justify_qc.clone()
1189 }
1190 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1192 self.upgrade_certificate.clone()
1193 }
1194 pub fn parent_commitment(&self) -> Commitment<Self> {
1196 self.parent_commitment
1197 }
1198 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1200 &self.block_header
1201 }
1202
1203 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1205 &mut self.block_header
1206 }
1207 pub fn fill_block_payload<V: Versions>(
1214 &mut self,
1215 block_payload: TYPES::BlockPayload,
1216 num_storage_nodes: usize,
1217 version: Version,
1218 ) -> std::result::Result<(), BlockError> {
1219 let encoded_txns = block_payload.encode();
1220 let commitment = vid_commitment::<V>(
1221 &encoded_txns,
1222 &self.block_header.metadata().encode(),
1223 num_storage_nodes,
1224 version,
1225 );
1226 if commitment != self.block_header.payload_commitment() {
1227 return Err(BlockError::InconsistentPayloadCommitment);
1228 }
1229 self.block_payload = Some(block_payload);
1230 Ok(())
1231 }
1232
1233 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1235 self.block_payload.take()
1236 }
1237
1238 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1241 self.block_payload = Some(block_payload);
1242 }
1243
1244 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1246 self.block_payload.clone()
1247 }
1248
1249 pub fn payload_commitment(&self) -> VidCommitment {
1251 self.block_header().payload_commitment()
1252 }
1253
1254 pub async fn extends_upgrade(
1262 &self,
1263 parent: &Self,
1264 decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1265 ) -> Result<()> {
1266 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1267 (None | Some(_), None) => {},
1271 (None, Some(parent_cert)) => {
1275 let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1276 ensure!(self.view_number() > parent_cert.data.new_version_first_view
1277 || (self.view_number() > parent_cert.data.decide_by && decided_upgrade_certificate_read.is_none()),
1278 "The new leaf is missing an upgrade certificate that was present in its parent, and should still be live."
1279 );
1280 },
1281 (Some(cert), Some(parent_cert)) => {
1285 ensure!(cert == parent_cert, "The new leaf does not extend the parent leaf, because it has attached a different upgrade certificate.");
1286 },
1287 }
1288
1289 Ok(())
1293 }
1294
1295 pub fn to_leaf_unsafe(self) -> Leaf<TYPES> {
1297 let bytes: [u8; 32] = self.parent_commitment.into();
1298
1299 Leaf {
1300 view_number: self.view_number,
1301 justify_qc: self.justify_qc.to_qc(),
1302 parent_commitment: Commitment::from_raw(bytes),
1303 block_header: self.block_header,
1304 upgrade_certificate: self.upgrade_certificate,
1305 block_payload: self.block_payload,
1306 }
1307 }
1308}
1309
1310impl<TYPES: NodeType> Committable for Leaf2<TYPES> {
1311 fn commit(&self) -> committable::Commitment<Self> {
1312 let Leaf2 {
1313 view_number,
1314 justify_qc,
1315 next_epoch_justify_qc,
1316 parent_commitment,
1317 block_header,
1318 upgrade_certificate,
1319 block_payload: _,
1320 view_change_evidence,
1321 next_drb_result,
1322 with_epoch,
1323 } = self;
1324
1325 let mut cb = RawCommitmentBuilder::new("leaf commitment")
1326 .u64_field("view number", **view_number)
1327 .field("parent leaf commitment", *parent_commitment)
1328 .field("block header", block_header.commit())
1329 .field("justify qc", justify_qc.commit())
1330 .optional("upgrade certificate", upgrade_certificate);
1331
1332 if *with_epoch {
1333 cb = cb
1334 .constant_str("with_epoch")
1335 .optional("next_epoch_justify_qc", next_epoch_justify_qc);
1336
1337 if let Some(next_drb_result) = next_drb_result {
1338 cb = cb
1339 .constant_str("next_drb_result")
1340 .fixed_size_bytes(next_drb_result);
1341 }
1342
1343 match view_change_evidence {
1344 Some(ViewChangeEvidence2::Timeout(cert)) => {
1345 cb = cb.field("timeout cert", cert.commit());
1346 },
1347 Some(ViewChangeEvidence2::ViewSync(cert)) => {
1348 cb = cb.field("viewsync cert", cert.commit());
1349 },
1350 None => {},
1351 }
1352 }
1353
1354 cb.finalize()
1355 }
1356}
1357
1358impl<TYPES: NodeType> Leaf<TYPES> {
1359 #[allow(clippy::unused_async)]
1360 pub async fn commit<V: Versions>(
1363 &self,
1364 _upgrade_lock: &UpgradeLock<TYPES, V>,
1365 ) -> Commitment<Self> {
1366 <Self as Committable>::commit(self)
1367 }
1368}
1369
1370impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
1371 fn eq(&self, other: &Self) -> bool {
1372 self.view_number == other.view_number
1373 && self.justify_qc == other.justify_qc
1374 && self.parent_commitment == other.parent_commitment
1375 && self.block_header == other.block_header
1376 }
1377}
1378
1379impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
1380 fn eq(&self, other: &Self) -> bool {
1381 let Leaf2 {
1382 view_number,
1383 justify_qc,
1384 next_epoch_justify_qc,
1385 parent_commitment,
1386 block_header,
1387 upgrade_certificate,
1388 block_payload: _,
1389 view_change_evidence,
1390 next_drb_result,
1391 with_epoch,
1392 } = self;
1393
1394 *view_number == other.view_number
1395 && *justify_qc == other.justify_qc
1396 && *next_epoch_justify_qc == other.next_epoch_justify_qc
1397 && *parent_commitment == other.parent_commitment
1398 && *block_header == other.block_header
1399 && *upgrade_certificate == other.upgrade_certificate
1400 && *view_change_evidence == other.view_change_evidence
1401 && *next_drb_result == other.next_drb_result
1402 && *with_epoch == other.with_epoch
1403 }
1404}
1405
1406impl<TYPES: NodeType> Hash for Leaf<TYPES> {
1407 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1408 self.view_number.hash(state);
1409 self.justify_qc.hash(state);
1410 self.parent_commitment.hash(state);
1411 self.block_header.hash(state);
1412 }
1413}
1414
1415impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
1416 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1417 self.commit().hash(state);
1418 self.view_number.hash(state);
1419 self.justify_qc.hash(state);
1420 self.parent_commitment.hash(state);
1421 self.block_header.hash(state);
1422 }
1423}
1424
1425impl<TYPES: NodeType> Display for Leaf<TYPES> {
1426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1427 write!(
1428 f,
1429 "view: {:?}, height: {:?}, justify: {}",
1430 self.view_number,
1431 self.height(),
1432 self.justify_qc
1433 )
1434 }
1435}
1436
1437impl<TYPES: NodeType> QuorumCertificate<TYPES> {
1438 #[must_use]
1439 pub async fn genesis<V: Versions>(
1441 validated_state: &TYPES::ValidatedState,
1442 instance_state: &TYPES::InstanceState,
1443 ) -> Self {
1444 let upgrade_lock = UpgradeLock::<TYPES, V>::new();
1446
1447 let genesis_view = <TYPES::View as ConsensusTime>::genesis();
1448
1449 let data = QuorumData {
1450 leaf_commit: Leaf::genesis::<V>(validated_state, instance_state)
1451 .await
1452 .commit(&upgrade_lock)
1453 .await,
1454 };
1455
1456 let versioned_data =
1457 VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1458 .await;
1459
1460 let bytes: [u8; 32] = versioned_data.commit().into();
1461
1462 Self::new(
1463 data,
1464 Commitment::from_raw(bytes),
1465 genesis_view,
1466 None,
1467 PhantomData,
1468 )
1469 }
1470}
1471
1472impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
1473 #[must_use]
1474 pub async fn genesis<V: Versions>(
1476 validated_state: &TYPES::ValidatedState,
1477 instance_state: &TYPES::InstanceState,
1478 ) -> Self {
1479 let upgrade_lock = UpgradeLock::<TYPES, V>::new();
1481
1482 let genesis_view = <TYPES::View as ConsensusTime>::genesis();
1483
1484 let genesis_leaf = Leaf2::genesis::<V>(validated_state, instance_state).await;
1485 let block_number = if upgrade_lock.epochs_enabled(genesis_view).await {
1486 Some(genesis_leaf.height())
1487 } else {
1488 None
1489 };
1490 let data = QuorumData2 {
1491 leaf_commit: genesis_leaf.commit(),
1492 epoch: genesis_epoch_from_version::<V, TYPES>(), block_number,
1494 };
1495
1496 let versioned_data =
1497 VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1498 .await;
1499
1500 let bytes: [u8; 32] = versioned_data.commit().into();
1501
1502 Self::new(
1503 data,
1504 Commitment::from_raw(bytes),
1505 genesis_view,
1506 None,
1507 PhantomData,
1508 )
1509 }
1510}
1511
1512impl<TYPES: NodeType> Leaf<TYPES> {
1513 #[must_use]
1520 pub async fn genesis<V: Versions>(
1521 validated_state: &TYPES::ValidatedState,
1522 instance_state: &TYPES::InstanceState,
1523 ) -> Self {
1524 let (payload, metadata) =
1525 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1526 .await
1527 .unwrap();
1528
1529 let genesis_view = TYPES::View::genesis();
1530
1531 let block_header =
1532 TYPES::BlockHeader::genesis::<V>(instance_state, payload.clone(), &metadata);
1533
1534 let null_quorum_data = QuorumData {
1535 leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
1536 };
1537
1538 let justify_qc = QuorumCertificate::new(
1539 null_quorum_data.clone(),
1540 null_quorum_data.commit(),
1541 genesis_view,
1542 None,
1543 PhantomData,
1544 );
1545
1546 Self {
1547 view_number: genesis_view,
1548 justify_qc,
1549 parent_commitment: null_quorum_data.leaf_commit,
1550 upgrade_certificate: None,
1551 block_header: block_header.clone(),
1552 block_payload: Some(payload),
1553 }
1554 }
1555
1556 pub fn view_number(&self) -> TYPES::View {
1558 self.view_number
1559 }
1560 pub fn height(&self) -> u64 {
1564 self.block_header.block_number()
1565 }
1566 pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
1568 self.justify_qc.clone()
1569 }
1570 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1572 self.upgrade_certificate.clone()
1573 }
1574 pub fn parent_commitment(&self) -> Commitment<Self> {
1576 self.parent_commitment
1577 }
1578 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1580 &self.block_header
1581 }
1582
1583 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1585 &mut self.block_header
1586 }
1587 pub fn fill_block_payload<V: Versions>(
1594 &mut self,
1595 block_payload: TYPES::BlockPayload,
1596 num_storage_nodes: usize,
1597 version: Version,
1598 ) -> std::result::Result<(), BlockError> {
1599 let encoded_txns = block_payload.encode();
1600 let commitment = vid_commitment::<V>(
1601 &encoded_txns,
1602 &self.block_header.metadata().encode(),
1603 num_storage_nodes,
1604 version,
1605 );
1606 if commitment != self.block_header.payload_commitment() {
1607 return Err(BlockError::InconsistentPayloadCommitment);
1608 }
1609 self.block_payload = Some(block_payload);
1610 Ok(())
1611 }
1612
1613 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1615 self.block_payload.take()
1616 }
1617
1618 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1621 self.block_payload = Some(block_payload);
1622 }
1623
1624 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1626 self.block_payload.clone()
1627 }
1628
1629 pub fn payload_commitment(&self) -> VidCommitment {
1631 self.block_header().payload_commitment()
1632 }
1633
1634 pub async fn extends_upgrade(
1642 &self,
1643 parent: &Self,
1644 decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1645 ) -> Result<()> {
1646 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1647 (None | Some(_), None) => {},
1651 (None, Some(parent_cert)) => {
1655 let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1656 ensure!(self.view_number() > parent_cert.data.new_version_first_view
1657 || (self.view_number() > parent_cert.data.decide_by && decided_upgrade_certificate_read.is_none()),
1658 "The new leaf is missing an upgrade certificate that was present in its parent, and should still be live."
1659 );
1660 },
1661 (Some(cert), Some(parent_cert)) => {
1665 ensure!(cert == parent_cert, "The new leaf does not extend the parent leaf, because it has attached a different upgrade certificate.");
1666 },
1667 }
1668
1669 Ok(())
1673 }
1674}
1675
1676impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
1677where
1678 TYPES::ValidatedState: TestableState<TYPES>,
1679 TYPES::BlockPayload: TestableBlock<TYPES>,
1680{
1681 type NodeType = TYPES;
1682
1683 fn create_random_transaction(
1684 &self,
1685 rng: &mut dyn rand::RngCore,
1686 padding: u64,
1687 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
1688 {
1689 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
1690 }
1691}
1692impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
1693where
1694 TYPES::ValidatedState: TestableState<TYPES>,
1695 TYPES::BlockPayload: TestableBlock<TYPES>,
1696{
1697 type NodeType = TYPES;
1698
1699 fn create_random_transaction(
1700 &self,
1701 rng: &mut dyn rand::RngCore,
1702 padding: u64,
1703 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
1704 {
1705 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
1706 }
1707}
1708#[must_use]
1710pub fn fake_commitment<S: Committable>() -> Commitment<S> {
1711 RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
1712}
1713
1714#[must_use]
1716pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
1717 let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
1718 RawCommitmentBuilder::new("Random Commitment")
1719 .constant_str("Random Field")
1720 .var_size_bytes(&random_array)
1721 .finalize()
1722}
1723
1724pub fn serialize_signature2<TYPES: NodeType>(
1728 signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
1729) -> Vec<u8> {
1730 let mut signatures_bytes = vec![];
1731 signatures_bytes.extend("Yes".as_bytes());
1732
1733 let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
1734 let proof_bytes = bincode_opts()
1735 .serialize(&proof.as_bitslice())
1736 .expect("This serialization shouldn't be able to fail");
1737 signatures_bytes.extend("bitvec proof".as_bytes());
1738 signatures_bytes.extend(proof_bytes.as_slice());
1739 let sig_bytes = bincode_opts()
1740 .serialize(&sig)
1741 .expect("This serialization shouldn't be able to fail");
1742 signatures_bytes.extend("aggregated signature".as_bytes());
1743 signatures_bytes.extend(sig_bytes.as_slice());
1744 signatures_bytes
1745}
1746
1747impl<TYPES: NodeType> Committable for Leaf<TYPES> {
1748 fn commit(&self) -> committable::Commitment<Self> {
1749 RawCommitmentBuilder::new("leaf commitment")
1750 .u64_field("view number", *self.view_number)
1751 .field("parent leaf commitment", self.parent_commitment)
1752 .field("block header", self.block_header.commit())
1753 .field("justify qc", self.justify_qc.commit())
1754 .optional("upgrade certificate", &self.upgrade_certificate)
1755 .finalize()
1756 }
1757}
1758
1759impl<TYPES: NodeType> Leaf2<TYPES> {
1760 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
1762 let QuorumProposalWrapper {
1765 proposal:
1766 QuorumProposal2 {
1767 view_number,
1768 epoch,
1769 justify_qc,
1770 next_epoch_justify_qc,
1771 block_header,
1772 upgrade_certificate,
1773 view_change_evidence,
1774 next_drb_result,
1775 state_cert: _,
1776 },
1777 } = quorum_proposal;
1778
1779 Self {
1780 view_number: *view_number,
1781 justify_qc: justify_qc.clone(),
1782 next_epoch_justify_qc: next_epoch_justify_qc.clone(),
1783 parent_commitment: justify_qc.data().leaf_commit,
1784 block_header: block_header.clone(),
1785 upgrade_certificate: upgrade_certificate.clone(),
1786 block_payload: None,
1787 view_change_evidence: view_change_evidence.clone(),
1788 next_drb_result: *next_drb_result,
1789 with_epoch: epoch.is_some(),
1790 }
1791 }
1792}
1793
1794impl<TYPES: NodeType> Leaf<TYPES> {
1795 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
1797 let QuorumProposal {
1800 view_number,
1801 justify_qc,
1802 block_header,
1803 upgrade_certificate,
1804 proposal_certificate: _,
1805 } = quorum_proposal;
1806
1807 Self {
1808 view_number: *view_number,
1809 justify_qc: justify_qc.clone(),
1810 parent_commitment: justify_qc.data().leaf_commit,
1811 block_header: block_header.clone(),
1812 upgrade_certificate: upgrade_certificate.clone(),
1813 block_payload: None,
1814 }
1815 }
1816}
1817
1818pub mod null_block {
1819 #![allow(missing_docs)]
1820
1821 use jf_vid::VidScheme;
1822 use vbs::version::StaticVersionType;
1823
1824 use crate::{
1825 data::VidCommitment,
1826 traits::{
1827 block_contents::BuilderFee,
1828 node_implementation::{NodeType, Versions},
1829 signature_key::BuilderSignatureKey,
1830 BlockPayload,
1831 },
1832 vid::advz::advz_scheme,
1833 };
1834
1835 #[must_use]
1844 pub fn commitment<V: Versions>(num_storage_nodes: usize) -> Option<VidCommitment> {
1845 let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
1846
1847 match vid_result {
1848 Ok(r) => Some(VidCommitment::V0(r)),
1849 Err(_) => None,
1850 }
1851 }
1852
1853 #[must_use]
1855 pub fn builder_fee<TYPES: NodeType, V: Versions>(
1856 num_storage_nodes: usize,
1857 version: vbs::version::Version,
1858 ) -> Option<BuilderFee<TYPES>> {
1859 const FEE_AMOUNT: u64 = 0;
1861
1862 let (pub_key, priv_key) =
1863 <TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
1864 [0_u8; 32], 0,
1865 );
1866
1867 if version >= V::Epochs::VERSION {
1868 let (_null_block, null_block_metadata) =
1869 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
1870
1871 match TYPES::BuilderSignatureKey::sign_fee(&priv_key, FEE_AMOUNT, &null_block_metadata)
1872 {
1873 Ok(sig) => Some(BuilderFee {
1874 fee_amount: FEE_AMOUNT,
1875 fee_account: pub_key,
1876 fee_signature: sig,
1877 }),
1878 Err(_) => None,
1879 }
1880 } else {
1881 let (_null_block, null_block_metadata) =
1882 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
1883
1884 match TYPES::BuilderSignatureKey::sign_fee_with_vid_commitment(
1885 &priv_key,
1886 FEE_AMOUNT,
1887 &null_block_metadata,
1888 &commitment::<V>(num_storage_nodes)?,
1889 ) {
1890 Ok(sig) => Some(BuilderFee {
1891 fee_amount: FEE_AMOUNT,
1892 fee_account: pub_key,
1893 fee_signature: sig,
1894 }),
1895 Err(_) => None,
1896 }
1897 }
1898 }
1899}
1900
1901#[derive(Debug, Eq, PartialEq, Clone)]
1903pub struct PackedBundle<TYPES: NodeType> {
1904 pub encoded_transactions: Arc<[u8]>,
1906
1907 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
1909
1910 pub view_number: TYPES::View,
1912
1913 pub epoch_number: Option<TYPES::Epoch>,
1915
1916 pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
1918}
1919
1920impl<TYPES: NodeType> PackedBundle<TYPES> {
1921 pub fn new(
1923 encoded_transactions: Arc<[u8]>,
1924 metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
1925 view_number: TYPES::View,
1926 epoch_number: Option<TYPES::Epoch>,
1927 sequencing_fees: Vec1<BuilderFee<TYPES>>,
1928 ) -> Self {
1929 Self {
1930 encoded_transactions,
1931 metadata,
1932 view_number,
1933 epoch_number,
1934 sequencing_fees,
1935 }
1936 }
1937}
1938
1939#[cfg(test)]
1940mod test {
1941 use super::*;
1942
1943 #[test]
1944 fn test_vid_commitment_display() {
1945 let vc = VidCommitment::V0(ADVZCommitment::default());
1946 assert_eq!(
1947 format!("{vc}"),
1948 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
1949 );
1950 assert_eq!(
1951 format!("{vc:?}"),
1952 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
1953 );
1954
1955 let vc = VidCommitment::V1(AvidMCommitment {
1956 commit: Default::default(),
1957 });
1958 assert_eq!(
1959 format!("{vc}"),
1960 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
1961 );
1962 assert_eq!(
1963 format!("{vc:?}"),
1964 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
1965 );
1966 }
1967}