Skip to main content

linera_chain/certificate/
validated.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;
8use linera_base::{
9    crypto::{CryptoHash, ValidatorPublicKey, ValidatorSignature},
10    data_types::Round,
11};
12use linera_execution::committee::Committee;
13use serde::{
14    ser::{Serialize, Serializer},
15    Deserialize, Deserializer,
16};
17
18use super::{generic::GenericCertificate, Certificate, Certified, LiteCertificate};
19use crate::{
20    block::{Block, ConversionError, ValidatedBlock},
21    justification::JustificationChain,
22    ChainError,
23};
24
25/// The serialized representation of a [`ValidatedBlockCertificate`]. Deriving the
26/// (de)serialization on this single type keeps both directions in sync and free of manual field
27/// bookkeeping; the manual impls only add the strictly-ordered-signatures invariant.
28#[derive(serde::Serialize, serde::Deserialize)]
29#[serde(rename = "ValidatedBlockCertificate")]
30struct Repr<'a> {
31    value: Cow<'a, ValidatedBlock>,
32    round: Round,
33    unlocking_round: Option<Round>,
34    justification_commitment: Option<CryptoHash>,
35    signatures: Cow<'a, [(ValidatorPublicKey, ValidatorSignature)]>,
36    justification: Cow<'a, JustificationChain>,
37}
38
39/// Certificate for a [`ValidatedBlock`] instance, certified in some round whose `ValidatedBlock`
40/// voters signed an unlocking round.
41///
42/// A validated block certificate means the block is valid (but not necessarily finalized yet).
43/// Since only one block per round is validated, there can be at most one such certificate in
44/// every round. It wraps the signed quorum and carries the justification chain that grounds the
45/// unlocking round the voters signed.
46#[derive(Clone, Debug, Allocative)]
47#[cfg_attr(with_testing, derive(Eq, PartialEq))]
48pub struct ValidatedBlockCertificate {
49    /// The signed quorum of `ValidatedBlock` votes. Its unlocking round equals
50    /// `justification.top_unlocking_round()`.
51    quorum: GenericCertificate<ValidatedBlock>,
52    /// The chain of validated quorums for the same block in rounds below this certificate's,
53    /// rising from the grounding round to its top link in the unlocking round. Empty iff
54    /// the unlocking round is `None`.
55    justification: JustificationChain,
56}
57
58impl ValidatedBlockCertificate {
59    /// Creates a validated block certificate with an empty justification chain (unlocking round `None`).
60    pub fn new(
61        value: ValidatedBlock,
62        round: Round,
63        signatures: Vec<(ValidatorPublicKey, ValidatorSignature)>,
64    ) -> Self {
65        Self {
66            quorum: GenericCertificate::new(value, round, signatures),
67            justification: JustificationChain::default(),
68        }
69    }
70
71    /// Creates a validated block certificate from a signed quorum and its justification chain.
72    pub fn from_parts(
73        quorum: GenericCertificate<ValidatedBlock>,
74        justification: JustificationChain,
75    ) -> Self {
76        Self {
77            quorum,
78            justification,
79        }
80    }
81
82    /// Returns the signed quorum of `ValidatedBlock` votes.
83    pub fn quorum(&self) -> &GenericCertificate<ValidatedBlock> {
84        &self.quorum
85    }
86
87    /// Returns the chain of validated quorums in rounds below this certificate's.
88    pub fn justification(&self) -> &JustificationChain {
89        &self.justification
90    }
91
92    /// Consumes this certificate, returning the signed quorum and the justification chain.
93    pub fn into_parts(self) -> (GenericCertificate<ValidatedBlock>, JustificationChain) {
94        (self.quorum, self.justification)
95    }
96
97    /// Returns the round in which the value was certified.
98    pub fn round(&self) -> Round {
99        self.quorum.round()
100    }
101
102    /// Consumes this certificate, returning the validated block it contains.
103    pub fn into_value(self) -> ValidatedBlock {
104        self.quorum.into_value()
105    }
106
107    /// Consumes this certificate, returning the validated block it contains.
108    pub fn into_inner(self) -> ValidatedBlock {
109        self.quorum.into_inner()
110    }
111
112    /// Returns the full justification chain that a certificate certified in a higher round on
113    /// top of this one would carry: the chain below it, with this certificate's own quorum
114    /// appended as the new top link.
115    pub fn full_justification(&self) -> JustificationChain {
116        self.justification
117            .append(self.quorum.round(), self.quorum.signatures().clone())
118    }
119
120    /// Returns the justification commitment that a vote citing this certificate signs: the hash
121    /// of this certificate's own quorum as a [`CommittedQuorum`], which transitively commits to
122    /// the chain below it. Equals [`full_justification`](Self::full_justification)'s commitment.
123    ///
124    /// [`CommittedQuorum`]: crate::justification::CommittedQuorum
125    pub fn full_justification_commitment(&self) -> CryptoHash {
126        crate::justification::CommittedQuorum {
127            value_hash: self.hash(),
128            round: self.quorum.round(),
129            unlocking_round: self.quorum.unlocking_round(),
130            previous: self.quorum.justification_commitment(),
131            signatures: self.quorum.signatures().clone(),
132        }
133        .commitment()
134    }
135
136    /// Verifies the certificate: its signatures, its justification chain, that the unlocking round
137    /// matches the top of the chain, and that the chain lies in rounds strictly below this
138    /// certificate's. Delegates to [`LiteCertificate::check`], the single source of truth for the
139    /// quorum-to-chain binding.
140    pub fn check(&self, committee: &Committee) -> Result<(), ChainError> {
141        self.lite_certificate().check(committee)?;
142        Ok(())
143    }
144
145    /// Returns the [`LiteCertificate`] corresponding to this certificate, borrowing the chain.
146    pub fn lite_certificate(&self) -> LiteCertificate<'_> {
147        let mut lite = self.quorum.lite_certificate_without_justification();
148        lite.justification = Cow::Borrowed(&self.justification);
149        lite
150    }
151}
152
153impl Deref for ValidatedBlockCertificate {
154    type Target = GenericCertificate<ValidatedBlock>;
155
156    fn deref(&self) -> &Self::Target {
157        &self.quorum
158    }
159}
160
161impl Certified for ValidatedBlockCertificate {
162    type Value = ValidatedBlock;
163
164    fn value(&self) -> &ValidatedBlock {
165        self.quorum.value()
166    }
167
168    fn round(&self) -> Round {
169        ValidatedBlockCertificate::round(self)
170    }
171
172    fn unlocking_round(&self) -> Option<Round> {
173        self.quorum.unlocking_round()
174    }
175
176    fn signatures(&self) -> &Vec<(ValidatorPublicKey, ValidatorSignature)> {
177        self.quorum.signatures()
178    }
179
180    fn lite_certificate(&self) -> LiteCertificate<'_> {
181        ValidatedBlockCertificate::lite_certificate(self)
182    }
183
184    fn check(&self, committee: &Committee) -> Result<(), ChainError> {
185        ValidatedBlockCertificate::check(self, committee)
186    }
187}
188
189impl GenericCertificate<ValidatedBlock> {
190    /// Returns the total number of outgoing messages in the certified block.
191    #[cfg(with_testing)]
192    pub fn outgoing_message_count(&self) -> usize {
193        self.block().messages().iter().map(Vec::len).sum()
194    }
195
196    /// Returns reference to the [`Block`] contained in this certificate.
197    pub fn block(&self) -> &Block {
198        self.inner().block()
199    }
200}
201
202impl TryFrom<Certificate> for ValidatedBlockCertificate {
203    type Error = ConversionError;
204
205    fn try_from(cert: Certificate) -> Result<Self, Self::Error> {
206        match cert {
207            Certificate::Validated(validated) => Ok(validated),
208            _ => Err(ConversionError::ValidatedBlock),
209        }
210    }
211}
212
213impl From<ValidatedBlockCertificate> for Certificate {
214    fn from(cert: ValidatedBlockCertificate) -> Certificate {
215        Certificate::Validated(cert)
216    }
217}
218
219impl Serialize for ValidatedBlockCertificate {
220    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
221        Repr {
222            value: Cow::Borrowed(self.quorum.inner()),
223            round: self.quorum.round(),
224            unlocking_round: self.quorum.unlocking_round(),
225            justification_commitment: self.quorum.justification_commitment(),
226            signatures: Cow::Borrowed(self.quorum.signatures().as_slice()),
227            justification: Cow::Borrowed(&self.justification),
228        }
229        .serialize(serializer)
230    }
231}
232
233impl<'de> Deserialize<'de> for ValidatedBlockCertificate {
234    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
235    where
236        D: Deserializer<'de>,
237    {
238        let inner = Repr::deserialize(deserializer)?;
239        let signatures = inner.signatures.into_owned();
240        if !crate::data_types::is_strictly_ordered(&signatures) {
241            Err(serde::de::Error::custom(
242                "Signatures are not strictly ordered",
243            ))
244        } else {
245            Ok(Self::from_parts(
246                GenericCertificate::new_with_payload(
247                    inner.value.into_owned(),
248                    inner.round,
249                    inner.unlocking_round,
250                    false,
251                    inner.justification_commitment,
252                    signatures,
253                ),
254                inner.justification.into_owned(),
255            ))
256        }
257    }
258}