Skip to main content

linera_chain/certificate/
generic.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use allocative::{Allocative, Key, Visitor};
6use custom_debug_derive::Debug;
7use linera_base::{
8    crypto::{CryptoHash, ValidatorPublicKey, ValidatorSignature},
9    data_types::Round,
10};
11use linera_execution::committee::Committee;
12
13use super::CertificateValue;
14use crate::{data_types::LiteValue, ChainError};
15
16/// Generic type representing a certificate for `value` of type `T`.
17#[derive(Debug)]
18pub struct GenericCertificate<T: CertificateValue> {
19    value: T,
20    /// The round in which the value was certified.
21    pub round: Round,
22    /// The unlocking round the `ValidatedBlock` voters signed (see [`VoteValue`]). Always `None`
23    /// for `ConfirmedBlock`/`Timeout` certificates and for validated blocks with no justification.
24    ///
25    /// [`VoteValue`]: crate::data_types::VoteValue
26    unlocking_round: Option<Round>,
27    /// The first-round attestation the `ConfirmedBlock` voters signed (see [`VoteValue`]). Only
28    /// `true` for a `ConfirmedBlock` certificate confirming a block in the chain's first round;
29    /// always `false` for `ValidatedBlock`/`Timeout` certificates.
30    ///
31    /// [`VoteValue`]: crate::data_types::VoteValue
32    first_round: bool,
33    /// The justification commitment the voters signed (see [`VoteValue`]): the hash of the
34    /// quorum the votes cite, or `None` if they cite none. Always `None` for `Timeout`
35    /// certificates.
36    ///
37    /// [`VoteValue`]: crate::data_types::VoteValue
38    justification_commitment: Option<CryptoHash>,
39    signatures: Vec<(ValidatorPublicKey, ValidatorSignature)>,
40}
41
42impl<T: Allocative + CertificateValue> Allocative for GenericCertificate<T> {
43    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
44        visitor.visit_field(Key::new("GenericCertificate_value"), &self.value);
45        visitor.visit_field(Key::new("GenericCertificate_round"), &self.round);
46        for (public_key, signature) in &self.signatures {
47            visitor.visit_field(Key::new("ValidatorPublicKey"), public_key);
48            visitor.visit_field(Key::new("ValidatorSignature"), signature);
49        }
50    }
51}
52
53impl<T: CertificateValue> GenericCertificate<T> {
54    /// Creates a new certificate from a value, round and list of signatures.
55    pub fn new(
56        value: T,
57        round: Round,
58        signatures: Vec<(ValidatorPublicKey, ValidatorSignature)>,
59    ) -> Self {
60        Self::new_with_payload(value, round, None, false, None, signatures)
61    }
62
63    /// Creates a new certificate that also records the signed payload fields beyond the value
64    /// and round: the unlocking round its `ValidatedBlock` voters signed, the first-round
65    /// attestation its `ConfirmedBlock` voters signed, and the justification commitment (see
66    /// [`VoteValue`]).
67    ///
68    /// [`VoteValue`]: crate::data_types::VoteValue
69    pub fn new_with_payload(
70        value: T,
71        round: Round,
72        unlocking_round: Option<Round>,
73        first_round: bool,
74        justification_commitment: Option<CryptoHash>,
75        mut signatures: Vec<(ValidatorPublicKey, ValidatorSignature)>,
76    ) -> Self {
77        signatures.sort_by_key(|&(validator_name, _)| validator_name);
78
79        Self {
80            value,
81            round,
82            unlocking_round,
83            first_round,
84            justification_commitment,
85            signatures,
86        }
87    }
88
89    /// Returns the round in which the value was certified.
90    pub fn round(&self) -> Round {
91        self.round
92    }
93
94    /// Returns the unlocking round the `ValidatedBlock` voters signed, if any.
95    pub fn unlocking_round(&self) -> Option<Round> {
96        self.unlocking_round
97    }
98
99    /// Returns the first-round attestation the `ConfirmedBlock` voters signed.
100    pub fn first_round(&self) -> bool {
101        self.first_round
102    }
103
104    /// Returns the justification commitment the voters signed, if any.
105    pub fn justification_commitment(&self) -> Option<CryptoHash> {
106        self.justification_commitment
107    }
108
109    /// Returns a reference to the `Hashed` value contained in this certificate.
110    pub fn value(&self) -> &T {
111        &self.value
112    }
113
114    /// Consumes this certificate, returning the value it contains.
115    pub fn into_value(self) -> T {
116        self.value
117    }
118
119    /// Returns reference to the value contained in this certificate.
120    pub fn inner(&self) -> &T {
121        &self.value
122    }
123
124    /// Consumes this certificate, returning the value it contains.
125    pub fn into_inner(self) -> T {
126        self.value
127    }
128
129    /// Returns the certified value's hash.
130    pub fn hash(&self) -> CryptoHash {
131        self.value.hash()
132    }
133
134    /// Returns the list of signatures on the certified value.
135    pub fn signatures(&self) -> &Vec<(ValidatorPublicKey, ValidatorSignature)> {
136        &self.signatures
137    }
138
139    /// Returns a mutable reference to the list of signatures on the certified value.
140    #[cfg(with_testing)]
141    pub fn signatures_mut(&mut self) -> &mut Vec<(ValidatorPublicKey, ValidatorSignature)> {
142        &mut self.signatures
143    }
144
145    /// Adds a signature to the certificate's list of signatures
146    /// It's the responsibility of the caller to not insert duplicates
147    pub fn add_signature(
148        &mut self,
149        signature: (ValidatorPublicKey, ValidatorSignature),
150    ) -> &Vec<(ValidatorPublicKey, ValidatorSignature)> {
151        let index = self
152            .signatures
153            .binary_search_by(|(name, _)| name.cmp(&signature.0))
154            .unwrap_or_else(std::convert::identity);
155        self.signatures.insert(index, signature);
156        &self.signatures
157    }
158
159    /// Returns whether the validator is among the signatories of this certificate.
160    pub fn is_signed_by(&self, validator_name: &ValidatorPublicKey) -> bool {
161        self.signatures
162            .binary_search_by(|(name, _)| name.cmp(validator_name))
163            .is_ok()
164    }
165
166    /// Verifies the certificate.
167    pub fn check(&self, committee: &Committee) -> Result<(), ChainError>
168    where
169        T: CertificateValue,
170    {
171        let value = crate::data_types::VoteValue(
172            self.hash(),
173            self.round,
174            T::KIND,
175            self.unlocking_round,
176            self.first_round,
177            self.justification_commitment,
178        );
179        crate::data_types::check_signatures(&value, &self.signatures, committee)?;
180        Ok(())
181    }
182
183    /// Returns the `LiteCertificate` corresponding to this certificate, without the value and
184    /// with an *empty* justification chain.
185    ///
186    /// Named explicitly because a block certificate's real chain lives on its wrapper
187    /// ([`ConfirmedBlockCertificate`]/[`ValidatedBlockCertificate`]), not on the inner quorum:
188    /// calling this on the quorum would silently produce a chainless lite certificate that fails
189    /// verification at the receiver. The wrappers use it and then attach their chain; `Timeout`
190    /// certificates carry no chain, so for them it is complete on its own.
191    ///
192    /// [`ConfirmedBlockCertificate`]: crate::certificate::ConfirmedBlockCertificate
193    /// [`ValidatedBlockCertificate`]: crate::certificate::ValidatedBlockCertificate
194    pub fn lite_certificate_without_justification(&self) -> crate::certificate::LiteCertificate<'_>
195    where
196        T: CertificateValue,
197    {
198        crate::certificate::LiteCertificate {
199            value: LiteValue::new(&self.value),
200            round: self.round,
201            unlocking_round: self.unlocking_round,
202            first_round: self.first_round,
203            justification_commitment: self.justification_commitment,
204            justification: std::borrow::Cow::Owned(
205                crate::justification::JustificationChain::default(),
206            ),
207            signatures: std::borrow::Cow::Borrowed(&self.signatures),
208        }
209    }
210}
211
212impl<T: CertificateValue> Clone for GenericCertificate<T> {
213    fn clone(&self) -> Self {
214        Self {
215            value: self.value.clone(),
216            round: self.round,
217            unlocking_round: self.unlocking_round,
218            first_round: self.first_round,
219            justification_commitment: self.justification_commitment,
220            signatures: self.signatures.clone(),
221        }
222    }
223}
224
225#[cfg(with_testing)]
226impl<T: CertificateValue + Eq + PartialEq> Eq for GenericCertificate<T> {}
227#[cfg(with_testing)]
228impl<T: CertificateValue + Eq + PartialEq> PartialEq for GenericCertificate<T> {
229    fn eq(&self, other: &Self) -> bool {
230        self.hash() == other.hash()
231            && self.round == other.round
232            && self.unlocking_round == other.unlocking_round
233            && self.first_round == other.first_round
234            && self.justification_commitment == other.justification_commitment
235            && self.signatures == other.signatures
236    }
237}