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