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