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, Vote2Data, 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 Certificate1<TYPES> = SimpleCertificate<TYPES, QuorumData2<TYPES>, SuccessThreshold>;
743pub type Certificate2<TYPES> = SimpleCertificate<TYPES, Vote2Data<TYPES>, SuccessThreshold>;
745pub type DaCertificate<TYPES> = SimpleCertificate<TYPES, DaData, SuccessThreshold>;
747pub type DaCertificate2<TYPES> = SimpleCertificate<TYPES, DaData2, SuccessThreshold>;
749pub type TimeoutCertificate<TYPES> = SimpleCertificate<TYPES, TimeoutData, SuccessThreshold>;
751pub type TimeoutCertificate2<TYPES> = SimpleCertificate<TYPES, TimeoutData2, SuccessThreshold>;
753pub type ViewSyncPreCommitCertificate<TYPES> =
755 SimpleCertificate<TYPES, ViewSyncPreCommitData, OneHonestThreshold>;
756pub type ViewSyncPreCommitCertificate2<TYPES> =
758 SimpleCertificate<TYPES, ViewSyncPreCommitData2, OneHonestThreshold>;
759pub type ViewSyncCommitCertificate<TYPES> =
761 SimpleCertificate<TYPES, ViewSyncCommitData, SuccessThreshold>;
762pub type ViewSyncCommitCertificate2<TYPES> =
764 SimpleCertificate<TYPES, ViewSyncCommitData2, SuccessThreshold>;
765pub type ViewSyncFinalizeCertificate<TYPES> =
767 SimpleCertificate<TYPES, ViewSyncFinalizeData, SuccessThreshold>;
768pub type ViewSyncFinalizeCertificate2<TYPES> =
770 SimpleCertificate<TYPES, ViewSyncFinalizeData2, SuccessThreshold>;
771pub type UpgradeCertificate<TYPES> =
773 SimpleCertificate<TYPES, UpgradeProposalData, UpgradeThreshold>;
774
775#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
777pub struct LightClientStateUpdateCertificateV2<TYPES: NodeType> {
778 pub epoch: EpochNumber,
780 pub light_client_state: LightClientState,
782 pub next_stake_table_state: StakeTableState,
784 #[allow(clippy::type_complexity)]
786 pub signatures: Vec<(
787 TYPES::StateSignatureKey,
788 <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, )>,
791 pub auth_root: FixedBytes<32>,
796}
797
798#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
800pub struct LightClientStateUpdateCertificateV1<TYPES: NodeType> {
801 pub epoch: EpochNumber,
803 pub light_client_state: LightClientState,
805 pub next_stake_table_state: StakeTableState,
807 pub signatures: Vec<(
809 TYPES::StateSignatureKey,
810 <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature,
811 )>,
812}
813
814impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV1<TYPES>>
815 for LightClientStateUpdateCertificateV2<TYPES>
816{
817 fn from(v1: LightClientStateUpdateCertificateV1<TYPES>) -> Self {
818 Self {
819 epoch: v1.epoch,
820 light_client_state: v1.light_client_state,
821 next_stake_table_state: v1.next_stake_table_state,
822 signatures: v1
823 .signatures
824 .into_iter()
825 .map(|(key, sig)| (key, sig.clone(), sig)) .collect(),
827 auth_root: Default::default(),
828 }
829 }
830}
831
832impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV2<TYPES>>
833 for LightClientStateUpdateCertificateV1<TYPES>
834{
835 fn from(v2: LightClientStateUpdateCertificateV2<TYPES>) -> Self {
836 Self {
837 epoch: v2.epoch,
838 light_client_state: v2.light_client_state,
839 next_stake_table_state: v2.next_stake_table_state,
840 signatures: v2
841 .signatures
842 .into_iter()
843 .map(|(key, _, sig)| (key, sig))
844 .collect(),
845 }
846 }
847}
848
849impl<TYPES: NodeType> HasViewNumber for LightClientStateUpdateCertificateV2<TYPES> {
850 fn view_number(&self) -> ViewNumber {
851 ViewNumber::new(self.light_client_state.view_number)
852 }
853}
854
855impl<TYPES: NodeType> HasEpoch for LightClientStateUpdateCertificateV2<TYPES> {
856 fn epoch(&self) -> Option<EpochNumber> {
857 Some(self.epoch)
858 }
859}
860
861impl<TYPES: NodeType> LightClientStateUpdateCertificateV1<TYPES> {
862 pub fn genesis() -> Self {
863 Self {
864 epoch: EpochNumber::genesis(),
865 light_client_state: Default::default(),
866 next_stake_table_state: Default::default(),
867 signatures: vec![],
868 }
869 }
870}
871
872impl<TYPES: NodeType> LightClientStateUpdateCertificateV2<TYPES> {
873 pub fn genesis() -> Self {
874 Self {
875 epoch: EpochNumber::genesis(),
876 light_client_state: Default::default(),
877 next_stake_table_state: Default::default(),
878 signatures: vec![],
879 auth_root: Default::default(),
880 }
881 }
882}
883
884#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
885#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
886pub struct EpochRootQuorumCertificateV2<TYPES: NodeType> {
887 pub qc: QuorumCertificate2<TYPES>,
888 pub state_cert: LightClientStateUpdateCertificateV2<TYPES>,
889}
890
891impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV2<TYPES> {
892 fn view_number(&self) -> ViewNumber {
893 self.qc.view_number()
894 }
895}
896
897impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV2<TYPES> {
898 fn epoch(&self) -> Option<EpochNumber> {
899 self.qc.epoch()
900 }
901}
902
903#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
904#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
905pub struct EpochRootQuorumCertificateV1<TYPES: NodeType> {
906 pub qc: QuorumCertificate2<TYPES>,
907 pub state_cert: LightClientStateUpdateCertificateV1<TYPES>,
908}
909
910impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV1<TYPES> {
911 fn view_number(&self) -> ViewNumber {
912 self.qc.view_number()
913 }
914}
915
916impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV1<TYPES> {
917 fn epoch(&self) -> Option<EpochNumber> {
918 self.qc.epoch()
919 }
920}
921
922impl<TYPES: NodeType> From<EpochRootQuorumCertificateV1<TYPES>>
923 for EpochRootQuorumCertificateV2<TYPES>
924{
925 fn from(root_qc: EpochRootQuorumCertificateV1<TYPES>) -> Self {
926 Self {
927 qc: root_qc.qc,
928 state_cert: root_qc.state_cert.into(),
929 }
930 }
931}
932
933impl<TYPES: NodeType> From<EpochRootQuorumCertificateV2<TYPES>>
934 for EpochRootQuorumCertificateV1<TYPES>
935{
936 fn from(root_qc: EpochRootQuorumCertificateV2<TYPES>) -> Self {
937 Self {
938 qc: root_qc.qc,
939 state_cert: root_qc.state_cert.into(),
940 }
941 }
942}
943
944#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
950#[serde(bound = "")]
951pub struct CertificatePair<TYPES: NodeType> {
952 qc: QuorumCertificate2<TYPES>,
954
955 next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
957}
958
959impl<TYPES: NodeType> CertificatePair<TYPES> {
960 pub fn new(
962 qc: QuorumCertificate2<TYPES>,
963 next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
964 ) -> Self {
965 Self { qc, next_epoch_qc }
966 }
967
968 pub fn non_epoch_change(qc: QuorumCertificate2<TYPES>) -> Self {
970 Self::new(qc, None)
971 }
972
973 pub fn for_parent(leaf: &Leaf2<TYPES>) -> Self {
975 Self {
976 qc: leaf.justify_qc(),
977 next_epoch_qc: leaf.next_epoch_justify_qc(),
978 }
979 }
980
981 pub fn qc(&self) -> &QuorumCertificate2<TYPES> {
983 &self.qc
984 }
985
986 pub fn next_epoch_qc(&self) -> Option<&NextEpochQuorumCertificate2<TYPES>> {
988 self.next_epoch_qc.as_ref()
989 }
990
991 pub fn leaf_commit(&self) -> Commitment<Leaf2<TYPES>> {
993 self.qc.data.leaf_commit
994 }
995
996 pub fn epoch(&self) -> Option<EpochNumber> {
1000 self.qc.data.epoch
1001 }
1002
1003 pub fn block_number(&self) -> Option<u64> {
1007 self.qc.data.block_number
1008 }
1009
1010 pub fn verify_next_epoch_qc(
1020 &self,
1021 epoch_height: u64,
1022 ) -> Result<Option<&NextEpochQuorumCertificate2<TYPES>>> {
1023 let block_number = self.qc.data.block_number.context(warn!(
1024 "QC for epoch {:?} has no block number",
1025 self.qc.data.epoch
1026 ))?;
1027 if !is_epoch_transition(block_number, epoch_height) {
1028 tracing::debug!(
1029 block_number,
1030 epoch_height,
1031 "QC is not in an epoch transition"
1032 );
1033 return Ok(None);
1034 }
1035
1036 let next_epoch_qc = self.next_epoch_qc.as_ref().context(warn!(
1037 "Received High QC for the transition block {block_number} but not the next epoch QC"
1038 ))?;
1039
1040 ensure!(self.qc.view_number == next_epoch_qc.view_number);
1042 ensure!(self.qc.data == *next_epoch_qc.data);
1043
1044 Ok(Some(next_epoch_qc))
1045 }
1046}
1047
1048impl<TYPES: NodeType> HasViewNumber for CertificatePair<TYPES> {
1049 fn view_number(&self) -> ViewNumber {
1050 self.qc.view_number()
1051 }
1052}
1053
1054pub fn check_qc_state_cert_correspondence<TYPES: NodeType>(
1057 qc: &QuorumCertificate2<TYPES>,
1058 state_cert: &LightClientStateUpdateCertificateV2<TYPES>,
1059 epoch_height: u64,
1060) -> bool {
1061 qc.data
1062 .block_number
1063 .is_some_and(|bn| is_epoch_root(bn, epoch_height))
1064 && Some(state_cert.epoch) == qc.data.epoch()
1065 && qc.view_number().u64() == state_cert.light_client_state.view_number
1066}