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)
347 .commit_only(encoded_transactions)
348 .map(VidCommitment::V0)
349 .unwrap_or_else(|err| {
350 panic!(
351 "VidScheme::commit_only \
352 failure:(total_weight,payload_byte_len)=({total_weight},{encoded_tx_len}) \
353 error: {err}"
354 )
355 })
356 } else {
357 let param = init_avidm_param(total_weight).unwrap();
358 let encoded_tx_len = encoded_transactions.len();
359 AvidMScheme::commit(
360 ¶m,
361 encoded_transactions,
362 ns_table::parse_ns_table(encoded_tx_len, metadata),
363 )
364 .map(VidCommitment::V1)
365 .unwrap()
366 }
367}
368
369#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
371pub enum VidShare {
372 V0(ADVZShare),
373 V1(AvidMShare),
374}
375
376impl From<AvidMShare> for VidShare {
384 fn from(share: AvidMShare) -> Self {
385 Self::V1(share)
386 }
387}
388
389pub mod ns_table;
390pub mod vid_disperse;
391
392pub struct VidDisperseAndDuration<TYPES: NodeType> {
394 pub disperse: VidDisperse<TYPES>,
396 pub duration: Duration,
398}
399
400#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
406#[serde(bound = "TYPES: NodeType")]
407pub enum VidDisperse<TYPES: NodeType> {
408 V0(vid_disperse::ADVZDisperse<TYPES>),
410 V1(vid_disperse::AvidMDisperse<TYPES>),
412}
413
414impl<TYPES: NodeType> From<vid_disperse::ADVZDisperse<TYPES>> for VidDisperse<TYPES> {
415 fn from(disperse: vid_disperse::ADVZDisperse<TYPES>) -> Self {
416 Self::V0(disperse)
417 }
418}
419
420impl<TYPES: NodeType> From<vid_disperse::AvidMDisperse<TYPES>> for VidDisperse<TYPES> {
421 fn from(disperse: vid_disperse::AvidMDisperse<TYPES>) -> Self {
422 Self::V1(disperse)
423 }
424}
425
426impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperse<TYPES> {
427 fn view_number(&self) -> TYPES::View {
428 match self {
429 Self::V0(disperse) => disperse.view_number(),
430 Self::V1(disperse) => disperse.view_number(),
431 }
432 }
433}
434
435impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperse<TYPES> {
436 fn epoch(&self) -> Option<TYPES::Epoch> {
437 match self {
438 Self::V0(disperse) => disperse.epoch(),
439 Self::V1(disperse) => disperse.epoch(),
440 }
441 }
442}
443
444impl<TYPES: NodeType> VidDisperse<TYPES> {
445 #[allow(clippy::panic)]
451 pub async fn calculate_vid_disperse<V: Versions>(
452 payload: &TYPES::BlockPayload,
453 membership: &EpochMembershipCoordinator<TYPES>,
454 view: TYPES::View,
455 target_epoch: Option<TYPES::Epoch>,
456 data_epoch: Option<TYPES::Epoch>,
457 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
458 upgrade_lock: &UpgradeLock<TYPES, V>,
459 ) -> Result<VidDisperseAndDuration<TYPES>> {
460 let version = upgrade_lock.version_infallible(view).await;
461 if version < V::Epochs::VERSION {
462 ADVZDisperse::calculate_vid_disperse(
463 payload,
464 membership,
465 view,
466 target_epoch,
467 data_epoch,
468 )
469 .await
470 .map(|(disperse, duration)| VidDisperseAndDuration {
471 disperse: Self::V0(disperse),
472 duration,
473 })
474 } else {
475 AvidMDisperse::calculate_vid_disperse(
476 payload,
477 membership,
478 view,
479 target_epoch,
480 data_epoch,
481 metadata,
482 )
483 .await
484 .map(|(disperse, duration)| VidDisperseAndDuration {
485 disperse: Self::V1(disperse),
486 duration,
487 })
488 }
489 }
490
491 pub fn payload_commitment(&self) -> VidCommitment {
493 match self {
494 Self::V0(disperse) => VidCommitment::V0(disperse.payload_commitment),
495 Self::V1(disperse) => disperse.payload_commitment.into(),
496 }
497 }
498
499 pub fn payload_commitment_ref(&self) -> &[u8] {
501 match self {
502 Self::V0(disperse) => disperse.payload_commitment.as_ref(),
503 Self::V1(disperse) => disperse.payload_commitment.as_ref(),
504 }
505 }
506
507 pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
509 match self {
510 Self::V0(share) => share.view_number = view_number,
511 Self::V1(share) => share.view_number = view_number,
512 }
513 }
514}
515
516#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
518#[serde(bound = "TYPES: NodeType")]
519pub enum VidDisperseShare<TYPES: NodeType> {
520 V0(vid_disperse::ADVZDisperseShare<TYPES>),
522 V1(vid_disperse::VidDisperseShare2<TYPES>),
524}
525
526impl<TYPES: NodeType> VidDisperseShare<TYPES> {
527 pub fn from_vid_disperse(vid_disperse: VidDisperse<TYPES>) -> Vec<Self> {
529 match vid_disperse {
530 VidDisperse::V0(vid_disperse) => {
531 ADVZDisperseShare::<TYPES>::from_advz_disperse(vid_disperse)
532 .into_iter()
533 .map(|share| Self::V0(share))
534 .collect()
535 },
536 VidDisperse::V1(vid_disperse) => {
537 VidDisperseShare2::<TYPES>::from_vid_disperse(vid_disperse)
538 .into_iter()
539 .map(|share| Self::V1(share))
540 .collect()
541 },
542 }
543 }
544
545 pub fn to_proposal(
547 self,
548 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
549 ) -> Option<Proposal<TYPES, Self>> {
550 let payload_commitment_ref: &[u8] = match &self {
551 Self::V0(share) => share.payload_commitment.as_ref(),
552 Self::V1(share) => share.payload_commitment.as_ref(),
553 };
554 let Ok(signature) = TYPES::SignatureKey::sign(private_key, payload_commitment_ref) else {
555 tracing::error!("VID: failed to sign dispersal share payload");
556 return None;
557 };
558 Some(Proposal {
559 signature,
560 _pd: PhantomData,
561 data: self,
562 })
563 }
564
565 pub fn to_vid_share_proposals(
567 vid_disperse_proposal: Proposal<TYPES, VidDisperse<TYPES>>,
568 ) -> Vec<Proposal<TYPES, Self>> {
569 match vid_disperse_proposal.data {
570 VidDisperse::V0(disperse) => ADVZDisperseShare::to_vid_share_proposals(
571 disperse,
572 &vid_disperse_proposal.signature,
573 )
574 .into_iter()
575 .map(|proposal| convert_proposal(proposal))
576 .collect(),
577 VidDisperse::V1(disperse) => VidDisperseShare2::to_vid_share_proposals(
578 disperse,
579 &vid_disperse_proposal.signature,
580 )
581 .into_iter()
582 .map(|proposal| convert_proposal(proposal))
583 .collect(),
584 }
585 }
586
587 pub fn recipient_key(&self) -> &TYPES::SignatureKey {
589 match self {
590 Self::V0(share) => &share.recipient_key,
591 Self::V1(share) => &share.recipient_key,
592 }
593 }
594
595 pub fn payload_byte_len(&self) -> u32 {
597 match self {
598 Self::V0(share) => share.payload_byte_len(),
599 Self::V1(share) => share.payload_byte_len(),
600 }
601 }
602
603 pub fn payload_commitment_ref(&self) -> &[u8] {
605 match self {
606 Self::V0(share) => share.payload_commitment.as_ref(),
607 Self::V1(share) => share.payload_commitment.as_ref(),
608 }
609 }
610
611 pub fn payload_commitment(&self) -> VidCommitment {
613 match self {
614 Self::V0(share) => VidCommitment::V0(share.payload_commitment),
615 Self::V1(share) => share.payload_commitment.into(),
616 }
617 }
618
619 pub fn target_epoch(&self) -> Option<<TYPES as NodeType>::Epoch> {
621 match self {
622 Self::V0(_) => None,
623 Self::V1(share) => share.target_epoch,
624 }
625 }
626
627 #[allow(clippy::result_unit_err)]
631 pub fn verify_share(&self, total_nodes: usize) -> std::result::Result<(), ()> {
632 match self {
633 Self::V0(share) => share.verify_share(total_nodes),
634 Self::V1(share) => share.verify_share(total_nodes),
635 }
636 }
637
638 pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
640 match self {
641 Self::V0(share) => share.view_number = view_number,
642 Self::V1(share) => share.view_number = view_number,
643 }
644 }
645}
646
647impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperseShare<TYPES> {
648 fn view_number(&self) -> TYPES::View {
649 match self {
650 Self::V0(disperse) => disperse.view_number(),
651 Self::V1(disperse) => disperse.view_number(),
652 }
653 }
654}
655
656impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperseShare<TYPES> {
657 fn epoch(&self) -> Option<TYPES::Epoch> {
658 match self {
659 Self::V0(_) => None,
660 Self::V1(share) => share.epoch(),
661 }
662 }
663}
664
665impl<TYPES: NodeType> From<vid_disperse::ADVZDisperseShare<TYPES>> for VidDisperseShare<TYPES> {
666 fn from(share: vid_disperse::ADVZDisperseShare<TYPES>) -> Self {
667 Self::V0(share)
668 }
669}
670
671impl<TYPES: NodeType> From<vid_disperse::VidDisperseShare2<TYPES>> for VidDisperseShare<TYPES> {
672 fn from(share: vid_disperse::VidDisperseShare2<TYPES>) -> Self {
673 Self::V1(share)
674 }
675}
676
677#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
680#[serde(bound(deserialize = ""))]
681pub enum ViewChangeEvidence<TYPES: NodeType> {
682 Timeout(TimeoutCertificate<TYPES>),
684 ViewSync(ViewSyncFinalizeCertificate<TYPES>),
686}
687
688impl<TYPES: NodeType> ViewChangeEvidence<TYPES> {
689 pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
691 match self {
692 ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
693 ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
694 }
695 }
696
697 pub fn to_evidence2(self) -> ViewChangeEvidence2<TYPES> {
699 match self {
700 ViewChangeEvidence::Timeout(timeout_cert) => {
701 ViewChangeEvidence2::Timeout(timeout_cert.to_tc2())
702 },
703 ViewChangeEvidence::ViewSync(view_sync_cert) => {
704 ViewChangeEvidence2::ViewSync(view_sync_cert.to_vsc2())
705 },
706 }
707 }
708}
709
710#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
713#[serde(bound(deserialize = ""))]
714pub enum ViewChangeEvidence2<TYPES: NodeType> {
715 Timeout(TimeoutCertificate2<TYPES>),
717 ViewSync(ViewSyncFinalizeCertificate2<TYPES>),
719}
720
721impl<TYPES: NodeType> ViewChangeEvidence2<TYPES> {
722 pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
724 match self {
725 ViewChangeEvidence2::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
726 ViewChangeEvidence2::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
727 }
728 }
729
730 pub fn to_evidence(self) -> ViewChangeEvidence<TYPES> {
732 match self {
733 ViewChangeEvidence2::Timeout(timeout_cert) => {
734 ViewChangeEvidence::Timeout(timeout_cert.to_tc())
735 },
736 ViewChangeEvidence2::ViewSync(view_sync_cert) => {
737 ViewChangeEvidence::ViewSync(view_sync_cert.to_vsc())
738 },
739 }
740 }
741}
742
743#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
745#[serde(bound(deserialize = ""))]
746pub struct QuorumProposal<TYPES: NodeType> {
747 pub block_header: TYPES::BlockHeader,
749
750 pub view_number: TYPES::View,
752
753 pub justify_qc: QuorumCertificate<TYPES>,
755
756 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
758
759 pub proposal_certificate: Option<ViewChangeEvidence<TYPES>>,
764}
765
766#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
768#[serde(bound(deserialize = ""))]
769pub struct QuorumProposal2<TYPES: NodeType> {
770 pub block_header: TYPES::BlockHeader,
772
773 pub view_number: TYPES::View,
775
776 pub epoch: Option<TYPES::Epoch>,
778
779 pub justify_qc: QuorumCertificate2<TYPES>,
781
782 pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
784
785 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
787
788 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
790
791 #[serde(with = "serde_bytes")]
796 pub next_drb_result: Option<DrbResult>,
797
798 pub state_cert: Option<LightClientStateUpdateCertificate<TYPES>>,
801}
802
803#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
805#[serde(bound(deserialize = ""))]
806pub struct QuorumProposalWrapper<TYPES: NodeType> {
807 pub proposal: QuorumProposal2<TYPES>,
809}
810
811impl<TYPES: NodeType> QuorumProposal2<TYPES> {
812 pub async fn validate_epoch<V: Versions>(
816 &self,
817 upgrade_lock: &UpgradeLock<TYPES, V>,
818 epoch_height: u64,
819 ) -> Result<()> {
820 let calculated_epoch = option_epoch_from_block_number::<TYPES>(
821 upgrade_lock.epochs_enabled(self.view_number()).await,
822 self.block_header.block_number(),
823 epoch_height,
824 );
825 ensure!(
826 calculated_epoch == self.epoch(),
827 "Quorum proposal invalid: inconsistent epoch."
828 );
829 Ok(())
830 }
831}
832
833impl<TYPES: NodeType> QuorumProposalWrapper<TYPES> {
834 pub fn block_header(&self) -> &TYPES::BlockHeader {
836 &self.proposal.block_header
837 }
838
839 pub fn view_number(&self) -> TYPES::View {
841 self.proposal.view_number
842 }
843
844 pub fn justify_qc(&self) -> &QuorumCertificate2<TYPES> {
846 &self.proposal.justify_qc
847 }
848
849 pub fn next_epoch_justify_qc(&self) -> &Option<NextEpochQuorumCertificate2<TYPES>> {
851 &self.proposal.next_epoch_justify_qc
852 }
853
854 pub fn upgrade_certificate(&self) -> &Option<UpgradeCertificate<TYPES>> {
856 &self.proposal.upgrade_certificate
857 }
858
859 pub fn view_change_evidence(&self) -> &Option<ViewChangeEvidence2<TYPES>> {
861 &self.proposal.view_change_evidence
862 }
863
864 pub fn next_drb_result(&self) -> &Option<DrbResult> {
866 &self.proposal.next_drb_result
867 }
868
869 pub async fn validate_epoch<V: Versions>(
873 &self,
874 upgrade_lock: &UpgradeLock<TYPES, V>,
875 epoch_height: u64,
876 ) -> Result<()> {
877 self.proposal
878 .validate_epoch(upgrade_lock, epoch_height)
879 .await
880 }
881
882 pub fn state_cert(&self) -> &Option<LightClientStateUpdateCertificate<TYPES>> {
884 &self.proposal.state_cert
885 }
886}
887
888impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposalWrapper<TYPES> {
889 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
890 Self {
891 proposal: quorum_proposal.into(),
892 }
893 }
894}
895
896impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposalWrapper<TYPES> {
897 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
898 Self {
899 proposal: quorum_proposal2,
900 }
901 }
902}
903
904impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal<TYPES> {
905 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
906 quorum_proposal_wrapper.proposal.into()
907 }
908}
909
910impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2<TYPES> {
911 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
912 quorum_proposal_wrapper.proposal
913 }
914}
915
916impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposal2<TYPES> {
917 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
918 Self {
919 block_header: quorum_proposal.block_header,
920 view_number: quorum_proposal.view_number,
921 epoch: None,
922 justify_qc: quorum_proposal.justify_qc.to_qc2(),
923 next_epoch_justify_qc: None,
924 upgrade_certificate: quorum_proposal.upgrade_certificate,
925 view_change_evidence: quorum_proposal
926 .proposal_certificate
927 .map(ViewChangeEvidence::to_evidence2),
928 next_drb_result: None,
929 state_cert: None,
930 }
931 }
932}
933
934impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal<TYPES> {
935 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
936 Self {
937 block_header: quorum_proposal2.block_header,
938 view_number: quorum_proposal2.view_number,
939 justify_qc: quorum_proposal2.justify_qc.to_qc(),
940 upgrade_certificate: quorum_proposal2.upgrade_certificate,
941 proposal_certificate: quorum_proposal2
942 .view_change_evidence
943 .map(ViewChangeEvidence2::to_evidence),
944 }
945 }
946}
947
948impl<TYPES: NodeType> From<Leaf<TYPES>> for Leaf2<TYPES> {
949 fn from(leaf: Leaf<TYPES>) -> Self {
950 let bytes: [u8; 32] = leaf.parent_commitment.into();
951
952 Self {
953 view_number: leaf.view_number,
954 justify_qc: leaf.justify_qc.to_qc2(),
955 next_epoch_justify_qc: None,
956 parent_commitment: Commitment::from_raw(bytes),
957 block_header: leaf.block_header,
958 upgrade_certificate: leaf.upgrade_certificate,
959 block_payload: leaf.block_payload,
960 view_change_evidence: None,
961 next_drb_result: None,
962 with_epoch: false,
963 }
964 }
965}
966
967impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal<TYPES> {
968 fn view_number(&self) -> TYPES::View {
969 self.view_number
970 }
971}
972
973impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal2<TYPES> {
974 fn view_number(&self) -> TYPES::View {
975 self.view_number
976 }
977}
978
979impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal<TYPES> {
980 fn view_number(&self) -> TYPES::View {
981 self.view_number
982 }
983}
984
985impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal2<TYPES> {
986 fn view_number(&self) -> TYPES::View {
987 self.view_number
988 }
989}
990
991impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposalWrapper<TYPES> {
992 fn view_number(&self) -> TYPES::View {
993 self.proposal.view_number
994 }
995}
996
997impl<TYPES: NodeType> HasViewNumber<TYPES> for UpgradeProposal<TYPES> {
998 fn view_number(&self) -> TYPES::View {
999 self.view_number
1000 }
1001}
1002
1003impl_has_epoch!(QuorumProposal2<TYPES>, DaProposal2<TYPES>);
1004
1005impl_has_none_epoch!(
1006 QuorumProposal<TYPES>,
1007 DaProposal<TYPES>,
1008 UpgradeProposal<TYPES>,
1009 ADVZDisperseShare<TYPES>
1010);
1011
1012impl<TYPES: NodeType> HasEpoch<TYPES> for QuorumProposalWrapper<TYPES> {
1013 #[allow(clippy::panic)]
1015 fn epoch(&self) -> Option<TYPES::Epoch> {
1016 self.proposal.epoch()
1017 }
1018}
1019
1020#[derive(Error, Debug, Serialize, Deserialize)]
1022pub enum BlockError {
1023 #[error("Invalid block header: {0}")]
1025 InvalidBlockHeader(String),
1026
1027 #[error("Inconsistent payload commitment")]
1029 InconsistentPayloadCommitment,
1030
1031 #[error("Failed to apply block header: {0}")]
1033 FailedHeaderApply(String),
1034}
1035
1036pub trait TestableLeaf {
1038 type NodeType: NodeType;
1040
1041 fn create_random_transaction(
1043 &self,
1044 rng: &mut dyn rand::RngCore,
1045 padding: u64,
1046 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction;
1047}
1048
1049#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1053#[serde(bound(deserialize = ""))]
1054pub struct Leaf<TYPES: NodeType> {
1055 view_number: TYPES::View,
1057
1058 justify_qc: QuorumCertificate<TYPES>,
1060
1061 parent_commitment: Commitment<Self>,
1064
1065 block_header: TYPES::BlockHeader,
1067
1068 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1070
1071 block_payload: Option<TYPES::BlockPayload>,
1075}
1076
1077#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1080#[serde(bound(deserialize = ""))]
1081pub struct Leaf2<TYPES: NodeType> {
1082 view_number: TYPES::View,
1084
1085 justify_qc: QuorumCertificate2<TYPES>,
1087
1088 next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1090
1091 parent_commitment: Commitment<Self>,
1094
1095 block_header: TYPES::BlockHeader,
1097
1098 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1100
1101 block_payload: Option<TYPES::BlockPayload>,
1105
1106 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1108
1109 #[serde(with = "serde_bytes")]
1114 pub next_drb_result: Option<DrbResult>,
1115
1116 pub with_epoch: bool,
1118}
1119
1120impl<TYPES: NodeType> Leaf2<TYPES> {
1121 #[must_use]
1128 pub async fn genesis<V: Versions>(
1129 validated_state: &TYPES::ValidatedState,
1130 instance_state: &TYPES::InstanceState,
1131 ) -> Self {
1132 let epoch = genesis_epoch_from_version::<V, TYPES>();
1133
1134 let (payload, metadata) =
1135 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1136 .await
1137 .unwrap();
1138
1139 let genesis_view = TYPES::View::genesis();
1140
1141 let block_header =
1142 TYPES::BlockHeader::genesis::<V>(instance_state, payload.clone(), &metadata);
1143
1144 let block_number = if V::Base::VERSION < V::Epochs::VERSION {
1145 None
1146 } else {
1147 Some(0u64)
1148 };
1149
1150 let null_quorum_data = QuorumData2 {
1151 leaf_commit: Commitment::<Leaf2<TYPES>>::default_commitment_no_preimage(),
1152 epoch,
1153 block_number,
1154 };
1155
1156 let justify_qc = QuorumCertificate2::new(
1157 null_quorum_data.clone(),
1158 null_quorum_data.commit(),
1159 genesis_view,
1160 None,
1161 PhantomData,
1162 );
1163
1164 Self {
1165 view_number: genesis_view,
1166 justify_qc,
1167 next_epoch_justify_qc: None,
1168 parent_commitment: null_quorum_data.leaf_commit,
1169 upgrade_certificate: None,
1170 block_header: block_header.clone(),
1171 block_payload: Some(payload),
1172 view_change_evidence: None,
1173 next_drb_result: None,
1174 with_epoch: epoch.is_some(),
1175 }
1176 }
1177 pub fn view_number(&self) -> TYPES::View {
1179 self.view_number
1180 }
1181 pub fn epoch(&self, epoch_height: u64) -> Option<TYPES::Epoch> {
1183 option_epoch_from_block_number::<TYPES>(
1184 self.with_epoch,
1185 self.block_header.block_number(),
1186 epoch_height,
1187 )
1188 }
1189 pub fn height(&self) -> u64 {
1193 self.block_header.block_number()
1194 }
1195 pub fn justify_qc(&self) -> QuorumCertificate2<TYPES> {
1197 self.justify_qc.clone()
1198 }
1199 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1201 self.upgrade_certificate.clone()
1202 }
1203 pub fn parent_commitment(&self) -> Commitment<Self> {
1205 self.parent_commitment
1206 }
1207 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1209 &self.block_header
1210 }
1211
1212 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1214 &mut self.block_header
1215 }
1216 pub fn fill_block_payload<V: Versions>(
1223 &mut self,
1224 block_payload: TYPES::BlockPayload,
1225 num_storage_nodes: usize,
1226 version: Version,
1227 ) -> std::result::Result<(), BlockError> {
1228 let encoded_txns = block_payload.encode();
1229 let commitment = vid_commitment::<V>(
1230 &encoded_txns,
1231 &self.block_header.metadata().encode(),
1232 num_storage_nodes,
1233 version,
1234 );
1235 if commitment != self.block_header.payload_commitment() {
1236 return Err(BlockError::InconsistentPayloadCommitment);
1237 }
1238 self.block_payload = Some(block_payload);
1239 Ok(())
1240 }
1241
1242 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1244 self.block_payload.take()
1245 }
1246
1247 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1250 self.block_payload = Some(block_payload);
1251 }
1252
1253 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1255 self.block_payload.clone()
1256 }
1257
1258 pub fn payload_commitment(&self) -> VidCommitment {
1260 self.block_header().payload_commitment()
1261 }
1262
1263 pub async fn extends_upgrade(
1271 &self,
1272 parent: &Self,
1273 decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1274 ) -> Result<()> {
1275 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1276 (None | Some(_), None) => {},
1280 (None, Some(parent_cert)) => {
1284 let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1285 ensure!(
1286 self.view_number() > parent_cert.data.new_version_first_view
1287 || (self.view_number() > parent_cert.data.decide_by
1288 && decided_upgrade_certificate_read.is_none()),
1289 "The new leaf is missing an upgrade certificate that was present in its \
1290 parent, and should still be live."
1291 );
1292 },
1293 (Some(cert), Some(parent_cert)) => {
1297 ensure!(
1298 cert == parent_cert,
1299 "The new leaf does not extend the parent leaf, because it has attached a \
1300 different upgrade certificate."
1301 );
1302 },
1303 }
1304
1305 Ok(())
1309 }
1310
1311 pub fn to_leaf_unsafe(self) -> Leaf<TYPES> {
1313 let bytes: [u8; 32] = self.parent_commitment.into();
1314
1315 Leaf {
1316 view_number: self.view_number,
1317 justify_qc: self.justify_qc.to_qc(),
1318 parent_commitment: Commitment::from_raw(bytes),
1319 block_header: self.block_header,
1320 upgrade_certificate: self.upgrade_certificate,
1321 block_payload: self.block_payload,
1322 }
1323 }
1324}
1325
1326impl<TYPES: NodeType> Committable for Leaf2<TYPES> {
1327 fn commit(&self) -> committable::Commitment<Self> {
1328 let Leaf2 {
1329 view_number,
1330 justify_qc,
1331 next_epoch_justify_qc,
1332 parent_commitment,
1333 block_header,
1334 upgrade_certificate,
1335 block_payload: _,
1336 view_change_evidence,
1337 next_drb_result,
1338 with_epoch,
1339 } = self;
1340
1341 let mut cb = RawCommitmentBuilder::new("leaf commitment")
1342 .u64_field("view number", **view_number)
1343 .field("parent leaf commitment", *parent_commitment)
1344 .field("block header", block_header.commit())
1345 .field("justify qc", justify_qc.commit())
1346 .optional("upgrade certificate", upgrade_certificate);
1347
1348 if *with_epoch {
1349 cb = cb
1350 .constant_str("with_epoch")
1351 .optional("next_epoch_justify_qc", next_epoch_justify_qc);
1352
1353 if let Some(next_drb_result) = next_drb_result {
1354 cb = cb
1355 .constant_str("next_drb_result")
1356 .fixed_size_bytes(next_drb_result);
1357 }
1358
1359 match view_change_evidence {
1360 Some(ViewChangeEvidence2::Timeout(cert)) => {
1361 cb = cb.field("timeout cert", cert.commit());
1362 },
1363 Some(ViewChangeEvidence2::ViewSync(cert)) => {
1364 cb = cb.field("viewsync cert", cert.commit());
1365 },
1366 None => {},
1367 }
1368 }
1369
1370 cb.finalize()
1371 }
1372}
1373
1374impl<TYPES: NodeType> Leaf<TYPES> {
1375 #[allow(clippy::unused_async)]
1376 pub async fn commit<V: Versions>(
1379 &self,
1380 _upgrade_lock: &UpgradeLock<TYPES, V>,
1381 ) -> Commitment<Self> {
1382 <Self as Committable>::commit(self)
1383 }
1384}
1385
1386impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
1387 fn eq(&self, other: &Self) -> bool {
1388 self.view_number == other.view_number
1389 && self.justify_qc == other.justify_qc
1390 && self.parent_commitment == other.parent_commitment
1391 && self.block_header == other.block_header
1392 }
1393}
1394
1395impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
1396 fn eq(&self, other: &Self) -> bool {
1397 let Leaf2 {
1398 view_number,
1399 justify_qc,
1400 next_epoch_justify_qc,
1401 parent_commitment,
1402 block_header,
1403 upgrade_certificate,
1404 block_payload: _,
1405 view_change_evidence,
1406 next_drb_result,
1407 with_epoch,
1408 } = self;
1409
1410 *view_number == other.view_number
1411 && *justify_qc == other.justify_qc
1412 && *next_epoch_justify_qc == other.next_epoch_justify_qc
1413 && *parent_commitment == other.parent_commitment
1414 && *block_header == other.block_header
1415 && *upgrade_certificate == other.upgrade_certificate
1416 && *view_change_evidence == other.view_change_evidence
1417 && *next_drb_result == other.next_drb_result
1418 && *with_epoch == other.with_epoch
1419 }
1420}
1421
1422impl<TYPES: NodeType> Hash for Leaf<TYPES> {
1423 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1424 self.view_number.hash(state);
1425 self.justify_qc.hash(state);
1426 self.parent_commitment.hash(state);
1427 self.block_header.hash(state);
1428 }
1429}
1430
1431impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
1432 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1433 self.commit().hash(state);
1434 self.view_number.hash(state);
1435 self.justify_qc.hash(state);
1436 self.parent_commitment.hash(state);
1437 self.block_header.hash(state);
1438 }
1439}
1440
1441impl<TYPES: NodeType> Display for Leaf<TYPES> {
1442 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1443 write!(
1444 f,
1445 "view: {:?}, height: {:?}, justify: {}",
1446 self.view_number,
1447 self.height(),
1448 self.justify_qc
1449 )
1450 }
1451}
1452
1453impl<TYPES: NodeType> QuorumCertificate<TYPES> {
1454 #[must_use]
1455 pub async fn genesis<V: Versions>(
1457 validated_state: &TYPES::ValidatedState,
1458 instance_state: &TYPES::InstanceState,
1459 ) -> Self {
1460 let upgrade_lock = UpgradeLock::<TYPES, V>::new();
1462
1463 let genesis_view = <TYPES::View as ConsensusTime>::genesis();
1464
1465 let data = QuorumData {
1466 leaf_commit: Leaf::genesis::<V>(validated_state, instance_state)
1467 .await
1468 .commit(&upgrade_lock)
1469 .await,
1470 };
1471
1472 let versioned_data =
1473 VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1474 .await;
1475
1476 let bytes: [u8; 32] = versioned_data.commit().into();
1477
1478 Self::new(
1479 data,
1480 Commitment::from_raw(bytes),
1481 genesis_view,
1482 None,
1483 PhantomData,
1484 )
1485 }
1486}
1487
1488impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
1489 #[must_use]
1490 pub async fn genesis<V: Versions>(
1492 validated_state: &TYPES::ValidatedState,
1493 instance_state: &TYPES::InstanceState,
1494 ) -> Self {
1495 let upgrade_lock = UpgradeLock::<TYPES, V>::new();
1497
1498 let genesis_view = <TYPES::View as ConsensusTime>::genesis();
1499
1500 let genesis_leaf = Leaf2::genesis::<V>(validated_state, instance_state).await;
1501 let block_number = if upgrade_lock.epochs_enabled(genesis_view).await {
1502 Some(genesis_leaf.height())
1503 } else {
1504 None
1505 };
1506 let data = QuorumData2 {
1507 leaf_commit: genesis_leaf.commit(),
1508 epoch: genesis_epoch_from_version::<V, TYPES>(), block_number,
1510 };
1511
1512 let versioned_data =
1513 VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1514 .await;
1515
1516 let bytes: [u8; 32] = versioned_data.commit().into();
1517
1518 Self::new(
1519 data,
1520 Commitment::from_raw(bytes),
1521 genesis_view,
1522 None,
1523 PhantomData,
1524 )
1525 }
1526}
1527
1528impl<TYPES: NodeType> Leaf<TYPES> {
1529 #[must_use]
1536 pub async fn genesis<V: Versions>(
1537 validated_state: &TYPES::ValidatedState,
1538 instance_state: &TYPES::InstanceState,
1539 ) -> Self {
1540 let (payload, metadata) =
1541 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1542 .await
1543 .unwrap();
1544
1545 let genesis_view = TYPES::View::genesis();
1546
1547 let block_header =
1548 TYPES::BlockHeader::genesis::<V>(instance_state, payload.clone(), &metadata);
1549
1550 let null_quorum_data = QuorumData {
1551 leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
1552 };
1553
1554 let justify_qc = QuorumCertificate::new(
1555 null_quorum_data.clone(),
1556 null_quorum_data.commit(),
1557 genesis_view,
1558 None,
1559 PhantomData,
1560 );
1561
1562 Self {
1563 view_number: genesis_view,
1564 justify_qc,
1565 parent_commitment: null_quorum_data.leaf_commit,
1566 upgrade_certificate: None,
1567 block_header: block_header.clone(),
1568 block_payload: Some(payload),
1569 }
1570 }
1571
1572 pub fn view_number(&self) -> TYPES::View {
1574 self.view_number
1575 }
1576 pub fn height(&self) -> u64 {
1580 self.block_header.block_number()
1581 }
1582 pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
1584 self.justify_qc.clone()
1585 }
1586 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1588 self.upgrade_certificate.clone()
1589 }
1590 pub fn parent_commitment(&self) -> Commitment<Self> {
1592 self.parent_commitment
1593 }
1594 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1596 &self.block_header
1597 }
1598
1599 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1601 &mut self.block_header
1602 }
1603 pub fn fill_block_payload<V: Versions>(
1610 &mut self,
1611 block_payload: TYPES::BlockPayload,
1612 num_storage_nodes: usize,
1613 version: Version,
1614 ) -> std::result::Result<(), BlockError> {
1615 let encoded_txns = block_payload.encode();
1616 let commitment = vid_commitment::<V>(
1617 &encoded_txns,
1618 &self.block_header.metadata().encode(),
1619 num_storage_nodes,
1620 version,
1621 );
1622 if commitment != self.block_header.payload_commitment() {
1623 return Err(BlockError::InconsistentPayloadCommitment);
1624 }
1625 self.block_payload = Some(block_payload);
1626 Ok(())
1627 }
1628
1629 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1631 self.block_payload.take()
1632 }
1633
1634 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1637 self.block_payload = Some(block_payload);
1638 }
1639
1640 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1642 self.block_payload.clone()
1643 }
1644
1645 pub fn payload_commitment(&self) -> VidCommitment {
1647 self.block_header().payload_commitment()
1648 }
1649
1650 pub async fn extends_upgrade(
1658 &self,
1659 parent: &Self,
1660 decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1661 ) -> Result<()> {
1662 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1663 (None | Some(_), None) => {},
1667 (None, Some(parent_cert)) => {
1671 let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1672 ensure!(
1673 self.view_number() > parent_cert.data.new_version_first_view
1674 || (self.view_number() > parent_cert.data.decide_by
1675 && decided_upgrade_certificate_read.is_none()),
1676 "The new leaf is missing an upgrade certificate that was present in its \
1677 parent, and should still be live."
1678 );
1679 },
1680 (Some(cert), Some(parent_cert)) => {
1684 ensure!(
1685 cert == parent_cert,
1686 "The new leaf does not extend the parent leaf, because it has attached a \
1687 different upgrade certificate."
1688 );
1689 },
1690 }
1691
1692 Ok(())
1696 }
1697}
1698
1699impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
1700where
1701 TYPES::ValidatedState: TestableState<TYPES>,
1702 TYPES::BlockPayload: TestableBlock<TYPES>,
1703{
1704 type NodeType = TYPES;
1705
1706 fn create_random_transaction(
1707 &self,
1708 rng: &mut dyn rand::RngCore,
1709 padding: u64,
1710 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
1711 {
1712 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
1713 }
1714}
1715impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
1716where
1717 TYPES::ValidatedState: TestableState<TYPES>,
1718 TYPES::BlockPayload: TestableBlock<TYPES>,
1719{
1720 type NodeType = TYPES;
1721
1722 fn create_random_transaction(
1723 &self,
1724 rng: &mut dyn rand::RngCore,
1725 padding: u64,
1726 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
1727 {
1728 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
1729 }
1730}
1731#[must_use]
1733pub fn fake_commitment<S: Committable>() -> Commitment<S> {
1734 RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
1735}
1736
1737#[must_use]
1739pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
1740 let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
1741 RawCommitmentBuilder::new("Random Commitment")
1742 .constant_str("Random Field")
1743 .var_size_bytes(&random_array)
1744 .finalize()
1745}
1746
1747pub fn serialize_signature2<TYPES: NodeType>(
1751 signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
1752) -> Vec<u8> {
1753 let mut signatures_bytes = vec![];
1754 signatures_bytes.extend("Yes".as_bytes());
1755
1756 let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
1757 let proof_bytes = bincode_opts()
1758 .serialize(&proof.as_bitslice())
1759 .expect("This serialization shouldn't be able to fail");
1760 signatures_bytes.extend("bitvec proof".as_bytes());
1761 signatures_bytes.extend(proof_bytes.as_slice());
1762 let sig_bytes = bincode_opts()
1763 .serialize(&sig)
1764 .expect("This serialization shouldn't be able to fail");
1765 signatures_bytes.extend("aggregated signature".as_bytes());
1766 signatures_bytes.extend(sig_bytes.as_slice());
1767 signatures_bytes
1768}
1769
1770impl<TYPES: NodeType> Committable for Leaf<TYPES> {
1771 fn commit(&self) -> committable::Commitment<Self> {
1772 RawCommitmentBuilder::new("leaf commitment")
1773 .u64_field("view number", *self.view_number)
1774 .field("parent leaf commitment", self.parent_commitment)
1775 .field("block header", self.block_header.commit())
1776 .field("justify qc", self.justify_qc.commit())
1777 .optional("upgrade certificate", &self.upgrade_certificate)
1778 .finalize()
1779 }
1780}
1781
1782impl<TYPES: NodeType> Leaf2<TYPES> {
1783 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
1785 let QuorumProposalWrapper {
1788 proposal:
1789 QuorumProposal2 {
1790 view_number,
1791 epoch,
1792 justify_qc,
1793 next_epoch_justify_qc,
1794 block_header,
1795 upgrade_certificate,
1796 view_change_evidence,
1797 next_drb_result,
1798 state_cert: _,
1799 },
1800 } = quorum_proposal;
1801
1802 Self {
1803 view_number: *view_number,
1804 justify_qc: justify_qc.clone(),
1805 next_epoch_justify_qc: next_epoch_justify_qc.clone(),
1806 parent_commitment: justify_qc.data().leaf_commit,
1807 block_header: block_header.clone(),
1808 upgrade_certificate: upgrade_certificate.clone(),
1809 block_payload: None,
1810 view_change_evidence: view_change_evidence.clone(),
1811 next_drb_result: *next_drb_result,
1812 with_epoch: epoch.is_some(),
1813 }
1814 }
1815}
1816
1817impl<TYPES: NodeType> Leaf<TYPES> {
1818 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
1820 let QuorumProposal {
1823 view_number,
1824 justify_qc,
1825 block_header,
1826 upgrade_certificate,
1827 proposal_certificate: _,
1828 } = quorum_proposal;
1829
1830 Self {
1831 view_number: *view_number,
1832 justify_qc: justify_qc.clone(),
1833 parent_commitment: justify_qc.data().leaf_commit,
1834 block_header: block_header.clone(),
1835 upgrade_certificate: upgrade_certificate.clone(),
1836 block_payload: None,
1837 }
1838 }
1839}
1840
1841pub mod null_block {
1842 #![allow(missing_docs)]
1843
1844 use jf_vid::VidScheme;
1845 use vbs::version::StaticVersionType;
1846
1847 use crate::{
1848 data::VidCommitment,
1849 traits::{
1850 block_contents::BuilderFee,
1851 node_implementation::{NodeType, Versions},
1852 signature_key::BuilderSignatureKey,
1853 BlockPayload,
1854 },
1855 vid::advz::advz_scheme,
1856 };
1857
1858 #[must_use]
1867 pub fn commitment<V: Versions>(num_storage_nodes: usize) -> Option<VidCommitment> {
1868 let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
1869
1870 match vid_result {
1871 Ok(r) => Some(VidCommitment::V0(r)),
1872 Err(_) => None,
1873 }
1874 }
1875
1876 #[must_use]
1878 pub fn builder_fee<TYPES: NodeType, V: Versions>(
1879 num_storage_nodes: usize,
1880 version: vbs::version::Version,
1881 ) -> Option<BuilderFee<TYPES>> {
1882 const FEE_AMOUNT: u64 = 0;
1884
1885 let (pub_key, priv_key) =
1886 <TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
1887 [0_u8; 32], 0,
1888 );
1889
1890 if version >= V::Epochs::VERSION {
1891 let (_null_block, null_block_metadata) =
1892 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
1893
1894 match TYPES::BuilderSignatureKey::sign_fee(&priv_key, FEE_AMOUNT, &null_block_metadata)
1895 {
1896 Ok(sig) => Some(BuilderFee {
1897 fee_amount: FEE_AMOUNT,
1898 fee_account: pub_key,
1899 fee_signature: sig,
1900 }),
1901 Err(_) => None,
1902 }
1903 } else {
1904 let (_null_block, null_block_metadata) =
1905 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
1906
1907 match TYPES::BuilderSignatureKey::sign_fee_with_vid_commitment(
1908 &priv_key,
1909 FEE_AMOUNT,
1910 &null_block_metadata,
1911 &commitment::<V>(num_storage_nodes)?,
1912 ) {
1913 Ok(sig) => Some(BuilderFee {
1914 fee_amount: FEE_AMOUNT,
1915 fee_account: pub_key,
1916 fee_signature: sig,
1917 }),
1918 Err(_) => None,
1919 }
1920 }
1921 }
1922}
1923
1924#[derive(Debug, Eq, PartialEq, Clone)]
1926pub struct PackedBundle<TYPES: NodeType> {
1927 pub encoded_transactions: Arc<[u8]>,
1929
1930 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
1932
1933 pub view_number: TYPES::View,
1935
1936 pub epoch_number: Option<TYPES::Epoch>,
1938
1939 pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
1941}
1942
1943impl<TYPES: NodeType> PackedBundle<TYPES> {
1944 pub fn new(
1946 encoded_transactions: Arc<[u8]>,
1947 metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
1948 view_number: TYPES::View,
1949 epoch_number: Option<TYPES::Epoch>,
1950 sequencing_fees: Vec1<BuilderFee<TYPES>>,
1951 ) -> Self {
1952 Self {
1953 encoded_transactions,
1954 metadata,
1955 view_number,
1956 epoch_number,
1957 sequencing_fees,
1958 }
1959 }
1960}
1961
1962#[cfg(test)]
1963mod test {
1964 use super::*;
1965
1966 #[test]
1967 fn test_vid_commitment_display() {
1968 let vc = VidCommitment::V0(ADVZCommitment::default());
1969 assert_eq!(
1970 format!("{vc}"),
1971 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
1972 );
1973 assert_eq!(
1974 format!("{vc:?}"),
1975 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
1976 );
1977
1978 let vc = VidCommitment::V1(AvidMCommitment {
1979 commit: Default::default(),
1980 });
1981 assert_eq!(
1982 format!("{vc}"),
1983 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
1984 );
1985 assert_eq!(
1986 format!("{vc:?}"),
1987 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
1988 );
1989 }
1990}