commonware_consensus/simplex/signing_scheme/
bls12381_multisig.rs

1//! BLS12-381 multi-signature implementation of the [`Scheme`] trait for `simplex`.
2//!
3//! [`Scheme`] is **attributable**: individual signatures can be
4//! used by an external observer as evidence of either liveness or of committing a fault.
5//! Certificates contain signer indices alongside an aggregated signature,
6//! enabling secure per-validator activity tracking and conflict detection.
7
8use 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/// BLS12-381 multi-signature implementation of the [`Scheme`] trait.
33#[derive(Clone, Debug)]
34pub struct Scheme<P: PublicKey, V: Variant> {
35    /// Participants in the committee.
36    participants: OrderedAssociated<P, V::Public>,
37    /// Key used for generating signatures.
38    signer: Option<(u32, Private)>,
39}
40
41impl<P: PublicKey, V: Variant> Scheme<P, V> {
42    /// Creates a new scheme instance with the provided key material.
43    ///
44    /// Participants have both an identity key and a consensus key. The identity key
45    /// is used for committee ordering and indexing, while the consensus key is used for
46    /// signing and verification.
47    ///
48    /// If the provided private key does not match any consensus key in the committee,
49    /// the instance will act as a verifier (unable to generate signatures).
50    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    /// Builds a verifier that can authenticate votes and certificates.
65    ///
66    /// Participants have both an identity key and a consensus key. The identity key
67    /// is used for committee ordering and indexing, while the consensus key is used for
68    /// verification.
69    pub fn verifier(participants: OrderedAssociated<P, V::Public>) -> Self {
70        Self {
71            participants,
72            signer: None,
73        }
74    }
75}
76
77/// Certificate formed by an aggregated BLS12-381 signature plus the signers that
78/// contributed to it.
79#[derive(Clone, Debug, PartialEq, Eq, Hash)]
80pub struct Certificate<V: Variant> {
81    /// Bitmap of validator indices that contributed signatures.
82    pub signers: Signers,
83    /// Aggregated BLS signature covering all votes in this certificate.
84    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 there are no candidates to verify, return before doing any work.
196        if candidates.is_empty() {
197            return VoteVerification::new(candidates, invalid.into_iter().collect());
198        }
199
200        // Verify the aggregate signature.
201        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        // Collect the invalid signers.
225        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        // Collect the signers and signatures.
239        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        // Produce signers and aggregate signature.
252        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 the certificate signers length does not match the participant set, return false.
267        if certificate.signers.len() != self.participants.len() {
268            return false;
269        }
270
271        // If the certificate does not meet the quorum, return false.
272        if certificate.signers.count() < self.participants.quorum() as usize {
273            return false;
274        }
275
276        // Collect the public keys.
277        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        // Verify the aggregate signature.
287        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        // Invalid signer index should be detected.
482        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        // Invalid signature should be detected.
495        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        // The certificate is valid
1029        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        // Make the signers bitmap size smaller
1040        let signers: Vec<u32> = certificate.signers.iter().collect();
1041        certificate.signers = Signers::from(participants.len() - 1, signers);
1042
1043        // The certificate verification should fail
1044        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        // Well-formed certificate decodes successfully.
1084        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        // Certificate with no signers is rejected.
1091        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        // Certificate containing more signers than the participant set is rejected.
1098        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}