1use std::{
10 fmt::{self, Debug, Display, Formatter},
11 hash::Hash,
12 marker::PhantomData,
13};
14
15use alloy::primitives::{FixedBytes, U256};
16use committable::{Commitment, Committable};
17use hotshot_utils::anytrace::*;
18use serde::{Deserialize, Serialize};
19
20use crate::{
21 PeerConfig,
22 data::{EpochNumber, Leaf2, ViewNumber, serialize_signature2},
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::NodeType,
35 signature_key::{SignatureKey, StateSignatureKey},
36 },
37 utils::{is_epoch_root, is_epoch_transition},
38 vote::{Certificate, HasViewNumber},
39};
40
41pub trait Threshold<TYPES: NodeType> {
43 fn threshold(membership: &EpochMembership<TYPES>) -> U256;
45}
46
47#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
49pub struct SuccessThreshold {}
50
51impl<TYPES: NodeType> Threshold<TYPES> for SuccessThreshold {
52 fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
53 membership.success_threshold()
54 }
55}
56
57#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
59pub struct OneHonestThreshold {}
60
61impl<TYPES: NodeType> Threshold<TYPES> for OneHonestThreshold {
62 fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
63 membership.failure_threshold()
64 }
65}
66
67#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
69pub struct UpgradeThreshold {}
70
71impl<TYPES: NodeType> Threshold<TYPES> for UpgradeThreshold {
72 fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
73 membership.upgrade_threshold()
74 }
75}
76
77#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
79pub struct SimpleCertificate<
80 TYPES: NodeType,
81 VOTEABLE: Voteable<TYPES>,
82 THRESHOLD: Threshold<TYPES>,
83> {
84 pub data: VOTEABLE,
86 vote_commitment: Commitment<VOTEABLE>,
88 pub view_number: ViewNumber,
90 pub signatures: Option<<TYPES::SignatureKey as SignatureKey>::QcType>,
92 pub _pd: PhantomData<(TYPES, THRESHOLD)>,
94}
95
96impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES>, THRESHOLD: Threshold<TYPES>>
97 SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
98{
99 pub fn new(
101 data: VOTEABLE,
102 vote_commitment: Commitment<VOTEABLE>,
103 view_number: ViewNumber,
104 signatures: Option<<TYPES::SignatureKey as SignatureKey>::QcType>,
105 pd: PhantomData<(TYPES, THRESHOLD)>,
106 ) -> Self {
107 Self {
108 data,
109 vote_commitment,
110 view_number,
111 signatures,
112 _pd: pd,
113 }
114 }
115
116 fn signers(
117 &self,
118 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
119 threshold: U256,
120 ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
121 if self.view_number == ViewNumber::genesis() {
122 return Ok(vec![]);
123 }
124 let real_qc_pp =
125 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
126
127 let Some(ref signatures) = self.signatures else {
128 bail!("No signatures found while retrieving signers");
129 };
130
131 <TYPES::SignatureKey as SignatureKey>::signers(&real_qc_pp, signatures)
132 .wrap()
133 .context(|e| warn!("Tracing signers: {e}"))
134 }
135}
136
137impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + Committable, THRESHOLD: Threshold<TYPES>>
138 Committable for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
139{
140 fn commit(&self) -> Commitment<Self> {
141 let signature_bytes = match self.signatures.as_ref() {
142 Some(sigs) => serialize_signature2::<TYPES>(sigs),
143 None => vec![],
144 };
145 committable::RawCommitmentBuilder::new("Certificate")
146 .field("data", self.data.commit())
147 .field("vote_commitment", self.vote_commitment)
148 .field("view number", self.view_number.commit())
149 .var_size_field("signatures", &signature_bytes)
150 .finalize()
151 }
152}
153
154impl<TYPES: NodeType, THRESHOLD: Threshold<TYPES>> Certificate<TYPES, DaData>
155 for SimpleCertificate<TYPES, DaData, THRESHOLD>
156{
157 type Voteable = DaData;
158 type Threshold = THRESHOLD;
159
160 fn create_signed_certificate(
161 vote_commitment: Commitment<VersionedVoteData<TYPES, DaData>>,
162 data: Self::Voteable,
163 sig: <TYPES::SignatureKey as SignatureKey>::QcType,
164 view: ViewNumber,
165 ) -> Self {
166 let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
167
168 SimpleCertificate {
169 data,
170 vote_commitment: Commitment::from_raw(vote_commitment_bytes),
171 view_number: view,
172 signatures: Some(sig),
173 _pd: PhantomData,
174 }
175 }
176 fn is_valid_cert(
177 &self,
178 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
179 threshold: U256,
180 upgrade_lock: &UpgradeLock<TYPES>,
181 ) -> Result<()> {
182 if self.view_number == ViewNumber::genesis() {
183 return Ok(());
184 }
185 let real_qc_pp =
186 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
187 let commit = self.data_commitment(upgrade_lock)?;
188
189 let Some(ref signatures) = self.signatures else {
190 bail!("No signatures found while validating certificate");
191 };
192
193 <TYPES::SignatureKey as SignatureKey>::check(&real_qc_pp, commit.as_ref(), signatures)
194 .wrap()
195 .context(|e| warn!("Signature check failed: {e}"))
196 }
197 fn signers(
198 &self,
199 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
200 threshold: U256,
201 ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
202 self.signers(stake_table, threshold)
203 }
204 fn stake_table_entry(
206 membership: &EpochMembership<TYPES>,
207 pub_key: &TYPES::SignatureKey,
208 ) -> Option<PeerConfig<TYPES>> {
209 membership.da_stake(pub_key)
210 }
211
212 fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
214 membership.da_stake_table().collect()
215 }
216
217 fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
219 membership.da_total_nodes()
220 }
221
222 fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
223 membership.da_success_threshold()
224 }
225
226 fn data(&self) -> &Self::Voteable {
227 &self.data
228 }
229
230 fn data_commitment(
231 &self,
232 upgrade_lock: &UpgradeLock<TYPES>,
233 ) -> Result<Commitment<VersionedVoteData<TYPES, DaData>>> {
234 Ok(VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)?.commit())
235 }
236}
237
238impl<TYPES: NodeType, THRESHOLD: Threshold<TYPES>> Certificate<TYPES, DaData2>
239 for SimpleCertificate<TYPES, DaData2, THRESHOLD>
240{
241 type Voteable = DaData2;
242 type Threshold = THRESHOLD;
243
244 fn create_signed_certificate(
245 vote_commitment: Commitment<VersionedVoteData<TYPES, DaData2>>,
246 data: Self::Voteable,
247 sig: <TYPES::SignatureKey as SignatureKey>::QcType,
248 view: ViewNumber,
249 ) -> Self {
250 let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
251
252 SimpleCertificate {
253 data,
254 vote_commitment: Commitment::from_raw(vote_commitment_bytes),
255 view_number: view,
256 signatures: Some(sig),
257 _pd: PhantomData,
258 }
259 }
260 fn is_valid_cert(
261 &self,
262 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
263 threshold: U256,
264 upgrade_lock: &UpgradeLock<TYPES>,
265 ) -> Result<()> {
266 if self.view_number == ViewNumber::genesis() {
267 return Ok(());
268 }
269 let real_qc_pp =
270 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
271 let commit = self.data_commitment(upgrade_lock)?;
272 let signatures = self
273 .signatures
274 .as_ref()
275 .ok_or_else(|| warn!("missing signatures"))?;
276
277 <TYPES::SignatureKey as SignatureKey>::check(&real_qc_pp, commit.as_ref(), signatures)
278 .wrap()
279 .context(|e| warn!("Signature check failed: {e}"))
280 }
281 fn signers(
282 &self,
283 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
284 threshold: U256,
285 ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
286 self.signers(stake_table, threshold)
287 }
288 fn stake_table_entry(
290 membership: &EpochMembership<TYPES>,
291 pub_key: &TYPES::SignatureKey,
292 ) -> Option<PeerConfig<TYPES>> {
293 membership.da_stake(pub_key)
294 }
295
296 fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
298 membership.da_stake_table().collect()
299 }
300
301 fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
303 membership.da_total_nodes()
304 }
305
306 fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
307 membership.da_success_threshold()
308 }
309
310 fn data(&self) -> &Self::Voteable {
311 &self.data
312 }
313
314 fn data_commitment(
315 &self,
316 upgrade_lock: &UpgradeLock<TYPES>,
317 ) -> Result<Commitment<VersionedVoteData<TYPES, DaData2>>> {
318 Ok(VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)?.commit())
319 }
320}
321
322impl<
323 TYPES: NodeType,
324 VOTEABLE: Voteable<TYPES> + 'static + QuorumMarker,
325 THRESHOLD: Threshold<TYPES>,
326> Certificate<TYPES, VOTEABLE> for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
327{
328 type Voteable = VOTEABLE;
329 type Threshold = THRESHOLD;
330
331 fn create_signed_certificate(
332 vote_commitment: Commitment<VersionedVoteData<TYPES, VOTEABLE>>,
333 data: Self::Voteable,
334 sig: <TYPES::SignatureKey as SignatureKey>::QcType,
335 view: ViewNumber,
336 ) -> Self {
337 let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
338
339 SimpleCertificate {
340 data,
341 vote_commitment: Commitment::from_raw(vote_commitment_bytes),
342 view_number: view,
343 signatures: Some(sig),
344 _pd: PhantomData,
345 }
346 }
347
348 fn is_valid_cert(
349 &self,
350 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
351 threshold: U256,
352 upgrade_lock: &UpgradeLock<TYPES>,
353 ) -> Result<()> {
354 if self.view_number == ViewNumber::genesis() {
355 return Ok(());
356 }
357 let real_qc_pp =
358 <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
359 let commit = self.data_commitment(upgrade_lock)?;
360 let signatures = self
361 .signatures
362 .as_ref()
363 .ok_or_else(|| warn!("missing signatures"))?;
364
365 <TYPES::SignatureKey as SignatureKey>::check(&real_qc_pp, commit.as_ref(), signatures)
366 .wrap()
367 .context(|e| warn!("Signature check failed: {e}"))
368 }
369
370 fn signers(
371 &self,
372 stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
373 threshold: U256,
374 ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
375 self.signers(stake_table, threshold)
376 }
377
378 fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
379 THRESHOLD::threshold(membership)
380 }
381
382 fn stake_table_entry(
383 membership: &EpochMembership<TYPES>,
384 pub_key: &TYPES::SignatureKey,
385 ) -> Option<PeerConfig<TYPES>> {
386 membership.stake(pub_key)
387 }
388
389 fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
390 membership.stake_table().collect()
391 }
392
393 fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
395 membership.total_nodes()
396 }
397
398 fn data(&self) -> &Self::Voteable {
399 &self.data
400 }
401 fn data_commitment(
402 &self,
403 upgrade_lock: &UpgradeLock<TYPES>,
404 ) -> Result<Commitment<VersionedVoteData<TYPES, VOTEABLE>>> {
405 Ok(VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)?.commit())
406 }
407}
408
409impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + 'static, THRESHOLD: Threshold<TYPES>>
410 HasViewNumber for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
411{
412 fn view_number(&self) -> ViewNumber {
413 self.view_number
414 }
415}
416
417impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + HasEpoch + 'static, THRESHOLD: Threshold<TYPES>>
418 HasEpoch for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
419{
420 fn epoch(&self) -> Option<EpochNumber> {
421 self.data.epoch()
422 }
423}
424
425impl<TYPES: NodeType> Display for QuorumCertificate<TYPES> {
426 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
427 write!(f, "view: {:?}", self.view_number)
428 }
429}
430
431impl<TYPES: NodeType> UpgradeCertificate<TYPES> {
432 pub async fn is_relevant(&self, view_number: ViewNumber) -> Result<()> {
438 ensure!(
439 self.data.new_version_first_view >= view_number,
440 "Upgrade certificate is no longer relevant."
441 );
442
443 Ok(())
444 }
445
446 pub async fn validate(
450 upgrade_certificate: &Option<Self>,
451 membership: &EpochMembership<TYPES>,
452 epoch: Option<EpochNumber>,
453 upgrade_lock: &UpgradeLock<TYPES>,
454 ) -> Result<()> {
455 ensure!(epoch == membership.epoch(), "Epochs don't match!");
456 if let Some(cert) = upgrade_certificate {
457 let membership_stake_table = membership.stake_table();
458 let membership_upgrade_threshold = membership.upgrade_threshold();
459
460 cert.is_valid_cert(
461 &StakeTableEntries::from_iter(membership_stake_table).0,
462 membership_upgrade_threshold,
463 upgrade_lock,
464 )
465 .context(|e| warn!("Invalid upgrade certificate: {e}"))?;
466 }
467
468 Ok(())
469 }
470
471 pub fn upgrading_in(&self, view: ViewNumber) -> bool {
474 view > self.data.old_version_last_view && view < self.data.new_version_first_view
475 }
476}
477
478impl<TYPES: NodeType> QuorumCertificate<TYPES> {
479 pub fn to_qc2(self) -> QuorumCertificate2<TYPES> {
481 let bytes: [u8; 32] = self.data.leaf_commit.into();
482 let data = QuorumData2 {
483 leaf_commit: Commitment::from_raw(bytes),
484 epoch: None,
485 block_number: None,
486 };
487
488 let bytes: [u8; 32] = self.vote_commitment.into();
489 let vote_commitment = Commitment::from_raw(bytes);
490
491 SimpleCertificate {
492 data,
493 vote_commitment,
494 view_number: self.view_number,
495 signatures: self.signatures.clone(),
496 _pd: PhantomData,
497 }
498 }
499}
500
501impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
502 pub fn to_qc(self) -> QuorumCertificate<TYPES> {
504 let bytes: [u8; 32] = self.data.leaf_commit.into();
505 let data = QuorumData {
506 leaf_commit: Commitment::from_raw(bytes),
507 };
508
509 let bytes: [u8; 32] = self.vote_commitment.into();
510 let vote_commitment = Commitment::from_raw(bytes);
511
512 SimpleCertificate {
513 data,
514 vote_commitment,
515 view_number: self.view_number,
516 signatures: self.signatures.clone(),
517 _pd: PhantomData,
518 }
519 }
520}
521
522impl<TYPES: NodeType> DaCertificate<TYPES> {
523 pub fn to_dac2(self) -> DaCertificate2<TYPES> {
525 let data = DaData2 {
526 payload_commit: self.data.payload_commit,
527 next_epoch_payload_commit: None,
528 epoch: None,
529 };
530
531 let bytes: [u8; 32] = self.vote_commitment.into();
532 let vote_commitment = Commitment::from_raw(bytes);
533
534 SimpleCertificate {
535 data,
536 vote_commitment,
537 view_number: self.view_number,
538 signatures: self.signatures.clone(),
539 _pd: PhantomData,
540 }
541 }
542}
543
544impl<TYPES: NodeType> DaCertificate2<TYPES> {
545 pub fn to_dac(self) -> DaCertificate<TYPES> {
547 let data = DaData {
548 payload_commit: self.data.payload_commit,
549 };
550
551 let bytes: [u8; 32] = self.vote_commitment.into();
552 let vote_commitment = Commitment::from_raw(bytes);
553
554 SimpleCertificate {
555 data,
556 vote_commitment,
557 view_number: self.view_number,
558 signatures: self.signatures.clone(),
559 _pd: PhantomData,
560 }
561 }
562}
563
564impl<TYPES: NodeType> ViewSyncPreCommitCertificate<TYPES> {
565 pub fn to_vsc2(self) -> ViewSyncPreCommitCertificate2<TYPES> {
567 let data = ViewSyncPreCommitData2 {
568 relay: self.data.relay,
569 round: self.data.round,
570 epoch: None,
571 };
572
573 let bytes: [u8; 32] = self.vote_commitment.into();
574 let vote_commitment = Commitment::from_raw(bytes);
575
576 SimpleCertificate {
577 data,
578 vote_commitment,
579 view_number: self.view_number,
580 signatures: self.signatures.clone(),
581 _pd: PhantomData,
582 }
583 }
584}
585
586impl<TYPES: NodeType> ViewSyncPreCommitCertificate2<TYPES> {
587 pub fn to_vsc(self) -> ViewSyncPreCommitCertificate<TYPES> {
589 let data = ViewSyncPreCommitData {
590 relay: self.data.relay,
591 round: self.data.round,
592 };
593
594 let bytes: [u8; 32] = self.vote_commitment.into();
595 let vote_commitment = Commitment::from_raw(bytes);
596
597 SimpleCertificate {
598 data,
599 vote_commitment,
600 view_number: self.view_number,
601 signatures: self.signatures.clone(),
602 _pd: PhantomData,
603 }
604 }
605}
606
607impl<TYPES: NodeType> ViewSyncCommitCertificate<TYPES> {
608 pub fn to_vsc2(self) -> ViewSyncCommitCertificate2<TYPES> {
610 let data = ViewSyncCommitData2 {
611 relay: self.data.relay,
612 round: self.data.round,
613 epoch: None,
614 };
615
616 let bytes: [u8; 32] = self.vote_commitment.into();
617 let vote_commitment = Commitment::from_raw(bytes);
618
619 SimpleCertificate {
620 data,
621 vote_commitment,
622 view_number: self.view_number,
623 signatures: self.signatures.clone(),
624 _pd: PhantomData,
625 }
626 }
627}
628
629impl<TYPES: NodeType> ViewSyncCommitCertificate2<TYPES> {
630 pub fn to_vsc(self) -> ViewSyncCommitCertificate<TYPES> {
632 let data = ViewSyncCommitData {
633 relay: self.data.relay,
634 round: self.data.round,
635 };
636
637 let bytes: [u8; 32] = self.vote_commitment.into();
638 let vote_commitment = Commitment::from_raw(bytes);
639
640 SimpleCertificate {
641 data,
642 vote_commitment,
643 view_number: self.view_number,
644 signatures: self.signatures.clone(),
645 _pd: PhantomData,
646 }
647 }
648}
649
650impl<TYPES: NodeType> ViewSyncFinalizeCertificate<TYPES> {
651 pub fn to_vsc2(self) -> ViewSyncFinalizeCertificate2<TYPES> {
653 let data = ViewSyncFinalizeData2 {
654 relay: self.data.relay,
655 round: self.data.round,
656 epoch: None,
657 };
658
659 let bytes: [u8; 32] = self.vote_commitment.into();
660 let vote_commitment = Commitment::from_raw(bytes);
661
662 SimpleCertificate {
663 data,
664 vote_commitment,
665 view_number: self.view_number,
666 signatures: self.signatures.clone(),
667 _pd: PhantomData,
668 }
669 }
670}
671
672impl<TYPES: NodeType> ViewSyncFinalizeCertificate2<TYPES> {
673 pub fn to_vsc(self) -> ViewSyncFinalizeCertificate<TYPES> {
675 let data = ViewSyncFinalizeData {
676 relay: self.data.relay,
677 round: self.data.round,
678 };
679
680 let bytes: [u8; 32] = self.vote_commitment.into();
681 let vote_commitment = Commitment::from_raw(bytes);
682
683 SimpleCertificate {
684 data,
685 vote_commitment,
686 view_number: self.view_number,
687 signatures: self.signatures.clone(),
688 _pd: PhantomData,
689 }
690 }
691}
692
693impl<TYPES: NodeType> TimeoutCertificate<TYPES> {
694 pub fn to_tc2(self) -> TimeoutCertificate2<TYPES> {
696 let data = TimeoutData2 {
697 view: self.data.view,
698 epoch: None,
699 };
700
701 let bytes: [u8; 32] = self.vote_commitment.into();
702 let vote_commitment = Commitment::from_raw(bytes);
703
704 SimpleCertificate {
705 data,
706 vote_commitment,
707 view_number: self.view_number,
708 signatures: self.signatures.clone(),
709 _pd: PhantomData,
710 }
711 }
712}
713
714impl<TYPES: NodeType> TimeoutCertificate2<TYPES> {
715 pub fn to_tc(self) -> TimeoutCertificate<TYPES> {
717 let data = TimeoutData {
718 view: self.data.view,
719 };
720
721 let bytes: [u8; 32] = self.vote_commitment.into();
722 let vote_commitment = Commitment::from_raw(bytes);
723
724 SimpleCertificate {
725 data,
726 vote_commitment,
727 view_number: self.view_number,
728 signatures: self.signatures.clone(),
729 _pd: PhantomData,
730 }
731 }
732}
733
734pub type QuorumCertificate<TYPES> = SimpleCertificate<TYPES, QuorumData<TYPES>, SuccessThreshold>;
736pub type QuorumCertificate2<TYPES> = SimpleCertificate<TYPES, QuorumData2<TYPES>, SuccessThreshold>;
738pub type NextEpochQuorumCertificate2<TYPES> =
740 SimpleCertificate<TYPES, NextEpochQuorumData2<TYPES>, SuccessThreshold>;
741pub type DaCertificate<TYPES> = SimpleCertificate<TYPES, DaData, SuccessThreshold>;
743pub type DaCertificate2<TYPES> = SimpleCertificate<TYPES, DaData2, SuccessThreshold>;
745pub type TimeoutCertificate<TYPES> = SimpleCertificate<TYPES, TimeoutData, SuccessThreshold>;
747pub type TimeoutCertificate2<TYPES> = SimpleCertificate<TYPES, TimeoutData2, SuccessThreshold>;
749pub type ViewSyncPreCommitCertificate<TYPES> =
751 SimpleCertificate<TYPES, ViewSyncPreCommitData, OneHonestThreshold>;
752pub type ViewSyncPreCommitCertificate2<TYPES> =
754 SimpleCertificate<TYPES, ViewSyncPreCommitData2, OneHonestThreshold>;
755pub type ViewSyncCommitCertificate<TYPES> =
757 SimpleCertificate<TYPES, ViewSyncCommitData, SuccessThreshold>;
758pub type ViewSyncCommitCertificate2<TYPES> =
760 SimpleCertificate<TYPES, ViewSyncCommitData2, SuccessThreshold>;
761pub type ViewSyncFinalizeCertificate<TYPES> =
763 SimpleCertificate<TYPES, ViewSyncFinalizeData, SuccessThreshold>;
764pub type ViewSyncFinalizeCertificate2<TYPES> =
766 SimpleCertificate<TYPES, ViewSyncFinalizeData2, SuccessThreshold>;
767pub type UpgradeCertificate<TYPES> =
769 SimpleCertificate<TYPES, UpgradeProposalData, UpgradeThreshold>;
770
771#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
773pub struct LightClientStateUpdateCertificateV2<TYPES: NodeType> {
774 pub epoch: EpochNumber,
776 pub light_client_state: LightClientState,
778 pub next_stake_table_state: StakeTableState,
780 #[allow(clippy::type_complexity)]
782 pub signatures: Vec<(
783 TYPES::StateSignatureKey,
784 <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, )>,
787 pub auth_root: FixedBytes<32>,
792}
793
794#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
796pub struct LightClientStateUpdateCertificateV1<TYPES: NodeType> {
797 pub epoch: EpochNumber,
799 pub light_client_state: LightClientState,
801 pub next_stake_table_state: StakeTableState,
803 pub signatures: Vec<(
805 TYPES::StateSignatureKey,
806 <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature,
807 )>,
808}
809
810impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV1<TYPES>>
811 for LightClientStateUpdateCertificateV2<TYPES>
812{
813 fn from(v1: LightClientStateUpdateCertificateV1<TYPES>) -> Self {
814 Self {
815 epoch: v1.epoch,
816 light_client_state: v1.light_client_state,
817 next_stake_table_state: v1.next_stake_table_state,
818 signatures: v1
819 .signatures
820 .into_iter()
821 .map(|(key, sig)| (key, sig.clone(), sig)) .collect(),
823 auth_root: Default::default(),
824 }
825 }
826}
827
828impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV2<TYPES>>
829 for LightClientStateUpdateCertificateV1<TYPES>
830{
831 fn from(v2: LightClientStateUpdateCertificateV2<TYPES>) -> Self {
832 Self {
833 epoch: v2.epoch,
834 light_client_state: v2.light_client_state,
835 next_stake_table_state: v2.next_stake_table_state,
836 signatures: v2
837 .signatures
838 .into_iter()
839 .map(|(key, _, sig)| (key, sig))
840 .collect(),
841 }
842 }
843}
844
845impl<TYPES: NodeType> HasViewNumber for LightClientStateUpdateCertificateV2<TYPES> {
846 fn view_number(&self) -> ViewNumber {
847 ViewNumber::new(self.light_client_state.view_number)
848 }
849}
850
851impl<TYPES: NodeType> HasEpoch for LightClientStateUpdateCertificateV2<TYPES> {
852 fn epoch(&self) -> Option<EpochNumber> {
853 Some(self.epoch)
854 }
855}
856
857impl<TYPES: NodeType> LightClientStateUpdateCertificateV1<TYPES> {
858 pub fn genesis() -> Self {
859 Self {
860 epoch: EpochNumber::genesis(),
861 light_client_state: Default::default(),
862 next_stake_table_state: Default::default(),
863 signatures: vec![],
864 }
865 }
866}
867
868impl<TYPES: NodeType> LightClientStateUpdateCertificateV2<TYPES> {
869 pub fn genesis() -> Self {
870 Self {
871 epoch: EpochNumber::genesis(),
872 light_client_state: Default::default(),
873 next_stake_table_state: Default::default(),
874 signatures: vec![],
875 auth_root: Default::default(),
876 }
877 }
878}
879
880#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
881#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
882pub struct EpochRootQuorumCertificateV2<TYPES: NodeType> {
883 pub qc: QuorumCertificate2<TYPES>,
884 pub state_cert: LightClientStateUpdateCertificateV2<TYPES>,
885}
886
887impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV2<TYPES> {
888 fn view_number(&self) -> ViewNumber {
889 self.qc.view_number()
890 }
891}
892
893impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV2<TYPES> {
894 fn epoch(&self) -> Option<EpochNumber> {
895 self.qc.epoch()
896 }
897}
898
899#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
900#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
901pub struct EpochRootQuorumCertificateV1<TYPES: NodeType> {
902 pub qc: QuorumCertificate2<TYPES>,
903 pub state_cert: LightClientStateUpdateCertificateV1<TYPES>,
904}
905
906impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV1<TYPES> {
907 fn view_number(&self) -> ViewNumber {
908 self.qc.view_number()
909 }
910}
911
912impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV1<TYPES> {
913 fn epoch(&self) -> Option<EpochNumber> {
914 self.qc.epoch()
915 }
916}
917
918impl<TYPES: NodeType> From<EpochRootQuorumCertificateV1<TYPES>>
919 for EpochRootQuorumCertificateV2<TYPES>
920{
921 fn from(root_qc: EpochRootQuorumCertificateV1<TYPES>) -> Self {
922 Self {
923 qc: root_qc.qc,
924 state_cert: root_qc.state_cert.into(),
925 }
926 }
927}
928
929impl<TYPES: NodeType> From<EpochRootQuorumCertificateV2<TYPES>>
930 for EpochRootQuorumCertificateV1<TYPES>
931{
932 fn from(root_qc: EpochRootQuorumCertificateV2<TYPES>) -> Self {
933 Self {
934 qc: root_qc.qc,
935 state_cert: root_qc.state_cert.into(),
936 }
937 }
938}
939
940#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
946#[serde(bound = "")]
947pub struct CertificatePair<TYPES: NodeType> {
948 qc: QuorumCertificate2<TYPES>,
950
951 next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
953}
954
955impl<TYPES: NodeType> CertificatePair<TYPES> {
956 pub fn new(
958 qc: QuorumCertificate2<TYPES>,
959 next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
960 ) -> Self {
961 Self { qc, next_epoch_qc }
962 }
963
964 pub fn non_epoch_change(qc: QuorumCertificate2<TYPES>) -> Self {
966 Self::new(qc, None)
967 }
968
969 pub fn for_parent(leaf: &Leaf2<TYPES>) -> Self {
971 Self {
972 qc: leaf.justify_qc(),
973 next_epoch_qc: leaf.next_epoch_justify_qc(),
974 }
975 }
976
977 pub fn qc(&self) -> &QuorumCertificate2<TYPES> {
979 &self.qc
980 }
981
982 pub fn next_epoch_qc(&self) -> Option<&NextEpochQuorumCertificate2<TYPES>> {
984 self.next_epoch_qc.as_ref()
985 }
986
987 pub fn leaf_commit(&self) -> Commitment<Leaf2<TYPES>> {
989 self.qc.data.leaf_commit
990 }
991
992 pub fn epoch(&self) -> Option<EpochNumber> {
996 self.qc.data.epoch
997 }
998
999 pub fn block_number(&self) -> Option<u64> {
1003 self.qc.data.block_number
1004 }
1005
1006 pub fn verify_next_epoch_qc(
1016 &self,
1017 epoch_height: u64,
1018 ) -> Result<Option<&NextEpochQuorumCertificate2<TYPES>>> {
1019 let block_number = self.qc.data.block_number.context(warn!(
1020 "QC for epoch {:?} has no block number",
1021 self.qc.data.epoch
1022 ))?;
1023 if !is_epoch_transition(block_number, epoch_height) {
1024 tracing::debug!(
1025 block_number,
1026 epoch_height,
1027 "QC is not in an epoch transition"
1028 );
1029 return Ok(None);
1030 }
1031
1032 let next_epoch_qc = self.next_epoch_qc.as_ref().context(warn!(
1033 "Received High QC for the transition block {block_number} but not the next epoch QC"
1034 ))?;
1035
1036 ensure!(self.qc.view_number == next_epoch_qc.view_number);
1038 ensure!(self.qc.data == *next_epoch_qc.data);
1039
1040 Ok(Some(next_epoch_qc))
1041 }
1042}
1043
1044impl<TYPES: NodeType> HasViewNumber for CertificatePair<TYPES> {
1045 fn view_number(&self) -> ViewNumber {
1046 self.qc.view_number()
1047 }
1048}
1049
1050pub fn check_qc_state_cert_correspondence<TYPES: NodeType>(
1053 qc: &QuorumCertificate2<TYPES>,
1054 state_cert: &LightClientStateUpdateCertificateV2<TYPES>,
1055 epoch_height: u64,
1056) -> bool {
1057 qc.data
1058 .block_number
1059 .is_some_and(|bn| is_epoch_root(bn, epoch_height))
1060 && Some(state_cert.epoch) == qc.data.epoch()
1061 && qc.view_number().u64() == state_cert.light_client_state.view_number
1062}