linera_chain/certificate/
lite.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::borrow::Cow;
6
7use linera_base::{
8    crypto::{ValidatorPublicKey, ValidatorSignature},
9    data_types::Round,
10};
11use linera_execution::committee::Committee;
12use serde::{Deserialize, Serialize};
13
14use super::{CertificateValue, GenericCertificate};
15use crate::{
16    data_types::{check_signatures, LiteValue, LiteVote},
17    ChainError,
18};
19
20/// A certified statement from the committee, without the value.
21#[derive(Clone, Debug, Serialize, Deserialize)]
22#[cfg_attr(with_testing, derive(Eq, PartialEq))]
23pub struct LiteCertificate<'a> {
24    /// Hash and chain ID of the certified value (used as key for storage).
25    pub value: LiteValue,
26    /// The round in which the value was certified.
27    pub round: Round,
28    /// Signatures on the value.
29    pub signatures: Cow<'a, [(ValidatorPublicKey, ValidatorSignature)]>,
30}
31
32impl LiteCertificate<'_> {
33    pub fn new(
34        value: LiteValue,
35        round: Round,
36        mut signatures: Vec<(ValidatorPublicKey, ValidatorSignature)>,
37    ) -> Self {
38        signatures.sort_by_key(|&(validator_name, _)| validator_name);
39
40        let signatures = Cow::Owned(signatures);
41        Self {
42            value,
43            round,
44            signatures,
45        }
46    }
47
48    /// Creates a [`LiteCertificate`] from a list of votes, without cryptographically checking the
49    /// signatures. Returns `None` if the votes are empty or don't have matching values and rounds.
50    pub fn try_from_votes(votes: impl IntoIterator<Item = LiteVote>) -> Option<Self> {
51        let mut votes = votes.into_iter();
52        let LiteVote {
53            value,
54            round,
55            public_key,
56            signature,
57        } = votes.next()?;
58        let mut signatures = vec![(public_key, signature)];
59        for vote in votes {
60            if vote.value.value_hash != value.value_hash || vote.round != round {
61                return None;
62            }
63            signatures.push((vote.public_key, vote.signature));
64        }
65        Some(LiteCertificate::new(value, round, signatures))
66    }
67
68    /// Verifies the certificate.
69    pub fn check(&self, committee: &Committee) -> Result<&LiteValue, ChainError> {
70        check_signatures(
71            self.value.value_hash,
72            self.value.kind,
73            self.round,
74            &self.signatures,
75            committee,
76        )?;
77        Ok(&self.value)
78    }
79
80    /// Checks whether the value matches this certificate.
81    pub fn check_value<T: CertificateValue>(&self, value: &T) -> bool {
82        self.value.chain_id == value.chain_id()
83            && T::KIND == self.value.kind
84            && self.value.value_hash == value.hash()
85    }
86
87    /// Returns the [`GenericCertificate`] with the specified value, if it matches.
88    pub fn with_value<T: CertificateValue>(self, value: T) -> Option<GenericCertificate<T>> {
89        if self.value.chain_id != value.chain_id()
90            || T::KIND != self.value.kind
91            || self.value.value_hash != value.hash()
92        {
93            return None;
94        }
95        Some(GenericCertificate::new(
96            value,
97            self.round,
98            self.signatures.into_owned(),
99        ))
100    }
101
102    /// Returns a [`LiteCertificate`] that owns the list of signatures.
103    pub fn cloned(&self) -> LiteCertificate<'static> {
104        LiteCertificate {
105            value: self.value.clone(),
106            round: self.round,
107            signatures: Cow::Owned(self.signatures.clone().into_owned()),
108        }
109    }
110}