1use crate::{account_address::AccountAddress, on_chain_config::ValidatorSet};
5use diem_crypto::{
6 ed25519::{Ed25519PublicKey, Ed25519Signature},
7 hash::CryptoHash,
8 Signature, VerifyingKey,
9};
10use serde::{Deserialize, Serialize};
11use std::{collections::BTreeMap, fmt};
12use thiserror::Error;
13
14#[cfg(any(test, feature = "fuzzing"))]
15use anyhow::{ensure, Result};
16#[cfg(any(test, feature = "fuzzing"))]
17use proptest_derive::Arbitrary;
18
19#[derive(Debug, Error, PartialEq)]
21pub enum VerifyError {
22 #[error("Author is unknown")]
23 UnknownAuthor,
25 #[error(
26 "The voting power ({}) is less than quorum voting power ({})",
27 voting_power,
28 quorum_voting_power
29 )]
30 TooLittleVotingPower {
31 voting_power: u64,
32 quorum_voting_power: u64,
33 },
34 #[error(
35 "The number of signatures ({}) is greater than total number of authors ({})",
36 num_of_signatures,
37 num_of_authors
38 )]
39 TooManySignatures {
40 num_of_signatures: usize,
41 num_of_authors: usize,
42 },
43 #[error("Signature is invalid")]
44 InvalidSignature,
46 #[error("Inconsistent Block Info")]
47 InconsistentBlockInfo,
48}
49
50#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
52#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
53pub struct ValidatorConsensusInfo {
54 public_key: Ed25519PublicKey,
55 voting_power: u64,
56}
57
58impl ValidatorConsensusInfo {
59 pub fn new(public_key: Ed25519PublicKey, voting_power: u64) -> Self {
60 ValidatorConsensusInfo {
61 public_key,
62 voting_power,
63 }
64 }
65}
66
67#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
71#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
72pub struct ValidatorVerifier {
73 address_to_validator_info: BTreeMap<AccountAddress, ValidatorConsensusInfo>,
76 quorum_voting_power: u64,
78 total_voting_power: u64,
80}
81
82impl ValidatorVerifier {
83 pub fn new(
86 address_to_validator_info: BTreeMap<AccountAddress, ValidatorConsensusInfo>,
87 ) -> Self {
88 let total_voting_power = sum_voting_power(&address_to_validator_info);
89 let quorum_voting_power = if address_to_validator_info.is_empty() {
90 0
91 } else {
92 total_voting_power * 2 / 3 + 1
93 };
94 ValidatorVerifier {
95 address_to_validator_info,
96 quorum_voting_power,
97 total_voting_power,
98 }
99 }
100
101 #[cfg(any(test, feature = "fuzzing"))]
103 pub fn new_with_quorum_voting_power(
104 address_to_validator_info: BTreeMap<AccountAddress, ValidatorConsensusInfo>,
105 quorum_voting_power: u64,
106 ) -> Result<Self> {
107 let total_voting_power = sum_voting_power(&address_to_validator_info);
108 ensure!(
109 quorum_voting_power <= total_voting_power,
110 "Quorum voting power is greater than the sum of all voting power of authors: {}, \
111 quorum_size: {}.",
112 quorum_voting_power,
113 total_voting_power
114 );
115 Ok(ValidatorVerifier {
116 address_to_validator_info,
117 quorum_voting_power,
118 total_voting_power,
119 })
120 }
121
122 #[cfg(any(test, feature = "fuzzing"))]
124 pub fn new_for_testing(
126 address_to_validator_info: BTreeMap<AccountAddress, ValidatorConsensusInfo>,
127 quorum_voting_power: u64,
128 total_voting_power: u64,
129 ) -> Self {
130 ValidatorVerifier {
131 address_to_validator_info,
132 quorum_voting_power,
133 total_voting_power,
134 }
135 }
136
137 pub fn new_single(author: AccountAddress, public_key: Ed25519PublicKey) -> Self {
139 let mut author_to_validator_info = BTreeMap::new();
140 author_to_validator_info.insert(author, ValidatorConsensusInfo::new(public_key, 1));
141 Self::new(author_to_validator_info)
142 }
143
144 pub fn verify<T: Serialize + CryptoHash>(
146 &self,
147 author: AccountAddress,
148 message: &T,
149 signature: &Ed25519Signature,
150 ) -> std::result::Result<(), VerifyError> {
151 match self.get_public_key(&author) {
152 Some(public_key) => {
153 if public_key
154 .verify_struct_signature(message, signature)
155 .is_err()
156 {
157 Err(VerifyError::InvalidSignature)
158 } else {
159 Ok(())
160 }
161 }
162 None => Err(VerifyError::UnknownAuthor),
163 }
164 }
165
166 pub fn verify_aggregated_struct_signature<T: CryptoHash + Serialize>(
172 &self,
173 message: &T,
174 aggregated_signature: &BTreeMap<AccountAddress, Ed25519Signature>,
175 ) -> std::result::Result<(), VerifyError> {
176 self.check_num_of_signatures(aggregated_signature)?;
177 self.check_voting_power(aggregated_signature.keys())?;
178 for (author, signature) in aggregated_signature {
179 self.verify(*author, message, &signature.clone())?;
180 }
181 Ok(())
182 }
183
184 pub fn batch_verify_aggregated_signatures<T: CryptoHash + Serialize>(
187 &self,
188 message: &T,
189 aggregated_signature: &BTreeMap<AccountAddress, Ed25519Signature>,
190 ) -> std::result::Result<(), VerifyError> {
191 self.check_num_of_signatures(aggregated_signature)?;
192 self.check_voting_power(aggregated_signature.keys())?;
193 let keys_and_signatures: Vec<(Ed25519PublicKey, Ed25519Signature)> = aggregated_signature
194 .iter()
195 .flat_map(|(address, signature)| {
196 let sig = signature.clone();
197 self.get_public_key(address).map(|pub_key| (pub_key, sig))
198 })
199 .collect();
200 if Ed25519Signature::batch_verify(message, keys_and_signatures).is_err() {
202 self.verify_aggregated_struct_signature(message, aggregated_signature)?
203 }
204 Ok(())
205 }
206
207 fn check_num_of_signatures(
209 &self,
210 aggregated_signature: &BTreeMap<AccountAddress, Ed25519Signature>,
211 ) -> std::result::Result<(), VerifyError> {
212 let num_of_signatures = aggregated_signature.len();
213 if num_of_signatures > self.len() {
214 return Err(VerifyError::TooManySignatures {
215 num_of_signatures,
216 num_of_authors: self.len(),
217 });
218 }
219 Ok(())
220 }
221 pub fn check_voting_power<'a>(
225 &self,
226 authors: impl Iterator<Item = &'a AccountAddress>,
227 ) -> std::result::Result<(), VerifyError> {
228 let mut aggregated_voting_power = 0;
230 for account_address in authors {
231 match self.get_voting_power(account_address) {
232 Some(voting_power) => aggregated_voting_power += voting_power,
233 None => return Err(VerifyError::UnknownAuthor),
234 }
235 }
236
237 if aggregated_voting_power < self.quorum_voting_power {
238 return Err(VerifyError::TooLittleVotingPower {
239 voting_power: aggregated_voting_power,
240 quorum_voting_power: self.quorum_voting_power,
241 });
242 }
243 Ok(())
244 }
245
246 pub fn get_public_key(&self, author: &AccountAddress) -> Option<Ed25519PublicKey> {
248 self.address_to_validator_info
249 .get(author)
250 .map(|validator_info| validator_info.public_key.clone())
251 }
252
253 pub fn get_voting_power(&self, author: &AccountAddress) -> Option<u64> {
255 self.address_to_validator_info
256 .get(author)
257 .map(|validator_info| validator_info.voting_power)
258 }
259
260 pub fn get_ordered_account_addresses_iter(&self) -> impl Iterator<Item = AccountAddress> + '_ {
262 self.address_to_validator_info.keys().copied()
265 }
266
267 pub fn len(&self) -> usize {
269 self.address_to_validator_info.len()
270 }
271
272 pub fn is_empty(&self) -> bool {
274 self.len() == 0
275 }
276
277 pub fn quorum_voting_power(&self) -> u64 {
279 self.quorum_voting_power
280 }
281}
282
283fn sum_voting_power(
285 address_to_validator_info: &BTreeMap<AccountAddress, ValidatorConsensusInfo>,
286) -> u64 {
287 address_to_validator_info.values().fold(0, |sum, x| {
288 sum.checked_add(x.voting_power)
289 .expect("sum of all voting power is greater than u64::max")
290 })
291}
292
293impl fmt::Display for ValidatorVerifier {
294 fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
295 write!(f, "ValidatorSet: [")?;
296 for (addr, info) in &self.address_to_validator_info {
297 write!(f, "{}: {}, ", addr.short_str_lossless(), info.voting_power)?;
298 }
299 write!(f, "]")
300 }
301}
302
303impl From<&ValidatorSet> for ValidatorVerifier {
304 fn from(validator_set: &ValidatorSet) -> Self {
305 ValidatorVerifier::new(validator_set.payload().iter().fold(
306 BTreeMap::new(),
307 |mut map, validator| {
308 map.insert(
309 *validator.account_address(),
310 ValidatorConsensusInfo::new(
311 validator.consensus_public_key().clone(),
312 validator.consensus_voting_power(),
313 ),
314 );
315 map
316 },
317 ))
318 }
319}
320
321#[cfg(any(test, feature = "fuzzing"))]
322impl From<&ValidatorVerifier> for ValidatorSet {
323 fn from(verifier: &ValidatorVerifier) -> Self {
324 ValidatorSet::new(
325 verifier
326 .get_ordered_account_addresses_iter()
327 .map(|addr| {
328 crate::validator_info::ValidatorInfo::new_with_test_network_keys(
329 addr,
330 verifier.get_public_key(&addr).unwrap(),
331 verifier.get_voting_power(&addr).unwrap(),
332 )
333 })
334 .collect(),
335 )
336 }
337}
338
339#[cfg(any(test, feature = "fuzzing"))]
343pub fn random_validator_verifier(
344 count: usize,
345 custom_voting_power_quorum: Option<u64>,
346 pseudo_random_account_address: bool,
347) -> (
348 Vec<crate::validator_signer::ValidatorSigner>,
349 ValidatorVerifier,
350) {
351 let mut signers = Vec::new();
352 let mut account_address_to_validator_info = BTreeMap::new();
353 for i in 0..count {
354 let random_signer = if pseudo_random_account_address {
355 crate::validator_signer::ValidatorSigner::from_int(i as u8)
356 } else {
357 crate::validator_signer::ValidatorSigner::random([i as u8; 32])
358 };
359 account_address_to_validator_info.insert(
360 random_signer.author(),
361 crate::validator_verifier::ValidatorConsensusInfo::new(random_signer.public_key(), 1),
362 );
363 signers.push(random_signer);
364 }
365 (
366 signers,
367 match custom_voting_power_quorum {
368 Some(custom_voting_power_quorum) => ValidatorVerifier::new_with_quorum_voting_power(
369 account_address_to_validator_info,
370 custom_voting_power_quorum,
371 )
372 .expect("Unable to create testing validator verifier"),
373 None => ValidatorVerifier::new(account_address_to_validator_info),
374 },
375 )
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381 use crate::validator_signer::ValidatorSigner;
382 use diem_crypto::test_utils::{TestDiemCrypto, TEST_SEED};
383 use std::collections::BTreeMap;
384
385 #[test]
386 fn test_check_voting_power() {
387 let (validator_signers, validator_verifier) = random_validator_verifier(2, None, false);
388 let mut author_to_signature_map = BTreeMap::new();
389
390 assert_eq!(
391 validator_verifier
392 .check_voting_power(author_to_signature_map.keys())
393 .unwrap_err(),
394 VerifyError::TooLittleVotingPower {
395 voting_power: 0,
396 quorum_voting_power: 2,
397 }
398 );
399
400 let dummy_struct = TestDiemCrypto("Hello, World".to_string());
401 for validator in validator_signers.iter() {
402 author_to_signature_map.insert(validator.author(), validator.sign(&dummy_struct));
403 }
404
405 assert_eq!(
406 validator_verifier.check_voting_power(author_to_signature_map.keys()),
407 Ok(())
408 );
409 }
410
411 #[test]
412 fn test_validator() {
413 let validator_signer = ValidatorSigner::random(TEST_SEED);
414 let dummy_struct = TestDiemCrypto("Hello, World".to_string());
415 let signature = validator_signer.sign(&dummy_struct);
416 let validator =
417 ValidatorVerifier::new_single(validator_signer.author(), validator_signer.public_key());
418 assert_eq!(
419 validator.verify(validator_signer.author(), &dummy_struct, &signature),
420 Ok(())
421 );
422 let unknown_validator_signer = ValidatorSigner::random([1; 32]);
423 let unknown_signature = unknown_validator_signer.sign(&dummy_struct);
424 assert_eq!(
425 validator.verify(
426 unknown_validator_signer.author(),
427 &dummy_struct,
428 &unknown_signature
429 ),
430 Err(VerifyError::UnknownAuthor)
431 );
432 assert_eq!(
433 validator.verify(validator_signer.author(), &dummy_struct, &unknown_signature),
434 Err(VerifyError::InvalidSignature)
435 );
436 }
437
438 #[test]
439 fn test_equal_vote_quorum_validators() {
440 const NUM_SIGNERS: u8 = 7;
441 let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
443 .map(|i| ValidatorSigner::random([i; 32]))
444 .collect();
445 let dummy_struct = TestDiemCrypto("Hello, World".to_string());
446
447 let mut author_to_public_key_map = BTreeMap::new();
449 for validator in validator_signers.iter() {
450 author_to_public_key_map.insert(
451 validator.author(),
452 ValidatorConsensusInfo::new(validator.public_key(), 1),
453 );
454 }
455
456 let mut author_to_signature_map = BTreeMap::new();
458 for validator in validator_signers.iter() {
459 author_to_signature_map.insert(validator.author(), validator.sign(&dummy_struct));
460 }
461
462 let validator_verifier =
465 ValidatorVerifier::new_with_quorum_voting_power(author_to_public_key_map, 5)
466 .expect("Incorrect quorum size.");
467
468 assert_eq!(
470 validator_verifier
471 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
472 Ok(())
473 );
474
475 let unknown_validator_signer = ValidatorSigner::random([NUM_SIGNERS + 1; 32]);
477 let unknown_signature = unknown_validator_signer.sign(&dummy_struct);
478 author_to_signature_map
479 .insert(unknown_validator_signer.author(), unknown_signature.clone());
480 assert_eq!(
481 validator_verifier
482 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
483 Err(VerifyError::TooManySignatures {
484 num_of_signatures: 8,
485 num_of_authors: 7
486 })
487 );
488
489 author_to_signature_map.clear();
491 for validator in validator_signers.iter().take(5) {
492 author_to_signature_map.insert(validator.author(), validator.sign(&dummy_struct));
493 }
494 assert_eq!(
495 validator_verifier
496 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
497 Ok(())
498 );
499
500 author_to_signature_map
503 .insert(unknown_validator_signer.author(), unknown_signature.clone());
504 assert_eq!(
505 validator_verifier
506 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
507 Err(VerifyError::UnknownAuthor)
508 );
509
510 author_to_signature_map.clear();
512 for validator in validator_signers.iter().take(4) {
513 author_to_signature_map.insert(validator.author(), validator.sign(&dummy_struct));
514 }
515 assert_eq!(
516 validator_verifier
517 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
518 Err(VerifyError::TooLittleVotingPower {
519 voting_power: 4,
520 quorum_voting_power: 5
521 })
522 );
523
524 author_to_signature_map.insert(unknown_validator_signer.author(), unknown_signature);
526 assert_eq!(
527 validator_verifier
528 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
529 Err(VerifyError::UnknownAuthor)
530 );
531 }
532
533 #[test]
534 #[should_panic]
535 fn test_very_unequal_vote_quorum_validators() {
536 const NUM_SIGNERS: u8 = 4;
537 let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
539 .map(|i| ValidatorSigner::random([i; 32]))
540 .collect();
541 let dummy_struct = TestDiemCrypto("Hello, World".to_string());
542
543 let mut author_to_public_key_map = BTreeMap::new();
546 let mut author_to_signature_map = BTreeMap::new();
547 for (i, validator_signer) in validator_signers.iter().enumerate() {
548 let mut voting_power: u64 = i as u64;
549 if i == 3 {
550 voting_power = u64::max_value()
551 }
552 author_to_public_key_map.insert(
553 validator_signer.author(),
554 ValidatorConsensusInfo::new(validator_signer.public_key(), voting_power),
555 );
556 author_to_signature_map.insert(
557 validator_signer.author(),
558 validator_signer.sign(&dummy_struct),
559 );
560 }
561
562 let _validator_verifier = ValidatorVerifier::new(author_to_public_key_map);
564 }
565
566 #[test]
567 fn test_unequal_vote_quorum_validators() {
568 const NUM_SIGNERS: u8 = 4;
569 let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
571 .map(|i| ValidatorSigner::random([i; 32]))
572 .collect();
573 let dummy_struct = TestDiemCrypto("Hello, World".to_string());
574
575 let mut author_to_public_key_map = BTreeMap::new();
578 let mut author_to_signature_map = BTreeMap::new();
579 for (i, validator_signer) in validator_signers.iter().enumerate() {
580 author_to_public_key_map.insert(
581 validator_signer.author(),
582 ValidatorConsensusInfo::new(validator_signer.public_key(), i as u64),
583 );
584 author_to_signature_map.insert(
585 validator_signer.author(),
586 validator_signer.sign(&dummy_struct),
587 );
588 }
589
590 let validator_verifier =
592 ValidatorVerifier::new_with_quorum_voting_power(author_to_public_key_map, 5)
593 .expect("Incorrect quorum size.");
594
595 assert_eq!(
597 validator_verifier
598 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
599 Ok(())
600 );
601
602 let unknown_validator_signer = ValidatorSigner::random([NUM_SIGNERS + 1; 32]);
604 let unknown_signature = unknown_validator_signer.sign(&dummy_struct);
605 author_to_signature_map
606 .insert(unknown_validator_signer.author(), unknown_signature.clone());
607 assert_eq!(
608 validator_verifier
609 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
610 Err(VerifyError::TooManySignatures {
611 num_of_signatures: 5,
612 num_of_authors: 4
613 })
614 );
615
616 author_to_signature_map.clear();
618 for validator in validator_signers.iter().skip(2) {
619 author_to_signature_map.insert(validator.author(), validator.sign(&dummy_struct));
620 }
621 assert_eq!(
622 validator_verifier
623 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
624 Ok(())
625 );
626
627 author_to_signature_map
630 .insert(unknown_validator_signer.author(), unknown_signature.clone());
631 assert_eq!(
632 validator_verifier
633 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
634 Err(VerifyError::UnknownAuthor)
635 );
636
637 author_to_signature_map.clear();
639 for validator in validator_signers.iter().take(3) {
640 author_to_signature_map.insert(validator.author(), validator.sign(&dummy_struct));
641 }
642 assert_eq!(
643 validator_verifier
644 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
645 Err(VerifyError::TooLittleVotingPower {
646 voting_power: 3,
647 quorum_voting_power: 5
648 })
649 );
650
651 author_to_signature_map.insert(unknown_validator_signer.author(), unknown_signature);
653 assert_eq!(
654 validator_verifier
655 .batch_verify_aggregated_signatures(&dummy_struct, &author_to_signature_map),
656 Err(VerifyError::UnknownAuthor)
657 );
658 }
659}