1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright (c) Facebook, Inc. and its affiliates.
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::borrow::Cow;

use linera_base::{
    crypto::{ValidatorPublicKey, ValidatorSignature},
    data_types::Round,
    hashed::Hashed,
};
use linera_execution::committee::Committee;
use serde::{Deserialize, Serialize};

use super::{CertificateValue, GenericCertificate};
use crate::{
    data_types::{check_signatures, LiteValue, LiteVote},
    ChainError,
};

/// A certified statement from the committee, without the value.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(with_testing, derive(Eq, PartialEq))]
pub struct LiteCertificate<'a> {
    /// Hash and chain ID of the certified value (used as key for storage).
    pub value: LiteValue,
    /// The round in which the value was certified.
    pub round: Round,
    /// Signatures on the value.
    pub signatures: Cow<'a, [(ValidatorPublicKey, ValidatorSignature)]>,
}

impl<'a> LiteCertificate<'a> {
    pub fn new(
        value: LiteValue,
        round: Round,
        mut signatures: Vec<(ValidatorPublicKey, ValidatorSignature)>,
    ) -> Self {
        signatures.sort_by_key(|&(validator_name, _)| validator_name);

        let signatures = Cow::Owned(signatures);
        Self {
            value,
            round,
            signatures,
        }
    }

    /// Creates a [`LiteCertificate`] from a list of votes, without cryptographically checking the
    /// signatures. Returns `None` if the votes are empty or don't have matching values and rounds.
    pub fn try_from_votes(votes: impl IntoIterator<Item = LiteVote>) -> Option<Self> {
        let mut votes = votes.into_iter();
        let LiteVote {
            value,
            round,
            public_key,
            signature,
        } = votes.next()?;
        let mut signatures = vec![(public_key, signature)];
        for vote in votes {
            if vote.value.value_hash != value.value_hash || vote.round != round {
                return None;
            }
            signatures.push((vote.public_key, vote.signature));
        }
        Some(LiteCertificate::new(value, round, signatures))
    }

    /// Verifies the certificate.
    pub fn check(&self, committee: &Committee) -> Result<&LiteValue, ChainError> {
        check_signatures(
            self.value.value_hash,
            self.value.kind,
            self.round,
            &self.signatures,
            committee,
        )?;
        Ok(&self.value)
    }

    /// Checks whether the value matches this certificate.
    pub fn check_value<T: CertificateValue>(&self, value: &Hashed<T>) -> bool {
        self.value.chain_id == value.inner().chain_id()
            && T::KIND == self.value.kind
            && self.value.value_hash == value.hash()
    }

    /// Returns the [`GenericCertificate`] with the specified value, if it matches.
    pub fn with_value<T: CertificateValue>(
        self,
        value: Hashed<T>,
    ) -> Option<GenericCertificate<T>> {
        if self.value.chain_id != value.inner().chain_id()
            || T::KIND != self.value.kind
            || self.value.value_hash != value.hash()
        {
            return None;
        }
        Some(GenericCertificate::new(
            value,
            self.round,
            self.signatures.into_owned(),
        ))
    }

    /// Returns a [`LiteCertificate`] that owns the list of signatures.
    pub fn cloned(&self) -> LiteCertificate<'static> {
        LiteCertificate {
            value: self.value.clone(),
            round: self.round,
            signatures: Cow::Owned(self.signatures.clone().into_owned()),
        }
    }
}