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_advz::VidScheme;
25use rand::Rng;
26use serde::{Deserialize, Serialize};
27use tagged_base64::{TaggedBase64, Tb64Error};
28use thiserror::Error;
29use vbs::version::Version;
30use vec1::Vec1;
31use versions::{Upgrade, EPOCH_VERSION, VID2_UPGRADE_VERSION};
32
33use crate::{
34 drb::DrbResult,
35 epoch_membership::EpochMembershipCoordinator,
36 message::{convert_proposal, Proposal, UpgradeLock},
37 simple_certificate::{
38 LightClientStateUpdateCertificateV1, LightClientStateUpdateCertificateV2,
39 NextEpochQuorumCertificate2, QuorumCertificate, QuorumCertificate2, TimeoutCertificate,
40 TimeoutCertificate2, UpgradeCertificate, ViewSyncFinalizeCertificate,
41 ViewSyncFinalizeCertificate2,
42 },
43 simple_vote::{HasEpoch, QuorumData, QuorumData2, UpgradeProposalData, VersionedVoteData},
44 traits::{
45 block_contents::{BlockHeader, BuilderFee, EncodeBytes, TestableBlock},
46 node_implementation::NodeType,
47 signature_key::SignatureKey,
48 states::TestableState,
49 BlockPayload,
50 },
51 utils::{
52 bincode_opts, genesis_epoch_from_version, option_epoch_from_block_number,
53 EpochTransitionIndicator,
54 },
55 vid::{
56 advz::{advz_scheme, ADVZScheme},
57 avidm::{init_avidm_param, AvidMScheme},
58 avidm_gf2::{init_avidm_gf2_param, AvidmGf2Scheme},
59 },
60 vote::{Certificate, HasViewNumber},
61};
62
63macro_rules! impl_u64_wrapper {
66 ($t:ty, $genesis_val:expr) => {
67 impl $t {
68 pub fn genesis() -> Self {
70 Self($genesis_val)
71 }
72 pub fn new(n: u64) -> Self {
74 Self(n)
75 }
76 pub fn u64(&self) -> u64 {
78 self.0
79 }
80 }
81
82 impl From<u64> for $t {
83 fn from(n: u64) -> Self {
84 Self(n)
85 }
86 }
87
88 impl From<$t> for u64 {
89 fn from(n: $t) -> Self {
90 n.0
91 }
92 }
93
94 impl Display for $t {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 write!(f, "{}", self.0)
97 }
98 }
99
100 impl std::ops::Add<u64> for $t {
101 type Output = $t;
102
103 fn add(self, rhs: u64) -> Self::Output {
104 Self(self.0 + rhs)
105 }
106 }
107
108 impl std::ops::AddAssign<u64> for $t {
109 fn add_assign(&mut self, rhs: u64) {
110 self.0 += rhs;
111 }
112 }
113
114 impl std::ops::Deref for $t {
115 type Target = u64;
116
117 fn deref(&self) -> &Self::Target {
118 &self.0
119 }
120 }
121
122 impl std::ops::Sub<u64> for $t {
123 type Output = $t;
124 fn sub(self, rhs: u64) -> Self::Output {
125 Self(self.0 - rhs)
126 }
127 }
128 };
129}
130
131#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
133pub struct ViewNumber(u64);
134
135impl Committable for ViewNumber {
136 fn commit(&self) -> Commitment<Self> {
137 let builder = RawCommitmentBuilder::new("View Number Commitment");
138 builder.u64(self.0).finalize()
139 }
140}
141
142impl_u64_wrapper!(ViewNumber, 0u64);
143
144#[derive(
146 Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
147)]
148pub struct EpochNumber(u64);
149
150impl Committable for EpochNumber {
151 fn commit(&self) -> Commitment<Self> {
152 let builder = RawCommitmentBuilder::new("Epoch Number Commitment");
153 builder.u64(self.0).finalize()
154 }
155}
156
157impl_u64_wrapper!(EpochNumber, 1u64);
158
159#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
161#[serde(bound = "TYPES: NodeType")]
162pub struct DaProposal<TYPES: NodeType> {
163 pub encoded_transactions: Arc<[u8]>,
165 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
167 pub view_number: ViewNumber,
169}
170
171#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
173#[serde(bound = "TYPES: NodeType")]
174pub struct DaProposal2<TYPES: NodeType> {
175 pub encoded_transactions: Arc<[u8]>,
177 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
179 pub view_number: ViewNumber,
181 pub epoch: Option<EpochNumber>,
183 pub epoch_transition_indicator: EpochTransitionIndicator,
186}
187
188impl<TYPES: NodeType> From<DaProposal<TYPES>> for DaProposal2<TYPES> {
189 fn from(da_proposal: DaProposal<TYPES>) -> Self {
190 Self {
191 encoded_transactions: da_proposal.encoded_transactions,
192 metadata: da_proposal.metadata,
193 view_number: da_proposal.view_number,
194 epoch: None,
195 epoch_transition_indicator: EpochTransitionIndicator::NotInTransition,
196 }
197 }
198}
199
200impl<TYPES: NodeType> From<DaProposal2<TYPES>> for DaProposal<TYPES> {
201 fn from(da_proposal2: DaProposal2<TYPES>) -> Self {
202 Self {
203 encoded_transactions: da_proposal2.encoded_transactions,
204 metadata: da_proposal2.metadata,
205 view_number: da_proposal2.view_number,
206 }
207 }
208}
209
210#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
212pub struct UpgradeProposal {
213 pub upgrade_proposal: UpgradeProposalData,
215 pub view_number: ViewNumber,
217}
218
219pub type VidCommitment0 = crate::vid::advz::ADVZCommitment;
221pub type VidCommitment1 = crate::vid::avidm::AvidMCommitment;
222pub type VidCommitment2 = crate::vid::avidm_gf2::AvidmGf2Commitment;
223
224#[derive(Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
226#[serde(
227 try_from = "tagged_base64::TaggedBase64",
228 into = "tagged_base64::TaggedBase64"
229)]
230pub enum VidCommitment {
231 V0(VidCommitment0),
232 V1(VidCommitment1),
233 V2(VidCommitment2),
234}
235
236impl Default for VidCommitment {
237 fn default() -> Self {
238 Self::V0(Default::default())
239 }
240}
241
242impl Display for VidCommitment {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 std::write!(f, "{}", TaggedBase64::from(self))
245 }
246}
247
248impl Debug for VidCommitment {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 std::fmt::Display::fmt(self, f)
251 }
252}
253
254impl From<VidCommitment> for TaggedBase64 {
255 fn from(val: VidCommitment) -> Self {
256 match val {
257 VidCommitment::V0(comm) => comm.into(),
258 VidCommitment::V1(comm) => comm.into(),
259 VidCommitment::V2(comm) => comm.into(),
260 }
261 }
262}
263
264impl From<&VidCommitment> for TaggedBase64 {
265 fn from(val: &VidCommitment) -> Self {
266 match val {
267 VidCommitment::V0(comm) => comm.into(),
268 VidCommitment::V1(comm) => comm.into(),
269 VidCommitment::V2(comm) => comm.into(),
270 }
271 }
272}
273
274impl TryFrom<TaggedBase64> for VidCommitment {
275 type Error = tagged_base64::Tb64Error;
276
277 fn try_from(value: TaggedBase64) -> std::result::Result<Self, Self::Error> {
278 match value.tag().as_str() {
279 "HASH" => VidCommitment0::try_from(value).map(Self::V0),
280 "AvidMCommit" => VidCommitment1::try_from(value).map(Self::V1),
281 "AvidmGf2Commit" => VidCommitment2::try_from(value).map(Self::V2),
282 _ => Err(Tb64Error::InvalidTag),
283 }
284 }
285}
286
287impl<'a> TryFrom<&'a TaggedBase64> for VidCommitment {
288 type Error = tagged_base64::Tb64Error;
289
290 fn try_from(value: &'a TaggedBase64) -> std::result::Result<Self, Self::Error> {
291 match value.tag().as_str() {
292 "HASH" => VidCommitment0::try_from(value).map(Self::V0),
293 "AvidMCommit" => VidCommitment1::try_from(value).map(Self::V1),
294 "AvidmGf2Commit" => VidCommitment2::try_from(value).map(Self::V2),
295 _ => Err(Tb64Error::InvalidTag),
296 }
297 }
298}
299
300impl std::str::FromStr for VidCommitment {
301 type Err = tagged_base64::Tb64Error;
302 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
303 use core::convert::TryFrom;
304 Self::try_from(TaggedBase64::from_str(s)?)
305 .map_err(|_| tagged_base64::Tb64Error::InvalidData)
306 }
307}
308
309impl From<VidCommitment1> for VidCommitment {
317 fn from(comm: VidCommitment1) -> Self {
318 Self::V1(comm)
319 }
320}
321
322impl From<VidCommitment2> for VidCommitment {
323 fn from(comm: VidCommitment2) -> Self {
324 Self::V2(comm)
325 }
326}
327
328impl AsRef<[u8]> for VidCommitment {
329 fn as_ref(&self) -> &[u8] {
330 match self {
331 Self::V0(comm) => comm.as_ref(),
332 Self::V1(comm) => comm.as_ref(),
333 Self::V2(comm) => comm.as_ref(),
334 }
335 }
336}
337
338impl AsRef<[u8; 32]> for VidCommitment {
339 fn as_ref(&self) -> &[u8; 32] {
340 match self {
341 Self::V0(comm) => comm.as_ref().as_ref(),
342 Self::V1(comm) => comm.as_ref(),
343 Self::V2(comm) => comm.as_ref(),
344 }
345 }
346}
347
348#[must_use]
353#[allow(clippy::panic)]
354pub fn vid_commitment(
355 encoded_transactions: &[u8],
356 metadata: &[u8],
357 total_weight: usize,
358 version: Version,
359) -> VidCommitment {
360 if version < EPOCH_VERSION {
361 let encoded_tx_len = encoded_transactions.len();
362 advz_scheme(total_weight)
363 .commit_only(encoded_transactions)
364 .map(VidCommitment::V0)
365 .unwrap_or_else(|err| {
366 panic!(
367 "VidScheme::commit_only \
368 failure:(total_weight,payload_byte_len)=({total_weight},{encoded_tx_len}) \
369 error: {err}"
370 )
371 })
372 } else if version < VID2_UPGRADE_VERSION {
373 let param = init_avidm_param(total_weight).unwrap();
374 let encoded_tx_len = encoded_transactions.len();
375 AvidMScheme::commit(
376 ¶m,
377 encoded_transactions,
378 ns_table::parse_ns_table(encoded_tx_len, metadata),
379 )
380 .map(VidCommitment::V1)
381 .unwrap()
382 } else {
383 let param = init_avidm_gf2_param(total_weight).unwrap();
384 let encoded_tx_len = encoded_transactions.len();
385 AvidmGf2Scheme::commit(
386 ¶m,
387 encoded_transactions,
388 ns_table::parse_ns_table(encoded_tx_len, metadata),
389 )
390 .map(|(comm, _)| VidCommitment::V2(comm))
391 .unwrap()
392 }
393}
394
395pub type VidCommon0 = crate::vid::advz::ADVZCommon;
397pub type VidCommon1 = crate::vid::avidm::AvidMCommon;
398pub type VidCommon2 = crate::vid::avidm_gf2::AvidmGf2Common;
399
400#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
402pub enum VidCommon {
403 V0(VidCommon0),
404 V1(VidCommon1),
405 V2(VidCommon2),
406}
407
408impl From<VidCommon1> for VidCommon {
409 fn from(comm: VidCommon1) -> Self {
410 Self::V1(comm)
411 }
412}
413
414impl From<VidCommon2> for VidCommon {
415 fn from(comm: VidCommon2) -> Self {
416 Self::V2(comm)
417 }
418}
419
420#[derive(Clone, Copy, Debug, Eq, PartialEq)]
422pub enum VidCommonRef<'a> {
423 V0(&'a VidCommon0),
424 V1(&'a VidCommon1),
425 V2(&'a VidCommon2),
426}
427
428impl<'a> VidCommonRef<'a> {
429 pub fn is_consistent(&self, comm: &VidCommitment) -> bool {
430 match (self, comm) {
431 (Self::V0(common), VidCommitment::V0(comm)) => {
432 ADVZScheme::is_consistent(comm, common).is_ok()
433 },
434 (Self::V1(_), VidCommitment::V1(_)) => true,
435 (Self::V2(common), VidCommitment::V2(comm)) => {
436 AvidmGf2Scheme::is_consistent(comm, common)
437 },
438 _ => false,
439 }
440 }
441}
442
443impl VidCommon {
444 pub fn as_ref(&self) -> VidCommonRef<'_> {
445 match self {
446 Self::V0(c) => VidCommonRef::V0(c),
447 Self::V1(c) => VidCommonRef::V1(c),
448 Self::V2(c) => VidCommonRef::V2(c),
449 }
450 }
451
452 pub fn is_consistent(&self, comm: &VidCommitment) -> bool {
453 self.as_ref().is_consistent(comm)
454 }
455}
456
457pub type VidShare0 = crate::vid::advz::ADVZShare;
459pub type VidShare1 = crate::vid::avidm::AvidMShare;
460pub type VidShare2 = crate::vid::avidm_gf2::AvidmGf2Share;
461
462#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
464pub enum VidShare {
465 V0(VidShare0),
466 V1(VidShare1),
467 V2(VidShare2),
468}
469
470impl From<VidShare1> for VidShare {
478 fn from(share: VidShare1) -> Self {
479 Self::V1(share)
480 }
481}
482
483impl From<VidShare2> for VidShare {
484 fn from(share: VidShare2) -> Self {
485 Self::V2(share)
486 }
487}
488
489pub mod ns_table;
490pub mod vid_disperse;
491
492pub struct VidDisperseAndDuration<TYPES: NodeType> {
494 pub disperse: VidDisperse<TYPES>,
496 pub duration: Duration,
498}
499
500pub type VidDisperse0<TYPES> = vid_disperse::ADVZDisperse<TYPES>;
502pub type VidDisperse1<TYPES> = vid_disperse::AvidMDisperse<TYPES>;
503pub type VidDisperse2<TYPES> = vid_disperse::AvidmGf2Disperse<TYPES>;
504
505#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
511#[serde(bound = "TYPES: NodeType")]
512pub enum VidDisperse<TYPES: NodeType> {
513 V0(VidDisperse0<TYPES>),
515 V1(VidDisperse1<TYPES>),
517 V2(VidDisperse2<TYPES>),
519}
520
521impl<TYPES: NodeType> From<VidDisperse0<TYPES>> for VidDisperse<TYPES> {
522 fn from(disperse: VidDisperse0<TYPES>) -> Self {
523 Self::V0(disperse)
524 }
525}
526
527impl<TYPES: NodeType> From<VidDisperse1<TYPES>> for VidDisperse<TYPES> {
528 fn from(disperse: VidDisperse1<TYPES>) -> Self {
529 Self::V1(disperse)
530 }
531}
532
533impl<TYPES: NodeType> From<VidDisperse2<TYPES>> for VidDisperse<TYPES> {
534 fn from(disperse: VidDisperse2<TYPES>) -> Self {
535 Self::V2(disperse)
536 }
537}
538
539impl<TYPES: NodeType> HasViewNumber for VidDisperse<TYPES> {
540 fn view_number(&self) -> ViewNumber {
541 match self {
542 Self::V0(disperse) => disperse.view_number(),
543 Self::V1(disperse) => disperse.view_number(),
544 Self::V2(disperse) => disperse.view_number(),
545 }
546 }
547}
548
549impl<TYPES: NodeType> HasEpoch for VidDisperse<TYPES> {
550 fn epoch(&self) -> Option<EpochNumber> {
551 match self {
552 Self::V0(disperse) => disperse.epoch(),
553 Self::V1(disperse) => disperse.epoch(),
554 Self::V2(disperse) => disperse.epoch(),
555 }
556 }
557}
558
559impl<TYPES: NodeType> VidDisperse<TYPES> {
560 #[allow(clippy::panic)]
566 pub async fn calculate_vid_disperse(
567 payload: &TYPES::BlockPayload,
568 membership: &EpochMembershipCoordinator<TYPES>,
569 view: ViewNumber,
570 target_epoch: Option<EpochNumber>,
571 data_epoch: Option<EpochNumber>,
572 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
573 upgrade_lock: &UpgradeLock<TYPES>,
574 ) -> Result<VidDisperseAndDuration<TYPES>> {
575 let epochs_enabled = upgrade_lock.epochs_enabled(view).await;
576 let upgraded_vid2 = upgrade_lock.upgraded_vid2(view).await;
577 if upgraded_vid2 {
578 VidDisperse2::calculate_vid_disperse(
579 payload,
580 membership,
581 view,
582 target_epoch,
583 data_epoch,
584 metadata,
585 )
586 .await
587 .map(|(disperse, duration)| VidDisperseAndDuration {
588 disperse: Self::V2(disperse),
589 duration,
590 })
591 } else if epochs_enabled {
592 VidDisperse1::calculate_vid_disperse(
593 payload,
594 membership,
595 view,
596 target_epoch,
597 data_epoch,
598 metadata,
599 )
600 .await
601 .map(|(disperse, duration)| VidDisperseAndDuration {
602 disperse: Self::V1(disperse),
603 duration,
604 })
605 } else {
606 VidDisperse0::calculate_vid_disperse(
607 payload,
608 membership,
609 view,
610 target_epoch,
611 data_epoch,
612 )
613 .await
614 .map(|(disperse, duration)| VidDisperseAndDuration {
615 disperse: Self::V0(disperse),
616 duration,
617 })
618 }
619 }
620
621 pub fn payload_commitment(&self) -> VidCommitment {
623 match self {
624 Self::V0(disperse) => VidCommitment::V0(disperse.payload_commitment),
625 Self::V1(disperse) => disperse.payload_commitment.into(),
626 Self::V2(disperse) => disperse.payload_commitment.into(),
627 }
628 }
629
630 pub fn payload_commitment_ref(&self) -> &[u8] {
632 match self {
633 Self::V0(disperse) => disperse.payload_commitment.as_ref(),
634 Self::V1(disperse) => disperse.payload_commitment.as_ref(),
635 Self::V2(disperse) => disperse.payload_commitment.as_ref(),
636 }
637 }
638
639 pub fn set_view_number(&mut self, view_number: ViewNumber) {
641 match self {
642 Self::V0(share) => share.view_number = view_number,
643 Self::V1(share) => share.view_number = view_number,
644 Self::V2(share) => share.view_number = view_number,
645 }
646 }
647
648 pub fn to_shares(self) -> Vec<VidDisperseShare<TYPES>> {
649 match self {
650 VidDisperse::V0(disperse) => disperse
651 .to_shares()
652 .into_iter()
653 .map(|share| VidDisperseShare::V0(share))
654 .collect(),
655 VidDisperse::V1(disperse) => disperse
656 .to_shares()
657 .into_iter()
658 .map(|share| VidDisperseShare::V1(share))
659 .collect(),
660 VidDisperse::V2(disperse) => disperse
661 .to_shares()
662 .into_iter()
663 .map(|share| VidDisperseShare::V2(share))
664 .collect(),
665 }
666 }
667
668 pub fn to_share_proposals(
670 proposal: Proposal<TYPES, Self>,
671 ) -> Vec<Proposal<TYPES, VidDisperseShare<TYPES>>> {
672 match proposal.data {
673 VidDisperse::V0(disperse) => disperse
674 .to_share_proposals(&proposal.signature)
675 .into_iter()
676 .map(|proposal| convert_proposal(proposal))
677 .collect(),
678 VidDisperse::V1(disperse) => disperse
679 .to_share_proposals(&proposal.signature)
680 .into_iter()
681 .map(|proposal| convert_proposal(proposal))
682 .collect(),
683 VidDisperse::V2(disperse) => disperse
684 .to_share_proposals(&proposal.signature)
685 .into_iter()
686 .map(|proposal| convert_proposal(proposal))
687 .collect(),
688 }
689 }
690}
691
692pub type VidDisperseShare0<TYPES> = vid_disperse::ADVZDisperseShare<TYPES>;
694pub type VidDisperseShare1<TYPES> = vid_disperse::AvidMDisperseShare<TYPES>;
695pub type VidDisperseShare2<TYPES> = vid_disperse::AvidmGf2DisperseShare<TYPES>;
696
697#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
699#[serde(bound = "TYPES: NodeType")]
700pub enum VidDisperseShare<TYPES: NodeType> {
701 V0(VidDisperseShare0<TYPES>),
703 V1(VidDisperseShare1<TYPES>),
705 V2(VidDisperseShare2<TYPES>),
707}
708
709impl<TYPES: NodeType> From<VidDisperseShare0<TYPES>> for VidDisperseShare<TYPES> {
710 fn from(share: VidDisperseShare0<TYPES>) -> Self {
711 Self::V0(share)
712 }
713}
714
715impl<TYPES: NodeType> From<VidDisperseShare1<TYPES>> for VidDisperseShare<TYPES> {
716 fn from(share: VidDisperseShare1<TYPES>) -> Self {
717 Self::V1(share)
718 }
719}
720
721impl<TYPES: NodeType> From<VidDisperseShare2<TYPES>> for VidDisperseShare<TYPES> {
722 fn from(share: VidDisperseShare2<TYPES>) -> Self {
723 Self::V2(share)
724 }
725}
726
727impl<TYPES: NodeType> VidDisperseShare<TYPES> {
728 pub fn to_proposal(
730 self,
731 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
732 ) -> Option<Proposal<TYPES, Self>> {
733 let payload_commitment_ref: &[u8] = match &self {
734 Self::V0(share) => share.payload_commitment.as_ref(),
735 Self::V1(share) => share.payload_commitment.as_ref(),
736 Self::V2(share) => share.payload_commitment.as_ref(),
737 };
738 let Ok(signature) = TYPES::SignatureKey::sign(private_key, payload_commitment_ref) else {
739 tracing::error!("VID: failed to sign dispersal share payload");
740 return None;
741 };
742 Some(Proposal {
743 signature,
744 _pd: PhantomData,
745 data: self,
746 })
747 }
748
749 pub fn recipient_key(&self) -> &TYPES::SignatureKey {
751 match self {
752 Self::V0(share) => &share.recipient_key,
753 Self::V1(share) => &share.recipient_key,
754 Self::V2(share) => &share.recipient_key,
755 }
756 }
757
758 pub fn payload_byte_len(&self) -> u32 {
760 match self {
761 Self::V0(share) => share.payload_byte_len(),
762 Self::V1(share) => share.payload_byte_len(),
763 Self::V2(share) => share.payload_byte_len(),
764 }
765 }
766
767 pub fn payload_commitment_ref(&self) -> &[u8] {
769 match self {
770 Self::V0(share) => share.payload_commitment.as_ref(),
771 Self::V1(share) => share.payload_commitment.as_ref(),
772 Self::V2(share) => share.payload_commitment.as_ref(),
773 }
774 }
775
776 pub fn payload_commitment(&self) -> VidCommitment {
778 match self {
779 Self::V0(share) => VidCommitment::V0(share.payload_commitment),
780 Self::V1(share) => share.payload_commitment.into(),
781 Self::V2(share) => share.payload_commitment.into(),
782 }
783 }
784
785 pub fn target_epoch(&self) -> Option<EpochNumber> {
787 match self {
788 Self::V0(_) => None,
789 Self::V1(share) => share.target_epoch,
790 Self::V2(share) => share.target_epoch,
791 }
792 }
793
794 pub fn common(&self) -> VidCommonRef<'_> {
796 match self {
797 Self::V0(share) => VidCommonRef::V0(&share.common),
798 Self::V1(share) => VidCommonRef::V1(&share.common),
799 Self::V2(share) => VidCommonRef::V2(&share.common),
800 }
801 }
802
803 pub fn is_consistent(&self) -> bool {
805 match self {
806 Self::V0(share) => share.is_consistent(),
807 Self::V1(share) => share.is_consistent(),
808 Self::V2(share) => share.is_consistent(),
809 }
810 }
811
812 pub fn verify_with_verified_common(&self) -> bool {
815 match self {
816 Self::V0(share) => share.verify_with_verified_common(),
817 Self::V1(share) => share.verify_with_verified_common(),
818 Self::V2(share) => share.verify_with_verified_common(),
819 }
820 }
821
822 pub fn verify(&self, total_nodes: usize) -> bool {
824 match self {
825 Self::V0(share) => share.verify(total_nodes),
826 Self::V1(share) => share.verify(total_nodes),
827 Self::V2(share) => share.verify(total_nodes),
828 }
829 }
830
831 pub fn set_view_number(&mut self, view_number: ViewNumber) {
833 match self {
834 Self::V0(share) => share.view_number = view_number,
835 Self::V1(share) => share.view_number = view_number,
836 Self::V2(share) => share.view_number = view_number,
837 }
838 }
839}
840
841impl<TYPES: NodeType> HasViewNumber for VidDisperseShare<TYPES> {
842 fn view_number(&self) -> ViewNumber {
843 match self {
844 Self::V0(disperse) => disperse.view_number(),
845 Self::V1(disperse) => disperse.view_number(),
846 Self::V2(disperse) => disperse.view_number(),
847 }
848 }
849}
850
851impl<TYPES: NodeType> HasEpoch for VidDisperseShare<TYPES> {
852 fn epoch(&self) -> Option<EpochNumber> {
853 match self {
854 Self::V0(_) => None,
855 Self::V1(share) => share.epoch(),
856 Self::V2(share) => share.epoch(),
857 }
858 }
859}
860
861#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
864#[serde(bound(deserialize = ""))]
865pub enum ViewChangeEvidence<TYPES: NodeType> {
866 Timeout(TimeoutCertificate<TYPES>),
868 ViewSync(ViewSyncFinalizeCertificate<TYPES>),
870}
871
872impl<TYPES: NodeType> ViewChangeEvidence<TYPES> {
873 pub fn is_valid_for_view(&self, view: &ViewNumber) -> bool {
875 match self {
876 ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
877 ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
878 }
879 }
880
881 pub fn to_evidence2(self) -> ViewChangeEvidence2<TYPES> {
883 match self {
884 ViewChangeEvidence::Timeout(timeout_cert) => {
885 ViewChangeEvidence2::Timeout(timeout_cert.to_tc2())
886 },
887 ViewChangeEvidence::ViewSync(view_sync_cert) => {
888 ViewChangeEvidence2::ViewSync(view_sync_cert.to_vsc2())
889 },
890 }
891 }
892}
893
894#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
897#[serde(bound(deserialize = ""))]
898pub enum ViewChangeEvidence2<TYPES: NodeType> {
899 Timeout(TimeoutCertificate2<TYPES>),
901 ViewSync(ViewSyncFinalizeCertificate2<TYPES>),
903}
904
905impl<TYPES: NodeType> ViewChangeEvidence2<TYPES> {
906 pub fn is_valid_for_view(&self, view: &ViewNumber) -> bool {
908 match self {
909 ViewChangeEvidence2::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
910 ViewChangeEvidence2::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
911 }
912 }
913
914 pub fn to_evidence(self) -> ViewChangeEvidence<TYPES> {
916 match self {
917 ViewChangeEvidence2::Timeout(timeout_cert) => {
918 ViewChangeEvidence::Timeout(timeout_cert.to_tc())
919 },
920 ViewChangeEvidence2::ViewSync(view_sync_cert) => {
921 ViewChangeEvidence::ViewSync(view_sync_cert.to_vsc())
922 },
923 }
924 }
925}
926
927#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
929#[serde(bound(deserialize = ""))]
930pub struct QuorumProposal<TYPES: NodeType> {
931 pub block_header: TYPES::BlockHeader,
933
934 pub view_number: ViewNumber,
936
937 pub justify_qc: QuorumCertificate<TYPES>,
939
940 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
942
943 pub proposal_certificate: Option<ViewChangeEvidence<TYPES>>,
948}
949
950#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
952#[serde(bound(deserialize = ""))]
953pub struct QuorumProposal2<TYPES: NodeType> {
954 pub block_header: TYPES::BlockHeader,
956
957 pub view_number: ViewNumber,
959
960 pub epoch: Option<EpochNumber>,
962
963 pub justify_qc: QuorumCertificate2<TYPES>,
965
966 pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
968
969 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
971
972 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
974
975 #[serde(with = "serde_bytes")]
980 pub next_drb_result: Option<DrbResult>,
981
982 pub state_cert: Option<LightClientStateUpdateCertificateV2<TYPES>>,
985}
986
987#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
996#[serde(bound(deserialize = ""))]
997pub struct QuorumProposal2Legacy<TYPES: NodeType> {
998 pub block_header: TYPES::BlockHeader,
1000
1001 pub view_number: ViewNumber,
1003
1004 pub epoch: Option<EpochNumber>,
1006
1007 pub justify_qc: QuorumCertificate2<TYPES>,
1009
1010 pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1012
1013 pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1015
1016 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1018
1019 #[serde(with = "serde_bytes")]
1024 pub next_drb_result: Option<DrbResult>,
1025
1026 pub state_cert: Option<LightClientStateUpdateCertificateV1<TYPES>>,
1030}
1031
1032impl<TYPES: NodeType> From<QuorumProposal2Legacy<TYPES>> for QuorumProposal2<TYPES> {
1033 fn from(quorum_proposal2: QuorumProposal2Legacy<TYPES>) -> Self {
1034 Self {
1035 block_header: quorum_proposal2.block_header,
1036 view_number: quorum_proposal2.view_number,
1037 epoch: quorum_proposal2.epoch,
1038 justify_qc: quorum_proposal2.justify_qc,
1039 next_epoch_justify_qc: quorum_proposal2.next_epoch_justify_qc,
1040 upgrade_certificate: quorum_proposal2.upgrade_certificate,
1041 view_change_evidence: quorum_proposal2.view_change_evidence,
1042 next_drb_result: quorum_proposal2.next_drb_result,
1043 state_cert: quorum_proposal2.state_cert.map(Into::into),
1044 }
1045 }
1046}
1047
1048impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal2Legacy<TYPES> {
1049 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
1050 Self {
1051 block_header: quorum_proposal2.block_header,
1052 view_number: quorum_proposal2.view_number,
1053 epoch: quorum_proposal2.epoch,
1054 justify_qc: quorum_proposal2.justify_qc,
1055 next_epoch_justify_qc: quorum_proposal2.next_epoch_justify_qc,
1056 upgrade_certificate: quorum_proposal2.upgrade_certificate,
1057 view_change_evidence: quorum_proposal2.view_change_evidence,
1058 next_drb_result: quorum_proposal2.next_drb_result,
1059 state_cert: quorum_proposal2.state_cert.map(Into::into),
1060 }
1061 }
1062}
1063
1064#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
1070#[serde(bound(deserialize = ""))]
1071pub struct QuorumProposalWrapperLegacy<TYPES: NodeType> {
1072 pub proposal: QuorumProposal2Legacy<TYPES>,
1074}
1075
1076#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
1078#[serde(bound(deserialize = ""))]
1079pub struct QuorumProposalWrapper<TYPES: NodeType> {
1080 pub proposal: QuorumProposal2<TYPES>,
1082}
1083
1084impl<TYPES: NodeType> From<QuorumProposalWrapperLegacy<TYPES>> for QuorumProposalWrapper<TYPES> {
1085 fn from(v3: QuorumProposalWrapperLegacy<TYPES>) -> Self {
1086 Self {
1087 proposal: v3.proposal.into(),
1088 }
1089 }
1090}
1091
1092impl<TYPES: NodeType> QuorumProposal2<TYPES> {
1093 pub async fn validate_epoch(
1097 &self,
1098 upgrade_lock: &UpgradeLock<TYPES>,
1099 epoch_height: u64,
1100 ) -> Result<()> {
1101 let calculated_epoch = option_epoch_from_block_number(
1102 upgrade_lock.epochs_enabled(self.view_number()).await,
1103 self.block_header.block_number(),
1104 epoch_height,
1105 );
1106 ensure!(
1107 calculated_epoch == self.epoch(),
1108 "Quorum proposal invalid: inconsistent epoch."
1109 );
1110 Ok(())
1111 }
1112}
1113
1114impl<TYPES: NodeType> QuorumProposalWrapper<TYPES> {
1115 pub fn block_header(&self) -> &TYPES::BlockHeader {
1117 &self.proposal.block_header
1118 }
1119
1120 pub fn view_number(&self) -> ViewNumber {
1122 self.proposal.view_number
1123 }
1124
1125 pub fn justify_qc(&self) -> &QuorumCertificate2<TYPES> {
1127 &self.proposal.justify_qc
1128 }
1129
1130 pub fn next_epoch_justify_qc(&self) -> &Option<NextEpochQuorumCertificate2<TYPES>> {
1132 &self.proposal.next_epoch_justify_qc
1133 }
1134
1135 pub fn upgrade_certificate(&self) -> &Option<UpgradeCertificate<TYPES>> {
1137 &self.proposal.upgrade_certificate
1138 }
1139
1140 pub fn view_change_evidence(&self) -> &Option<ViewChangeEvidence2<TYPES>> {
1142 &self.proposal.view_change_evidence
1143 }
1144
1145 pub fn next_drb_result(&self) -> &Option<DrbResult> {
1147 &self.proposal.next_drb_result
1148 }
1149
1150 pub async fn validate_epoch(
1154 &self,
1155 upgrade_lock: &UpgradeLock<TYPES>,
1156 epoch_height: u64,
1157 ) -> Result<()> {
1158 self.proposal
1159 .validate_epoch(upgrade_lock, epoch_height)
1160 .await
1161 }
1162
1163 pub fn state_cert(&self) -> &Option<LightClientStateUpdateCertificateV2<TYPES>> {
1165 &self.proposal.state_cert
1166 }
1167}
1168
1169impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposalWrapper<TYPES> {
1170 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
1171 Self {
1172 proposal: quorum_proposal.into(),
1173 }
1174 }
1175}
1176
1177impl<TYPES: NodeType> From<QuorumProposal2Legacy<TYPES>> for QuorumProposalWrapper<TYPES> {
1178 fn from(quorum_proposal: QuorumProposal2Legacy<TYPES>) -> Self {
1179 Self {
1180 proposal: quorum_proposal.into(),
1181 }
1182 }
1183}
1184
1185impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposalWrapper<TYPES> {
1186 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
1187 Self {
1188 proposal: quorum_proposal2,
1189 }
1190 }
1191}
1192
1193impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal<TYPES> {
1194 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
1195 quorum_proposal_wrapper.proposal.into()
1196 }
1197}
1198
1199impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2Legacy<TYPES> {
1200 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
1201 quorum_proposal_wrapper.proposal.into()
1202 }
1203}
1204
1205impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2<TYPES> {
1206 fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
1207 quorum_proposal_wrapper.proposal
1208 }
1209}
1210
1211impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposal2<TYPES> {
1212 fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
1213 Self {
1214 block_header: quorum_proposal.block_header,
1215 view_number: quorum_proposal.view_number,
1216 epoch: None,
1217 justify_qc: quorum_proposal.justify_qc.to_qc2(),
1218 next_epoch_justify_qc: None,
1219 upgrade_certificate: quorum_proposal.upgrade_certificate,
1220 view_change_evidence: quorum_proposal
1221 .proposal_certificate
1222 .map(ViewChangeEvidence::to_evidence2),
1223 next_drb_result: None,
1224 state_cert: None,
1225 }
1226 }
1227}
1228
1229impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal<TYPES> {
1230 fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
1231 Self {
1232 block_header: quorum_proposal2.block_header,
1233 view_number: quorum_proposal2.view_number,
1234 justify_qc: quorum_proposal2.justify_qc.to_qc(),
1235 upgrade_certificate: quorum_proposal2.upgrade_certificate,
1236 proposal_certificate: quorum_proposal2
1237 .view_change_evidence
1238 .map(ViewChangeEvidence2::to_evidence),
1239 }
1240 }
1241}
1242
1243impl<TYPES: NodeType> From<Leaf<TYPES>> for Leaf2<TYPES> {
1244 fn from(leaf: Leaf<TYPES>) -> Self {
1245 let bytes: [u8; 32] = leaf.parent_commitment.into();
1246
1247 Self {
1248 view_number: leaf.view_number,
1249 justify_qc: leaf.justify_qc.to_qc2(),
1250 next_epoch_justify_qc: None,
1251 parent_commitment: Commitment::from_raw(bytes),
1252 block_header: leaf.block_header,
1253 upgrade_certificate: leaf.upgrade_certificate,
1254 block_payload: leaf.block_payload,
1255 view_change_evidence: None,
1256 next_drb_result: None,
1257 with_epoch: false,
1258 }
1259 }
1260}
1261
1262impl<TYPES: NodeType> HasViewNumber for DaProposal<TYPES> {
1263 fn view_number(&self) -> ViewNumber {
1264 self.view_number
1265 }
1266}
1267
1268impl<TYPES: NodeType> HasViewNumber for DaProposal2<TYPES> {
1269 fn view_number(&self) -> ViewNumber {
1270 self.view_number
1271 }
1272}
1273
1274impl<TYPES: NodeType> HasViewNumber for QuorumProposal<TYPES> {
1275 fn view_number(&self) -> ViewNumber {
1276 self.view_number
1277 }
1278}
1279
1280impl<TYPES: NodeType> HasViewNumber for QuorumProposal2<TYPES> {
1281 fn view_number(&self) -> ViewNumber {
1282 self.view_number
1283 }
1284}
1285
1286impl<TYPES: NodeType> HasViewNumber for QuorumProposal2Legacy<TYPES> {
1287 fn view_number(&self) -> ViewNumber {
1288 self.view_number
1289 }
1290}
1291
1292impl<TYPES: NodeType> HasViewNumber for QuorumProposalWrapper<TYPES> {
1293 fn view_number(&self) -> ViewNumber {
1294 self.proposal.view_number
1295 }
1296}
1297
1298impl<TYPES: NodeType> HasViewNumber for QuorumProposalWrapperLegacy<TYPES> {
1299 fn view_number(&self) -> ViewNumber {
1300 self.proposal.view_number
1301 }
1302}
1303
1304impl HasViewNumber for UpgradeProposal {
1305 fn view_number(&self) -> ViewNumber {
1306 self.view_number
1307 }
1308}
1309
1310impl<NODE: NodeType> HasEpoch for QuorumProposal2<NODE> {
1311 fn epoch(&self) -> Option<EpochNumber> {
1312 self.epoch
1313 }
1314}
1315
1316impl<NODE: NodeType> HasEpoch for DaProposal2<NODE> {
1317 fn epoch(&self) -> Option<EpochNumber> {
1318 self.epoch
1319 }
1320}
1321
1322impl<NODE: NodeType> HasEpoch for QuorumProposal2Legacy<NODE> {
1323 fn epoch(&self) -> Option<EpochNumber> {
1324 self.epoch
1325 }
1326}
1327
1328impl HasEpoch for UpgradeProposal {
1329 fn epoch(&self) -> Option<EpochNumber> {
1330 None
1331 }
1332}
1333
1334impl<NODE: NodeType> HasEpoch for QuorumProposal<NODE> {
1335 fn epoch(&self) -> Option<EpochNumber> {
1336 None
1337 }
1338}
1339
1340impl<NODE: NodeType> HasEpoch for DaProposal<NODE> {
1341 fn epoch(&self) -> Option<EpochNumber> {
1342 None
1343 }
1344}
1345
1346impl<NODE: NodeType> HasEpoch for QuorumProposalWrapper<NODE> {
1347 #[allow(clippy::panic)]
1349 fn epoch(&self) -> Option<EpochNumber> {
1350 self.proposal.epoch()
1351 }
1352}
1353
1354impl<TYPES: NodeType> HasEpoch for QuorumProposalWrapperLegacy<TYPES> {
1355 #[allow(clippy::panic)]
1357 fn epoch(&self) -> Option<EpochNumber> {
1358 self.proposal.epoch()
1359 }
1360}
1361
1362#[derive(Error, Debug, Serialize, Deserialize)]
1364pub enum BlockError {
1365 #[error("Invalid block header: {0}")]
1367 InvalidBlockHeader(String),
1368
1369 #[error("Inconsistent payload commitment")]
1371 InconsistentPayloadCommitment,
1372
1373 #[error("Failed to apply block header: {0}")]
1375 FailedHeaderApply(String),
1376}
1377
1378pub trait TestableLeaf {
1380 type NodeType: NodeType;
1382
1383 fn create_random_transaction(
1385 &self,
1386 rng: &mut dyn rand::RngCore,
1387 padding: u64,
1388 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction;
1389}
1390
1391#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1395#[serde(bound(deserialize = ""))]
1396pub struct Leaf<TYPES: NodeType> {
1397 view_number: ViewNumber,
1399
1400 justify_qc: QuorumCertificate<TYPES>,
1402
1403 parent_commitment: Commitment<Self>,
1406
1407 block_header: TYPES::BlockHeader,
1409
1410 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1412
1413 block_payload: Option<TYPES::BlockPayload>,
1417}
1418
1419#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
1422#[serde(bound(deserialize = ""))]
1423pub struct Leaf2<TYPES: NodeType> {
1424 view_number: ViewNumber,
1426
1427 justify_qc: QuorumCertificate2<TYPES>,
1429
1430 next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
1432
1433 parent_commitment: Commitment<Self>,
1436
1437 block_header: TYPES::BlockHeader,
1439
1440 upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
1442
1443 block_payload: Option<TYPES::BlockPayload>,
1447
1448 pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
1450
1451 #[serde(with = "serde_bytes")]
1456 pub next_drb_result: Option<DrbResult>,
1457
1458 pub with_epoch: bool,
1460}
1461
1462impl<TYPES: NodeType> Leaf2<TYPES> {
1463 #[must_use]
1470 pub async fn genesis(
1471 validated_state: &TYPES::ValidatedState,
1472 instance_state: &TYPES::InstanceState,
1473 version: Version,
1474 ) -> Self {
1475 let epoch = genesis_epoch_from_version(version);
1476
1477 let (payload, metadata) =
1478 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1479 .await
1480 .unwrap();
1481
1482 let genesis_view = ViewNumber::genesis();
1483
1484 let block_header =
1485 TYPES::BlockHeader::genesis(instance_state, payload.clone(), &metadata, version);
1486
1487 let block_number = if version < EPOCH_VERSION {
1488 None
1489 } else {
1490 Some(0u64)
1491 };
1492
1493 let null_quorum_data = QuorumData2 {
1494 leaf_commit: Commitment::<Leaf2<TYPES>>::default_commitment_no_preimage(),
1495 epoch,
1496 block_number,
1497 };
1498
1499 let justify_qc = QuorumCertificate2::new(
1500 null_quorum_data.clone(),
1501 null_quorum_data.commit(),
1502 genesis_view,
1503 None,
1504 PhantomData,
1505 );
1506
1507 Self {
1508 view_number: genesis_view,
1509 justify_qc,
1510 next_epoch_justify_qc: None,
1511 parent_commitment: null_quorum_data.leaf_commit,
1512 upgrade_certificate: None,
1513 block_header: block_header.clone(),
1514 block_payload: Some(payload),
1515 view_change_evidence: None,
1516 next_drb_result: None,
1517 with_epoch: epoch.is_some(),
1518 }
1519 }
1520 pub fn view_number(&self) -> ViewNumber {
1522 self.view_number
1523 }
1524 pub fn epoch(&self, epoch_height: u64) -> Option<EpochNumber> {
1526 option_epoch_from_block_number(
1527 self.with_epoch,
1528 self.block_header.block_number(),
1529 epoch_height,
1530 )
1531 }
1532 pub fn height(&self) -> u64 {
1536 self.block_header.block_number()
1537 }
1538 pub fn justify_qc(&self) -> QuorumCertificate2<TYPES> {
1540 self.justify_qc.clone()
1541 }
1542 pub fn next_epoch_justify_qc(&self) -> Option<NextEpochQuorumCertificate2<TYPES>> {
1546 self.next_epoch_justify_qc.clone()
1547 }
1548 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1550 self.upgrade_certificate.clone()
1551 }
1552 pub fn parent_commitment(&self) -> Commitment<Self> {
1554 self.parent_commitment
1555 }
1556 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1558 &self.block_header
1559 }
1560
1561 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1563 &mut self.block_header
1564 }
1565 pub fn fill_block_payload(
1572 &mut self,
1573 block_payload: TYPES::BlockPayload,
1574 num_storage_nodes: usize,
1575 version: Version,
1576 ) -> std::result::Result<(), BlockError> {
1577 let encoded_txns = block_payload.encode();
1578 let commitment = vid_commitment(
1579 &encoded_txns,
1580 &self.block_header.metadata().encode(),
1581 num_storage_nodes,
1582 version,
1583 );
1584 if commitment != self.block_header.payload_commitment() {
1585 return Err(BlockError::InconsistentPayloadCommitment);
1586 }
1587 self.block_payload = Some(block_payload);
1588 Ok(())
1589 }
1590
1591 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1593 self.block_payload.take()
1594 }
1595
1596 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1599 self.block_payload = Some(block_payload);
1600 }
1601
1602 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1604 self.block_payload.clone()
1605 }
1606
1607 pub fn payload_commitment(&self) -> VidCommitment {
1609 self.block_header().payload_commitment()
1610 }
1611
1612 pub async fn extends_upgrade(
1620 &self,
1621 parent: &Self,
1622 decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
1623 ) -> Result<()> {
1624 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
1625 (None | Some(_), None) => {},
1629 (None, Some(parent_cert)) => {
1633 let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
1634 ensure!(
1635 self.view_number() > parent_cert.data.new_version_first_view
1636 || (self.view_number() > parent_cert.data.decide_by
1637 && decided_upgrade_certificate_read.is_none()),
1638 "The new leaf is missing an upgrade certificate that was present in its \
1639 parent, and should still be live."
1640 );
1641 },
1642 (Some(cert), Some(parent_cert)) => {
1646 ensure!(
1647 cert == parent_cert,
1648 "The new leaf does not extend the parent leaf, because it has attached a \
1649 different upgrade certificate."
1650 );
1651 },
1652 }
1653
1654 Ok(())
1658 }
1659
1660 pub fn to_leaf_unsafe(self) -> Leaf<TYPES> {
1662 let bytes: [u8; 32] = self.parent_commitment.into();
1663
1664 Leaf {
1665 view_number: self.view_number,
1666 justify_qc: self.justify_qc.to_qc(),
1667 parent_commitment: Commitment::from_raw(bytes),
1668 block_header: self.block_header,
1669 upgrade_certificate: self.upgrade_certificate,
1670 block_payload: self.block_payload,
1671 }
1672 }
1673}
1674
1675impl<TYPES: NodeType> Committable for Leaf2<TYPES> {
1676 fn commit(&self) -> committable::Commitment<Self> {
1677 let Leaf2 {
1678 view_number,
1679 justify_qc,
1680 next_epoch_justify_qc,
1681 parent_commitment,
1682 block_header,
1683 upgrade_certificate,
1684 block_payload: _,
1685 view_change_evidence,
1686 next_drb_result,
1687 with_epoch,
1688 } = self;
1689
1690 let mut cb = RawCommitmentBuilder::new("leaf commitment")
1691 .u64_field("view number", **view_number)
1692 .field("parent leaf commitment", *parent_commitment)
1693 .field("block header", block_header.commit())
1694 .field("justify qc", justify_qc.commit())
1695 .optional("upgrade certificate", upgrade_certificate);
1696
1697 if *with_epoch {
1698 cb = cb
1699 .constant_str("with_epoch")
1700 .optional("next_epoch_justify_qc", next_epoch_justify_qc);
1701
1702 if let Some(next_drb_result) = next_drb_result {
1703 cb = cb
1704 .constant_str("next_drb_result")
1705 .fixed_size_bytes(next_drb_result);
1706 }
1707
1708 match view_change_evidence {
1709 Some(ViewChangeEvidence2::Timeout(cert)) => {
1710 cb = cb.field("timeout cert", cert.commit());
1711 },
1712 Some(ViewChangeEvidence2::ViewSync(cert)) => {
1713 cb = cb.field("viewsync cert", cert.commit());
1714 },
1715 None => {},
1716 }
1717 }
1718
1719 cb.finalize()
1720 }
1721}
1722
1723impl<TYPES: NodeType> Leaf<TYPES> {
1724 #[allow(clippy::unused_async)]
1725 pub async fn commit(&self, _upgrade_lock: &UpgradeLock<TYPES>) -> Commitment<Self> {
1728 <Self as Committable>::commit(self)
1729 }
1730}
1731
1732impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
1733 fn eq(&self, other: &Self) -> bool {
1734 self.view_number == other.view_number
1735 && self.justify_qc == other.justify_qc
1736 && self.parent_commitment == other.parent_commitment
1737 && self.block_header == other.block_header
1738 }
1739}
1740
1741impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
1742 fn eq(&self, other: &Self) -> bool {
1743 let Leaf2 {
1744 view_number,
1745 justify_qc,
1746 next_epoch_justify_qc,
1747 parent_commitment,
1748 block_header,
1749 upgrade_certificate,
1750 block_payload: _,
1751 view_change_evidence,
1752 next_drb_result,
1753 with_epoch,
1754 } = self;
1755
1756 *view_number == other.view_number
1757 && *justify_qc == other.justify_qc
1758 && *next_epoch_justify_qc == other.next_epoch_justify_qc
1759 && *parent_commitment == other.parent_commitment
1760 && *block_header == other.block_header
1761 && *upgrade_certificate == other.upgrade_certificate
1762 && *view_change_evidence == other.view_change_evidence
1763 && *next_drb_result == other.next_drb_result
1764 && *with_epoch == other.with_epoch
1765 }
1766}
1767
1768impl<TYPES: NodeType> Hash for Leaf<TYPES> {
1769 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1770 self.view_number.hash(state);
1771 self.justify_qc.hash(state);
1772 self.parent_commitment.hash(state);
1773 self.block_header.hash(state);
1774 }
1775}
1776
1777impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
1778 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1779 self.commit().hash(state);
1780 self.view_number.hash(state);
1781 self.justify_qc.hash(state);
1782 self.parent_commitment.hash(state);
1783 self.block_header.hash(state);
1784 }
1785}
1786
1787impl<TYPES: NodeType> Display for Leaf<TYPES> {
1788 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1789 write!(
1790 f,
1791 "view: {:?}, height: {:?}, justify: {}",
1792 self.view_number,
1793 self.height(),
1794 self.justify_qc
1795 )
1796 }
1797}
1798
1799impl<TYPES: NodeType> QuorumCertificate<TYPES> {
1800 #[must_use]
1802 pub async fn genesis(
1803 validated_state: &TYPES::ValidatedState,
1804 instance_state: &TYPES::InstanceState,
1805 upgrade: Upgrade,
1806 ) -> Self {
1807 let upgrade_lock = UpgradeLock::<TYPES>::new(upgrade);
1809
1810 let genesis_view = ViewNumber::genesis();
1811
1812 let data = QuorumData {
1813 leaf_commit: Leaf::genesis(validated_state, instance_state, upgrade.base)
1814 .await
1815 .commit(&upgrade_lock)
1816 .await,
1817 };
1818
1819 let versioned_data =
1820 VersionedVoteData::<_, _>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1821 .await;
1822
1823 let bytes: [u8; 32] = versioned_data.commit().into();
1824
1825 Self::new(
1826 data,
1827 Commitment::from_raw(bytes),
1828 genesis_view,
1829 None,
1830 PhantomData,
1831 )
1832 }
1833}
1834
1835impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
1836 #[must_use]
1838 pub async fn genesis(
1839 validated_state: &TYPES::ValidatedState,
1840 instance_state: &TYPES::InstanceState,
1841 upgrade: Upgrade,
1842 ) -> Self {
1843 let upgrade_lock = UpgradeLock::<TYPES>::new(upgrade);
1845
1846 let genesis_view = ViewNumber::genesis();
1847
1848 let genesis_leaf = Leaf2::genesis(validated_state, instance_state, upgrade.base).await;
1849 let block_number = if upgrade_lock.epochs_enabled(genesis_view).await {
1850 Some(genesis_leaf.height())
1851 } else {
1852 None
1853 };
1854 let data = QuorumData2 {
1855 leaf_commit: genesis_leaf.commit(),
1856 epoch: genesis_epoch_from_version(upgrade.base), block_number,
1858 };
1859
1860 let versioned_data =
1861 VersionedVoteData::<_, _>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
1862 .await;
1863
1864 let bytes: [u8; 32] = versioned_data.commit().into();
1865
1866 Self::new(
1867 data,
1868 Commitment::from_raw(bytes),
1869 genesis_view,
1870 None,
1871 PhantomData,
1872 )
1873 }
1874}
1875
1876impl<TYPES: NodeType> Leaf<TYPES> {
1877 #[must_use]
1884 pub async fn genesis(
1885 validated_state: &TYPES::ValidatedState,
1886 instance_state: &TYPES::InstanceState,
1887 version: Version,
1888 ) -> Self {
1889 let (payload, metadata) =
1890 TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
1891 .await
1892 .unwrap();
1893
1894 let genesis_view = ViewNumber::genesis();
1895
1896 let block_header =
1897 TYPES::BlockHeader::genesis(instance_state, payload.clone(), &metadata, version);
1898
1899 let null_quorum_data = QuorumData {
1900 leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
1901 };
1902
1903 let justify_qc = QuorumCertificate::new(
1904 null_quorum_data.clone(),
1905 null_quorum_data.commit(),
1906 genesis_view,
1907 None,
1908 PhantomData,
1909 );
1910
1911 Self {
1912 view_number: genesis_view,
1913 justify_qc,
1914 parent_commitment: null_quorum_data.leaf_commit,
1915 upgrade_certificate: None,
1916 block_header: block_header.clone(),
1917 block_payload: Some(payload),
1918 }
1919 }
1920
1921 pub fn view_number(&self) -> ViewNumber {
1923 self.view_number
1924 }
1925 pub fn height(&self) -> u64 {
1929 self.block_header.block_number()
1930 }
1931 pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
1933 self.justify_qc.clone()
1934 }
1935 pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
1937 self.upgrade_certificate.clone()
1938 }
1939 pub fn parent_commitment(&self) -> Commitment<Self> {
1941 self.parent_commitment
1942 }
1943 pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
1945 &self.block_header
1946 }
1947
1948 pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
1950 &mut self.block_header
1951 }
1952 pub fn fill_block_payload(
1959 &mut self,
1960 block_payload: TYPES::BlockPayload,
1961 num_storage_nodes: usize,
1962 version: Version,
1963 ) -> std::result::Result<(), BlockError> {
1964 let encoded_txns = block_payload.encode();
1965 let commitment = vid_commitment(
1966 &encoded_txns,
1967 &self.block_header.metadata().encode(),
1968 num_storage_nodes,
1969 version,
1970 );
1971 if commitment != self.block_header.payload_commitment() {
1972 return Err(BlockError::InconsistentPayloadCommitment);
1973 }
1974 self.block_payload = Some(block_payload);
1975 Ok(())
1976 }
1977
1978 pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
1980 self.block_payload.take()
1981 }
1982
1983 pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
1986 self.block_payload = Some(block_payload);
1987 }
1988
1989 pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
1991 self.block_payload.clone()
1992 }
1993
1994 pub fn payload_commitment(&self) -> VidCommitment {
1996 self.block_header().payload_commitment()
1997 }
1998
1999 pub async fn extends_upgrade(
2007 &self,
2008 parent: &Self,
2009 decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
2010 ) -> Result<()> {
2011 match (self.upgrade_certificate(), parent.upgrade_certificate()) {
2012 (None | Some(_), None) => {},
2016 (None, Some(parent_cert)) => {
2020 let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
2021 ensure!(
2022 self.view_number() > parent_cert.data.new_version_first_view
2023 || (self.view_number() > parent_cert.data.decide_by
2024 && decided_upgrade_certificate_read.is_none()),
2025 "The new leaf is missing an upgrade certificate that was present in its \
2026 parent, and should still be live."
2027 );
2028 },
2029 (Some(cert), Some(parent_cert)) => {
2033 ensure!(
2034 cert == parent_cert,
2035 "The new leaf does not extend the parent leaf, because it has attached a \
2036 different upgrade certificate."
2037 );
2038 },
2039 }
2040
2041 Ok(())
2045 }
2046}
2047
2048impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
2049where
2050 TYPES::ValidatedState: TestableState<TYPES>,
2051 TYPES::BlockPayload: TestableBlock<TYPES>,
2052{
2053 type NodeType = TYPES;
2054
2055 fn create_random_transaction(
2056 &self,
2057 rng: &mut dyn rand::RngCore,
2058 padding: u64,
2059 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
2060 {
2061 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
2062 }
2063}
2064impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
2065where
2066 TYPES::ValidatedState: TestableState<TYPES>,
2067 TYPES::BlockPayload: TestableBlock<TYPES>,
2068{
2069 type NodeType = TYPES;
2070
2071 fn create_random_transaction(
2072 &self,
2073 rng: &mut dyn rand::RngCore,
2074 padding: u64,
2075 ) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
2076 {
2077 TYPES::ValidatedState::create_random_transaction(None, rng, padding)
2078 }
2079}
2080#[must_use]
2082pub fn fake_commitment<S: Committable>() -> Commitment<S> {
2083 RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
2084}
2085
2086#[must_use]
2088pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
2089 let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
2090 RawCommitmentBuilder::new("Random Commitment")
2091 .constant_str("Random Field")
2092 .var_size_bytes(&random_array)
2093 .finalize()
2094}
2095
2096pub fn serialize_signature2<TYPES: NodeType>(
2100 signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
2101) -> Vec<u8> {
2102 let mut signatures_bytes = vec![];
2103 signatures_bytes.extend("Yes".as_bytes());
2104
2105 let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
2106 let proof_bytes = bincode_opts()
2107 .serialize(&proof.as_bitslice())
2108 .expect("This serialization shouldn't be able to fail");
2109 signatures_bytes.extend("bitvec proof".as_bytes());
2110 signatures_bytes.extend(proof_bytes.as_slice());
2111 let sig_bytes = bincode_opts()
2112 .serialize(&sig)
2113 .expect("This serialization shouldn't be able to fail");
2114 signatures_bytes.extend("aggregated signature".as_bytes());
2115 signatures_bytes.extend(sig_bytes.as_slice());
2116 signatures_bytes
2117}
2118
2119impl<TYPES: NodeType> Committable for Leaf<TYPES> {
2120 fn commit(&self) -> committable::Commitment<Self> {
2121 RawCommitmentBuilder::new("leaf commitment")
2122 .u64_field("view number", *self.view_number)
2123 .field("parent leaf commitment", self.parent_commitment)
2124 .field("block header", self.block_header.commit())
2125 .field("justify qc", self.justify_qc.commit())
2126 .optional("upgrade certificate", &self.upgrade_certificate)
2127 .finalize()
2128 }
2129}
2130
2131impl<TYPES: NodeType> Leaf2<TYPES> {
2132 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
2134 let QuorumProposalWrapper {
2137 proposal:
2138 QuorumProposal2 {
2139 view_number,
2140 epoch,
2141 justify_qc,
2142 next_epoch_justify_qc,
2143 block_header,
2144 upgrade_certificate,
2145 view_change_evidence,
2146 next_drb_result,
2147 state_cert: _,
2148 },
2149 } = quorum_proposal;
2150
2151 Self {
2152 view_number: *view_number,
2153 justify_qc: justify_qc.clone(),
2154 next_epoch_justify_qc: next_epoch_justify_qc.clone(),
2155 parent_commitment: justify_qc.data().leaf_commit,
2156 block_header: block_header.clone(),
2157 upgrade_certificate: upgrade_certificate.clone(),
2158 block_payload: None,
2159 view_change_evidence: view_change_evidence.clone(),
2160 next_drb_result: *next_drb_result,
2161 with_epoch: epoch.is_some(),
2162 }
2163 }
2164}
2165
2166impl<TYPES: NodeType> Leaf<TYPES> {
2167 pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
2169 let QuorumProposal {
2172 view_number,
2173 justify_qc,
2174 block_header,
2175 upgrade_certificate,
2176 proposal_certificate: _,
2177 } = quorum_proposal;
2178
2179 Self {
2180 view_number: *view_number,
2181 justify_qc: justify_qc.clone(),
2182 parent_commitment: justify_qc.data().leaf_commit,
2183 block_header: block_header.clone(),
2184 upgrade_certificate: upgrade_certificate.clone(),
2185 block_payload: None,
2186 }
2187 }
2188}
2189
2190pub mod null_block {
2191 #![allow(missing_docs)]
2192
2193 use jf_advz::VidScheme;
2194 use vbs::version::Version;
2195 use versions::EPOCH_VERSION;
2196
2197 use crate::{
2198 data::VidCommitment,
2199 traits::{
2200 block_contents::BuilderFee, node_implementation::NodeType,
2201 signature_key::BuilderSignatureKey, BlockPayload,
2202 },
2203 vid::advz::advz_scheme,
2204 };
2205
2206 #[must_use]
2215 pub fn commitment(num_storage_nodes: usize) -> Option<VidCommitment> {
2216 let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
2217
2218 match vid_result {
2219 Ok(r) => Some(VidCommitment::V0(r)),
2220 Err(_) => None,
2221 }
2222 }
2223
2224 #[must_use]
2226 pub fn builder_fee<TYPES: NodeType>(
2227 num_storage_nodes: usize,
2228 version: Version,
2229 ) -> Option<BuilderFee<TYPES>> {
2230 const FEE_AMOUNT: u64 = 0;
2232
2233 let (pub_key, priv_key) =
2234 <TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
2235 [0_u8; 32], 0,
2236 );
2237
2238 if version >= EPOCH_VERSION {
2239 let (_null_block, null_block_metadata) =
2240 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
2241
2242 match TYPES::BuilderSignatureKey::sign_fee(&priv_key, FEE_AMOUNT, &null_block_metadata)
2243 {
2244 Ok(sig) => Some(BuilderFee {
2245 fee_amount: FEE_AMOUNT,
2246 fee_account: pub_key,
2247 fee_signature: sig,
2248 }),
2249 Err(_) => None,
2250 }
2251 } else {
2252 let (_null_block, null_block_metadata) =
2253 <TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
2254
2255 match TYPES::BuilderSignatureKey::sign_fee_with_vid_commitment(
2256 &priv_key,
2257 FEE_AMOUNT,
2258 &null_block_metadata,
2259 &commitment(num_storage_nodes)?,
2260 ) {
2261 Ok(sig) => Some(BuilderFee {
2262 fee_amount: FEE_AMOUNT,
2263 fee_account: pub_key,
2264 fee_signature: sig,
2265 }),
2266 Err(_) => None,
2267 }
2268 }
2269 }
2270}
2271
2272#[derive(Debug, Eq, PartialEq, Clone)]
2274pub struct PackedBundle<TYPES: NodeType> {
2275 pub encoded_transactions: Arc<[u8]>,
2277
2278 pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
2280
2281 pub view_number: ViewNumber,
2283
2284 pub epoch_number: Option<EpochNumber>,
2286
2287 pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
2289}
2290
2291impl<TYPES: NodeType> PackedBundle<TYPES> {
2292 pub fn new(
2294 encoded_transactions: Arc<[u8]>,
2295 metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
2296 view_number: ViewNumber,
2297 epoch_number: Option<EpochNumber>,
2298 sequencing_fees: Vec1<BuilderFee<TYPES>>,
2299 ) -> Self {
2300 Self {
2301 encoded_transactions,
2302 metadata,
2303 view_number,
2304 epoch_number,
2305 sequencing_fees,
2306 }
2307 }
2308}
2309
2310#[cfg(test)]
2311mod test {
2312 use super::*;
2313
2314 #[test]
2315 fn test_vid_commitment_display() {
2316 let vc = VidCommitment::V0(VidCommitment0::default());
2317 assert_eq!(
2318 format!("{vc}"),
2319 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
2320 );
2321 assert_eq!(
2322 format!("{vc:?}"),
2323 "HASH~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI"
2324 );
2325
2326 let vc = VidCommitment::V1(VidCommitment1::default());
2327 assert_eq!(
2328 format!("{vc}"),
2329 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
2330 );
2331 assert_eq!(
2332 format!("{vc:?}"),
2333 "AvidMCommit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADr"
2334 );
2335
2336 let vc = VidCommitment::V2(VidCommitment2::default());
2337 assert_eq!(
2338 format!("{vc}"),
2339 "AvidmGf2Commit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq"
2340 );
2341 assert_eq!(
2342 format!("{vc:?}"),
2343 "AvidmGf2Commit~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACq"
2344 );
2345 }
2346}