hotshot_types/
simple_certificate.rs

1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Implementations of the simple certificate type.  Used for Quorum, DA, and Timeout Certificates
8
9use 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    PeerConfig,
23    data::{EpochNumber, Leaf2, ViewNumber, serialize_signature2},
24    epoch_membership::EpochMembership,
25    light_client::{LightClientState, StakeTableState},
26    message::UpgradeLock,
27    simple_vote::{
28        DaData, DaData2, HasEpoch, NextEpochQuorumData2, QuorumData, QuorumData2, QuorumMarker,
29        TimeoutData, TimeoutData2, UpgradeProposalData, VersionedVoteData, ViewSyncCommitData,
30        ViewSyncCommitData2, ViewSyncFinalizeData, ViewSyncFinalizeData2, ViewSyncPreCommitData,
31        ViewSyncPreCommitData2, Voteable,
32    },
33    stake_table::{HSStakeTable, StakeTableEntries},
34    traits::{
35        node_implementation::NodeType,
36        signature_key::{SignatureKey, StateSignatureKey},
37    },
38    utils::is_epoch_transition,
39    vote::{Certificate, HasViewNumber},
40};
41
42/// Trait which allows use to inject different threshold calculations into a Certificate type
43pub trait Threshold<TYPES: NodeType> {
44    /// Calculate a threshold based on the membership
45    fn threshold(membership: &EpochMembership<TYPES>) -> impl Future<Output = U256> + Send;
46}
47
48/// Defines a threshold which is 2f + 1 (Amount needed for Quorum)
49#[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/// Defines a threshold which is f + 1 (i.e at least one of the stake is honest)
59#[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/// Defines a threshold which is 0.9n + 1 (i.e. over 90% of the nodes with stake)
69#[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/// A certificate which can be created by aggregating many simple votes on the commitment.
79#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
80pub struct SimpleCertificate<
81    TYPES: NodeType,
82    VOTEABLE: Voteable<TYPES>,
83    THRESHOLD: Threshold<TYPES>,
84> {
85    /// The data this certificate is for.  I.e the thing that was voted on to create this Certificate
86    pub data: VOTEABLE,
87    /// commitment of all the votes this cert should be signed over
88    vote_commitment: Commitment<VOTEABLE>,
89    /// Which view this QC relates to
90    pub view_number: ViewNumber,
91    /// assembled signature for certificate aggregation
92    pub signatures: Option<<TYPES::SignatureKey as SignatureKey>::QcType>,
93    /// phantom data for `THRESHOLD` and `TYPES`
94    pub _pd: PhantomData<(TYPES, THRESHOLD)>,
95}
96
97impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES>, THRESHOLD: Threshold<TYPES>>
98    SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
99{
100    /// Creates a new instance of `SimpleCertificate`
101    pub fn new(
102        data: VOTEABLE,
103        vote_commitment: Commitment<VOTEABLE>,
104        view_number: ViewNumber,
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    fn signers(
118        &self,
119        stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
120        threshold: U256,
121    ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
122        if self.view_number == ViewNumber::genesis() {
123            return Ok(vec![]);
124        }
125        let real_qc_pp =
126            <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
127
128        let Some(ref signatures) = self.signatures else {
129            bail!("No signatures found while retrieving signers");
130        };
131
132        <TYPES::SignatureKey as SignatureKey>::signers(&real_qc_pp, signatures)
133            .wrap()
134            .context(|e| warn!("Tracing signers: {e}"))
135    }
136}
137
138impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + Committable, THRESHOLD: Threshold<TYPES>>
139    Committable for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
140{
141    fn commit(&self) -> Commitment<Self> {
142        let signature_bytes = match self.signatures.as_ref() {
143            Some(sigs) => serialize_signature2::<TYPES>(sigs),
144            None => vec![],
145        };
146        committable::RawCommitmentBuilder::new("Certificate")
147            .field("data", self.data.commit())
148            .field("vote_commitment", self.vote_commitment)
149            .field("view number", self.view_number.commit())
150            .var_size_field("signatures", &signature_bytes)
151            .finalize()
152    }
153}
154
155impl<TYPES: NodeType, THRESHOLD: Threshold<TYPES>> Certificate<TYPES, DaData>
156    for SimpleCertificate<TYPES, DaData, THRESHOLD>
157{
158    type Voteable = DaData;
159    type Threshold = THRESHOLD;
160
161    fn create_signed_certificate(
162        vote_commitment: Commitment<VersionedVoteData<TYPES, DaData>>,
163        data: Self::Voteable,
164        sig: <TYPES::SignatureKey as SignatureKey>::QcType,
165        view: ViewNumber,
166    ) -> Self {
167        let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
168
169        SimpleCertificate {
170            data,
171            vote_commitment: Commitment::from_raw(vote_commitment_bytes),
172            view_number: view,
173            signatures: Some(sig),
174            _pd: PhantomData,
175        }
176    }
177    async fn is_valid_cert(
178        &self,
179        stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
180        threshold: U256,
181        upgrade_lock: &UpgradeLock<TYPES>,
182    ) -> Result<()> {
183        if self.view_number == ViewNumber::genesis() {
184            return Ok(());
185        }
186        let real_qc_pp =
187            <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
188        let commit = self.data_commitment(upgrade_lock).await?;
189
190        let Some(ref signatures) = self.signatures else {
191            bail!("No signatures found while validating certificate");
192        };
193
194        <TYPES::SignatureKey as SignatureKey>::check(&real_qc_pp, commit.as_ref(), signatures)
195            .wrap()
196            .context(|e| warn!("Signature check failed: {e}"))
197    }
198    fn signers(
199        &self,
200        stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
201        threshold: U256,
202    ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
203        self.signers(stake_table, threshold)
204    }
205    /// Proxy's to `Membership.stake`
206    async fn stake_table_entry(
207        membership: &EpochMembership<TYPES>,
208        pub_key: &TYPES::SignatureKey,
209    ) -> Option<PeerConfig<TYPES>> {
210        membership.da_stake(pub_key).await
211    }
212
213    /// Proxy's to `Membership.da_stake_table`
214    async fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
215        membership.da_stake_table().await
216    }
217    /// Proxy's to `Membership.da_total_nodes`
218    async fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
219        membership.da_total_nodes().await
220    }
221    async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
222        membership.da_success_threshold().await
223    }
224    fn data(&self) -> &Self::Voteable {
225        &self.data
226    }
227    async fn data_commitment(
228        &self,
229        upgrade_lock: &UpgradeLock<TYPES>,
230    ) -> Result<Commitment<VersionedVoteData<TYPES, DaData>>> {
231        Ok(
232            VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)
233                .await?
234                .commit(),
235        )
236    }
237}
238
239impl<TYPES: NodeType, THRESHOLD: Threshold<TYPES>> Certificate<TYPES, DaData2>
240    for SimpleCertificate<TYPES, DaData2, THRESHOLD>
241{
242    type Voteable = DaData2;
243    type Threshold = THRESHOLD;
244
245    fn create_signed_certificate(
246        vote_commitment: Commitment<VersionedVoteData<TYPES, DaData2>>,
247        data: Self::Voteable,
248        sig: <TYPES::SignatureKey as SignatureKey>::QcType,
249        view: ViewNumber,
250    ) -> Self {
251        let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
252
253        SimpleCertificate {
254            data,
255            vote_commitment: Commitment::from_raw(vote_commitment_bytes),
256            view_number: view,
257            signatures: Some(sig),
258            _pd: PhantomData,
259        }
260    }
261    async fn is_valid_cert(
262        &self,
263        stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
264        threshold: U256,
265        upgrade_lock: &UpgradeLock<TYPES>,
266    ) -> Result<()> {
267        if self.view_number == ViewNumber::genesis() {
268            return Ok(());
269        }
270        let real_qc_pp =
271            <TYPES::SignatureKey as SignatureKey>::public_parameter(stake_table, threshold);
272        let commit = self.data_commitment(upgrade_lock).await?;
273
274        <TYPES::SignatureKey as SignatureKey>::check(
275            &real_qc_pp,
276            commit.as_ref(),
277            self.signatures.as_ref().unwrap(),
278        )
279        .wrap()
280        .context(|e| warn!("Signature check failed: {e}"))
281    }
282    fn signers(
283        &self,
284        stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
285        threshold: U256,
286    ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
287        self.signers(stake_table, threshold)
288    }
289    /// Proxy's to `Membership.stake`
290    async fn stake_table_entry(
291        membership: &EpochMembership<TYPES>,
292        pub_key: &TYPES::SignatureKey,
293    ) -> Option<PeerConfig<TYPES>> {
294        membership.da_stake(pub_key).await
295    }
296
297    /// Proxy's to `Membership.da_stake_table`
298    async fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
299        membership.da_stake_table().await
300    }
301    /// Proxy's to `Membership.da_total_nodes`
302    async fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
303        membership.da_total_nodes().await
304    }
305    async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
306        membership.da_success_threshold().await
307    }
308    fn data(&self) -> &Self::Voteable {
309        &self.data
310    }
311    async fn data_commitment(
312        &self,
313        upgrade_lock: &UpgradeLock<TYPES>,
314    ) -> Result<Commitment<VersionedVoteData<TYPES, DaData2>>> {
315        Ok(
316            VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)
317                .await?
318                .commit(),
319        )
320    }
321}
322
323impl<
324    TYPES: NodeType,
325    VOTEABLE: Voteable<TYPES> + 'static + QuorumMarker,
326    THRESHOLD: Threshold<TYPES>,
327> Certificate<TYPES, VOTEABLE> for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
328{
329    type Voteable = VOTEABLE;
330    type Threshold = THRESHOLD;
331
332    fn create_signed_certificate(
333        vote_commitment: Commitment<VersionedVoteData<TYPES, VOTEABLE>>,
334        data: Self::Voteable,
335        sig: <TYPES::SignatureKey as SignatureKey>::QcType,
336        view: ViewNumber,
337    ) -> Self {
338        let vote_commitment_bytes: [u8; 32] = vote_commitment.into();
339
340        SimpleCertificate {
341            data,
342            vote_commitment: Commitment::from_raw(vote_commitment_bytes),
343            view_number: view,
344            signatures: Some(sig),
345            _pd: PhantomData,
346        }
347    }
348    async 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).await?;
360
361        <TYPES::SignatureKey as SignatureKey>::check(
362            &real_qc_pp,
363            commit.as_ref(),
364            self.signatures.as_ref().unwrap(),
365        )
366        .wrap()
367        .context(|e| warn!("Signature check failed: {e}"))
368    }
369    fn signers(
370        &self,
371        stake_table: &[<TYPES::SignatureKey as SignatureKey>::StakeTableEntry],
372        threshold: U256,
373    ) -> Result<Vec<<TYPES::SignatureKey as SignatureKey>::VerificationKeyType>> {
374        self.signers(stake_table, threshold)
375    }
376    async fn threshold(membership: &EpochMembership<TYPES>) -> U256 {
377        THRESHOLD::threshold(membership).await
378    }
379
380    async fn stake_table_entry(
381        membership: &EpochMembership<TYPES>,
382        pub_key: &TYPES::SignatureKey,
383    ) -> Option<PeerConfig<TYPES>> {
384        membership.stake(pub_key).await
385    }
386
387    async fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
388        membership.stake_table().await
389    }
390
391    /// Proxy's to `Membership.total_nodes`
392    async fn total_nodes(membership: &EpochMembership<TYPES>) -> usize {
393        membership.total_nodes().await
394    }
395
396    fn data(&self) -> &Self::Voteable {
397        &self.data
398    }
399    async fn data_commitment(
400        &self,
401        upgrade_lock: &UpgradeLock<TYPES>,
402    ) -> Result<Commitment<VersionedVoteData<TYPES, VOTEABLE>>> {
403        Ok(
404            VersionedVoteData::new(self.data.clone(), self.view_number, upgrade_lock)
405                .await?
406                .commit(),
407        )
408    }
409}
410
411impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + 'static, THRESHOLD: Threshold<TYPES>>
412    HasViewNumber for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
413{
414    fn view_number(&self) -> ViewNumber {
415        self.view_number
416    }
417}
418
419impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES> + HasEpoch + 'static, THRESHOLD: Threshold<TYPES>>
420    HasEpoch for SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
421{
422    fn epoch(&self) -> Option<EpochNumber> {
423        self.data.epoch()
424    }
425}
426
427impl<TYPES: NodeType> Display for QuorumCertificate<TYPES> {
428    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
429        write!(f, "view: {:?}", self.view_number)
430    }
431}
432
433impl<TYPES: NodeType> UpgradeCertificate<TYPES> {
434    /// Determines whether or not a certificate is relevant (i.e. we still have time to reach a
435    /// decide)
436    ///
437    /// # Errors
438    /// Returns an error when the certificate is no longer relevant
439    pub async fn is_relevant(&self, view_number: ViewNumber) -> Result<()> {
440        ensure!(
441            self.data.new_version_first_view >= view_number,
442            "Upgrade certificate is no longer relevant."
443        );
444
445        Ok(())
446    }
447
448    /// Validate an upgrade certificate.
449    /// # Errors
450    /// Returns an error when the upgrade certificate is invalid.
451    pub async fn validate(
452        upgrade_certificate: &Option<Self>,
453        membership: &EpochMembership<TYPES>,
454        epoch: Option<EpochNumber>,
455        upgrade_lock: &UpgradeLock<TYPES>,
456    ) -> Result<()> {
457        ensure!(epoch == membership.epoch(), "Epochs don't match!");
458        if let Some(cert) = upgrade_certificate {
459            let membership_stake_table = membership.stake_table().await;
460            let membership_upgrade_threshold = membership.upgrade_threshold().await;
461
462            cert.is_valid_cert(
463                &StakeTableEntries::<TYPES>::from(membership_stake_table).0,
464                membership_upgrade_threshold,
465                upgrade_lock,
466            )
467            .await
468            .context(|e| warn!("Invalid upgrade certificate: {e}"))?;
469        }
470
471        Ok(())
472    }
473
474    /// Given an upgrade certificate and a view, tests whether the view is in the period
475    /// where we are upgrading, which requires that we propose with null blocks.
476    pub fn upgrading_in(&self, view: ViewNumber) -> bool {
477        view > self.data.old_version_last_view && view < self.data.new_version_first_view
478    }
479}
480
481impl<TYPES: NodeType> QuorumCertificate<TYPES> {
482    /// Convert a `QuorumCertificate` into a `QuorumCertificate2`
483    pub fn to_qc2(self) -> QuorumCertificate2<TYPES> {
484        let bytes: [u8; 32] = self.data.leaf_commit.into();
485        let data = QuorumData2 {
486            leaf_commit: Commitment::from_raw(bytes),
487            epoch: None,
488            block_number: None,
489        };
490
491        let bytes: [u8; 32] = self.vote_commitment.into();
492        let vote_commitment = Commitment::from_raw(bytes);
493
494        SimpleCertificate {
495            data,
496            vote_commitment,
497            view_number: self.view_number,
498            signatures: self.signatures.clone(),
499            _pd: PhantomData,
500        }
501    }
502}
503
504impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
505    /// Convert a `QuorumCertificate2` into a `QuorumCertificate`
506    pub fn to_qc(self) -> QuorumCertificate<TYPES> {
507        let bytes: [u8; 32] = self.data.leaf_commit.into();
508        let data = QuorumData {
509            leaf_commit: Commitment::from_raw(bytes),
510        };
511
512        let bytes: [u8; 32] = self.vote_commitment.into();
513        let vote_commitment = Commitment::from_raw(bytes);
514
515        SimpleCertificate {
516            data,
517            vote_commitment,
518            view_number: self.view_number,
519            signatures: self.signatures.clone(),
520            _pd: PhantomData,
521        }
522    }
523}
524
525impl<TYPES: NodeType> DaCertificate<TYPES> {
526    /// Convert a `DaCertificate` into a `DaCertificate2`
527    pub fn to_dac2(self) -> DaCertificate2<TYPES> {
528        let data = DaData2 {
529            payload_commit: self.data.payload_commit,
530            next_epoch_payload_commit: None,
531            epoch: None,
532        };
533
534        let bytes: [u8; 32] = self.vote_commitment.into();
535        let vote_commitment = Commitment::from_raw(bytes);
536
537        SimpleCertificate {
538            data,
539            vote_commitment,
540            view_number: self.view_number,
541            signatures: self.signatures.clone(),
542            _pd: PhantomData,
543        }
544    }
545}
546
547impl<TYPES: NodeType> DaCertificate2<TYPES> {
548    /// Convert a `DaCertificate` into a `DaCertificate2`
549    pub fn to_dac(self) -> DaCertificate<TYPES> {
550        let data = DaData {
551            payload_commit: self.data.payload_commit,
552        };
553
554        let bytes: [u8; 32] = self.vote_commitment.into();
555        let vote_commitment = Commitment::from_raw(bytes);
556
557        SimpleCertificate {
558            data,
559            vote_commitment,
560            view_number: self.view_number,
561            signatures: self.signatures.clone(),
562            _pd: PhantomData,
563        }
564    }
565}
566
567impl<TYPES: NodeType> ViewSyncPreCommitCertificate<TYPES> {
568    /// Convert a `DaCertificate` into a `DaCertificate2`
569    pub fn to_vsc2(self) -> ViewSyncPreCommitCertificate2<TYPES> {
570        let data = ViewSyncPreCommitData2 {
571            relay: self.data.relay,
572            round: self.data.round,
573            epoch: None,
574        };
575
576        let bytes: [u8; 32] = self.vote_commitment.into();
577        let vote_commitment = Commitment::from_raw(bytes);
578
579        SimpleCertificate {
580            data,
581            vote_commitment,
582            view_number: self.view_number,
583            signatures: self.signatures.clone(),
584            _pd: PhantomData,
585        }
586    }
587}
588
589impl<TYPES: NodeType> ViewSyncPreCommitCertificate2<TYPES> {
590    /// Convert a `DaCertificate` into a `DaCertificate2`
591    pub fn to_vsc(self) -> ViewSyncPreCommitCertificate<TYPES> {
592        let data = ViewSyncPreCommitData {
593            relay: self.data.relay,
594            round: self.data.round,
595        };
596
597        let bytes: [u8; 32] = self.vote_commitment.into();
598        let vote_commitment = Commitment::from_raw(bytes);
599
600        SimpleCertificate {
601            data,
602            vote_commitment,
603            view_number: self.view_number,
604            signatures: self.signatures.clone(),
605            _pd: PhantomData,
606        }
607    }
608}
609
610impl<TYPES: NodeType> ViewSyncCommitCertificate<TYPES> {
611    /// Convert a `DaCertificate` into a `DaCertificate2`
612    pub fn to_vsc2(self) -> ViewSyncCommitCertificate2<TYPES> {
613        let data = ViewSyncCommitData2 {
614            relay: self.data.relay,
615            round: self.data.round,
616            epoch: None,
617        };
618
619        let bytes: [u8; 32] = self.vote_commitment.into();
620        let vote_commitment = Commitment::from_raw(bytes);
621
622        SimpleCertificate {
623            data,
624            vote_commitment,
625            view_number: self.view_number,
626            signatures: self.signatures.clone(),
627            _pd: PhantomData,
628        }
629    }
630}
631
632impl<TYPES: NodeType> ViewSyncCommitCertificate2<TYPES> {
633    /// Convert a `DaCertificate` into a `DaCertificate2`
634    pub fn to_vsc(self) -> ViewSyncCommitCertificate<TYPES> {
635        let data = ViewSyncCommitData {
636            relay: self.data.relay,
637            round: self.data.round,
638        };
639
640        let bytes: [u8; 32] = self.vote_commitment.into();
641        let vote_commitment = Commitment::from_raw(bytes);
642
643        SimpleCertificate {
644            data,
645            vote_commitment,
646            view_number: self.view_number,
647            signatures: self.signatures.clone(),
648            _pd: PhantomData,
649        }
650    }
651}
652
653impl<TYPES: NodeType> ViewSyncFinalizeCertificate<TYPES> {
654    /// Convert a `DaCertificate` into a `DaCertificate2`
655    pub fn to_vsc2(self) -> ViewSyncFinalizeCertificate2<TYPES> {
656        let data = ViewSyncFinalizeData2 {
657            relay: self.data.relay,
658            round: self.data.round,
659            epoch: None,
660        };
661
662        let bytes: [u8; 32] = self.vote_commitment.into();
663        let vote_commitment = Commitment::from_raw(bytes);
664
665        SimpleCertificate {
666            data,
667            vote_commitment,
668            view_number: self.view_number,
669            signatures: self.signatures.clone(),
670            _pd: PhantomData,
671        }
672    }
673}
674
675impl<TYPES: NodeType> ViewSyncFinalizeCertificate2<TYPES> {
676    /// Convert a `DaCertificate` into a `DaCertificate2`
677    pub fn to_vsc(self) -> ViewSyncFinalizeCertificate<TYPES> {
678        let data = ViewSyncFinalizeData {
679            relay: self.data.relay,
680            round: self.data.round,
681        };
682
683        let bytes: [u8; 32] = self.vote_commitment.into();
684        let vote_commitment = Commitment::from_raw(bytes);
685
686        SimpleCertificate {
687            data,
688            vote_commitment,
689            view_number: self.view_number,
690            signatures: self.signatures.clone(),
691            _pd: PhantomData,
692        }
693    }
694}
695
696impl<TYPES: NodeType> TimeoutCertificate<TYPES> {
697    /// Convert a `DaCertificate` into a `DaCertificate2`
698    pub fn to_tc2(self) -> TimeoutCertificate2<TYPES> {
699        let data = TimeoutData2 {
700            view: self.data.view,
701            epoch: None,
702        };
703
704        let bytes: [u8; 32] = self.vote_commitment.into();
705        let vote_commitment = Commitment::from_raw(bytes);
706
707        SimpleCertificate {
708            data,
709            vote_commitment,
710            view_number: self.view_number,
711            signatures: self.signatures.clone(),
712            _pd: PhantomData,
713        }
714    }
715}
716
717impl<TYPES: NodeType> TimeoutCertificate2<TYPES> {
718    /// Convert a `DaCertificate` into a `DaCertificate2`
719    pub fn to_tc(self) -> TimeoutCertificate<TYPES> {
720        let data = TimeoutData {
721            view: self.data.view,
722        };
723
724        let bytes: [u8; 32] = self.vote_commitment.into();
725        let vote_commitment = Commitment::from_raw(bytes);
726
727        SimpleCertificate {
728            data,
729            vote_commitment,
730            view_number: self.view_number,
731            signatures: self.signatures.clone(),
732            _pd: PhantomData,
733        }
734    }
735}
736
737/// Type alias for a `QuorumCertificate`, which is a `SimpleCertificate` over `QuorumData`
738pub type QuorumCertificate<TYPES> = SimpleCertificate<TYPES, QuorumData<TYPES>, SuccessThreshold>;
739/// Type alias for a `QuorumCertificate2`, which is a `SimpleCertificate` over `QuorumData2`
740pub type QuorumCertificate2<TYPES> = SimpleCertificate<TYPES, QuorumData2<TYPES>, SuccessThreshold>;
741/// Type alias for a `QuorumCertificate2`, which is a `SimpleCertificate` over `QuorumData2`
742pub type NextEpochQuorumCertificate2<TYPES> =
743    SimpleCertificate<TYPES, NextEpochQuorumData2<TYPES>, SuccessThreshold>;
744/// Type alias for a `DaCertificate`, which is a `SimpleCertificate` over `DaData`
745pub type DaCertificate<TYPES> = SimpleCertificate<TYPES, DaData, SuccessThreshold>;
746/// Type alias for a `DaCertificate2`, which is a `SimpleCertificate` over `DaData2`
747pub type DaCertificate2<TYPES> = SimpleCertificate<TYPES, DaData2, SuccessThreshold>;
748/// Type alias for a Timeout certificate over a view number
749pub type TimeoutCertificate<TYPES> = SimpleCertificate<TYPES, TimeoutData, SuccessThreshold>;
750/// Type alias for a `TimeoutCertificate2`, which is a `SimpleCertificate` over `TimeoutData2`
751pub type TimeoutCertificate2<TYPES> = SimpleCertificate<TYPES, TimeoutData2, SuccessThreshold>;
752/// Type alias for a `ViewSyncPreCommit` certificate over a view number
753pub type ViewSyncPreCommitCertificate<TYPES> =
754    SimpleCertificate<TYPES, ViewSyncPreCommitData, OneHonestThreshold>;
755/// Type alias for a `ViewSyncPreCommitCertificate2`, which is a `SimpleCertificate` over `ViewSyncPreCommitData2`
756pub type ViewSyncPreCommitCertificate2<TYPES> =
757    SimpleCertificate<TYPES, ViewSyncPreCommitData2, OneHonestThreshold>;
758/// Type alias for a `ViewSyncCommit` certificate over a view number
759pub type ViewSyncCommitCertificate<TYPES> =
760    SimpleCertificate<TYPES, ViewSyncCommitData, SuccessThreshold>;
761/// Type alias for a `ViewSyncCommitCertificate2`, which is a `SimpleCertificate` over `ViewSyncCommitData2`
762pub type ViewSyncCommitCertificate2<TYPES> =
763    SimpleCertificate<TYPES, ViewSyncCommitData2, SuccessThreshold>;
764/// Type alias for a `ViewSyncFinalize` certificate over a view number
765pub type ViewSyncFinalizeCertificate<TYPES> =
766    SimpleCertificate<TYPES, ViewSyncFinalizeData, SuccessThreshold>;
767/// Type alias for a `ViewSyncFinalizeCertificate2`, which is a `SimpleCertificate` over `ViewSyncFinalizeData2`
768pub type ViewSyncFinalizeCertificate2<TYPES> =
769    SimpleCertificate<TYPES, ViewSyncFinalizeData2, SuccessThreshold>;
770/// Type alias for a `UpgradeCertificate`, which is a `SimpleCertificate` of `UpgradeProposalData`
771pub type UpgradeCertificate<TYPES> =
772    SimpleCertificate<TYPES, UpgradeProposalData, UpgradeThreshold>;
773
774/// Type for light client state update certificate
775#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
776pub struct LightClientStateUpdateCertificateV2<TYPES: NodeType> {
777    /// The epoch of the light client state
778    pub epoch: EpochNumber,
779    /// Light client state for epoch transition
780    pub light_client_state: LightClientState,
781    /// Next epoch stake table state
782    pub next_stake_table_state: StakeTableState,
783    /// Signatures to the light client state
784    #[allow(clippy::type_complexity)]
785    pub signatures: Vec<(
786        TYPES::StateSignatureKey,
787        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, // LCV3 signature
788        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, // LCV2 signature
789    )>,
790    /// Present in versions >= V4.
791    ///
792    /// This field stores the Keccak-256 hash of the concatenated Merkle roots.
793    /// Currently, it includes only the Espresso reward Merkle tree root.
794    pub auth_root: FixedBytes<32>,
795}
796
797/// Type for light client state update certificate
798#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
799pub struct LightClientStateUpdateCertificateV1<TYPES: NodeType> {
800    /// The epoch of the light client state
801    pub epoch: EpochNumber,
802    /// Light client state for epoch transition
803    pub light_client_state: LightClientState,
804    /// Next epoch stake table state
805    pub next_stake_table_state: StakeTableState,
806    /// Signatures to the light client state
807    pub signatures: Vec<(
808        TYPES::StateSignatureKey,
809        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature,
810    )>,
811}
812
813impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV1<TYPES>>
814    for LightClientStateUpdateCertificateV2<TYPES>
815{
816    fn from(v1: LightClientStateUpdateCertificateV1<TYPES>) -> Self {
817        Self {
818            epoch: v1.epoch,
819            light_client_state: v1.light_client_state,
820            next_stake_table_state: v1.next_stake_table_state,
821            signatures: v1
822                .signatures
823                .into_iter()
824                .map(|(key, sig)| (key, sig.clone(), sig)) // Cloning the signatures here because we use it only for storage.
825                .collect(),
826            auth_root: Default::default(),
827        }
828    }
829}
830
831impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV2<TYPES>>
832    for LightClientStateUpdateCertificateV1<TYPES>
833{
834    fn from(v2: LightClientStateUpdateCertificateV2<TYPES>) -> Self {
835        Self {
836            epoch: v2.epoch,
837            light_client_state: v2.light_client_state,
838            next_stake_table_state: v2.next_stake_table_state,
839            signatures: v2
840                .signatures
841                .into_iter()
842                .map(|(key, _, sig)| (key, sig))
843                .collect(),
844        }
845    }
846}
847
848impl<TYPES: NodeType> HasViewNumber for LightClientStateUpdateCertificateV2<TYPES> {
849    fn view_number(&self) -> ViewNumber {
850        ViewNumber::new(self.light_client_state.view_number)
851    }
852}
853
854impl<TYPES: NodeType> HasEpoch for LightClientStateUpdateCertificateV2<TYPES> {
855    fn epoch(&self) -> Option<EpochNumber> {
856        Some(self.epoch)
857    }
858}
859
860impl<TYPES: NodeType> LightClientStateUpdateCertificateV1<TYPES> {
861    pub fn genesis() -> Self {
862        Self {
863            epoch: EpochNumber::genesis(),
864            light_client_state: Default::default(),
865            next_stake_table_state: Default::default(),
866            signatures: vec![],
867        }
868    }
869}
870
871impl<TYPES: NodeType> LightClientStateUpdateCertificateV2<TYPES> {
872    pub fn genesis() -> Self {
873        Self {
874            epoch: EpochNumber::genesis(),
875            light_client_state: Default::default(),
876            next_stake_table_state: Default::default(),
877            signatures: vec![],
878            auth_root: Default::default(),
879        }
880    }
881}
882
883#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
884#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
885pub struct EpochRootQuorumCertificateV2<TYPES: NodeType> {
886    pub qc: QuorumCertificate2<TYPES>,
887    pub state_cert: LightClientStateUpdateCertificateV2<TYPES>,
888}
889
890impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV2<TYPES> {
891    fn view_number(&self) -> ViewNumber {
892        self.qc.view_number()
893    }
894}
895
896impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV2<TYPES> {
897    fn epoch(&self) -> Option<EpochNumber> {
898        self.qc.epoch()
899    }
900}
901
902#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
903#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
904pub struct EpochRootQuorumCertificateV1<TYPES: NodeType> {
905    pub qc: QuorumCertificate2<TYPES>,
906    pub state_cert: LightClientStateUpdateCertificateV1<TYPES>,
907}
908
909impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV1<TYPES> {
910    fn view_number(&self) -> ViewNumber {
911        self.qc.view_number()
912    }
913}
914
915impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV1<TYPES> {
916    fn epoch(&self) -> Option<EpochNumber> {
917        self.qc.epoch()
918    }
919}
920
921impl<TYPES: NodeType> From<EpochRootQuorumCertificateV1<TYPES>>
922    for EpochRootQuorumCertificateV2<TYPES>
923{
924    fn from(root_qc: EpochRootQuorumCertificateV1<TYPES>) -> Self {
925        Self {
926            qc: root_qc.qc,
927            state_cert: root_qc.state_cert.into(),
928        }
929    }
930}
931
932impl<TYPES: NodeType> From<EpochRootQuorumCertificateV2<TYPES>>
933    for EpochRootQuorumCertificateV1<TYPES>
934{
935    fn from(root_qc: EpochRootQuorumCertificateV2<TYPES>) -> Self {
936        Self {
937            qc: root_qc.qc,
938            state_cert: root_qc.state_cert.into(),
939        }
940    }
941}
942
943/// A pair of QCs (or a single QC) attesting to a leaf.
944///
945/// Generally we only need a single QC, but during an epoch transition, we require a pair: one
946/// signed by the current membership and one signed by the next epoch's membership. This type
947/// encapsulates both.
948#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
949#[serde(bound = "")]
950pub struct CertificatePair<TYPES: NodeType> {
951    /// The basic QC.
952    qc: QuorumCertificate2<TYPES>,
953
954    /// A QC from the next epoch's membership, if this QC is part of an epoch transition.
955    next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
956}
957
958impl<TYPES: NodeType> CertificatePair<TYPES> {
959    /// Create a certificate pair.
960    pub fn new(
961        qc: QuorumCertificate2<TYPES>,
962        next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
963    ) -> Self {
964        Self { qc, next_epoch_qc }
965    }
966
967    /// Create a certificate for a non-epoch-transitioning block.
968    pub fn non_epoch_change(qc: QuorumCertificate2<TYPES>) -> Self {
969        Self::new(qc, None)
970    }
971
972    /// Create a certificate for the parent of a leaf, using the justifying QCs in the leaf.
973    pub fn for_parent(leaf: &Leaf2<TYPES>) -> Self {
974        Self {
975            qc: leaf.justify_qc(),
976            next_epoch_qc: leaf.next_epoch_justify_qc(),
977        }
978    }
979
980    /// The raw QC.
981    pub fn qc(&self) -> &QuorumCertificate2<TYPES> {
982        &self.qc
983    }
984
985    /// A raw QC from the subsequent epoch's quorum, if this certificate is part of an epoch change.
986    pub fn next_epoch_qc(&self) -> Option<&NextEpochQuorumCertificate2<TYPES>> {
987        self.next_epoch_qc.as_ref()
988    }
989
990    /// The leaf commitment signed by this certificate.
991    pub fn leaf_commit(&self) -> Commitment<Leaf2<TYPES>> {
992        self.qc.data.leaf_commit
993    }
994
995    /// The epoch number this certificate belongs to.
996    ///
997    /// [`None`] if this certificate originated before epochs were enabled.
998    pub fn epoch(&self) -> Option<EpochNumber> {
999        self.qc.data.epoch
1000    }
1001
1002    /// The block number attached to this certificate.
1003    ///
1004    /// [`None`] if this certificate originated before epochs were enabled.
1005    pub fn block_number(&self) -> Option<u64> {
1006        self.qc.data.block_number
1007    }
1008
1009    /// Verify that the next epoch QC is present and consistent if required.
1010    ///
1011    /// This checks that if required, the next epoch QC is present and is consistent with the
1012    /// primary QC. It does not check the signature on either QC, only that the data being signed
1013    /// over is consistent between the two.
1014    ///
1015    /// Returns the next epoch QC if it is present and invariants are satisfied. Returns an error if
1016    /// a required next epoch QC is missing or if it is inconsistent with the primary QC. Returns
1017    /// [`None`] if a next epoch QC is not required for this certificate.
1018    pub fn verify_next_epoch_qc(
1019        &self,
1020        epoch_height: u64,
1021    ) -> Result<Option<&NextEpochQuorumCertificate2<TYPES>>> {
1022        let block_number = self.qc.data.block_number.context(warn!(
1023            "QC for epoch {:?} has no block number",
1024            self.qc.data.epoch
1025        ))?;
1026        if !is_epoch_transition(block_number, epoch_height) {
1027            tracing::debug!(
1028                block_number,
1029                epoch_height,
1030                "QC is not in an epoch transition"
1031            );
1032            return Ok(None);
1033        }
1034
1035        let next_epoch_qc = self.next_epoch_qc.as_ref().context(warn!(
1036            "Received High QC for the transition block {block_number} but not the next epoch QC"
1037        ))?;
1038
1039        // The signature from the next epoch must be over the same data.
1040        ensure!(self.qc.view_number == next_epoch_qc.view_number);
1041        ensure!(self.qc.data == *next_epoch_qc.data);
1042
1043        Ok(Some(next_epoch_qc))
1044    }
1045}
1046
1047impl<TYPES: NodeType> HasViewNumber for CertificatePair<TYPES> {
1048    fn view_number(&self) -> ViewNumber {
1049        self.qc.view_number()
1050    }
1051}