Skip to main content

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    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
41/// Trait which allows use to inject different threshold calculations into a Certificate type
42pub trait Threshold<TYPES: NodeType> {
43    /// Calculate a threshold based on the membership
44    fn threshold(membership: &EpochMembership<TYPES>) -> U256;
45}
46
47/// Defines a threshold which is 2f + 1 (Amount needed for Quorum)
48#[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/// Defines a threshold which is f + 1 (i.e at least one of the stake is honest)
58#[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/// Defines a threshold which is 0.9n + 1 (i.e. over 90% of the nodes with stake)
68#[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/// A certificate which can be created by aggregating many simple votes on the commitment.
78#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
79pub struct SimpleCertificate<
80    TYPES: NodeType,
81    VOTEABLE: Voteable<TYPES>,
82    THRESHOLD: Threshold<TYPES>,
83> {
84    /// The data this certificate is for.  I.e the thing that was voted on to create this Certificate
85    pub data: VOTEABLE,
86    /// commitment of all the votes this cert should be signed over
87    vote_commitment: Commitment<VOTEABLE>,
88    /// Which view this QC relates to
89    pub view_number: ViewNumber,
90    /// assembled signature for certificate aggregation
91    pub signatures: Option<<TYPES::SignatureKey as SignatureKey>::QcType>,
92    /// phantom data for `THRESHOLD` and `TYPES`
93    pub _pd: PhantomData<(TYPES, THRESHOLD)>,
94}
95
96impl<TYPES: NodeType, VOTEABLE: Voteable<TYPES>, THRESHOLD: Threshold<TYPES>>
97    SimpleCertificate<TYPES, VOTEABLE, THRESHOLD>
98{
99    /// Creates a new instance of `SimpleCertificate`
100    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    /// Proxy's to `Membership.stake`
205    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    /// Proxy's to `Membership.da_stake_table`
213    fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
214        membership.da_stake_table().collect()
215    }
216
217    /// Proxy's to `Membership.da_total_nodes`
218    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    /// Proxy's to `Membership.stake`
289    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    /// Proxy's to `Membership.da_stake_table`
297    fn stake_table(membership: &EpochMembership<TYPES>) -> HSStakeTable<TYPES> {
298        membership.da_stake_table().collect()
299    }
300
301    /// Proxy's to `Membership.da_total_nodes`
302    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    /// Proxy's to `Membership.total_nodes`
394    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    /// Determines whether or not a certificate is relevant (i.e. we still have time to reach a
433    /// decide)
434    ///
435    /// # Errors
436    /// Returns an error when the certificate is no longer relevant
437    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    /// Validate an upgrade certificate.
447    /// # Errors
448    /// Returns an error when the upgrade certificate is invalid.
449    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    /// Given an upgrade certificate and a view, tests whether the view is in the period
472    /// where we are upgrading, which requires that we propose with null blocks.
473    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    /// Convert a `QuorumCertificate` into a `QuorumCertificate2`
480    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    /// Convert a `QuorumCertificate2` into a `QuorumCertificate`
503    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    /// Convert a `DaCertificate` into a `DaCertificate2`
524    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    /// Convert a `DaCertificate` into a `DaCertificate2`
546    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    /// Convert a `DaCertificate` into a `DaCertificate2`
566    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    /// Convert a `DaCertificate` into a `DaCertificate2`
588    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    /// Convert a `DaCertificate` into a `DaCertificate2`
609    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    /// Convert a `DaCertificate` into a `DaCertificate2`
631    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    /// Convert a `DaCertificate` into a `DaCertificate2`
652    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    /// Convert a `DaCertificate` into a `DaCertificate2`
674    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    /// Convert a `DaCertificate` into a `DaCertificate2`
695    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    /// Convert a `DaCertificate` into a `DaCertificate2`
716    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
734/// Type alias for a `QuorumCertificate`, which is a `SimpleCertificate` over `QuorumData`
735pub type QuorumCertificate<TYPES> = SimpleCertificate<TYPES, QuorumData<TYPES>, SuccessThreshold>;
736/// Type alias for a `QuorumCertificate2`, which is a `SimpleCertificate` over `QuorumData2`
737pub type QuorumCertificate2<TYPES> = SimpleCertificate<TYPES, QuorumData2<TYPES>, SuccessThreshold>;
738/// Type alias for a `QuorumCertificate2`, which is a `SimpleCertificate` over `QuorumData2`
739pub type NextEpochQuorumCertificate2<TYPES> =
740    SimpleCertificate<TYPES, NextEpochQuorumData2<TYPES>, SuccessThreshold>;
741/// Type alias for the new protocol's phase-1 certificate over `QuorumData2`
742pub type Certificate1<TYPES> = SimpleCertificate<TYPES, QuorumData2<TYPES>, SuccessThreshold>;
743/// Type alias for the new protocol's phase-2 certificate over `Vote2Data`
744pub type Certificate2<TYPES> = SimpleCertificate<TYPES, Vote2Data<TYPES>, SuccessThreshold>;
745/// Type alias for a `DaCertificate`, which is a `SimpleCertificate` over `DaData`
746pub type DaCertificate<TYPES> = SimpleCertificate<TYPES, DaData, SuccessThreshold>;
747/// Type alias for a `DaCertificate2`, which is a `SimpleCertificate` over `DaData2`
748pub type DaCertificate2<TYPES> = SimpleCertificate<TYPES, DaData2, SuccessThreshold>;
749/// Type alias for a Timeout certificate over a view number
750pub type TimeoutCertificate<TYPES> = SimpleCertificate<TYPES, TimeoutData, SuccessThreshold>;
751/// Type alias for a `TimeoutCertificate2`, which is a `SimpleCertificate` over `TimeoutData2`
752pub type TimeoutCertificate2<TYPES> = SimpleCertificate<TYPES, TimeoutData2, SuccessThreshold>;
753/// Type alias for a `ViewSyncPreCommit` certificate over a view number
754pub type ViewSyncPreCommitCertificate<TYPES> =
755    SimpleCertificate<TYPES, ViewSyncPreCommitData, OneHonestThreshold>;
756/// Type alias for a `ViewSyncPreCommitCertificate2`, which is a `SimpleCertificate` over `ViewSyncPreCommitData2`
757pub type ViewSyncPreCommitCertificate2<TYPES> =
758    SimpleCertificate<TYPES, ViewSyncPreCommitData2, OneHonestThreshold>;
759/// Type alias for a `ViewSyncCommit` certificate over a view number
760pub type ViewSyncCommitCertificate<TYPES> =
761    SimpleCertificate<TYPES, ViewSyncCommitData, SuccessThreshold>;
762/// Type alias for a `ViewSyncCommitCertificate2`, which is a `SimpleCertificate` over `ViewSyncCommitData2`
763pub type ViewSyncCommitCertificate2<TYPES> =
764    SimpleCertificate<TYPES, ViewSyncCommitData2, SuccessThreshold>;
765/// Type alias for a `ViewSyncFinalize` certificate over a view number
766pub type ViewSyncFinalizeCertificate<TYPES> =
767    SimpleCertificate<TYPES, ViewSyncFinalizeData, SuccessThreshold>;
768/// Type alias for a `ViewSyncFinalizeCertificate2`, which is a `SimpleCertificate` over `ViewSyncFinalizeData2`
769pub type ViewSyncFinalizeCertificate2<TYPES> =
770    SimpleCertificate<TYPES, ViewSyncFinalizeData2, SuccessThreshold>;
771/// Type alias for a `UpgradeCertificate`, which is a `SimpleCertificate` of `UpgradeProposalData`
772pub type UpgradeCertificate<TYPES> =
773    SimpleCertificate<TYPES, UpgradeProposalData, UpgradeThreshold>;
774
775/// Type for light client state update certificate
776#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
777pub struct LightClientStateUpdateCertificateV2<TYPES: NodeType> {
778    /// The epoch of the light client state
779    pub epoch: EpochNumber,
780    /// Light client state for epoch transition
781    pub light_client_state: LightClientState,
782    /// Next epoch stake table state
783    pub next_stake_table_state: StakeTableState,
784    /// Signatures to the light client state
785    #[allow(clippy::type_complexity)]
786    pub signatures: Vec<(
787        TYPES::StateSignatureKey,
788        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, // LCV3 signature
789        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, // LCV2 signature
790    )>,
791    /// Present in versions >= V4.
792    ///
793    /// This field stores the Keccak-256 hash of the concatenated Merkle roots.
794    /// Currently, it includes only the Espresso reward Merkle tree root.
795    pub auth_root: FixedBytes<32>,
796}
797
798/// Type for light client state update certificate
799#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
800pub struct LightClientStateUpdateCertificateV1<TYPES: NodeType> {
801    /// The epoch of the light client state
802    pub epoch: EpochNumber,
803    /// Light client state for epoch transition
804    pub light_client_state: LightClientState,
805    /// Next epoch stake table state
806    pub next_stake_table_state: StakeTableState,
807    /// Signatures to the light client state
808    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)) // Cloning the signatures here because we use it only for storage.
826                .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/// A pair of QCs (or a single QC) attesting to a leaf.
945///
946/// Generally we only need a single QC, but during an epoch transition, we require a pair: one
947/// signed by the current membership and one signed by the next epoch's membership. This type
948/// encapsulates both.
949#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
950#[serde(bound = "")]
951pub struct CertificatePair<TYPES: NodeType> {
952    /// The basic QC.
953    qc: QuorumCertificate2<TYPES>,
954
955    /// A QC from the next epoch's membership, if this QC is part of an epoch transition.
956    next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
957}
958
959impl<TYPES: NodeType> CertificatePair<TYPES> {
960    /// Create a certificate pair.
961    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    /// Create a certificate for a non-epoch-transitioning block.
969    pub fn non_epoch_change(qc: QuorumCertificate2<TYPES>) -> Self {
970        Self::new(qc, None)
971    }
972
973    /// Create a certificate for the parent of a leaf, using the justifying QCs in the leaf.
974    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    /// The raw QC.
982    pub fn qc(&self) -> &QuorumCertificate2<TYPES> {
983        &self.qc
984    }
985
986    /// A raw QC from the subsequent epoch's quorum, if this certificate is part of an epoch change.
987    pub fn next_epoch_qc(&self) -> Option<&NextEpochQuorumCertificate2<TYPES>> {
988        self.next_epoch_qc.as_ref()
989    }
990
991    /// The leaf commitment signed by this certificate.
992    pub fn leaf_commit(&self) -> Commitment<Leaf2<TYPES>> {
993        self.qc.data.leaf_commit
994    }
995
996    /// The epoch number this certificate belongs to.
997    ///
998    /// [`None`] if this certificate originated before epochs were enabled.
999    pub fn epoch(&self) -> Option<EpochNumber> {
1000        self.qc.data.epoch
1001    }
1002
1003    /// The block number attached to this certificate.
1004    ///
1005    /// [`None`] if this certificate originated before epochs were enabled.
1006    pub fn block_number(&self) -> Option<u64> {
1007        self.qc.data.block_number
1008    }
1009
1010    /// Verify that the next epoch QC is present and consistent if required.
1011    ///
1012    /// This checks that if required, the next epoch QC is present and is consistent with the
1013    /// primary QC. It does not check the signature on either QC, only that the data being signed
1014    /// over is consistent between the two.
1015    ///
1016    /// Returns the next epoch QC if it is present and invariants are satisfied. Returns an error if
1017    /// a required next epoch QC is missing or if it is inconsistent with the primary QC. Returns
1018    /// [`None`] if a next epoch QC is not required for this certificate.
1019    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        // The signature from the next epoch must be over the same data.
1041        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
1054/// Check that a light client state update certificate corresponds to a given QC:
1055/// the QC is for an epoch root block, the epochs match, and the view numbers match.
1056pub 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}