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