1use crate::{
9 simplex::{
10 signing_scheme::{self, utils::Signers, vote_namespace_and_message},
11 types::{OrderedExt, Vote, VoteContext, VoteVerification},
12 },
13 types::Round,
14};
15use bytes::{Buf, BufMut};
16use commonware_codec::{EncodeSize, Error, Read, ReadRangeExt, Write};
17use commonware_cryptography::{
18 ed25519::{self, Batch},
19 BatchVerifier, Digest, Signer as _, Verifier as _,
20};
21use commonware_utils::set::Ordered;
22use rand::{CryptoRng, Rng};
23use std::collections::BTreeSet;
24
25#[derive(Clone, Debug)]
27pub struct Scheme {
28 participants: Ordered<ed25519::PublicKey>,
30 signer: Option<(u32, ed25519::PrivateKey)>,
32}
33
34impl Scheme {
35 pub fn new(
42 participants: Ordered<ed25519::PublicKey>,
43 private_key: ed25519::PrivateKey,
44 ) -> Self {
45 let signer = participants
46 .position(&private_key.public_key())
47 .map(|index| (index as u32, private_key));
48
49 Self {
50 participants,
51 signer,
52 }
53 }
54
55 pub fn verifier(participants: Ordered<ed25519::PublicKey>) -> Self {
59 Self {
60 participants,
61 signer: None,
62 }
63 }
64
65 fn batch_verify_certificate<'a, D: Digest>(
67 &self,
68 batch: &mut Batch,
69 namespace: &[u8],
70 context: VoteContext<'a, D>,
71 certificate: &'a Certificate,
72 ) -> bool {
73 if certificate.signers.len() != self.participants.len() {
75 return false;
76 }
77
78 if certificate.signers.count() != certificate.signatures.len() {
80 return false;
81 }
82
83 if certificate.signers.count() < self.participants.quorum() as usize {
85 return false;
86 }
87
88 let (namespace, message) = vote_namespace_and_message(namespace, context);
90 for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) {
91 let Some(public_key) = self.participants.get(signer as usize) else {
92 return false;
93 };
94
95 batch.add(
96 Some(namespace.as_ref()),
97 message.as_ref(),
98 public_key,
99 signature,
100 );
101 }
102
103 true
104 }
105}
106
107#[derive(Clone, Debug, PartialEq, Eq, Hash)]
108pub struct Certificate {
109 pub signers: Signers,
111 pub signatures: Vec<ed25519::Signature>,
113}
114
115impl Write for Certificate {
116 fn write(&self, writer: &mut impl BufMut) {
117 self.signers.write(writer);
118 self.signatures.write(writer);
119 }
120}
121
122impl EncodeSize for Certificate {
123 fn encode_size(&self) -> usize {
124 self.signers.encode_size() + self.signatures.encode_size()
125 }
126}
127
128impl Read for Certificate {
129 type Cfg = usize;
130
131 fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
132 let signers = Signers::read_cfg(reader, participants)?;
133 if signers.count() == 0 {
134 return Err(Error::Invalid(
135 "consensus::simplex::signing_scheme::ed25519::Certificate",
136 "Certificate contains no signers",
137 ));
138 }
139
140 let signatures = Vec::<ed25519::Signature>::read_range(reader, ..=*participants)?;
141 if signers.count() != signatures.len() {
142 return Err(Error::Invalid(
143 "consensus::simplex::signing_scheme::ed25519::Certificate",
144 "Signers and signatures counts differ",
145 ));
146 }
147
148 Ok(Self {
149 signers,
150 signatures,
151 })
152 }
153}
154
155impl signing_scheme::Scheme for Scheme {
156 type PublicKey = ed25519::PublicKey;
157 type Signature = ed25519::Signature;
158 type Certificate = Certificate;
159 type Seed = ();
160
161 fn me(&self) -> Option<u32> {
162 self.signer.as_ref().map(|(index, _)| *index)
163 }
164
165 fn participants(&self) -> &Ordered<Self::PublicKey> {
166 &self.participants
167 }
168
169 fn sign_vote<D: Digest>(
170 &self,
171 namespace: &[u8],
172 context: VoteContext<'_, D>,
173 ) -> Option<Vote<Self>> {
174 let (index, private_key) = self.signer.as_ref()?;
175
176 let (namespace, message) = vote_namespace_and_message(namespace, context);
177 let signature = private_key.sign(Some(namespace.as_ref()), message.as_ref());
178
179 Some(Vote {
180 signer: *index,
181 signature,
182 })
183 }
184
185 fn verify_vote<D: Digest>(
186 &self,
187 namespace: &[u8],
188 context: VoteContext<'_, D>,
189 vote: &Vote<Self>,
190 ) -> bool {
191 let Some(public_key) = self.participants.get(vote.signer as usize) else {
192 return false;
193 };
194
195 let (namespace, message) = vote_namespace_and_message(namespace, context);
196 public_key.verify(Some(namespace.as_ref()), message.as_ref(), &vote.signature)
197 }
198
199 fn verify_votes<R, D, I>(
200 &self,
201 rng: &mut R,
202 namespace: &[u8],
203 context: VoteContext<'_, D>,
204 votes: I,
205 ) -> VoteVerification<Self>
206 where
207 R: Rng + CryptoRng,
208 D: Digest,
209 I: IntoIterator<Item = Vote<Self>>,
210 {
211 let (namespace, message) = vote_namespace_and_message(namespace, context);
212
213 let mut invalid = BTreeSet::new();
214 let mut candidates = Vec::new();
215 let mut batch = Batch::new();
216
217 for vote in votes.into_iter() {
218 let Some(public_key) = self.participants.get(vote.signer as usize) else {
219 invalid.insert(vote.signer);
220 continue;
221 };
222
223 batch.add(
224 Some(namespace.as_ref()),
225 message.as_ref(),
226 public_key,
227 &vote.signature,
228 );
229
230 candidates.push((vote, public_key));
231 }
232
233 if !candidates.is_empty() && !batch.verify(rng) {
234 for (vote, public_key) in &candidates {
236 if !public_key.verify(Some(namespace.as_ref()), message.as_ref(), &vote.signature) {
237 invalid.insert(vote.signer);
238 }
239 }
240 }
241
242 let verified = candidates
243 .into_iter()
244 .filter_map(|(vote, _)| {
245 if invalid.contains(&vote.signer) {
246 None
247 } else {
248 Some(vote)
249 }
250 })
251 .collect();
252
253 VoteVerification::new(verified, invalid.into_iter().collect())
254 }
255
256 fn assemble_certificate<I>(&self, votes: I) -> Option<Self::Certificate>
257 where
258 I: IntoIterator<Item = Vote<Self>>,
259 {
260 let mut entries = Vec::new();
262 for Vote { signer, signature } in votes {
263 if signer as usize >= self.participants.len() {
264 return None;
265 }
266
267 entries.push((signer, signature));
268 }
269 if entries.len() < self.participants.quorum() as usize {
270 return None;
271 }
272
273 entries.sort_by_key(|(signer, _)| *signer);
275 let (signer, signatures): (Vec<u32>, Vec<_>) = entries.into_iter().unzip();
276 let signers = Signers::from(self.participants.len(), signer);
277
278 Some(Certificate {
279 signers,
280 signatures,
281 })
282 }
283
284 fn verify_certificate<R: Rng + CryptoRng, D: Digest>(
285 &self,
286 rng: &mut R,
287 namespace: &[u8],
288 context: VoteContext<'_, D>,
289 certificate: &Self::Certificate,
290 ) -> bool {
291 let mut batch = Batch::new();
292 if !self.batch_verify_certificate(&mut batch, namespace, context, certificate) {
293 return false;
294 }
295
296 batch.verify(rng)
297 }
298
299 fn verify_certificates<'a, R, D, I>(
300 &self,
301 rng: &mut R,
302 namespace: &[u8],
303 certificates: I,
304 ) -> bool
305 where
306 R: Rng + CryptoRng,
307 D: Digest,
308 I: Iterator<Item = (VoteContext<'a, D>, &'a Self::Certificate)>,
309 {
310 let mut batch = Batch::new();
311 for (context, certificate) in certificates {
312 if !self.batch_verify_certificate(&mut batch, namespace, context, certificate) {
313 return false;
314 }
315 }
316
317 batch.verify(rng)
318 }
319
320 fn seed(&self, _: Round, _: &Self::Certificate) -> Option<Self::Seed> {
321 None
322 }
323
324 fn is_attributable(&self) -> bool {
325 true
326 }
327
328 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {
329 self.participants.len()
330 }
331
332 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {
333 u32::MAX as usize
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use crate::{
341 simplex::{
342 mocks::fixtures::{ed25519, Fixture},
343 signing_scheme::Scheme as _,
344 types::{Proposal, VoteContext},
345 },
346 types::Round,
347 };
348 use commonware_codec::{Decode, Encode};
349 use commonware_cryptography::{sha256::Digest as Sha256Digest, Hasher, Sha256};
350 use commonware_utils::quorum;
351 use rand::{
352 rngs::{OsRng, StdRng},
353 thread_rng, SeedableRng,
354 };
355
356 const NAMESPACE: &[u8] = b"ed25519-signing-scheme";
357
358 fn setup_signers(n: u32, seed: u64) -> (Vec<Scheme>, Ordered<ed25519::PublicKey>) {
359 let mut rng = StdRng::seed_from_u64(seed);
360 let Fixture {
361 participants,
362 schemes,
363 ..
364 } = ed25519(&mut rng, n);
365
366 (schemes, participants.into())
367 }
368
369 fn sample_proposal(round: u64, view: u64, tag: u8) -> Proposal<Sha256Digest> {
370 Proposal::new(
371 Round::new(round, view),
372 view.saturating_sub(1),
373 Sha256::hash(&[tag]),
374 )
375 }
376
377 #[test]
378 fn test_sign_vote_roundtrip_for_each_context() {
379 let (schemes, _) = setup_signers(4, 42);
380 let scheme = &schemes[0];
381
382 let proposal = sample_proposal(0, 2, 1);
383 let vote = scheme
384 .sign_vote(
385 NAMESPACE,
386 VoteContext::Notarize {
387 proposal: &proposal,
388 },
389 )
390 .unwrap();
391 assert!(scheme.verify_vote(
392 NAMESPACE,
393 VoteContext::Notarize {
394 proposal: &proposal,
395 },
396 &vote
397 ));
398
399 let vote = scheme
400 .sign_vote::<Sha256Digest>(
401 NAMESPACE,
402 VoteContext::Nullify {
403 round: proposal.round,
404 },
405 )
406 .unwrap();
407 assert!(scheme.verify_vote::<Sha256Digest>(
408 NAMESPACE,
409 VoteContext::Nullify {
410 round: proposal.round,
411 },
412 &vote
413 ));
414
415 let vote = scheme
416 .sign_vote(
417 NAMESPACE,
418 VoteContext::Finalize {
419 proposal: &proposal,
420 },
421 )
422 .unwrap();
423 assert!(scheme.verify_vote(
424 NAMESPACE,
425 VoteContext::Finalize {
426 proposal: &proposal,
427 },
428 &vote
429 ));
430 }
431
432 #[test]
433 fn test_verify_votes_filters_bad_signers() {
434 let (schemes, _) = setup_signers(5, 42);
435 let quorum = quorum(schemes.len() as u32) as usize;
436 let proposal = sample_proposal(0, 5, 3);
437
438 let mut votes: Vec<_> = schemes
439 .iter()
440 .take(quorum)
441 .map(|scheme| {
442 scheme
443 .sign_vote(
444 NAMESPACE,
445 VoteContext::Notarize {
446 proposal: &proposal,
447 },
448 )
449 .unwrap()
450 })
451 .collect();
452
453 let scheme = &schemes[0];
454 let verification = scheme.verify_votes(
455 &mut thread_rng(),
456 NAMESPACE,
457 VoteContext::Notarize {
458 proposal: &proposal,
459 },
460 votes.clone(),
461 );
462 assert!(verification.invalid_signers.is_empty());
463 assert_eq!(verification.verified.len(), quorum);
464
465 votes[0].signer = 999;
467 let verification = scheme.verify_votes(
468 &mut thread_rng(),
469 NAMESPACE,
470 VoteContext::Notarize {
471 proposal: &proposal,
472 },
473 votes.clone(),
474 );
475 assert_eq!(verification.invalid_signers, vec![999]);
476 assert_eq!(verification.verified.len(), quorum - 1);
477
478 votes[0].signer = 0;
480 votes[0].signature = votes[1].signature.clone();
481 let verification = scheme.verify_votes(
482 &mut thread_rng(),
483 NAMESPACE,
484 VoteContext::Notarize {
485 proposal: &proposal,
486 },
487 votes,
488 );
489 assert_eq!(verification.invalid_signers, vec![0]);
490 assert_eq!(verification.verified.len(), quorum - 1);
491 }
492
493 #[test]
494 fn test_assemble_certificate_sorts_signers() {
495 let (schemes, _) = setup_signers(4, 42);
496 let proposal = sample_proposal(0, 7, 4);
497
498 let votes = [
499 schemes[2]
500 .sign_vote(
501 NAMESPACE,
502 VoteContext::Finalize {
503 proposal: &proposal,
504 },
505 )
506 .unwrap(),
507 schemes[0]
508 .sign_vote(
509 NAMESPACE,
510 VoteContext::Finalize {
511 proposal: &proposal,
512 },
513 )
514 .unwrap(),
515 schemes[1]
516 .sign_vote(
517 NAMESPACE,
518 VoteContext::Finalize {
519 proposal: &proposal,
520 },
521 )
522 .unwrap(),
523 ];
524
525 let certificate = schemes[0]
526 .assemble_certificate(votes)
527 .expect("assemble certificate");
528 assert_eq!(
529 certificate.signers.iter().collect::<Vec<_>>(),
530 vec![0, 1, 2]
531 );
532 }
533
534 #[test]
535 fn test_assemble_certificate_requires_quorum() {
536 let (schemes, _) = setup_signers(4, 42);
537 let proposal = sample_proposal(0, 9, 5);
538
539 let votes: Vec<_> = schemes
540 .iter()
541 .take(2)
542 .map(|scheme| {
543 scheme
544 .sign_vote(
545 NAMESPACE,
546 VoteContext::Notarize {
547 proposal: &proposal,
548 },
549 )
550 .unwrap()
551 })
552 .collect();
553
554 assert!(schemes[0].assemble_certificate(votes).is_none());
555 }
556
557 #[test]
558 fn test_assemble_certificate_rejects_out_of_range_signer() {
559 let (schemes, _) = setup_signers(4, 42);
560 let proposal = sample_proposal(0, 13, 7);
561
562 let mut votes: Vec<_> = schemes
563 .iter()
564 .take(3)
565 .map(|scheme| {
566 scheme
567 .sign_vote(
568 NAMESPACE,
569 VoteContext::Notarize {
570 proposal: &proposal,
571 },
572 )
573 .unwrap()
574 })
575 .collect();
576 votes[0].signer = 42;
577
578 assert!(schemes[0].assemble_certificate(votes).is_none());
579 }
580
581 #[test]
582 #[should_panic(expected = "duplicate signer index: 2")]
583 fn test_assemble_certificate_rejects_duplicate_signers() {
584 let (schemes, _) = setup_signers(4, 42);
585 let proposal = sample_proposal(0, 25, 13);
586
587 let mut votes: Vec<_> = schemes
588 .iter()
589 .take(3)
590 .map(|scheme| {
591 scheme
592 .sign_vote(
593 NAMESPACE,
594 VoteContext::Finalize {
595 proposal: &proposal,
596 },
597 )
598 .unwrap()
599 })
600 .collect();
601
602 votes.push(votes.last().unwrap().clone());
603
604 schemes[0].assemble_certificate(votes);
605 }
606
607 #[test]
608 fn test_verify_certificate_detects_corruption() {
609 let (schemes, participants) = setup_signers(4, 42);
610 let proposal = sample_proposal(0, 15, 8);
611
612 let votes: Vec<_> = schemes
613 .iter()
614 .take(3)
615 .map(|scheme| {
616 scheme
617 .sign_vote(
618 NAMESPACE,
619 VoteContext::Finalize {
620 proposal: &proposal,
621 },
622 )
623 .unwrap()
624 })
625 .collect();
626
627 let certificate = schemes[0]
628 .assemble_certificate(votes)
629 .expect("assemble certificate");
630
631 let verifier = Scheme::verifier(participants);
632 assert!(verifier.verify_certificate(
633 &mut thread_rng(),
634 NAMESPACE,
635 VoteContext::Finalize {
636 proposal: &proposal,
637 },
638 &certificate,
639 ));
640
641 let mut corrupted = certificate.clone();
642 corrupted.signatures[0] = corrupted.signatures[1].clone();
643 assert!(!verifier.verify_certificate(
644 &mut thread_rng(),
645 NAMESPACE,
646 VoteContext::Finalize {
647 proposal: &proposal,
648 },
649 &corrupted,
650 ));
651 }
652
653 #[test]
654 fn test_certificate_codec_roundtrip() {
655 let (schemes, _) = setup_signers(4, 42);
656 let proposal = sample_proposal(0, 17, 9);
657
658 let votes: Vec<_> = schemes
659 .iter()
660 .take(3)
661 .map(|scheme| {
662 scheme
663 .sign_vote(
664 NAMESPACE,
665 VoteContext::Notarize {
666 proposal: &proposal,
667 },
668 )
669 .unwrap()
670 })
671 .collect();
672
673 let certificate = schemes[0]
674 .assemble_certificate(votes)
675 .expect("assemble certificate");
676 let encoded = certificate.encode();
677 let decoded = Certificate::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
678 assert_eq!(decoded, certificate);
679 }
680
681 #[test]
682 fn test_scheme_clone_and_verifier() {
683 let (schemes, participants) = setup_signers(4, 42);
684 let signer = schemes[0].clone();
685 let proposal = sample_proposal(0, 21, 11);
686
687 assert!(
688 signer
689 .sign_vote(
690 NAMESPACE,
691 VoteContext::Notarize {
692 proposal: &proposal,
693 },
694 )
695 .is_some(),
696 "signer should produce votes"
697 );
698
699 let verifier = Scheme::verifier(participants);
700 assert!(
701 verifier
702 .sign_vote(
703 NAMESPACE,
704 VoteContext::Notarize {
705 proposal: &proposal,
706 },
707 )
708 .is_none(),
709 "verifier should not produce votes"
710 );
711 }
712
713 #[test]
714 fn test_certificate_decode_validation() {
715 let (schemes, participants) = setup_signers(4, 42);
716 let proposal = sample_proposal(0, 19, 10);
717
718 let votes: Vec<_> = schemes
719 .iter()
720 .take(3)
721 .map(|scheme| {
722 scheme
723 .sign_vote(
724 NAMESPACE,
725 VoteContext::Notarize {
726 proposal: &proposal,
727 },
728 )
729 .unwrap()
730 })
731 .collect();
732
733 let certificate = schemes[0]
734 .assemble_certificate(votes)
735 .expect("assemble certificate");
736
737 let encoded = certificate.encode();
739 let mut cursor = &encoded[..];
740 let decoded =
741 Certificate::read_cfg(&mut cursor, &participants.len()).expect("decode certificate");
742 assert_eq!(decoded, certificate);
743
744 let empty = Certificate {
746 signers: Signers::from(participants.len(), std::iter::empty::<u32>()),
747 signatures: Vec::new(),
748 };
749 assert!(Certificate::decode_cfg(empty.encode(), &participants.len()).is_err());
750
751 let mismatched = Certificate {
753 signers: Signers::from(participants.len(), [0u32, 1]),
754 signatures: vec![certificate.signatures[0].clone()],
755 };
756 assert!(Certificate::decode_cfg(mismatched.encode(), &participants.len()).is_err());
757
758 let mut signers = certificate.signers.iter().collect::<Vec<_>>();
760 signers.push(participants.len() as u32);
761 let mut signatures = certificate.signatures.clone();
762 signatures.push(certificate.signatures[0].clone());
763 let extended = Certificate {
764 signers: Signers::from(participants.len() + 1, signers),
765 signatures,
766 };
767 assert!(Certificate::decode_cfg(extended.encode(), &participants.len()).is_err());
768 }
769
770 #[test]
771 fn test_verify_certificate() {
772 let (schemes, participants) = setup_signers(4, 42);
773 let proposal = sample_proposal(0, 21, 11);
774
775 let votes: Vec<_> = schemes
776 .iter()
777 .take(quorum(schemes.len() as u32) as usize)
778 .map(|scheme| {
779 scheme
780 .sign_vote(
781 NAMESPACE,
782 VoteContext::Finalize {
783 proposal: &proposal,
784 },
785 )
786 .unwrap()
787 })
788 .collect();
789
790 let certificate = schemes[0]
791 .assemble_certificate(votes)
792 .expect("assemble certificate");
793
794 let verifier = Scheme::verifier(participants);
795 assert!(verifier.verify_certificate(
796 &mut OsRng,
797 NAMESPACE,
798 VoteContext::Finalize {
799 proposal: &proposal,
800 },
801 &certificate,
802 ));
803 }
804
805 #[test]
806 fn test_verify_certificate_rejects_sub_quorum() {
807 let (schemes, participants) = setup_signers(4, 42);
808 let proposal = sample_proposal(0, 23, 12);
809
810 let votes: Vec<_> = schemes
811 .iter()
812 .take(3)
813 .map(|scheme| {
814 scheme
815 .sign_vote(
816 NAMESPACE,
817 VoteContext::Finalize {
818 proposal: &proposal,
819 },
820 )
821 .unwrap()
822 })
823 .collect();
824
825 let certificate = schemes[0]
826 .assemble_certificate(votes)
827 .expect("assemble certificate");
828
829 let mut truncated = certificate.clone();
830 let mut signers: Vec<u32> = truncated.signers.iter().collect();
831 signers.pop();
832 truncated.signers = Signers::from(participants.len(), signers);
833 truncated.signatures.pop();
834
835 let verifier = Scheme::verifier(participants);
836 assert!(!verifier.verify_certificate(
837 &mut thread_rng(),
838 NAMESPACE,
839 VoteContext::Finalize {
840 proposal: &proposal,
841 },
842 &truncated,
843 ));
844 }
845
846 #[test]
847 fn test_verify_certificate_rejects_unknown_signer() {
848 let (schemes, participants) = setup_signers(4, 42);
849 let proposal = sample_proposal(0, 25, 13);
850
851 let votes: Vec<_> = schemes
852 .iter()
853 .take(3)
854 .map(|scheme| {
855 scheme
856 .sign_vote(
857 NAMESPACE,
858 VoteContext::Finalize {
859 proposal: &proposal,
860 },
861 )
862 .unwrap()
863 })
864 .collect();
865
866 let mut certificate = schemes[0]
867 .assemble_certificate(votes)
868 .expect("assemble certificate");
869
870 let mut signers: Vec<u32> = certificate.signers.iter().collect();
871 signers.push(participants.len() as u32);
872 certificate.signers = Signers::from(participants.len() + 1, signers);
873 certificate
874 .signatures
875 .push(certificate.signatures[0].clone());
876
877 let verifier = Scheme::verifier(participants);
878 assert!(!verifier.verify_certificate(
879 &mut thread_rng(),
880 NAMESPACE,
881 VoteContext::Finalize {
882 proposal: &proposal,
883 },
884 &certificate,
885 ));
886 }
887
888 #[test]
889 fn test_verify_certificate_rejects_invalid_certificate_signers_size() {
890 let (schemes, participants) = setup_signers(4, 42);
891 let proposal = sample_proposal(0, 26, 14);
892
893 let votes: Vec<_> = schemes
894 .iter()
895 .take(3)
896 .map(|scheme| {
897 scheme
898 .sign_vote(
899 NAMESPACE,
900 VoteContext::Finalize {
901 proposal: &proposal,
902 },
903 )
904 .unwrap()
905 })
906 .collect();
907
908 let mut certificate = schemes[0]
909 .assemble_certificate(votes)
910 .expect("assemble certificate");
911
912 let verifier = Scheme::verifier(participants.clone());
914 assert!(verifier.verify_certificate(
915 &mut thread_rng(),
916 NAMESPACE,
917 VoteContext::Finalize {
918 proposal: &proposal,
919 },
920 &certificate,
921 ));
922
923 let signers: Vec<u32> = certificate.signers.iter().collect();
925 certificate.signers = Signers::from(participants.len() - 1, signers);
926
927 assert!(!verifier.verify_certificate(
929 &mut thread_rng(),
930 NAMESPACE,
931 VoteContext::Finalize {
932 proposal: &proposal,
933 },
934 &certificate,
935 ));
936 }
937
938 #[test]
939 fn test_verify_certificate_rejects_mismatched_signature_count() {
940 let (schemes, participants) = setup_signers(4, 42);
941 let proposal = sample_proposal(0, 27, 14);
942
943 let votes: Vec<_> = schemes
944 .iter()
945 .take(3)
946 .map(|scheme| {
947 scheme
948 .sign_vote(
949 NAMESPACE,
950 VoteContext::Finalize {
951 proposal: &proposal,
952 },
953 )
954 .unwrap()
955 })
956 .collect();
957
958 let mut certificate = schemes[0]
959 .assemble_certificate(votes)
960 .expect("assemble certificate");
961 certificate.signatures.pop();
962
963 let verifier = Scheme::verifier(participants);
964 assert!(!verifier.verify_certificate(
965 &mut thread_rng(),
966 NAMESPACE,
967 VoteContext::Finalize {
968 proposal: &proposal,
969 },
970 &certificate,
971 ));
972 }
973
974 #[test]
975 fn test_verify_certificates_batch_detects_failure() {
976 let (schemes, participants) = setup_signers(4, 42);
977 let proposal_a = sample_proposal(0, 23, 12);
978 let proposal_b = sample_proposal(1, 24, 13);
979
980 let votes_a: Vec<_> = schemes
981 .iter()
982 .take(3)
983 .map(|scheme| {
984 scheme
985 .sign_vote(
986 NAMESPACE,
987 VoteContext::Notarize {
988 proposal: &proposal_a,
989 },
990 )
991 .unwrap()
992 })
993 .collect();
994 let votes_b: Vec<_> = schemes
995 .iter()
996 .take(3)
997 .map(|scheme| {
998 scheme
999 .sign_vote(
1000 NAMESPACE,
1001 VoteContext::Finalize {
1002 proposal: &proposal_b,
1003 },
1004 )
1005 .unwrap()
1006 })
1007 .collect();
1008
1009 let certificate_a = schemes[0]
1010 .assemble_certificate(votes_a)
1011 .expect("assemble certificate");
1012 let mut bad_certificate = schemes[0]
1013 .assemble_certificate(votes_b)
1014 .expect("assemble certificate");
1015 bad_certificate.signatures[0] = bad_certificate.signatures[1].clone();
1016
1017 let verifier = Scheme::verifier(participants);
1018 let mut iter = [
1019 (
1020 VoteContext::Notarize {
1021 proposal: &proposal_a,
1022 },
1023 &certificate_a,
1024 ),
1025 (
1026 VoteContext::Finalize {
1027 proposal: &proposal_b,
1028 },
1029 &bad_certificate,
1030 ),
1031 ]
1032 .into_iter();
1033
1034 assert!(!verifier.verify_certificates(&mut thread_rng(), NAMESPACE, &mut iter));
1035 }
1036}