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, 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 a `DaCertificate`, which is a `SimpleCertificate` over `DaData`
742pub type DaCertificate<TYPES> = SimpleCertificate<TYPES, DaData, SuccessThreshold>;
743/// Type alias for a `DaCertificate2`, which is a `SimpleCertificate` over `DaData2`
744pub type DaCertificate2<TYPES> = SimpleCertificate<TYPES, DaData2, SuccessThreshold>;
745/// Type alias for a Timeout certificate over a view number
746pub type TimeoutCertificate<TYPES> = SimpleCertificate<TYPES, TimeoutData, SuccessThreshold>;
747/// Type alias for a `TimeoutCertificate2`, which is a `SimpleCertificate` over `TimeoutData2`
748pub type TimeoutCertificate2<TYPES> = SimpleCertificate<TYPES, TimeoutData2, SuccessThreshold>;
749/// Type alias for a `ViewSyncPreCommit` certificate over a view number
750pub type ViewSyncPreCommitCertificate<TYPES> =
751    SimpleCertificate<TYPES, ViewSyncPreCommitData, OneHonestThreshold>;
752/// Type alias for a `ViewSyncPreCommitCertificate2`, which is a `SimpleCertificate` over `ViewSyncPreCommitData2`
753pub type ViewSyncPreCommitCertificate2<TYPES> =
754    SimpleCertificate<TYPES, ViewSyncPreCommitData2, OneHonestThreshold>;
755/// Type alias for a `ViewSyncCommit` certificate over a view number
756pub type ViewSyncCommitCertificate<TYPES> =
757    SimpleCertificate<TYPES, ViewSyncCommitData, SuccessThreshold>;
758/// Type alias for a `ViewSyncCommitCertificate2`, which is a `SimpleCertificate` over `ViewSyncCommitData2`
759pub type ViewSyncCommitCertificate2<TYPES> =
760    SimpleCertificate<TYPES, ViewSyncCommitData2, SuccessThreshold>;
761/// Type alias for a `ViewSyncFinalize` certificate over a view number
762pub type ViewSyncFinalizeCertificate<TYPES> =
763    SimpleCertificate<TYPES, ViewSyncFinalizeData, SuccessThreshold>;
764/// Type alias for a `ViewSyncFinalizeCertificate2`, which is a `SimpleCertificate` over `ViewSyncFinalizeData2`
765pub type ViewSyncFinalizeCertificate2<TYPES> =
766    SimpleCertificate<TYPES, ViewSyncFinalizeData2, SuccessThreshold>;
767/// Type alias for a `UpgradeCertificate`, which is a `SimpleCertificate` of `UpgradeProposalData`
768pub type UpgradeCertificate<TYPES> =
769    SimpleCertificate<TYPES, UpgradeProposalData, UpgradeThreshold>;
770
771/// Type for light client state update certificate
772#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
773pub struct LightClientStateUpdateCertificateV2<TYPES: NodeType> {
774    /// The epoch of the light client state
775    pub epoch: EpochNumber,
776    /// Light client state for epoch transition
777    pub light_client_state: LightClientState,
778    /// Next epoch stake table state
779    pub next_stake_table_state: StakeTableState,
780    /// Signatures to the light client state
781    #[allow(clippy::type_complexity)]
782    pub signatures: Vec<(
783        TYPES::StateSignatureKey,
784        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, // LCV3 signature
785        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature, // LCV2 signature
786    )>,
787    /// Present in versions >= V4.
788    ///
789    /// This field stores the Keccak-256 hash of the concatenated Merkle roots.
790    /// Currently, it includes only the Espresso reward Merkle tree root.
791    pub auth_root: FixedBytes<32>,
792}
793
794/// Type for light client state update certificate
795#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
796pub struct LightClientStateUpdateCertificateV1<TYPES: NodeType> {
797    /// The epoch of the light client state
798    pub epoch: EpochNumber,
799    /// Light client state for epoch transition
800    pub light_client_state: LightClientState,
801    /// Next epoch stake table state
802    pub next_stake_table_state: StakeTableState,
803    /// Signatures to the light client state
804    pub signatures: Vec<(
805        TYPES::StateSignatureKey,
806        <TYPES::StateSignatureKey as StateSignatureKey>::StateSignature,
807    )>,
808}
809
810impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV1<TYPES>>
811    for LightClientStateUpdateCertificateV2<TYPES>
812{
813    fn from(v1: LightClientStateUpdateCertificateV1<TYPES>) -> Self {
814        Self {
815            epoch: v1.epoch,
816            light_client_state: v1.light_client_state,
817            next_stake_table_state: v1.next_stake_table_state,
818            signatures: v1
819                .signatures
820                .into_iter()
821                .map(|(key, sig)| (key, sig.clone(), sig)) // Cloning the signatures here because we use it only for storage.
822                .collect(),
823            auth_root: Default::default(),
824        }
825    }
826}
827
828impl<TYPES: NodeType> From<LightClientStateUpdateCertificateV2<TYPES>>
829    for LightClientStateUpdateCertificateV1<TYPES>
830{
831    fn from(v2: LightClientStateUpdateCertificateV2<TYPES>) -> Self {
832        Self {
833            epoch: v2.epoch,
834            light_client_state: v2.light_client_state,
835            next_stake_table_state: v2.next_stake_table_state,
836            signatures: v2
837                .signatures
838                .into_iter()
839                .map(|(key, _, sig)| (key, sig))
840                .collect(),
841        }
842    }
843}
844
845impl<TYPES: NodeType> HasViewNumber for LightClientStateUpdateCertificateV2<TYPES> {
846    fn view_number(&self) -> ViewNumber {
847        ViewNumber::new(self.light_client_state.view_number)
848    }
849}
850
851impl<TYPES: NodeType> HasEpoch for LightClientStateUpdateCertificateV2<TYPES> {
852    fn epoch(&self) -> Option<EpochNumber> {
853        Some(self.epoch)
854    }
855}
856
857impl<TYPES: NodeType> LightClientStateUpdateCertificateV1<TYPES> {
858    pub fn genesis() -> Self {
859        Self {
860            epoch: EpochNumber::genesis(),
861            light_client_state: Default::default(),
862            next_stake_table_state: Default::default(),
863            signatures: vec![],
864        }
865    }
866}
867
868impl<TYPES: NodeType> LightClientStateUpdateCertificateV2<TYPES> {
869    pub fn genesis() -> Self {
870        Self {
871            epoch: EpochNumber::genesis(),
872            light_client_state: Default::default(),
873            next_stake_table_state: Default::default(),
874            signatures: vec![],
875            auth_root: Default::default(),
876        }
877    }
878}
879
880#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
881#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
882pub struct EpochRootQuorumCertificateV2<TYPES: NodeType> {
883    pub qc: QuorumCertificate2<TYPES>,
884    pub state_cert: LightClientStateUpdateCertificateV2<TYPES>,
885}
886
887impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV2<TYPES> {
888    fn view_number(&self) -> ViewNumber {
889        self.qc.view_number()
890    }
891}
892
893impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV2<TYPES> {
894    fn epoch(&self) -> Option<EpochNumber> {
895        self.qc.epoch()
896    }
897}
898
899#[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)]
900#[serde(bound(deserialize = "QuorumCertificate2<TYPES>:for<'a> Deserialize<'a>"))]
901pub struct EpochRootQuorumCertificateV1<TYPES: NodeType> {
902    pub qc: QuorumCertificate2<TYPES>,
903    pub state_cert: LightClientStateUpdateCertificateV1<TYPES>,
904}
905
906impl<TYPES: NodeType> HasViewNumber for EpochRootQuorumCertificateV1<TYPES> {
907    fn view_number(&self) -> ViewNumber {
908        self.qc.view_number()
909    }
910}
911
912impl<TYPES: NodeType> HasEpoch for EpochRootQuorumCertificateV1<TYPES> {
913    fn epoch(&self) -> Option<EpochNumber> {
914        self.qc.epoch()
915    }
916}
917
918impl<TYPES: NodeType> From<EpochRootQuorumCertificateV1<TYPES>>
919    for EpochRootQuorumCertificateV2<TYPES>
920{
921    fn from(root_qc: EpochRootQuorumCertificateV1<TYPES>) -> Self {
922        Self {
923            qc: root_qc.qc,
924            state_cert: root_qc.state_cert.into(),
925        }
926    }
927}
928
929impl<TYPES: NodeType> From<EpochRootQuorumCertificateV2<TYPES>>
930    for EpochRootQuorumCertificateV1<TYPES>
931{
932    fn from(root_qc: EpochRootQuorumCertificateV2<TYPES>) -> Self {
933        Self {
934            qc: root_qc.qc,
935            state_cert: root_qc.state_cert.into(),
936        }
937    }
938}
939
940/// A pair of QCs (or a single QC) attesting to a leaf.
941///
942/// Generally we only need a single QC, but during an epoch transition, we require a pair: one
943/// signed by the current membership and one signed by the next epoch's membership. This type
944/// encapsulates both.
945#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
946#[serde(bound = "")]
947pub struct CertificatePair<TYPES: NodeType> {
948    /// The basic QC.
949    qc: QuorumCertificate2<TYPES>,
950
951    /// A QC from the next epoch's membership, if this QC is part of an epoch transition.
952    next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
953}
954
955impl<TYPES: NodeType> CertificatePair<TYPES> {
956    /// Create a certificate pair.
957    pub fn new(
958        qc: QuorumCertificate2<TYPES>,
959        next_epoch_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
960    ) -> Self {
961        Self { qc, next_epoch_qc }
962    }
963
964    /// Create a certificate for a non-epoch-transitioning block.
965    pub fn non_epoch_change(qc: QuorumCertificate2<TYPES>) -> Self {
966        Self::new(qc, None)
967    }
968
969    /// Create a certificate for the parent of a leaf, using the justifying QCs in the leaf.
970    pub fn for_parent(leaf: &Leaf2<TYPES>) -> Self {
971        Self {
972            qc: leaf.justify_qc(),
973            next_epoch_qc: leaf.next_epoch_justify_qc(),
974        }
975    }
976
977    /// The raw QC.
978    pub fn qc(&self) -> &QuorumCertificate2<TYPES> {
979        &self.qc
980    }
981
982    /// A raw QC from the subsequent epoch's quorum, if this certificate is part of an epoch change.
983    pub fn next_epoch_qc(&self) -> Option<&NextEpochQuorumCertificate2<TYPES>> {
984        self.next_epoch_qc.as_ref()
985    }
986
987    /// The leaf commitment signed by this certificate.
988    pub fn leaf_commit(&self) -> Commitment<Leaf2<TYPES>> {
989        self.qc.data.leaf_commit
990    }
991
992    /// The epoch number this certificate belongs to.
993    ///
994    /// [`None`] if this certificate originated before epochs were enabled.
995    pub fn epoch(&self) -> Option<EpochNumber> {
996        self.qc.data.epoch
997    }
998
999    /// The block number attached to this certificate.
1000    ///
1001    /// [`None`] if this certificate originated before epochs were enabled.
1002    pub fn block_number(&self) -> Option<u64> {
1003        self.qc.data.block_number
1004    }
1005
1006    /// Verify that the next epoch QC is present and consistent if required.
1007    ///
1008    /// This checks that if required, the next epoch QC is present and is consistent with the
1009    /// primary QC. It does not check the signature on either QC, only that the data being signed
1010    /// over is consistent between the two.
1011    ///
1012    /// Returns the next epoch QC if it is present and invariants are satisfied. Returns an error if
1013    /// a required next epoch QC is missing or if it is inconsistent with the primary QC. Returns
1014    /// [`None`] if a next epoch QC is not required for this certificate.
1015    pub fn verify_next_epoch_qc(
1016        &self,
1017        epoch_height: u64,
1018    ) -> Result<Option<&NextEpochQuorumCertificate2<TYPES>>> {
1019        let block_number = self.qc.data.block_number.context(warn!(
1020            "QC for epoch {:?} has no block number",
1021            self.qc.data.epoch
1022        ))?;
1023        if !is_epoch_transition(block_number, epoch_height) {
1024            tracing::debug!(
1025                block_number,
1026                epoch_height,
1027                "QC is not in an epoch transition"
1028            );
1029            return Ok(None);
1030        }
1031
1032        let next_epoch_qc = self.next_epoch_qc.as_ref().context(warn!(
1033            "Received High QC for the transition block {block_number} but not the next epoch QC"
1034        ))?;
1035
1036        // The signature from the next epoch must be over the same data.
1037        ensure!(self.qc.view_number == next_epoch_qc.view_number);
1038        ensure!(self.qc.data == *next_epoch_qc.data);
1039
1040        Ok(Some(next_epoch_qc))
1041    }
1042}
1043
1044impl<TYPES: NodeType> HasViewNumber for CertificatePair<TYPES> {
1045    fn view_number(&self) -> ViewNumber {
1046        self.qc.view_number()
1047    }
1048}
1049
1050/// Check that a light client state update certificate corresponds to a given QC:
1051/// the QC is for an epoch root block, the epochs match, and the view numbers match.
1052pub fn check_qc_state_cert_correspondence<TYPES: NodeType>(
1053    qc: &QuorumCertificate2<TYPES>,
1054    state_cert: &LightClientStateUpdateCertificateV2<TYPES>,
1055    epoch_height: u64,
1056) -> bool {
1057    qc.data
1058        .block_number
1059        .is_some_and(|bn| is_epoch_root(bn, epoch_height))
1060        && Some(state_cert.epoch) == qc.data.epoch()
1061        && qc.view_number().u64() == state_cert.light_client_state.view_number
1062}