smoldot/finality/
verify.rs

1// Smoldot
2// Copyright (C) 2023  Pierre Krieger
3// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18use crate::finality::decode;
19
20use alloc::vec::Vec;
21use core::{cmp, iter, mem};
22use rand_chacha::{
23    ChaCha20Rng,
24    rand_core::{RngCore as _, SeedableRng as _},
25};
26
27/// Configuration for a commit verification process.
28#[derive(Debug)]
29pub struct CommitVerifyConfig<C> {
30    /// SCALE-encoded commit to verify.
31    pub commit: C,
32
33    /// Number of bytes used for encoding the block number in the SCALE-encoded commit.
34    pub block_number_bytes: usize,
35
36    // TODO: document
37    pub expected_authorities_set_id: u64,
38
39    /// Number of authorities that are allowed to emit pre-commits. Used to calculate the
40    /// threshold of the number of required signatures.
41    pub num_authorities: u32,
42
43    /// Seed for a PRNG used for various purposes during the verification.
44    ///
45    /// > **Note**: The verification is nonetheless deterministic.
46    pub randomness_seed: [u8; 32],
47}
48
49/// Commit verification in progress.
50#[must_use]
51pub enum CommitVerify<C> {
52    /// See [`CommitVerifyIsAuthority`].
53    IsAuthority(CommitVerifyIsAuthority<C>),
54    /// See [`CommitVerifyIsParent`].
55    IsParent(CommitVerifyIsParent<C>),
56    /// Verification is finished. Contains an error if the commit message is invalid.
57    Finished(Result<(), CommitVerifyError>),
58    /// Verification is finished, but [`CommitVerifyIsParent::resume`] has been called with `None`,
59    /// meaning that some signatures couldn't be verified, and the commit message doesn't contain
60    /// enough signatures that are known to be valid.
61    ///
62    /// The commit must be verified again after more blocks are available.
63    FinishedUnknown,
64}
65
66/// Verifies that a commit is valid.
67pub fn verify_commit<C: AsRef<[u8]>>(config: CommitVerifyConfig<C>) -> CommitVerify<C> {
68    let decoded_commit =
69        match decode::decode_grandpa_commit(config.commit.as_ref(), config.block_number_bytes) {
70            Ok(c) => c,
71            Err(_) => return CommitVerify::Finished(Err(CommitVerifyError::InvalidFormat)),
72        };
73
74    if decoded_commit.set_id != config.expected_authorities_set_id {
75        return CommitVerify::Finished(Err(CommitVerifyError::BadSetId));
76    }
77
78    if decoded_commit.auth_data.len() != decoded_commit.precommits.len() {
79        return CommitVerify::Finished(Err(CommitVerifyError::InvalidFormat));
80    }
81
82    let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
83
84    // Make sure that there is no duplicate authority public key.
85    {
86        let mut unique = hashbrown::HashSet::with_capacity_and_hasher(
87            decoded_commit.auth_data.len(),
88            crate::util::SipHasherBuild::new({
89                let mut seed = [0; 16];
90                randomness.fill_bytes(&mut seed);
91                seed
92            }),
93        );
94        if let Some((_, faulty_pub_key)) = decoded_commit
95            .auth_data
96            .iter()
97            .find(|(_, pubkey)| !unique.insert(pubkey))
98        {
99            return CommitVerify::Finished(Err(CommitVerifyError::DuplicateSignature {
100                authority_key: **faulty_pub_key,
101            }));
102        }
103    }
104
105    CommitVerification {
106        commit: config.commit,
107        block_number_bytes: config.block_number_bytes,
108        next_precommit_index: 0,
109        next_precommit_author_verified: false,
110        next_precommit_block_verified: false,
111        num_verified_signatures: 0,
112        num_authorities: config.num_authorities,
113        signatures_batch: ed25519_zebra::batch::Verifier::new(),
114        randomness,
115    }
116    .resume()
117}
118
119/// Must return whether a certain public key is in the list of authorities that are allowed to
120/// generate pre-commits.
121#[must_use]
122pub struct CommitVerifyIsAuthority<C> {
123    inner: CommitVerification<C>,
124}
125
126impl<C: AsRef<[u8]>> CommitVerifyIsAuthority<C> {
127    /// Public key to verify.
128    pub fn authority_public_key(&self) -> &[u8; 32] {
129        debug_assert!(!self.inner.next_precommit_author_verified);
130        let decoded_commit = decode::decode_grandpa_commit(
131            self.inner.commit.as_ref(),
132            self.inner.block_number_bytes,
133        )
134        .unwrap();
135        decoded_commit.auth_data[self.inner.next_precommit_index].1
136    }
137
138    /// Resumes the verification process.
139    ///
140    /// Must be passed `true` if the public key is indeed in the list of authorities.
141    /// Passing `false` always returns [`CommitVerify::Finished`] containing an error.
142    pub fn resume(mut self, is_authority: bool) -> CommitVerify<C> {
143        if !is_authority {
144            let key = *self.authority_public_key();
145            return CommitVerify::Finished(Err(CommitVerifyError::NotAuthority {
146                authority_key: key,
147            }));
148        }
149
150        self.inner.next_precommit_author_verified = true;
151        self.inner.resume()
152    }
153}
154
155/// Must return whether a certain block is a descendant of the target block.
156#[must_use]
157pub struct CommitVerifyIsParent<C> {
158    inner: CommitVerification<C>,
159    /// For performance reasons, the block number is copied here, but not the block hash. This
160    /// hasn't actually been benchmarked, so feel free to do so.
161    block_number: u64,
162}
163
164impl<C: AsRef<[u8]>> CommitVerifyIsParent<C> {
165    /// Height of the block to check.
166    pub fn block_number(&self) -> u64 {
167        self.block_number
168    }
169
170    /// Hash of the block to check.
171    pub fn block_hash(&self) -> &[u8; 32] {
172        debug_assert!(!self.inner.next_precommit_block_verified);
173        let decoded_commit = decode::decode_grandpa_commit(
174            self.inner.commit.as_ref(),
175            self.inner.block_number_bytes,
176        )
177        .unwrap();
178        decoded_commit.precommits[self.inner.next_precommit_index].target_hash
179    }
180
181    /// Height of the block that must be the ancestor of the block to check.
182    pub fn target_block_number(&self) -> u64 {
183        let decoded_commit = decode::decode_grandpa_commit(
184            self.inner.commit.as_ref(),
185            self.inner.block_number_bytes,
186        )
187        .unwrap();
188        decoded_commit.target_number
189    }
190
191    /// Hash of the block that must be the ancestor of the block to check.
192    pub fn target_block_hash(&self) -> &[u8; 32] {
193        let decoded_commit = decode::decode_grandpa_commit(
194            self.inner.commit.as_ref(),
195            self.inner.block_number_bytes,
196        )
197        .unwrap();
198        decoded_commit.target_hash
199    }
200
201    /// Resumes the verification process.
202    ///
203    /// Must be passed `Some(true)` if the block is known to be a descendant of the target block,
204    /// or `None` if it is unknown.
205    /// Passing `Some(false)` always returns [`CommitVerify::Finished`] containing an
206    /// error.
207    pub fn resume(mut self, is_parent: Option<bool>) -> CommitVerify<C> {
208        match is_parent {
209            None => {}
210            Some(true) => self.inner.num_verified_signatures += 1,
211            Some(false) => {
212                return CommitVerify::Finished(Err(CommitVerifyError::BadAncestry));
213            }
214        }
215
216        self.inner.next_precommit_block_verified = true;
217        self.inner.resume()
218    }
219}
220
221struct CommitVerification<C> {
222    /// Encoded commit message. Guaranteed to decode successfully.
223    commit: C,
224
225    /// See [`CommitVerifyConfig::block_number_bytes`].
226    block_number_bytes: usize,
227
228    /// Index of the next pre-commit to process within the commit.
229    next_precommit_index: usize,
230
231    /// Whether the precommit whose index is [`CommitVerification::next_precommit_index`] has been
232    /// verified as coming from the list of authorities.
233    next_precommit_author_verified: bool,
234
235    /// Whether the precommit whose index is [`CommitVerification::next_precommit_index`] has been
236    /// verified to be about a block that is a descendant of the target block.
237    next_precommit_block_verified: bool,
238
239    /// Number of signatures that have been pushed for verification. Needs to be above a certain
240    /// threshold for the commit to be valid.
241    num_verified_signatures: usize,
242
243    /// Number of authorities in the list. Used to calculate the threshold of the number of
244    /// required signatures.
245    num_authorities: u32,
246
247    /// Verifying all the signatures together brings better performances than verifying them one
248    /// by one.
249    /// Note that batched Ed25519 verification has some issues. The code below uses a special
250    /// flavor of Ed25519 where ambiguities are removed.
251    /// See <https://docs.rs/ed25519-zebra/2.2.0/ed25519_zebra/batch/index.html> and
252    /// <https://github.com/zcash/zips/blob/master/zip-0215.rst>
253    signatures_batch: ed25519_zebra::batch::Verifier,
254
255    /// Randomness generator used during the batch verification.
256    randomness: ChaCha20Rng,
257}
258
259impl<C: AsRef<[u8]>> CommitVerification<C> {
260    fn resume(mut self) -> CommitVerify<C> {
261        // The `verify` function that starts the verification performs the preliminary check that
262        // the commit has the correct format.
263        let decoded_commit =
264            decode::decode_grandpa_commit(self.commit.as_ref(), self.block_number_bytes).unwrap();
265
266        loop {
267            if let Some(precommit) = decoded_commit.precommits.get(self.next_precommit_index) {
268                if !self.next_precommit_author_verified {
269                    return CommitVerify::IsAuthority(CommitVerifyIsAuthority { inner: self });
270                }
271
272                if !self.next_precommit_block_verified {
273                    if precommit.target_hash == decoded_commit.target_hash
274                        && precommit.target_number == decoded_commit.target_number
275                    {
276                        self.next_precommit_block_verified = true;
277                    } else {
278                        return CommitVerify::IsParent(CommitVerifyIsParent {
279                            block_number: precommit.target_number,
280                            inner: self,
281                        });
282                    }
283                }
284
285                let authority_public_key = decoded_commit.auth_data[self.next_precommit_index].1;
286                let signature = decoded_commit.auth_data[self.next_precommit_index].0;
287
288                let mut msg = Vec::with_capacity(1 + 32 + self.block_number_bytes + 8 + 8);
289                msg.push(1u8); // This `1` indicates which kind of message is being signed.
290                msg.extend_from_slice(&precommit.target_hash[..]);
291                // The message contains the little endian block number. While simple in concept,
292                // in reality it is more complicated because we don't know the number of bytes of
293                // this block number at compile time. We thus copy as many bytes as appropriate and
294                // pad with 0s if necessary.
295                msg.extend_from_slice(
296                    &precommit.target_number.to_le_bytes()[..cmp::min(
297                        mem::size_of_val(&precommit.target_number),
298                        self.block_number_bytes,
299                    )],
300                );
301                msg.extend(
302                    iter::repeat(0).take(
303                        self.block_number_bytes
304                            .saturating_sub(mem::size_of_val(&precommit.target_number)),
305                    ),
306                );
307                msg.extend_from_slice(&u64::to_le_bytes(decoded_commit.round_number)[..]);
308                msg.extend_from_slice(&u64::to_le_bytes(decoded_commit.set_id)[..]);
309                debug_assert_eq!(msg.len(), msg.capacity());
310
311                self.signatures_batch
312                    .queue(ed25519_zebra::batch::Item::from((
313                        ed25519_zebra::VerificationKeyBytes::from(*authority_public_key),
314                        ed25519_zebra::Signature::from(*signature),
315                        &msg,
316                    )));
317
318                self.next_precommit_index += 1;
319                self.next_precommit_author_verified = false;
320                self.next_precommit_block_verified = false;
321            } else {
322                debug_assert!(!self.next_precommit_author_verified);
323                debug_assert!(!self.next_precommit_block_verified);
324
325                // Check that commit contains a number of signatures equal to at least 2/3rd of the
326                // number of authorities.
327                // Duplicate signatures are checked below.
328                // The logic of the check is `actual >= (expected * 2 / 3) + 1`.
329                if decoded_commit.precommits.len()
330                    < (usize::try_from(self.num_authorities).unwrap() * 2 / 3) + 1
331                {
332                    return CommitVerify::FinishedUnknown;
333                }
334
335                // Actual signatures verification performed here.
336                match self.signatures_batch.verify(&mut self.randomness) {
337                    Ok(()) => {}
338                    Err(_) => return CommitVerify::Finished(Err(CommitVerifyError::BadSignature)),
339                }
340
341                return CommitVerify::Finished(Ok(()));
342            }
343        }
344    }
345}
346
347/// Error that can happen while verifying a commit.
348#[derive(Debug, derive_more::Display, derive_more::Error)]
349pub enum CommitVerifyError {
350    /// Failed to decode the commit message.
351    InvalidFormat,
352    /// The authorities set id of the commit doesn't match the one that is expected.
353    BadSetId,
354    /// One of the public keys is invalid.
355    BadPublicKey,
356    /// One of the signatures can't be verified.
357    BadSignature,
358    /// One authority has produced two signatures.
359    #[display("One authority has produced two signatures")]
360    DuplicateSignature { authority_key: [u8; 32] },
361    /// One of the public keys isn't in the list of authorities.
362    #[display("One of the public keys isn't in the list of authorities")]
363    NotAuthority { authority_key: [u8; 32] },
364    /// Commit contains a vote for a block that isn't a descendant of the target block.
365    BadAncestry,
366}
367
368// TODO: tests
369
370/// Configuration for a justification verification process.
371#[derive(Debug)]
372pub struct JustificationVerifyConfig<J, I> {
373    /// Justification to verify.
374    pub justification: J,
375
376    pub block_number_bytes: usize,
377
378    // TODO: document
379    pub authorities_set_id: u64,
380
381    /// List of authorities that are allowed to emit pre-commits for the block referred to by
382    /// the justification. Must implement `Iterator<Item = &[u8]>`, where each item is
383    /// the public key of an authority.
384    pub authorities_list: I,
385
386    /// Seed for a PRNG used for various purposes during the verification.
387    ///
388    /// > **Note**: The verification is nonetheless deterministic.
389    pub randomness_seed: [u8; 32],
390}
391
392/// Verifies that a justification is valid.
393pub fn verify_justification<'a>(
394    config: JustificationVerifyConfig<impl AsRef<[u8]>, impl Iterator<Item = &'a [u8]>>,
395) -> Result<(), JustificationVerifyError> {
396    let decoded_justification = match decode::decode_grandpa_justification(
397        config.justification.as_ref(),
398        config.block_number_bytes,
399    ) {
400        Ok(c) => c,
401        Err(_) => return Err(JustificationVerifyError::InvalidFormat),
402    };
403
404    let num_precommits = decoded_justification.precommits.iter().count();
405
406    let mut randomness = ChaCha20Rng::from_seed(config.randomness_seed);
407
408    // Collect the authorities in a set in order to be able to determine with a low complexity
409    // whether a public key is an authority.
410    // For each authority, contains a boolean indicating whether the authority has been seen
411    // before in the list of pre-commits.
412    let mut authorities_list = {
413        let mut list = hashbrown::HashMap::<&[u8], _, _>::with_capacity_and_hasher(
414            0,
415            crate::util::SipHasherBuild::new({
416                let mut seed = [0; 16];
417                randomness.fill_bytes(&mut seed);
418                seed
419            }),
420        );
421        for authority in config.authorities_list {
422            list.insert(authority, false);
423        }
424        list
425    };
426
427    // Check that justification contains a number of signatures equal to at least 2/3rd of the
428    // number of authorities.
429    // Duplicate signatures are checked below.
430    // The logic of the check is `actual >= (expected * 2 / 3) + 1`.
431    if num_precommits < (authorities_list.len() * 2 / 3) + 1 {
432        return Err(JustificationVerifyError::NotEnoughSignatures);
433    }
434
435    // Verifying all the signatures together brings better performances than verifying them one
436    // by one.
437    // Note that batched ed25519 verification has some issues. The code below uses a special
438    // flavour of ed25519 where ambiguities are removed.
439    // See https://docs.rs/ed25519-zebra/2.2.0/ed25519_zebra/batch/index.html and
440    // https://github.com/zcash/zips/blob/master/zip-0215.rst
441    let mut batch = ed25519_zebra::batch::Verifier::new();
442
443    for precommit in decoded_justification.precommits.iter() {
444        match authorities_list.entry(precommit.authority_public_key) {
445            hashbrown::hash_map::Entry::Occupied(mut entry) => {
446                if entry.insert(true) {
447                    return Err(JustificationVerifyError::DuplicateSignature {
448                        authority_key: *precommit.authority_public_key,
449                    });
450                }
451            }
452            hashbrown::hash_map::Entry::Vacant(_) => {
453                return Err(JustificationVerifyError::NotAuthority {
454                    authority_key: *precommit.authority_public_key,
455                });
456            }
457        }
458
459        // TODO: must check signed block ancestry using `votes_ancestries`
460
461        let mut msg = Vec::with_capacity(1 + 32 + 4 + 8 + 8);
462        msg.push(1u8); // This `1` indicates which kind of message is being signed.
463        msg.extend_from_slice(&precommit.target_hash[..]);
464        // The message contains the little endian block number. While simple in concept,
465        // in reality it is more complicated because we don't know the number of bytes of
466        // this block number at compile time. We thus copy as many bytes as appropriate and
467        // pad with 0s if necessary.
468        msg.extend_from_slice(
469            &precommit.target_number.to_le_bytes()[..cmp::min(
470                mem::size_of_val(&precommit.target_number),
471                config.block_number_bytes,
472            )],
473        );
474        msg.extend(
475            iter::repeat(0).take(
476                config
477                    .block_number_bytes
478                    .saturating_sub(mem::size_of_val(&precommit.target_number)),
479            ),
480        );
481        msg.extend_from_slice(&u64::to_le_bytes(decoded_justification.round)[..]);
482        msg.extend_from_slice(&u64::to_le_bytes(config.authorities_set_id)[..]);
483        debug_assert_eq!(msg.len(), msg.capacity());
484
485        batch.queue(ed25519_zebra::batch::Item::from((
486            ed25519_zebra::VerificationKeyBytes::from(*precommit.authority_public_key),
487            ed25519_zebra::Signature::from(*precommit.signature),
488            &msg,
489        )));
490    }
491
492    // Actual signatures verification performed here.
493    batch
494        .verify(&mut randomness)
495        .map_err(|_| JustificationVerifyError::BadSignature)?;
496
497    // TODO: must check that votes_ancestries doesn't contain any unused entry
498    // TODO: there's also a "ghost" thing?
499
500    Ok(())
501}
502
503/// Error that can happen while verifying a justification.
504#[derive(Debug, derive_more::Display, derive_more::Error)]
505pub enum JustificationVerifyError {
506    /// Failed to decode the justification.
507    InvalidFormat,
508    /// One of the public keys is invalid.
509    BadPublicKey,
510    /// One of the signatures can't be verified.
511    BadSignature,
512    /// One authority has produced two signatures.
513    #[display("One authority has produced two signatures")]
514    DuplicateSignature { authority_key: [u8; 32] },
515    /// One of the public keys isn't in the list of authorities.
516    #[display("One of the public keys isn't in the list of authorities")]
517    NotAuthority { authority_key: [u8; 32] },
518    /// Justification doesn't contain enough authorities signatures to be valid.
519    NotEnoughSignatures,
520}