diem_types/
validator_verifier.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{account_address::AccountAddress, on_chain_config::ValidatorSet};
9use diem_crypto::{
10    bls::deserialize_bls_public_key_unchecked, hash::CryptoHash, Signature,
11    VRFPublicKey, VerifyingKey,
12};
13use serde::{Deserialize, Serialize};
14use std::{collections::BTreeMap, fmt};
15use thiserror::Error;
16
17use crate::{
18    on_chain_config::OnChainConfig,
19    validator_config::{
20        ConsensusPublicKey, ConsensusSignature, ConsensusVRFProof,
21        ConsensusVRFPublicKey,
22    },
23};
24#[cfg(any(test, feature = "fuzzing"))]
25use anyhow::{ensure, Result};
26#[cfg(any(test, feature = "fuzzing"))]
27use proptest_derive::Arbitrary;
28
29/// Errors possible during signature verification.
30#[derive(Debug, Error, PartialEq)]
31pub enum VerifyError {
32    #[error("Author is unknown")]
33    /// The author for this signature is unknown by this validator.
34    UnknownAuthor,
35    #[error(
36        "The voting power ({}) is less than quorum voting power ({})",
37        voting_power,
38        quorum_voting_power
39    )]
40    TooLittleVotingPower {
41        voting_power: u64,
42        quorum_voting_power: u64,
43    },
44    #[error(
45        "The number of signatures ({}) is greater than total number of authors ({})",
46        num_of_signatures,
47        num_of_authors
48    )]
49    TooManySignatures {
50        num_of_signatures: usize,
51        num_of_authors: usize,
52    },
53    #[error("Signature is invalid")]
54    /// The signature does not match the hash.
55    InvalidSignature,
56    #[error("Invalid VRF proof")]
57    InvalidVrfProof,
58}
59
60/// Helper struct to manage validator information for validation
61#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
62#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
63pub struct ValidatorConsensusInfo {
64    // We always vote for our local EpochState, and EpochState is included in
65    // the voted hash. Thus, if a malicious pubkey is provided here, its
66    // LedgerInfo won't get a QC.
67    #[serde(deserialize_with = "deserialize_bls_public_key_unchecked")]
68    public_key: ConsensusPublicKey,
69    /// None if we do not need VRF.
70    vrf_public_key: Option<ConsensusVRFPublicKey>,
71    voting_power: u64,
72}
73
74impl ValidatorConsensusInfo {
75    pub fn new(
76        public_key: ConsensusPublicKey,
77        vrf_public_key: Option<ConsensusVRFPublicKey>, voting_power: u64,
78    ) -> Self {
79        ValidatorConsensusInfo {
80            public_key,
81            vrf_public_key,
82            voting_power,
83        }
84    }
85
86    pub fn voting_power(&self) -> u64 { self.voting_power }
87
88    pub fn public_key(&self) -> &ConsensusPublicKey { &self.public_key }
89
90    pub fn vrf_public_key(&self) -> &Option<ConsensusVRFPublicKey> {
91        &self.vrf_public_key
92    }
93}
94
95/// Supports validation of signatures for known authors with individual voting
96/// powers. This struct can be used for all signature verification operations
97/// including block and network signature verification, respectively.
98#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
99#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
100pub struct ValidatorVerifier {
101    /// An ordered map of each validator's on-chain account address to its
102    /// pubkeys and voting power.
103    address_to_validator_info: BTreeMap<AccountAddress, ValidatorConsensusInfo>,
104    /// The minimum voting power required to achieve a quorum
105    quorum_voting_power: u64,
106    /// Total voting power of all validators (cached from
107    /// address_to_validator_info)
108    total_voting_power: u64,
109}
110
111impl ValidatorVerifier {
112    /// Initialize with a map of account address to validator info and set
113    /// quorum size to default (`2f + 1`) or zero if
114    /// `address_to_validator_info` is empty.
115    pub fn new(
116        address_to_validator_info: BTreeMap<
117            AccountAddress,
118            ValidatorConsensusInfo,
119        >,
120    ) -> Self {
121        let total_voting_power = sum_voting_power(&address_to_validator_info);
122        let quorum_voting_power = if address_to_validator_info.is_empty() {
123            0
124        } else {
125            total_voting_power * 2 / 3 + 1
126        };
127        ValidatorVerifier {
128            address_to_validator_info,
129            quorum_voting_power,
130            total_voting_power,
131        }
132    }
133
134    /// Initializes a validator verifier with a specified quorum voting power.
135    #[cfg(any(test, feature = "fuzzing"))]
136    pub fn new_with_quorum_voting_power(
137        address_to_validator_info: BTreeMap<
138            AccountAddress,
139            ValidatorConsensusInfo,
140        >,
141        quorum_voting_power: u64,
142    ) -> Result<Self> {
143        let total_voting_power = sum_voting_power(&address_to_validator_info);
144        ensure!(
145            quorum_voting_power <= total_voting_power,
146            "Quorum voting power is greater than the sum of all voting power of authors: {}, \
147             quorum_size: {}.",
148            quorum_voting_power,
149            total_voting_power
150        );
151        Ok(ValidatorVerifier {
152            address_to_validator_info,
153            quorum_voting_power,
154            total_voting_power,
155        })
156    }
157
158    /// Initializes a validator verifier with a specified quorum voting power
159    /// and total power.
160    #[cfg(any(test, feature = "fuzzing"))]
161    // This method should only used by tests and fuzzers to produce an arbitrary
162    // ValidatorVerifier.
163    pub fn new_for_testing(
164        address_to_validator_info: BTreeMap<
165            AccountAddress,
166            ValidatorConsensusInfo,
167        >,
168        quorum_voting_power: u64, total_voting_power: u64,
169    ) -> Self {
170        ValidatorVerifier {
171            address_to_validator_info,
172            quorum_voting_power,
173            total_voting_power,
174        }
175    }
176
177    /// Helper method to initialize with a single author and public key with
178    /// quorum voting power 1.
179    pub fn new_single(
180        author: AccountAddress, public_key: ConsensusPublicKey,
181        vrf_public_key: Option<ConsensusVRFPublicKey>,
182    ) -> Self {
183        let mut author_to_validator_info = BTreeMap::new();
184        author_to_validator_info.insert(
185            author,
186            ValidatorConsensusInfo::new(public_key, vrf_public_key, 1),
187        );
188        Self::new(author_to_validator_info)
189    }
190
191    /// Verify the correctness of a signature of a message by a known author.
192    pub fn verify<T: Serialize + CryptoHash>(
193        &self, author: AccountAddress, message: &T,
194        signature: &ConsensusSignature,
195    ) -> std::result::Result<(), VerifyError> {
196        match self.get_public_key(&author) {
197            Some(public_key) => {
198                if public_key
199                    .verify_struct_signature(message, signature)
200                    .is_err()
201                {
202                    Err(VerifyError::InvalidSignature)
203                } else {
204                    Ok(())
205                }
206            }
207            None => Err(VerifyError::UnknownAuthor),
208        }
209    }
210
211    /// Verify the correctness of a signature of a message by a known author.
212    pub fn verify_vrf(
213        &self, author: AccountAddress, seed: &[u8], proof: &ConsensusVRFProof,
214    ) -> std::result::Result<(), VerifyError> {
215        match self.get_vrf_public_key(&author) {
216            Some(Some(public_key)) => {
217                if public_key.verify_proof(seed, proof).is_err() {
218                    Err(VerifyError::InvalidVrfProof)
219                } else {
220                    Ok(())
221                }
222            }
223            _ => Err(VerifyError::UnknownAuthor),
224        }
225    }
226
227    /// This function will successfully return when at least quorum_size
228    /// signatures of known authors are successfully verified. Also, an
229    /// aggregated signature is considered invalid if any of the
230    /// attached signatures is invalid or it does not correspond to a known
231    /// author. The latter is to prevent malicious users from adding
232    /// arbitrary content to the signature payload that would go unnoticed.
233    pub fn verify_aggregated_struct_signature<T: CryptoHash + Serialize>(
234        &self, message: &T,
235        aggregated_signature: &BTreeMap<AccountAddress, ConsensusSignature>,
236    ) -> std::result::Result<(), VerifyError> {
237        self.check_num_of_signatures(aggregated_signature)?;
238        self.check_voting_power(aggregated_signature.keys())?;
239        for (author, signature) in aggregated_signature {
240            self.verify(*author, message, &signature.clone())?;
241        }
242        Ok(())
243    }
244
245    /// This function will try batch signature verification and falls back to
246    /// normal iterated verification if batching fails.
247    pub fn batch_verify_aggregated_signatures<T: CryptoHash + Serialize>(
248        &self, message: &T,
249        aggregated_signature: &BTreeMap<AccountAddress, ConsensusSignature>,
250    ) -> std::result::Result<(), VerifyError> {
251        self.check_num_of_signatures(aggregated_signature)?;
252        self.check_voting_power(aggregated_signature.keys())?;
253        let keys_and_signatures: Vec<(ConsensusPublicKey, ConsensusSignature)> =
254            aggregated_signature
255                .iter()
256                .flat_map(|(address, signature)| {
257                    let sig = signature.clone();
258                    self.get_public_key(&address).map(|pub_key| (pub_key, sig))
259                })
260                .collect();
261        // Fallback is required to identify the source of the problem if
262        // batching fails.
263        if ConsensusSignature::batch_verify(message, keys_and_signatures)
264            .is_err()
265        {
266            self.verify_aggregated_struct_signature(
267                message,
268                aggregated_signature,
269            )?
270        }
271        Ok(())
272    }
273
274    /// Ensure there are not more than the maximum expected signatures (all
275    /// possible signatures).
276    fn check_num_of_signatures(
277        &self,
278        aggregated_signature: &BTreeMap<AccountAddress, ConsensusSignature>,
279    ) -> std::result::Result<(), VerifyError> {
280        let num_of_signatures = aggregated_signature.len();
281        if num_of_signatures > self.len() {
282            return Err(VerifyError::TooManySignatures {
283                num_of_signatures,
284                num_of_authors: self.len(),
285            });
286        }
287        Ok(())
288    }
289
290    /// Ensure there is at least quorum_voting_power in the provided signatures
291    /// and there are only known authors. According to the threshold
292    /// verification policy, invalid public keys are not allowed.
293    pub fn check_voting_power<'a>(
294        &self, authors: impl Iterator<Item = &'a AccountAddress>,
295    ) -> std::result::Result<(), VerifyError> {
296        // Add voting power for valid accounts, exiting early for unknown
297        // authors
298        let mut aggregated_voting_power = 0;
299        for account_address in authors {
300            match self.get_voting_power(&account_address) {
301                Some(voting_power) => aggregated_voting_power += voting_power,
302                None => return Err(VerifyError::UnknownAuthor),
303            }
304        }
305
306        if aggregated_voting_power < self.quorum_voting_power {
307            return Err(VerifyError::TooLittleVotingPower {
308                voting_power: aggregated_voting_power,
309                quorum_voting_power: self.quorum_voting_power,
310            });
311        }
312        Ok(())
313    }
314
315    /// Returns the public key for this address.
316    pub fn get_public_key(
317        &self, author: &AccountAddress,
318    ) -> Option<ConsensusPublicKey> {
319        self.address_to_validator_info
320            .get(&author)
321            .map(|validator_info| validator_info.public_key.clone())
322    }
323
324    /// Returns the VRF public key for this address.
325    pub fn get_vrf_public_key(
326        &self, author: &AccountAddress,
327    ) -> Option<Option<ConsensusVRFPublicKey>> {
328        self.address_to_validator_info
329            .get(&author)
330            .map(|validator_info| validator_info.vrf_public_key.clone())
331    }
332
333    /// Returns the voting power for this address.
334    pub fn get_voting_power(&self, author: &AccountAddress) -> Option<u64> {
335        self.address_to_validator_info
336            .get(&author)
337            .map(|validator_info| validator_info.voting_power)
338    }
339
340    /// Returns an ordered list of account addresses as an `Iterator`.
341    pub fn get_ordered_account_addresses_iter(
342        &self,
343    ) -> impl Iterator<Item = AccountAddress> + '_ {
344        // Since `address_to_validator_info` is a `BTreeMap`, the `.keys()`
345        // iterator is guaranteed to be sorted.
346        self.address_to_validator_info.keys().copied()
347    }
348
349    /// Returns the number of authors to be validated.
350    pub fn len(&self) -> usize { self.address_to_validator_info.len() }
351
352    /// Is there at least one author?
353    pub fn is_empty(&self) -> bool { self.len() == 0 }
354
355    /// Returns quorum voting power.
356    pub fn quorum_voting_power(&self) -> u64 { self.quorum_voting_power }
357
358    // Returns total voting power.
359    pub fn total_voting_power(&self) -> u64 { self.total_voting_power }
360
361    pub fn address_to_validator_info(
362        &self,
363    ) -> &BTreeMap<AccountAddress, ValidatorConsensusInfo> {
364        &self.address_to_validator_info
365    }
366
367    /// Return the extra vote compared to `quorum_voting_power`.
368    pub fn extra_vote_count<'a>(
369        &self, signers: impl Iterator<Item = &'a AccountAddress>,
370    ) -> anyhow::Result<u64> {
371        let mut total_count = 0;
372        for signer in signers {
373            total_count += self
374                .get_voting_power(signer)
375                .ok_or(anyhow::anyhow!("Signer is not a validator"))?;
376        }
377        total_count.checked_sub(self.quorum_voting_power).ok_or(
378            anyhow::anyhow!("counted voting power overflows total power"),
379        )
380    }
381}
382
383impl OnChainConfig for ValidatorVerifier {
384    const IDENTIFIER: &'static str = "DiemSystem";
385}
386
387/// Returns sum of voting power from Map of validator account addresses,
388/// validator consensus info
389fn sum_voting_power(
390    address_to_validator_info: &BTreeMap<
391        AccountAddress,
392        ValidatorConsensusInfo,
393    >,
394) -> u64 {
395    address_to_validator_info.values().fold(0, |sum, x| {
396        sum.checked_add(x.voting_power)
397            .expect("sum of all voting power is greater than u64::max")
398    })
399}
400
401impl fmt::Display for ValidatorVerifier {
402    fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
403        write!(f, "ValidatorSet: [")?;
404        for (addr, info) in &self.address_to_validator_info {
405            write!(
406                f,
407                "{}: {}, ",
408                addr.short_str_lossless(),
409                info.voting_power
410            )?;
411        }
412        write!(f, "]")
413    }
414}
415
416impl From<&ValidatorSet> for ValidatorVerifier {
417    fn from(validator_set: &ValidatorSet) -> Self {
418        ValidatorVerifier::new(validator_set.payload().iter().fold(
419            BTreeMap::new(),
420            |mut map, validator| {
421                map.insert(
422                    *validator.account_address(),
423                    ValidatorConsensusInfo::new(
424                        validator.consensus_public_key().clone(),
425                        validator.vrf_public_key().clone(),
426                        validator.consensus_voting_power(),
427                    ),
428                );
429                map
430            },
431        ))
432    }
433}
434
435#[cfg(any(test, feature = "fuzzing"))]
436impl From<&ValidatorVerifier> for ValidatorSet {
437    fn from(verifier: &ValidatorVerifier) -> Self {
438        ValidatorSet::new(
439            verifier
440                .get_ordered_account_addresses_iter()
441                .map(|addr| {
442                    crate::validator_info::ValidatorInfo::new_with_test_network_keys(
443                        addr,
444                        verifier.get_public_key(&addr).unwrap(),
445                        verifier.get_vrf_public_key(&addr).unwrap(),
446                        verifier.get_voting_power(&addr).unwrap(),
447                    )
448                })
449                .collect(),
450        )
451    }
452}
453
454/// Helper function to get random validator signers and a corresponding
455/// validator verifier for testing.  If custom_voting_power_quorum is not None,
456/// set a custom voting power quorum amount. With pseudo_random_account_address
457/// enabled, logs show 0 -> [0000], 1 -> [1000]
458#[cfg(any(test, feature = "fuzzing"))]
459pub fn random_validator_verifier(
460    count: usize, custom_voting_power_quorum: Option<u64>,
461    pseudo_random_account_address: bool,
462) -> (
463    Vec<crate::validator_signer::ValidatorSigner>,
464    ValidatorVerifier,
465) {
466    let mut signers = Vec::new();
467    let mut account_address_to_validator_info = BTreeMap::new();
468    for i in 0..count {
469        let random_signer = if pseudo_random_account_address {
470            crate::validator_signer::ValidatorSigner::from_int(i as u8)
471        } else {
472            crate::validator_signer::ValidatorSigner::random([i as u8; 32])
473        };
474        account_address_to_validator_info.insert(
475            random_signer.author(),
476            crate::validator_verifier::ValidatorConsensusInfo::new(
477                random_signer.public_key(),
478                random_signer.vrf_public_key(),
479                1,
480            ),
481        );
482        signers.push(random_signer);
483    }
484    (
485        signers,
486        match custom_voting_power_quorum {
487            Some(custom_voting_power_quorum) => {
488                ValidatorVerifier::new_with_quorum_voting_power(
489                    account_address_to_validator_info,
490                    custom_voting_power_quorum,
491                )
492                .expect("Unable to create testing validator verifier")
493            }
494            None => ValidatorVerifier::new(account_address_to_validator_info),
495        },
496    )
497}
498
499#[cfg(test)]
500mod tests {
501    use super::*;
502    use crate::validator_signer::ValidatorSigner;
503    use diem_crypto::test_utils::{TestDiemCrypto, TEST_SEED};
504    use std::collections::BTreeMap;
505
506    #[test]
507    fn test_check_voting_power() {
508        let (validator_signers, validator_verifier) =
509            random_validator_verifier(2, None, false);
510        let mut author_to_signature_map = BTreeMap::new();
511
512        assert_eq!(
513            validator_verifier
514                .check_voting_power(author_to_signature_map.keys())
515                .unwrap_err(),
516            VerifyError::TooLittleVotingPower {
517                voting_power: 0,
518                quorum_voting_power: 2,
519            }
520        );
521
522        let dummy_struct = TestDiemCrypto("Hello, World".to_string());
523        for validator in validator_signers.iter() {
524            author_to_signature_map
525                .insert(validator.author(), validator.sign(&dummy_struct));
526        }
527
528        assert_eq!(
529            validator_verifier
530                .check_voting_power(author_to_signature_map.keys()),
531            Ok(())
532        );
533    }
534
535    #[test]
536    fn test_validator() {
537        let validator_signer = ValidatorSigner::random(TEST_SEED);
538        let dummy_struct = TestDiemCrypto("Hello, World".to_string());
539        let signature = validator_signer.sign(&dummy_struct);
540        let validator = ValidatorVerifier::new_single(
541            validator_signer.author(),
542            validator_signer.public_key(),
543            validator_signer.vrf_public_key(),
544        );
545        assert_eq!(
546            validator.verify(
547                validator_signer.author(),
548                &dummy_struct,
549                &signature
550            ),
551            Ok(())
552        );
553        let unknown_validator_signer = ValidatorSigner::random([1; 32]);
554        let unknown_signature = unknown_validator_signer.sign(&dummy_struct);
555        assert_eq!(
556            validator.verify(
557                unknown_validator_signer.author(),
558                &dummy_struct,
559                &unknown_signature
560            ),
561            Err(VerifyError::UnknownAuthor)
562        );
563        assert_eq!(
564            validator.verify(
565                validator_signer.author(),
566                &dummy_struct,
567                &unknown_signature
568            ),
569            Err(VerifyError::InvalidSignature)
570        );
571    }
572
573    #[test]
574    fn test_equal_vote_quorum_validators() {
575        const NUM_SIGNERS: u8 = 7;
576        // Generate NUM_SIGNERS random signers.
577        let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
578            .map(|i| ValidatorSigner::random([i; 32]))
579            .collect();
580        let dummy_struct = TestDiemCrypto("Hello, World".to_string());
581
582        // Create a map from authors to public keys with equal voting power.
583        let mut author_to_public_key_map = BTreeMap::new();
584        for validator in validator_signers.iter() {
585            author_to_public_key_map.insert(
586                validator.author(),
587                ValidatorConsensusInfo::new(
588                    validator.public_key(),
589                    validator.vrf_public_key(),
590                    1,
591                ),
592            );
593        }
594
595        // Create a map from author to signatures.
596        let mut author_to_signature_map = BTreeMap::new();
597        for validator in validator_signers.iter() {
598            author_to_signature_map
599                .insert(validator.author(), validator.sign(&dummy_struct));
600        }
601
602        // Let's assume our verifier needs to satisfy at least 5 signatures from
603        // the original NUM_SIGNERS.
604        let validator_verifier =
605            ValidatorVerifier::new_with_quorum_voting_power(
606                author_to_public_key_map,
607                5,
608            )
609            .expect("Incorrect quorum size.");
610
611        // Check against signatures == N; this will pass.
612        assert_eq!(
613            validator_verifier.batch_verify_aggregated_signatures(
614                &dummy_struct,
615                &author_to_signature_map
616            ),
617            Ok(())
618        );
619
620        // Add an extra unknown signer, signatures > N; this will fail.
621        let unknown_validator_signer =
622            ValidatorSigner::random([NUM_SIGNERS + 1; 32]);
623        let unknown_signature = unknown_validator_signer.sign(&dummy_struct);
624        author_to_signature_map.insert(
625            unknown_validator_signer.author(),
626            unknown_signature.clone(),
627        );
628        assert_eq!(
629            validator_verifier.batch_verify_aggregated_signatures(
630                &dummy_struct,
631                &author_to_signature_map
632            ),
633            Err(VerifyError::TooManySignatures {
634                num_of_signatures: 8,
635                num_of_authors: 7
636            })
637        );
638
639        // Add 5 valid signers only (quorum threshold is met); this will pass.
640        author_to_signature_map.clear();
641        for validator in validator_signers.iter().take(5) {
642            author_to_signature_map
643                .insert(validator.author(), validator.sign(&dummy_struct));
644        }
645        assert_eq!(
646            validator_verifier.batch_verify_aggregated_signatures(
647                &dummy_struct,
648                &author_to_signature_map
649            ),
650            Ok(())
651        );
652
653        // Add an unknown signer, but quorum is satisfied and signatures <= N;
654        // this will fail as we don't tolerate invalid signatures.
655        author_to_signature_map.insert(
656            unknown_validator_signer.author(),
657            unknown_signature.clone(),
658        );
659        assert_eq!(
660            validator_verifier.batch_verify_aggregated_signatures(
661                &dummy_struct,
662                &author_to_signature_map
663            ),
664            Err(VerifyError::UnknownAuthor)
665        );
666
667        // Add 4 valid signers only (quorum threshold is NOT met); this will
668        // fail.
669        author_to_signature_map.clear();
670        for validator in validator_signers.iter().take(4) {
671            author_to_signature_map
672                .insert(validator.author(), validator.sign(&dummy_struct));
673        }
674        assert_eq!(
675            validator_verifier.batch_verify_aggregated_signatures(
676                &dummy_struct,
677                &author_to_signature_map
678            ),
679            Err(VerifyError::TooLittleVotingPower {
680                voting_power: 4,
681                quorum_voting_power: 5
682            })
683        );
684
685        // Add an unknown signer, we have 5 signers, but one of them is invalid;
686        // this will fail.
687        author_to_signature_map
688            .insert(unknown_validator_signer.author(), unknown_signature);
689        assert_eq!(
690            validator_verifier.batch_verify_aggregated_signatures(
691                &dummy_struct,
692                &author_to_signature_map
693            ),
694            Err(VerifyError::UnknownAuthor)
695        );
696    }
697
698    #[test]
699    #[should_panic]
700    fn test_very_unequal_vote_quorum_validators() {
701        const NUM_SIGNERS: u8 = 4;
702        // Generate NUM_SIGNERS random signers.
703        let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
704            .map(|i| ValidatorSigner::random([i; 32]))
705            .collect();
706        let dummy_struct = TestDiemCrypto("Hello, World".to_string());
707
708        // Create a map from authors to public keys with increasing weights (0,
709        // 1, 2, 3) and a map of author to signature.
710        let mut author_to_public_key_map = BTreeMap::new();
711        let mut author_to_signature_map = BTreeMap::new();
712        for (i, validator_signer) in validator_signers.iter().enumerate() {
713            let mut voting_power: u64 = i as u64;
714            if i == 3 {
715                voting_power = u64::max_value()
716            }
717            author_to_public_key_map.insert(
718                validator_signer.author(),
719                ValidatorConsensusInfo::new(
720                    validator_signer.public_key(),
721                    validator_signer.vrf_public_key(),
722                    voting_power,
723                ),
724            );
725            author_to_signature_map.insert(
726                validator_signer.author(),
727                validator_signer.sign(&dummy_struct),
728            );
729        }
730
731        // expect this to panic
732        let _validator_verifier =
733            ValidatorVerifier::new(author_to_public_key_map);
734    }
735
736    #[test]
737    fn test_unequal_vote_quorum_validators() {
738        const NUM_SIGNERS: u8 = 4;
739        // Generate NUM_SIGNERS random signers.
740        let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
741            .map(|i| ValidatorSigner::random([i; 32]))
742            .collect();
743        let dummy_struct = TestDiemCrypto("Hello, World".to_string());
744
745        // Create a map from authors to public keys with increasing weights (0,
746        // 1, 2, 3) and a map of author to signature.
747        let mut author_to_public_key_map = BTreeMap::new();
748        let mut author_to_signature_map = BTreeMap::new();
749        for (i, validator_signer) in validator_signers.iter().enumerate() {
750            author_to_public_key_map.insert(
751                validator_signer.author(),
752                ValidatorConsensusInfo::new(
753                    validator_signer.public_key(),
754                    validator_signer.vrf_public_key(),
755                    i as u64,
756                ),
757            );
758            author_to_signature_map.insert(
759                validator_signer.author(),
760                validator_signer.sign(&dummy_struct),
761            );
762        }
763
764        // Let's assume our verifier needs to satisfy at least 5 quorum voting
765        // power
766        let validator_verifier =
767            ValidatorVerifier::new_with_quorum_voting_power(
768                author_to_public_key_map,
769                5,
770            )
771            .expect("Incorrect quorum size.");
772
773        // Check against all signatures (6 voting power); this will pass.
774        assert_eq!(
775            validator_verifier.batch_verify_aggregated_signatures(
776                &dummy_struct,
777                &author_to_signature_map
778            ),
779            Ok(())
780        );
781
782        // Add an extra unknown signer, signatures > N; this will fail.
783        let unknown_validator_signer =
784            ValidatorSigner::random([NUM_SIGNERS + 1; 32]);
785        let unknown_signature = unknown_validator_signer.sign(&dummy_struct);
786        author_to_signature_map.insert(
787            unknown_validator_signer.author(),
788            unknown_signature.clone(),
789        );
790        assert_eq!(
791            validator_verifier.batch_verify_aggregated_signatures(
792                &dummy_struct,
793                &author_to_signature_map
794            ),
795            Err(VerifyError::TooManySignatures {
796                num_of_signatures: 5,
797                num_of_authors: 4
798            })
799        );
800
801        // Add 5 voting power signers only (quorum threshold is met) with (2, 3)
802        // ; this will pass.
803        author_to_signature_map.clear();
804        for validator in validator_signers.iter().skip(2) {
805            author_to_signature_map
806                .insert(validator.author(), validator.sign(&dummy_struct));
807        }
808        assert_eq!(
809            validator_verifier.batch_verify_aggregated_signatures(
810                &dummy_struct,
811                &author_to_signature_map
812            ),
813            Ok(())
814        );
815
816        // Add an unknown signer, but quorum is satisfied and signatures <= N;
817        // this will fail as we don't tolerate invalid signatures.
818        author_to_signature_map.insert(
819            unknown_validator_signer.author(),
820            unknown_signature.clone(),
821        );
822        assert_eq!(
823            validator_verifier.batch_verify_aggregated_signatures(
824                &dummy_struct,
825                &author_to_signature_map
826            ),
827            Err(VerifyError::UnknownAuthor)
828        );
829
830        // Add first 3 valid signers only (quorum threshold is NOT met); this
831        // will fail.
832        author_to_signature_map.clear();
833        for validator in validator_signers.iter().take(3) {
834            author_to_signature_map
835                .insert(validator.author(), validator.sign(&dummy_struct));
836        }
837        assert_eq!(
838            validator_verifier.batch_verify_aggregated_signatures(
839                &dummy_struct,
840                &author_to_signature_map
841            ),
842            Err(VerifyError::TooLittleVotingPower {
843                voting_power: 3,
844                quorum_voting_power: 5
845            })
846        );
847
848        // Add an unknown signer, we have 5 signers, but one of them is invalid;
849        // this will fail.
850        author_to_signature_map
851            .insert(unknown_validator_signer.author(), unknown_signature);
852        assert_eq!(
853            validator_verifier.batch_verify_aggregated_signatures(
854                &dummy_struct,
855                &author_to_signature_map
856            ),
857            Err(VerifyError::UnknownAuthor)
858        );
859    }
860}