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 with their validator public keys, 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(
51        votes: impl IntoIterator<Item = (ValidatorPublicKey, LiteVote)>,
52    ) -> Option<Self> {
53        let mut votes = votes.into_iter();
54        let (
55            public_key,
56            LiteVote {
57                value,
58                round,
59                signature,
60            },
61        ) = votes.next()?;
62        let mut signatures = vec![(public_key, signature)];
63        for (validator_key, vote) in votes {
64            if vote.value.value_hash != value.value_hash || vote.round != round {
65                return None;
66            }
67            signatures.push((validator_key, vote.signature));
68        }
69        Some(LiteCertificate::new(value, round, signatures))
70    }
71
72    /// Verifies the certificate.
73    pub fn check(&self, committee: &Committee) -> Result<&LiteValue, ChainError> {
74        check_signatures(
75            self.value.value_hash,
76            self.value.kind,
77            self.round,
78            &self.signatures,
79            committee,
80        )?;
81        Ok(&self.value)
82    }
83
84    /// Checks whether the value matches this certificate.
85    pub fn check_value<T: CertificateValue>(&self, value: &T) -> bool {
86        self.value.chain_id == value.chain_id()
87            && T::KIND == self.value.kind
88            && self.value.value_hash == value.hash()
89    }
90
91    /// Returns the [`GenericCertificate`] with the specified value, if it matches.
92    pub fn with_value<T: CertificateValue>(self, value: T) -> Option<GenericCertificate<T>> {
93        if self.value.chain_id != value.chain_id()
94            || T::KIND != self.value.kind
95            || self.value.value_hash != value.hash()
96        {
97            return None;
98        }
99        Some(GenericCertificate::new(
100            value,
101            self.round,
102            self.signatures.into_owned(),
103        ))
104    }
105
106    /// Returns a [`LiteCertificate`] that owns the list of signatures.
107    pub fn cloned(&self) -> LiteCertificate<'static> {
108        LiteCertificate {
109            value: self.value.clone(),
110            round: self.round,
111            signatures: Cow::Owned(self.signatures.clone().into_owned()),
112        }
113    }
114}