1use std::{
10 fmt::{self, Debug, Display, Formatter},
11 future::Future,
12 hash::Hash,
13 marker::PhantomData,
14};
15
16use alloy::primitives::{FixedBytes, U256};
17use committable::{Commitment, Committable};
18use hotshot_utils::anytrace::*;
19use serde::{Deserialize, Serialize};
20
21use crate::{
22 data::{serialize_signature2, Leaf2},
23 epoch_membership::EpochMembership,
24 light_client::{LightClientState, StakeTableState},
25 message::UpgradeLock,
26 simple_vote::{
27 DaData, DaData2, HasEpoch, NextEpochQuorumData2, QuorumData, QuorumData2, QuorumMarker,
28 TimeoutData, TimeoutData2, UpgradeProposalData, VersionedVoteData, ViewSyncCommitData,
29 ViewSyncCommitData2, ViewSyncFinalizeData, ViewSyncFinalizeData2, ViewSyncPreCommitData,
30 ViewSyncPreCommitData2, Voteable,
31 },
32 stake_table::{HSStakeTable, StakeTableEntries},
33 traits::{
34 node_implementation::{ConsensusTime, NodeType, Versions},
35 signature_key::{SignatureKey, StateSignatureKey},
36 },
37 utils::is_epoch_transition,
38 vote::{Certificate, HasViewNumber},
39 PeerConfig,
40};
41
42pub trait Threshold<TYPES: NodeType> {
44 fn threshold(membership: &EpochMembership<TYPES>) -> impl Future<Output = U256> + Send;
46}
47
48#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
50pub struct SuccessThreshold {}
51
52impl<TYPES: NodeType> Threshold<TYPES> for SuccessThreshold {
53 async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
54 membership.success_threshold().await
55 }
56}
57
58#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
60pub struct OneHonestThreshold {}
61
62impl<TYPES: NodeType> Threshold<TYPES> for OneHonestThreshold {
63 async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
64 membership.failure_threshold().await
65 }
66}
67
68#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
70pub struct UpgradeThreshold {}
71
72impl<TYPES: NodeType> Threshold<TYPES> for UpgradeThreshold {
73 async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
74 membership.upgrade_threshold().await
75 }
76}
77
78#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
80pub struct SimpleCertificate<
81 TYPES: NodeType,
82 VOTEABLE: Voteable<TYPES>,
83 THRESHOLD: Threshold<TYPES>,
84> {
85 pub data: VOTEABLE,
87 vote_commitment: Commitment<VOTEABLE>,
89 pub view_number: TYPES::View,
91 pub signatures: Option<<TYPES::SignatureKey as SignatureKey>::QcType>,
93 pub _pd: PhantomData<(TYPES, THRESHOLD)>,
95}
96
97impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES>, THRESHOLD: Threshold<TYPES>>
98 SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
99{
100 pub fn new(
102 data: VOTEABLE,
103 vote_commitment: Commitment<VOTEABLE>,
104 view_number: TYPES::View,
105 signatures: Option<<TYPES::SignatureKey as SignatureKey>::QcType>,
106 pd: PhantomData<(TYPES, THRESHOLD)>,
107 ) -> Self {
108 Self {
109 data,
110 vote_commitment,
111 view_number,
112 signatures,
113 _pd: pd,
114 }
115 }
116}
117
118impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + Committable, THRESHOLD: Threshold<TYPES>>
119 Committable for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
120{
121 fn commit(&self) -> Commitment<Self> {
122 let signature_bytes = match self.signatures.as_ref() {
123 Some(sigs) => serialize_signature2::<TYPES>(sigs),
124 None => vec![],
125 };
126 committable::RawCommitmentBuilder::new("Certificate")
127 .field("data", self.data.commit())
128 .field("vote_commitment", self.vote_commitment)
129 .field("view number", self.view_number.commit())
130 .var_size_field("signatures", &signature_bytes)
131 .finalize()
132 }
133}
134
135impl<TYPES: NodeType, THRESHOLD: Threshold<TYPES>> Certificate<TYPES, DaData>
136 for SimpleCertificate<TYPES, DaData, THRESHOLD>
137{
138 type Voteable = DaData;
139 type Threshold = THRESHOLD;
140
141 fn create_signed_certificate<V: Versions>(
142 vote_commitment: Commitment<VersionedVoteData<TYPES, DaData, V>>,
143 data: Self::Voteable,
144 sig: <TYPES::SignatureKey as SignatureKey>::QcType,
145 view: TYPES::View,
146 ) -> Self {
147 let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
148
149 SimpleCertificate {
150 data,
151 vote_commitment: Commitment::from_raw(vote_commitment_bytes),
152 view_number: view,
153 signatures: Some(sig),
154 _pd: PhantomData,
155 }
156 }
157 async fn is_valid_cert<V: Versions>(
158 &self,
159 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
160 threshold: U256,
161 upgrade_lock: &UpgradeLock<TYPES, V>,
162 ) -> Result<()> {
163 if self.view_number == TYPES::View::genesis() {
164 return Ok(());
165 }
166 let real_qc_pp =
167 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
168 let commit = self.data_commitment(upgrade_lock).await?;
169
170 <TYPES::SignatureKey as SignatureKey>::check(
171 &real_qc_pp,
172 commit.as_ref(),
173 self.signatures.as_ref().unwrap(),
174 )
175 .wrap()
176 .context(|e| warn!("Signature check failed: {e}"))
177 }
178 async fn stake_table_entry(
180 membership: &EpochMembership<TYPES>,
181 pub_key: &TYPES::SignatureKey,
182 ) -> Option<PeerConfig<TYPES>> {
183 membership.da_stake(pub_key).await
184 }
185
186 async fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
188 membership.da_stake_table().await
189 }
190 async fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
192 membership.da_total_nodes().await
193 }
194 async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
195 membership.da_success_threshold().await
196 }
197 fn data(&self) -> &Self::Voteable {
198 &self.data
199 }
200 async fn data_commitment<V: Versions>(
201 &self,
202 upgrade_lock: &UpgradeLock<TYPES, V>,
203 ) -> Result<Commitment<VersionedVoteData<TYPES, DaData, V>>> {
204 Ok(
205 VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)
206 .await?
207 .commit(),
208 )
209 }
210}
211
212impl<TYPES: NodeType, THRESHOLD: Threshold<TYPES>> Certificate<TYPES, DaData2<TYPES>>
213 for SimpleCertificate<TYPES, DaData2<TYPES>, THRESHOLD>
214{
215 type Voteable = DaData2<TYPES>;
216 type Threshold = THRESHOLD;
217
218 fn create_signed_certificate<V: Versions>(
219 vote_commitment: Commitment<VersionedVoteData<TYPES, DaData2<TYPES>, V>>,
220 data: Self::Voteable,
221 sig: <TYPES::SignatureKey as SignatureKey>::QcType,
222 view: TYPES::View,
223 ) -> Self {
224 let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
225
226 SimpleCertificate {
227 data,
228 vote_commitment: Commitment::from_raw(vote_commitment_bytes),
229 view_number: view,
230 signatures: Some(sig),
231 _pd: PhantomData,
232 }
233 }
234 async fn is_valid_cert<V: Versions>(
235 &self,
236 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
237 threshold: U256,
238 upgrade_lock: &UpgradeLock<TYPES, V>,
239 ) -> Result<()> {
240 if self.view_number == TYPES::View::genesis() {
241 return Ok(());
242 }
243 let real_qc_pp =
244 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
245 let commit = self.data_commitment(upgrade_lock).await?;
246
247 <TYPES::SignatureKey as SignatureKey>::check(
248 &real_qc_pp,
249 commit.as_ref(),
250 self.signatures.as_ref().unwrap(),
251 )
252 .wrap()
253 .context(|e| warn!("Signature check failed: {e}"))
254 }
255 async fn stake_table_entry(
257 membership: &EpochMembership<TYPES>,
258 pub_key: &TYPES::SignatureKey,
259 ) -> Option<PeerConfig<TYPES>> {
260 membership.da_stake(pub_key).await
261 }
262
263 async fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
265 membership.da_stake_table().await
266 }
267 async fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
269 membership.da_total_nodes().await
270 }
271 async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
272 membership.da_success_threshold().await
273 }
274 fn data(&self) -> &Self::Voteable {
275 &self.data
276 }
277 async fn data_commitment<V: Versions>(
278 &self,
279 upgrade_lock: &UpgradeLock<TYPES, V>,
280 ) -> Result<Commitment<VersionedVoteData<TYPES, DaData2<TYPES>, V>>> {
281 Ok(
282 VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)
283 .await?
284 .commit(),
285 )
286 }
287}
288
289impl<
290 TYPES: NodeType,
291 VOTEABLE: Voteable<TYPES> + 'static + QuorumMarker,
292 THRESHOLD: Threshold<TYPES>,
293 > Certificate<TYPES, VOTEABLE> for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
294{
295 type Voteable = VOTEABLE;
296 type Threshold = THRESHOLD;
297
298 fn create_signed_certificate<V: Versions>(
299 vote_commitment: Commitment<VersionedVoteData<TYPES, VOTEABLE, V>>,
300 data: Self::Voteable,
301 sig: <TYPES::SignatureKey as SignatureKey>::QcType,
302 view: TYPES::View,
303 ) -> Self {
304 let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
305
306 SimpleCertificate {
307 data,
308 vote_commitment: Commitment::from_raw(vote_commitment_bytes),
309 view_number: view,
310 signatures: Some(sig),
311 _pd: PhantomData,
312 }
313 }
314 async fn is_valid_cert<V: Versions>(
315 &self,
316 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
317 threshold: U256,
318 upgrade_lock: &UpgradeLock<TYPES, V>,
319 ) -> Result<()> {
320 if self.view_number == TYPES::View::genesis() {
321 return Ok(());
322 }
323 let real_qc_pp =
324 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
325 let commit = self.data_commitment(upgrade_lock).await?;
326
327 <TYPES::SignatureKey as SignatureKey>::check(
328 &real_qc_pp,
329 commit.as_ref(),
330 self.signatures.as_ref().unwrap(),
331 )
332 .wrap()
333 .context(|e| warn!("Signature check failed: {e}"))
334 }
335 async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
336 THRESHOLD::threshold(membership).await
337 }
338
339 async fn stake_table_entry(
340 membership: &EpochMembership<TYPES>,
341 pub_key: &TYPES::SignatureKey,
342 ) -> Option<PeerConfig<TYPES>> {
343 membership.stake(pub_key).await
344 }
345
346 async fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
347 membership.stake_table().await
348 }
349
350 async fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
352 membership.total_nodes().await
353 }
354
355 fn data(&self) -> &Self::Voteable {
356 &self.data
357 }
358 async fn data_commitment<V: Versions>(
359 &self,
360 upgrade_lock: &UpgradeLock<TYPES, V>,
361 ) -> Result<Commitment<VersionedVoteData<TYPES, VOTEABLE, V>>> {
362 Ok(
363 VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)
364 .await?
365 .commit(),
366 )
367 }
368}
369
370impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + 'static, THRESHOLD: Threshold<TYPES>>
371 HasViewNumber<TYPES> for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
372{
373 fn view_number(&self) -> TYPES::View {
374 self.view_number
375 }
376}
377
378impl<
379 TYPES: NodeType,
380 VOTEABLE: Voteable<TYPES> + HasEpoch<TYPES> + 'static,
381 THRESHOLD: Threshold<TYPES>,
382 > HasEpoch<TYPES> for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
383{
384 fn epoch(&self) -> Option<TYPES::Epoch> {
385 self.data.epoch()
386 }
387}
388
389impl<TYPES: NodeType> Display for QuorumCertificate<TYPES> {
390 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
391 write!(f, "view: {:?}", self.view_number)
392 }
393}
394
395impl<TYPES: NodeType> UpgradeCertificate<TYPES> {
396 pub async fn is_relevant(&self, view_number: TYPES::View) -> Result<()> {
402 ensure!(
403 self.data.new_version_first_view >= view_number,
404 "Upgrade certificate is no longer relevant."
405 );
406
407 Ok(())
408 }
409
410 pub async fn validate<V: Versions>(
414 upgrade_certificate: &Option<Self>,
415 membership: &EpochMembership<TYPES>,
416 epoch: Option<TYPES::Epoch>,
417 upgrade_lock: &UpgradeLock<TYPES, V>,
418 ) -> Result<()> {
419 ensure!(epoch == membership.epoch(), "Epochs don't match!");
420 if let Some(ref cert) = upgrade_certificate {
421 let membership_stake_table = membership.stake_table().await;
422 let membership_upgrade_threshold = membership.upgrade_threshold().await;
423
424 cert.is_valid_cert(
425 &StakeTableEntries::<TYPES>::from(membership_stake_table).0,
426 membership_upgrade_threshold,
427 upgrade_lock,
428 )
429 .await
430 .context(|e| warn!("Invalid upgrade certificate: {e}"))?;
431 }
432
433 Ok(())
434 }
435
436 pub fn upgrading_in(&self, view: TYPES::View) -> bool {
439 view > self.data.old_version_last_view && view < self.data.new_version_first_view
440 }
441}
442
443impl<TYPES: NodeType> QuorumCertificate<TYPES> {
444 pub fn to_qc2(self) -> QuorumCertificate2<TYPES> {
446 let bytes: [u8; 32] = self.data.leaf_commit.into();
447 let data = QuorumData2 {
448 leaf_commit: Commitment::from_raw(bytes),
449 epoch: None,
450 block_number: None,
451 };
452
453 let bytes: [u8; 32] = self.vote_commitment.into();
454 let vote_commitment = Commitment::from_raw(bytes);
455
456 SimpleCertificate {
457 data,
458 vote_commitment,
459 view_number: self.view_number,
460 signatures: self.signatures.clone(),
461 _pd: PhantomData,
462 }
463 }
464}
465
466impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
467 pub fn to_qc(self) -> QuorumCertificate<TYPES> {
469 let bytes: [u8; 32] = self.data.leaf_commit.into();
470 let data = QuorumData {
471 leaf_commit: Commitment::from_raw(bytes),
472 };
473
474 let bytes: [u8; 32] = self.vote_commitment.into();
475 let vote_commitment = Commitment::from_raw(bytes);
476
477 SimpleCertificate {
478 data,
479 vote_commitment,
480 view_number: self.view_number,
481 signatures: self.signatures.clone(),
482 _pd: PhantomData,
483 }
484 }
485}
486
487impl<TYPES: NodeType> DaCertificate<TYPES> {
488 pub fn to_dac2(self) -> DaCertificate2<TYPES> {
490 let data = DaData2 {
491 payload_commit: self.data.payload_commit,
492 next_epoch_payload_commit: None,
493 epoch: None,
494 };
495
496 let bytes: [u8; 32] = self.vote_commitment.into();
497 let vote_commitment = Commitment::from_raw(bytes);
498
499 SimpleCertificate {
500 data,
501 vote_commitment,
502 view_number: self.view_number,
503 signatures: self.signatures.clone(),
504 _pd: PhantomData,
505 }
506 }
507}
508
509impl<TYPES: NodeType> DaCertificate2<TYPES> {
510 pub fn to_dac(self) -> DaCertificate<TYPES> {
512 let data = DaData {
513 payload_commit: self.data.payload_commit,
514 };
515
516 let bytes: [u8; 32] = self.vote_commitment.into();
517 let vote_commitment = Commitment::from_raw(bytes);
518
519 SimpleCertificate {
520 data,
521 vote_commitment,
522 view_number: self.view_number,
523 signatures: self.signatures.clone(),
524 _pd: PhantomData,
525 }
526 }
527}
528
529impl<TYPES: NodeType> ViewSyncPreCommitCertificate<TYPES> {
530 pub fn to_vsc2(self) -> ViewSyncPreCommitCertificate2<TYPES> {
532 let data = ViewSyncPreCommitData2 {
533 relay: self.data.relay,
534 round: self.data.round,
535 epoch: None,
536 };
537
538 let bytes: [u8; 32] = self.vote_commitment.into();
539 let vote_commitment = Commitment::from_raw(bytes);
540
541 SimpleCertificate {
542 data,
543 vote_commitment,
544 view_number: self.view_number,
545 signatures: self.signatures.clone(),
546 _pd: PhantomData,
547 }
548 }
549}
550
551impl<TYPES: NodeType> ViewSyncPreCommitCertificate2<TYPES> {
552 pub fn to_vsc(self) -> ViewSyncPreCommitCertificate<TYPES> {
554 let data = ViewSyncPreCommitData {
555 relay: self.data.relay,
556 round: self.data.round,
557 };
558
559 let bytes: [u8; 32] = self.vote_commitment.into();
560 let vote_commitment = Commitment::from_raw(bytes);
561
562 SimpleCertificate {
563 data,
564 vote_commitment,
565 view_number: self.view_number,
566 signatures: self.signatures.clone(),
567 _pd: PhantomData,
568 }
569 }
570}
571
572impl<TYPES: NodeType> ViewSyncCommitCertificate<TYPES> {
573 pub fn to_vsc2(self) -> ViewSyncCommitCertificate2<TYPES> {
575 let data = ViewSyncCommitData2 {
576 relay: self.data.relay,
577 round: self.data.round,
578 epoch: None,
579 };
580
581 let bytes: [u8; 32] = self.vote_commitment.into();
582 let vote_commitment = Commitment::from_raw(bytes);
583
584 SimpleCertificate {
585 data,
586 vote_commitment,
587 view_number: self.view_number,
588 signatures: self.signatures.clone(),
589 _pd: PhantomData,
590 }
591 }
592}
593
594impl<TYPES: NodeType> ViewSyncCommitCertificate2<TYPES> {
595 pub fn to_vsc(self) -> ViewSyncCommitCertificate<TYPES> {
597 let data = ViewSyncCommitData {
598 relay: self.data.relay,
599 round: self.data.round,
600 };
601
602 let bytes: [u8; 32] = self.vote_commitment.into();
603 let vote_commitment = Commitment::from_raw(bytes);
604
605 SimpleCertificate {
606 data,
607 vote_commitment,
608 view_number: self.view_number,
609 signatures: self.signatures.clone(),
610 _pd: PhantomData,
611 }
612 }
613}
614
615impl<TYPES: NodeType> ViewSyncFinalizeCertificate<TYPES> {
616 pub fn to_vsc2(self) -> ViewSyncFinalizeCertificate2<TYPES> {
618 let data = ViewSyncFinalizeData2 {
619 relay: self.data.relay,
620 round: self.data.round,
621 epoch: None,
622 };
623
624 let bytes: [u8; 32] = self.vote_commitment.into();
625 let vote_commitment = Commitment::from_raw(bytes);
626
627 SimpleCertificate {
628 data,
629 vote_commitment,
630 view_number: self.view_number,
631 signatures: self.signatures.clone(),
632 _pd: PhantomData,
633 }
634 }
635}
636
637impl<TYPES: NodeType> ViewSyncFinalizeCertificate2<TYPES> {
638 pub fn to_vsc(self) -> ViewSyncFinalizeCertificate<TYPES> {
640 let data = ViewSyncFinalizeData {
641 relay: self.data.relay,
642 round: self.data.round,
643 };
644
645 let bytes: [u8; 32] = self.vote_commitment.into();
646 let vote_commitment = Commitment::from_raw(bytes);
647
648 SimpleCertificate {
649 data,
650 vote_commitment,
651 view_number: self.view_number,
652 signatures: self.signatures.clone(),
653 _pd: PhantomData,
654 }
655 }
656}
657
658impl<TYPES: NodeType> TimeoutCertificate<TYPES> {
659 pub fn to_tc2(self) -> TimeoutCertificate2<TYPES> {
661 let data = TimeoutData2 {
662 view: self.data.view,
663 epoch: None,
664 };
665
666 let bytes: [u8; 32] = self.vote_commitment.into();
667 let vote_commitment = Commitment::from_raw(bytes);
668
669 SimpleCertificate {
670 data,
671 vote_commitment,
672 view_number: self.view_number,
673 signatures: self.signatures.clone(),
674 _pd: PhantomData,
675 }
676 }
677}
678
679impl<TYPES: NodeType> TimeoutCertificate2<TYPES> {
680 pub fn to_tc(self) -> TimeoutCertificate<TYPES> {
682 let data = TimeoutData {
683 view: self.data.view,
684 };
685
686 let bytes: [u8; 32] = self.vote_commitment.into();
687 let vote_commitment = Commitment::from_raw(bytes);
688
689 SimpleCertificate {
690 data,
691 vote_commitment,
692 view_number: self.view_number,
693 signatures: self.signatures.clone(),
694 _pd: PhantomData,
695 }
696 }
697}
698
699pub type QuorumCertificate<TYPES> = SimpleCertificate<TYPES, QuorumData<TYPES>, SuccessThreshold>;
701pub type QuorumCertificate2<TYPES> = SimpleCertificate<TYPES, QuorumData2<TYPES>, SuccessThreshold>;
703pub type NextEpochQuorumCertificate2<TYPES> =
705 SimpleCertificate<TYPES, NextEpochQuorumData2<TYPES>, SuccessThreshold>;
706pub type DaCertificate<TYPES> = SimpleCertificate<TYPES, DaData, SuccessThreshold>;
708pub type DaCertificate2<TYPES> = SimpleCertificate<TYPES, DaData2<TYPES>, SuccessThreshold>;
710pub type TimeoutCertificate<TYPES> = SimpleCertificate<TYPES, TimeoutData<TYPES>, SuccessThreshold>;
712pub type TimeoutCertificate2<TYPES> =
714 SimpleCertificate<TYPES, TimeoutData2<TYPES>, SuccessThreshold>;
715pub type ViewSyncPreCommitCertificate<TYPES> =
717 SimpleCertificate<TYPES, ViewSyncPreCommitData<TYPES>, OneHonestThreshold>;
718pub type ViewSyncPreCommitCertificate2<TYPES> =
720 SimpleCertificate<TYPES, ViewSyncPreCommitData2<TYPES>, OneHonestThreshold>;
721pub type ViewSyncCommitCertificate<TYPES> =
723 SimpleCertificate<TYPES, ViewSyncCommitData<TYPES>, SuccessThreshold>;
724pub type ViewSyncCommitCertificate2<TYPES> =
726 SimpleCertificate<TYPES, ViewSyncCommitData2<TYPES>, SuccessThreshold>;
727pub type ViewSyncFinalizeCertificate<TYPES> =
729 SimpleCertificate<TYPES, ViewSyncFinalizeData<TYPES>, SuccessThreshold>;
730pub type ViewSyncFinalizeCertificate2<TYPES> =
732 SimpleCertificate<TYPES, ViewSyncFinalizeData2<TYPES>, SuccessThreshold>;
733pub type UpgradeCertificate<TYPES> =
735 SimpleCertificate<TYPES, UpgradeProposalData<TYPES>, UpgradeThreshold>;
736
737#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
739pub struct LightClientStateUpdateCertificateV2<TYPES: NodeType> {
740 pub epoch: TYPES::Epoch,
742 pub light_client_state: LightClientState,
744 pub next_stake_table_state: StakeTableState,
746 #[allow(clippy::type_complexity)]
748 pub signatures: Vec<(
749 TYPES::StateSignatureKey,
750 <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, )>,
753 pub auth_root: FixedBytes<32>,
758}
759
760#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
762pub struct LightClientStateUpdateCertificateV1<TYPES: NodeType> {
763 pub epoch: TYPES::Epoch,
765 pub light_client_state: LightClientState,
767 pub next_stake_table_state: StakeTableState,
769 pub signatures: Vec<(
771 TYPES::StateSignatureKey,
772 <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature,
773 )>,
774}
775
776impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV1<TYPES>>
777 for LightClientStateUpdateCertificateV2<TYPES>
778{
779 fn from(v1: LightClientStateUpdateCertificateV1<TYPES>) -> Self {
780 Self {
781 epoch: v1.epoch,
782 light_client_state: v1.light_client_state,
783 next_stake_table_state: v1.next_stake_table_state,
784 signatures: v1
785 .signatures
786 .into_iter()
787 .map(|(key, sig)| (key, sig.clone(), sig)) .collect(),
789 auth_root: Default::default(),
790 }
791 }
792}
793
794impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV2<TYPES>>
795 for LightClientStateUpdateCertificateV1<TYPES>
796{
797 fn from(v2: LightClientStateUpdateCertificateV2<TYPES>) -> Self {
798 Self {
799 epoch: v2.epoch,
800 light_client_state: v2.light_client_state,
801 next_stake_table_state: v2.next_stake_table_state,
802 signatures: v2
803 .signatures
804 .into_iter()
805 .map(|(key, _, sig)| (key, sig))
806 .collect(),
807 }
808 }
809}
810
811impl<TYPES: NodeType> HasViewNumber<TYPES> for LightClientStateUpdateCertificateV2<TYPES> {
812 fn view_number(&self) -> TYPES::View {
813 TYPES::View::new(self.light_client_state.view_number)
814 }
815}
816
817impl<TYPES: NodeType> HasEpoch<TYPES> for LightClientStateUpdateCertificateV2<TYPES> {
818 fn epoch(&self) -> Option<TYPES::Epoch> {
819 Some(self.epoch)
820 }
821}
822
823impl<TYPES: NodeType> LightClientStateUpdateCertificateV1<TYPES> {
824 pub fn genesis() -> Self {
825 Self {
826 epoch: TYPES::Epoch::genesis(),
827 light_client_state: Default::default(),
828 next_stake_table_state: Default::default(),
829 signatures: vec![],
830 }
831 }
832}
833
834impl<TYPES: NodeType> LightClientStateUpdateCertificateV2<TYPES> {
835 pub fn genesis() -> Self {
836 Self {
837 epoch: TYPES::Epoch::genesis(),
838 light_client_state: Default::default(),
839 next_stake_table_state: Default::default(),
840 signatures: vec![],
841 auth_root: Default::default(),
842 }
843 }
844}
845
846#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
847#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
848pub struct EpochRootQuorumCertificateV2<TYPES: NodeType> {
849 pub qc: QuorumCertificate2<TYPES>,
850 pub state_cert: LightClientStateUpdateCertificateV2<TYPES>,
851}
852
853impl<TYPES: NodeType> HasViewNumber<TYPES> for EpochRootQuorumCertificateV2<TYPES> {
854 fn view_number(&self) -> TYPES::View {
855 self.qc.view_number()
856 }
857}
858
859impl<TYPES: NodeType> HasEpoch<TYPES> for EpochRootQuorumCertificateV2<TYPES> {
860 fn epoch(&self) -> Option<TYPES::Epoch> {
861 self.qc.epoch()
862 }
863}
864
865#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
866#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
867pub struct EpochRootQuorumCertificateV1<TYPES: NodeType> {
868 pub qc: QuorumCertificate2<TYPES>,
869 pub state_cert: LightClientStateUpdateCertificateV1<TYPES>,
870}
871
872impl<TYPES: NodeType> HasViewNumber<TYPES> for EpochRootQuorumCertificateV1<TYPES> {
873 fn view_number(&self) -> TYPES::View {
874 self.qc.view_number()
875 }
876}
877
878impl<TYPES: NodeType> HasEpoch<TYPES> for EpochRootQuorumCertificateV1<TYPES> {
879 fn epoch(&self) -> Option<TYPES::Epoch> {
880 self.qc.epoch()
881 }
882}
883
884impl<TYPES: NodeType> From<EpochRootQuorumCertificateV1<TYPES>>
885 for EpochRootQuorumCertificateV2<TYPES>
886{
887 fn from(root_qc: EpochRootQuorumCertificateV1<TYPES>) -> Self {
888 Self {
889 qc: root_qc.qc,
890 state_cert: root_qc.state_cert.into(),
891 }
892 }
893}
894
895impl<TYPES: NodeType> From<EpochRootQuorumCertificateV2<TYPES>>
896 for EpochRootQuorumCertificateV1<TYPES>
897{
898 fn from(root_qc: EpochRootQuorumCertificateV2<TYPES>) -> Self {
899 Self {
900 qc: root_qc.qc,
901 state_cert: root_qc.state_cert.into(),
902 }
903 }
904}
905
906#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
912#[serde(bound = "")]
913pub struct CertificatePair<TYPES: NodeType> {
914 qc: QuorumCertificate2<TYPES>,
916
917 next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
919}
920
921impl<TYPES: NodeType> CertificatePair<TYPES> {
922 pub fn new(
924 qc: QuorumCertificate2<TYPES>,
925 next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
926 ) -> Self {
927 Self { qc, next_epoch_qc }
928 }
929
930 pub fn non_epoch_change(qc: QuorumCertificate2<TYPES>) -> Self {
932 Self::new(qc, None)
933 }
934
935 pub fn for_parent(leaf: &Leaf2<TYPES>) -> Self {
937 Self {
938 qc: leaf.justify_qc(),
939 next_epoch_qc: leaf.next_epoch_justify_qc(),
940 }
941 }
942
943 pub fn qc(&self) -> &QuorumCertificate2<TYPES> {
945 &self.qc
946 }
947
948 pub fn next_epoch_qc(&self) -> Option<&NextEpochQuorumCertificate2<TYPES>> {
950 self.next_epoch_qc.as_ref()
951 }
952
953 pub fn leaf_commit(&self) -> Commitment<Leaf2<TYPES>> {
955 self.qc.data.leaf_commit
956 }
957
958 pub fn epoch(&self) -> Option<TYPES::Epoch> {
962 self.qc.data.epoch
963 }
964
965 pub fn block_number(&self) -> Option<u64> {
969 self.qc.data.block_number
970 }
971
972 pub fn verify_next_epoch_qc(
982 &self,
983 epoch_height: u64,
984 ) -> Result<Option<&NextEpochQuorumCertificate2<TYPES>>> {
985 let block_number = self.qc.data.block_number.context(warn!(
986 "QC for epoch {:?} has no block number",
987 self.qc.data.epoch
988 ))?;
989 if !is_epoch_transition(block_number, epoch_height) {
990 tracing::debug!(
991 block_number,
992 epoch_height,
993 "QC is not in an epoch transition"
994 );
995 return Ok(None);
996 }
997
998 let next_epoch_qc = self.next_epoch_qc.as_ref().context(warn!(
999 "Received High QC for the transition block {block_number} but not the next epoch QC"
1000 ))?;
1001
1002 ensure!(self.qc.view_number == next_epoch_qc.view_number);
1004 ensure!(self.qc.data == *next_epoch_qc.data);
1005
1006 Ok(Some(next_epoch_qc))
1007 }
1008}
1009
1010impl<TYPES: NodeType> HasViewNumber<TYPES> for CertificatePair<TYPES> {
1011 fn view_number(&self) -> TYPES::View {
1012 self.qc.view_number()
1013 }
1014}