commonware_consensus/simplex/signing_scheme/
bls12381_threshold.rs

1//! BLS12-381 threshold implementation of the [`Scheme`] trait for `simplex`.
2//!
3//! [`Scheme`] is **non-attributable**: exposing partial signatures
4//! as evidence of either liveness or of committing a fault is not safe. With threshold signatures,
5//! any `t` valid partial signatures can be used to forge a partial signature for any other player,
6//! enabling equivocation attacks. Because peer connections are authenticated, evidence can be used locally
7//! (as it must be sent by said participant) but can't be used by an external observer.
8
9use crate::{
10    simplex::{
11        signing_scheme::{
12            self, finalize_namespace, notarize_namespace, nullify_namespace, seed_namespace,
13            seed_namespace_and_message, vote_namespace_and_message,
14        },
15        types::{Finalization, Notarization, OrderedExt, Vote, VoteContext, VoteVerification},
16    },
17    types::{Epoch, Round, View},
18    Epochable, Viewable,
19};
20use bytes::{Buf, BufMut};
21use commonware_codec::{Encode, EncodeSize, Error, FixedSize, Read, ReadExt, Write};
22use commonware_cryptography::{
23    bls12381::{
24        dkg::ops,
25        primitives::{
26            group::Share,
27            ops::{
28                aggregate_signatures, aggregate_verify_multiple_messages, partial_sign_message,
29                partial_verify_multiple_public_keys_precomputed, threshold_signature_recover_pair,
30                verify_message,
31            },
32            poly::{self, PartialSignature, Public},
33            variant::Variant,
34        },
35    },
36    Digest, PublicKey,
37};
38use commonware_utils::set::{Ordered, OrderedAssociated};
39use rand::{CryptoRng, Rng};
40use std::{
41    collections::{BTreeSet, HashMap},
42    fmt::Debug,
43};
44
45/// BLS12-381 threshold implementation of the [`Scheme`] trait.
46///
47/// It is possible for a node to play one of the following roles: a signer (with its share),
48/// a verifier (with evaluated public polynomial), or an external verifier that
49/// only checks recovered certificates.
50#[derive(Clone, Debug)]
51pub enum Scheme<P: PublicKey, V: Variant> {
52    Signer {
53        /// Participants in the committee.
54        participants: OrderedAssociated<P, V::Public>,
55        /// Public identity of the committee (constant across reshares).
56        identity: V::Public,
57        /// Local share used to generate partial signatures.
58        share: Share,
59    },
60    Verifier {
61        /// Participants in the committee.
62        participants: OrderedAssociated<P, V::Public>,
63        /// Public identity of the committee (constant across reshares).
64        identity: V::Public,
65    },
66    CertificateVerifier {
67        /// Public identity of the committee (constant across reshares).
68        identity: V::Public,
69    },
70}
71
72impl<P: PublicKey, V: Variant> Scheme<P, V> {
73    /// Constructs a signer instance with a private share and evaluated public polynomial.
74    ///
75    /// The participant identity keys are used for committee ordering and indexing.
76    /// The polynomial can be evaluated to obtain public verification keys for partial
77    /// signatures produced by committee members.
78    ///
79    /// If the provided share does not match the polynomial evaluation at its index,
80    /// the instance will act as a verifier (unable to sign votes).
81    ///
82    /// * `participants` - ordered set of participant identity keys
83    /// * `polynomial` - public polynomial for threshold verification
84    /// * `share` - local threshold share for signing
85    pub fn new(participants: Ordered<P>, polynomial: &Public<V>, share: Share) -> Self {
86        let identity = *poly::public::<V>(polynomial);
87        let polynomial = ops::evaluate_all::<V>(polynomial, participants.len() as u32);
88        let participants = participants
89            .into_iter()
90            .zip(polynomial)
91            .collect::<OrderedAssociated<_, _>>();
92
93        let public_key = share.public::<V>();
94        if let Some(index) = participants.values().iter().position(|p| p == &public_key) {
95            assert_eq!(
96                index as u32, share.index,
97                "share index must match participant index"
98            );
99            Self::Signer {
100                participants,
101                identity,
102                share,
103            }
104        } else {
105            Self::Verifier {
106                participants,
107                identity,
108            }
109        }
110    }
111
112    /// Produces a verifier that can authenticate votes but does not hold signing state.
113    ///
114    /// The participant identity keys are used for committee ordering and indexing.
115    /// The polynomial can be evaluated to obtain public verification keys for partial
116    /// signatures produced by committee members.
117    ///
118    /// * `participants` - ordered set of participant identity keys
119    /// * `polynomial` - public polynomial for threshold verification
120    pub fn verifier(participants: Ordered<P>, polynomial: &Public<V>) -> Self {
121        let identity = *poly::public::<V>(polynomial);
122        let polynomial = ops::evaluate_all::<V>(polynomial, participants.len() as u32);
123        let participants = participants
124            .into_iter()
125            .zip(polynomial)
126            .collect::<OrderedAssociated<_, _>>();
127
128        Self::Verifier {
129            participants,
130            identity,
131        }
132    }
133
134    /// Creates a verifier that only checks recovered certificates.
135    ///
136    /// This lightweight verifier can authenticate recovered threshold certificates but cannot
137    /// verify individual votes or partial signatures.
138    ///
139    /// * `identity` - public identity of the committee (constant across reshares)
140    pub fn certificate_verifier(identity: V::Public) -> Self {
141        Self::CertificateVerifier { identity }
142    }
143
144    /// Returns the ordered set of participant public identity keys in the committee.
145    pub fn participants(&self) -> &Ordered<P> {
146        match self {
147            Scheme::Signer { participants, .. } => participants,
148            Scheme::Verifier { participants, .. } => participants,
149            _ => panic!("can only be called for signer and verifier"),
150        }
151    }
152
153    /// Returns the public identity of the committee (constant across reshares).
154    pub fn identity(&self) -> &V::Public {
155        match self {
156            Scheme::Signer { identity, .. } => identity,
157            Scheme::Verifier { identity, .. } => identity,
158            Scheme::CertificateVerifier { identity, .. } => identity,
159        }
160    }
161
162    /// Returns the local share if this instance can generate partial signatures.
163    pub fn share(&self) -> Option<&Share> {
164        match self {
165            Scheme::Signer { share, .. } => Some(share),
166            _ => None,
167        }
168    }
169
170    /// Returns the evaluated public polynomial for validating partial signatures produced by committee members.
171    pub fn polynomial(&self) -> &[V::Public] {
172        match self {
173            Scheme::Signer { participants, .. } => participants.values(),
174            Scheme::Verifier { participants, .. } => participants.values(),
175            _ => panic!("can only be called for signer and verifier"),
176        }
177    }
178}
179
180/// Combined vote/seed signature pair emitted by the BLS12-381 threshold scheme.
181#[derive(Clone, Debug, PartialEq, Eq, Hash)]
182pub struct Signature<V: Variant> {
183    /// Signature over the consensus vote message (partial or recovered aggregate).
184    pub vote_signature: V::Signature,
185    /// Signature over the per-view seed (partial or recovered aggregate).
186    pub seed_signature: V::Signature,
187}
188
189impl<V: Variant> Write for Signature<V> {
190    fn write(&self, writer: &mut impl BufMut) {
191        self.vote_signature.write(writer);
192        self.seed_signature.write(writer);
193    }
194}
195
196impl<V: Variant> Read for Signature<V> {
197    type Cfg = ();
198
199    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
200        let vote_signature = V::Signature::read(reader)?;
201        let seed_signature = V::Signature::read(reader)?;
202
203        Ok(Self {
204            vote_signature,
205            seed_signature,
206        })
207    }
208}
209
210impl<V: Variant> FixedSize for Signature<V> {
211    const SIZE: usize = V::Signature::SIZE * 2;
212}
213
214/// Seed represents a threshold signature over the current view.
215#[derive(Clone, Debug, PartialEq, Hash, Eq)]
216pub struct Seed<V: Variant> {
217    /// The round for which this seed is generated
218    pub round: Round,
219    /// The threshold signature on the seed.
220    pub signature: V::Signature,
221}
222
223impl<V: Variant> Seed<V> {
224    /// Creates a new seed with the given view and signature.
225    pub fn new(round: Round, signature: V::Signature) -> Self {
226        Seed { round, signature }
227    }
228
229    /// Verifies the threshold signature on this [Seed].
230    pub fn verify<P: PublicKey>(&self, scheme: &Scheme<P, V>, namespace: &[u8]) -> bool {
231        let seed_namespace = seed_namespace(namespace);
232        let seed_message = self.round.encode();
233
234        verify_message::<V>(
235            scheme.identity(),
236            Some(&seed_namespace),
237            &seed_message,
238            &self.signature,
239        )
240        .is_ok()
241    }
242
243    /// Returns the round associated with this seed.
244    pub fn round(&self) -> Round {
245        self.round
246    }
247}
248
249impl<V: Variant> Epochable for Seed<V> {
250    type Epoch = Epoch;
251
252    fn epoch(&self) -> Epoch {
253        self.round.epoch()
254    }
255}
256
257impl<V: Variant> Viewable for Seed<V> {
258    type View = View;
259
260    fn view(&self) -> View {
261        self.round.view()
262    }
263}
264
265impl<V: Variant> Write for Seed<V> {
266    fn write(&self, writer: &mut impl BufMut) {
267        self.round.write(writer);
268        self.signature.write(writer);
269    }
270}
271
272impl<V: Variant> Read for Seed<V> {
273    type Cfg = ();
274
275    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
276        let round = Round::read(reader)?;
277        let signature = V::Signature::read(reader)?;
278
279        Ok(Self { round, signature })
280    }
281}
282
283impl<V: Variant> EncodeSize for Seed<V> {
284    fn encode_size(&self) -> usize {
285        self.round.encode_size() + self.signature.encode_size()
286    }
287}
288
289/// Seedable is a trait that provides access to the seed associated with a message.
290pub trait Seedable<V: Variant> {
291    /// Returns the seed associated with this object.
292    fn seed(&self) -> Seed<V>;
293}
294
295impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Notarization<Scheme<P, V>, D> {
296    fn seed(&self) -> Seed<V> {
297        Seed::new(self.proposal.round, self.certificate.seed_signature)
298    }
299}
300
301impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Finalization<Scheme<P, V>, D> {
302    fn seed(&self) -> Seed<V> {
303        Seed::new(self.proposal.round, self.certificate.seed_signature)
304    }
305}
306
307impl<P: PublicKey, V: Variant + Send + Sync> signing_scheme::Scheme for Scheme<P, V> {
308    type PublicKey = P;
309    type Signature = Signature<V>;
310    type Certificate = Signature<V>;
311    type Seed = Seed<V>;
312
313    fn me(&self) -> Option<u32> {
314        match self {
315            Scheme::Signer { share, .. } => Some(share.index),
316            _ => None,
317        }
318    }
319
320    fn participants(&self) -> &Ordered<Self::PublicKey> {
321        self.participants()
322    }
323
324    fn sign_vote<D: Digest>(
325        &self,
326        namespace: &[u8],
327        context: VoteContext<'_, D>,
328    ) -> Option<Vote<Self>> {
329        let share = self.share()?;
330
331        let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, context);
332        let vote_signature =
333            partial_sign_message::<V>(share, Some(vote_namespace.as_ref()), vote_message.as_ref())
334                .value;
335
336        let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, context);
337        let seed_signature =
338            partial_sign_message::<V>(share, Some(seed_namespace.as_ref()), seed_message.as_ref())
339                .value;
340
341        let signature = Signature {
342            vote_signature,
343            seed_signature,
344        };
345
346        Some(Vote {
347            signer: share.index,
348            signature,
349        })
350    }
351
352    fn assemble_certificate<I>(&self, votes: I) -> Option<Self::Certificate>
353    where
354        I: IntoIterator<Item = Vote<Self>>,
355    {
356        let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = votes
357            .into_iter()
358            .map(|vote| {
359                (
360                    PartialSignature::<V> {
361                        index: vote.signer,
362                        value: vote.signature.vote_signature,
363                    },
364                    PartialSignature::<V> {
365                        index: vote.signer,
366                        value: vote.signature.seed_signature,
367                    },
368                )
369            })
370            .unzip();
371
372        let quorum = self.participants().quorum();
373        if vote_partials.len() < quorum as usize {
374            return None;
375        }
376
377        let (vote_signature, seed_signature) = threshold_signature_recover_pair::<V, _>(
378            quorum,
379            vote_partials.iter(),
380            seed_partials.iter(),
381        )
382        .ok()?;
383
384        Some(Signature {
385            vote_signature,
386            seed_signature,
387        })
388    }
389
390    fn verify_vote<D: Digest>(
391        &self,
392        namespace: &[u8],
393        context: VoteContext<'_, D>,
394        vote: &Vote<Self>,
395    ) -> bool {
396        let Some(evaluated) = self.polynomial().get(vote.signer as usize) else {
397            return false;
398        };
399
400        let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, context);
401        let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, context);
402
403        let signature = aggregate_signatures::<V, _>(&[
404            vote.signature.vote_signature,
405            vote.signature.seed_signature,
406        ]);
407
408        aggregate_verify_multiple_messages::<V, _>(
409            evaluated,
410            &[
411                (Some(vote_namespace.as_ref()), vote_message.as_ref()),
412                (Some(seed_namespace.as_ref()), seed_message.as_ref()),
413            ],
414            &signature,
415            1,
416        )
417        .is_ok()
418    }
419
420    fn verify_votes<R, D, I>(
421        &self,
422        _rng: &mut R,
423        namespace: &[u8],
424        context: VoteContext<'_, D>,
425        votes: I,
426    ) -> VoteVerification<Self>
427    where
428        R: Rng + CryptoRng,
429        D: Digest,
430        I: IntoIterator<Item = Vote<Self>>,
431    {
432        let mut invalid = BTreeSet::new();
433        let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = votes
434            .into_iter()
435            .map(|vote| {
436                (
437                    PartialSignature::<V> {
438                        index: vote.signer,
439                        value: vote.signature.vote_signature,
440                    },
441                    PartialSignature::<V> {
442                        index: vote.signer,
443                        value: vote.signature.seed_signature,
444                    },
445                )
446            })
447            .unzip();
448
449        let polynomial = self.polynomial();
450        let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, context);
451        if let Err(errs) = partial_verify_multiple_public_keys_precomputed::<V, _>(
452            polynomial,
453            Some(vote_namespace.as_ref()),
454            vote_message.as_ref(),
455            vote_partials.iter(),
456        ) {
457            for partial in errs {
458                invalid.insert(partial.index);
459            }
460        }
461
462        let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, context);
463        if let Err(errs) = partial_verify_multiple_public_keys_precomputed::<V, _>(
464            polynomial,
465            Some(seed_namespace.as_ref()),
466            seed_message.as_ref(),
467            seed_partials
468                .iter()
469                .filter(|partial| !invalid.contains(&partial.index)),
470        ) {
471            for partial in errs {
472                invalid.insert(partial.index);
473            }
474        }
475
476        let verified = vote_partials
477            .into_iter()
478            .zip(seed_partials)
479            .map(|(vote, seed)| Vote {
480                signer: vote.index,
481                signature: Signature {
482                    vote_signature: vote.value,
483                    seed_signature: seed.value,
484                },
485            })
486            .filter(|vote| !invalid.contains(&vote.signer))
487            .collect();
488
489        let invalid_signers = invalid.into_iter().collect();
490
491        VoteVerification::new(verified, invalid_signers)
492    }
493
494    fn verify_certificate<R: Rng + CryptoRng, D: Digest>(
495        &self,
496        _rng: &mut R,
497        namespace: &[u8],
498        context: VoteContext<'_, D>,
499        certificate: &Self::Certificate,
500    ) -> bool {
501        let identity = self.identity();
502
503        let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, context);
504        let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, context);
505
506        let signature =
507            aggregate_signatures::<V, _>(&[certificate.vote_signature, certificate.seed_signature]);
508
509        aggregate_verify_multiple_messages::<V, _>(
510            identity,
511            &[
512                (Some(vote_namespace.as_ref()), vote_message.as_ref()),
513                (Some(seed_namespace.as_ref()), seed_message.as_ref()),
514            ],
515            &signature,
516            1,
517        )
518        .is_ok()
519    }
520
521    fn verify_certificates<'a, R, D, I>(
522        &self,
523        _rng: &mut R,
524        namespace: &[u8],
525        certificates: I,
526    ) -> bool
527    where
528        R: Rng + CryptoRng,
529        D: Digest,
530        I: Iterator<Item = (VoteContext<'a, D>, &'a Self::Certificate)>,
531    {
532        let identity = self.identity();
533
534        let mut seeds = HashMap::new();
535        let mut messages = Vec::new();
536        let mut signatures = Vec::new();
537
538        let notarize_namespace = notarize_namespace(namespace);
539        let nullify_namespace = nullify_namespace(namespace);
540        let finalize_namespace = finalize_namespace(namespace);
541        let seed_namespace = seed_namespace(namespace);
542
543        for (context, certificate) in certificates {
544            match context {
545                VoteContext::Notarize { proposal } => {
546                    // Prepare notarize message
547                    let notarize_message = proposal.encode();
548                    let notarize_message = (Some(notarize_namespace.as_slice()), notarize_message);
549                    messages.push(notarize_message);
550                }
551                VoteContext::Nullify { round } => {
552                    // Prepare nullify message
553                    let nullify_encoded = round.encode();
554                    let nullify_message =
555                        (Some(nullify_namespace.as_slice()), nullify_encoded.clone());
556                    messages.push(nullify_message);
557                }
558                VoteContext::Finalize { proposal } => {
559                    // Prepare finalize message
560                    let finalize_message = proposal.encode();
561                    let finalize_message = (Some(finalize_namespace.as_slice()), finalize_message);
562                    messages.push(finalize_message);
563                }
564            }
565            signatures.push(&certificate.vote_signature);
566
567            // Add seed message (if not already present)
568            if let Some(previous) = seeds.get(&context.view()) {
569                if *previous != &certificate.seed_signature {
570                    return false;
571                }
572            } else {
573                let seed_message = match context {
574                    VoteContext::Notarize { proposal } | VoteContext::Finalize { proposal } => {
575                        proposal.round.encode()
576                    }
577                    VoteContext::Nullify { round } => round.encode(),
578                };
579
580                messages.push((Some(seed_namespace.as_slice()), seed_message));
581                signatures.push(&certificate.seed_signature);
582                seeds.insert(context.view(), &certificate.seed_signature);
583            }
584        }
585
586        // Aggregate signatures
587        let signature = aggregate_signatures::<V, _>(signatures);
588        aggregate_verify_multiple_messages::<V, _>(
589            identity,
590            &messages
591                .iter()
592                .map(|(namespace, message)| (namespace.as_deref(), message.as_ref()))
593                .collect::<Vec<_>>(),
594            &signature,
595            1,
596        )
597        .is_ok()
598    }
599
600    fn seed(&self, round: Round, certificate: &Self::Certificate) -> Option<Self::Seed> {
601        Some(Seed::new(round, certificate.seed_signature))
602    }
603
604    fn is_attributable(&self) -> bool {
605        false
606    }
607
608    fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {}
609
610    fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {}
611}
612
613#[cfg(test)]
614mod tests {
615    use super::*;
616    use crate::{
617        simplex::{
618            mocks::fixtures::{bls12381_threshold, ed25519_participants, Fixture},
619            signing_scheme::{notarize_namespace, seed_namespace, Scheme as _},
620            types::{Finalization, Finalize, Notarization, Notarize, Proposal, VoteContext},
621        },
622        types::Round,
623    };
624    use commonware_codec::{Decode, Encode};
625    use commonware_cryptography::{
626        bls12381::primitives::{
627            ops::partial_sign_message,
628            variant::{MinPk, MinSig, Variant},
629        },
630        ed25519,
631        sha256::Digest as Sha256Digest,
632        Hasher, Sha256,
633    };
634    use commonware_utils::quorum;
635    use rand::{rngs::StdRng, thread_rng, SeedableRng};
636
637    const NAMESPACE: &[u8] = b"bls-threshold-signing-scheme";
638
639    type Scheme<V> = super::Scheme<ed25519::PublicKey, V>;
640    type Signature<V> = super::Signature<V>;
641
642    fn setup_signers<V: Variant>(n: u32, seed: u64) -> (Vec<Scheme<V>>, Scheme<V>) {
643        let mut rng = StdRng::seed_from_u64(seed);
644        let Fixture {
645            schemes, verifier, ..
646        } = bls12381_threshold::<V, _>(&mut rng, n);
647
648        (schemes, verifier)
649    }
650
651    fn sample_proposal(round: u64, view: u64, tag: u8) -> Proposal<Sha256Digest> {
652        Proposal::new(
653            Round::new(round, view),
654            view.saturating_sub(1),
655            Sha256::hash(&[tag]),
656        )
657    }
658
659    fn signer_shares_must_match_participant_indices<V: Variant>() {
660        let mut rng = StdRng::seed_from_u64(7);
661        let participants = ed25519_participants(&mut rng, 4);
662        let (polynomial, mut shares) = ops::generate_shares::<_, V>(&mut rng, None, 4, 3);
663        shares[0].index = 999;
664        Scheme::<V>::new(participants.keys().clone(), &polynomial, shares[0].clone());
665    }
666
667    #[test]
668    #[should_panic(expected = "share index must match participant index")]
669    fn test_signer_shares_must_match_participant_indices() {
670        signer_shares_must_match_participant_indices::<MinPk>();
671        signer_shares_must_match_participant_indices::<MinSig>();
672    }
673
674    fn sign_vote_roundtrip_for_each_context<V: Variant>() {
675        let (schemes, _) = setup_signers::<V>(4, 7);
676        let scheme = &schemes[0];
677
678        let proposal = sample_proposal(0, 2, 1);
679        let notarize_vote = scheme
680            .sign_vote(
681                NAMESPACE,
682                VoteContext::Notarize {
683                    proposal: &proposal,
684                },
685            )
686            .unwrap();
687        assert!(scheme.verify_vote(
688            NAMESPACE,
689            VoteContext::Notarize {
690                proposal: &proposal,
691            },
692            &notarize_vote
693        ));
694
695        let nullify_vote = scheme
696            .sign_vote::<Sha256Digest>(
697                NAMESPACE,
698                VoteContext::Nullify {
699                    round: proposal.round,
700                },
701            )
702            .unwrap();
703        assert!(scheme.verify_vote::<Sha256Digest>(
704            NAMESPACE,
705            VoteContext::Nullify {
706                round: proposal.round,
707            },
708            &nullify_vote
709        ));
710
711        let finalize_vote = scheme
712            .sign_vote(
713                NAMESPACE,
714                VoteContext::Finalize {
715                    proposal: &proposal,
716                },
717            )
718            .unwrap();
719        assert!(scheme.verify_vote(
720            NAMESPACE,
721            VoteContext::Finalize {
722                proposal: &proposal,
723            },
724            &finalize_vote
725        ));
726    }
727
728    #[test]
729    fn test_sign_vote_roundtrip_for_each_context() {
730        sign_vote_roundtrip_for_each_context::<MinPk>();
731        sign_vote_roundtrip_for_each_context::<MinSig>();
732    }
733
734    fn verifier_cannot_sign<V: Variant>() {
735        let (_, verifier) = setup_signers::<V>(4, 11);
736
737        let proposal = sample_proposal(0, 3, 2);
738        assert!(
739            verifier
740                .sign_vote(
741                    NAMESPACE,
742                    VoteContext::Notarize {
743                        proposal: &proposal,
744                    },
745                )
746                .is_none(),
747            "verifier should not produce signatures"
748        );
749    }
750
751    #[test]
752    fn test_verifier_cannot_sign() {
753        verifier_cannot_sign::<MinPk>();
754        verifier_cannot_sign::<MinSig>();
755    }
756
757    fn verifier_accepts_votes<V: Variant>() {
758        let (schemes, verifier) = setup_signers::<V>(4, 11);
759        let proposal = sample_proposal(0, 3, 2);
760        let vote = schemes[1]
761            .sign_vote(
762                NAMESPACE,
763                VoteContext::Notarize {
764                    proposal: &proposal,
765                },
766            )
767            .unwrap();
768        assert!(verifier.verify_vote(
769            NAMESPACE,
770            VoteContext::Notarize {
771                proposal: &proposal,
772            },
773            &vote
774        ));
775    }
776
777    #[test]
778    fn test_verifier_accepts_votes() {
779        verifier_accepts_votes::<MinPk>();
780        verifier_accepts_votes::<MinSig>();
781    }
782
783    fn verify_votes_filters_bad_signers<V: Variant>() {
784        let (schemes, _) = setup_signers::<V>(5, 13);
785        let quorum = quorum(schemes.len() as u32) as usize;
786        let proposal = sample_proposal(0, 5, 3);
787
788        let mut votes: Vec<_> = schemes
789            .iter()
790            .take(quorum)
791            .map(|scheme| {
792                scheme
793                    .sign_vote(
794                        NAMESPACE,
795                        VoteContext::Notarize {
796                            proposal: &proposal,
797                        },
798                    )
799                    .unwrap()
800            })
801            .collect();
802
803        let verification = schemes[0].verify_votes(
804            &mut thread_rng(),
805            NAMESPACE,
806            VoteContext::Notarize {
807                proposal: &proposal,
808            },
809            votes.clone(),
810        );
811        assert!(verification.invalid_signers.is_empty());
812        assert_eq!(verification.verified.len(), quorum);
813
814        votes[0].signer = 999;
815        let verification = schemes[0].verify_votes(
816            &mut thread_rng(),
817            NAMESPACE,
818            VoteContext::Notarize {
819                proposal: &proposal,
820            },
821            votes,
822        );
823        assert_eq!(verification.invalid_signers, vec![999]);
824        assert_eq!(verification.verified.len(), quorum - 1);
825    }
826
827    #[test]
828    fn test_verify_votes_filters_bad_signers() {
829        verify_votes_filters_bad_signers::<MinPk>();
830        verify_votes_filters_bad_signers::<MinSig>();
831    }
832
833    fn assemble_certificate_requires_quorum<V: Variant>() {
834        let (schemes, _) = setup_signers::<V>(4, 17);
835        let quorum = quorum(schemes.len() as u32) as usize;
836        let proposal = sample_proposal(0, 7, 4);
837
838        let votes: Vec<_> = schemes
839            .iter()
840            .take(quorum - 1)
841            .map(|scheme| {
842                scheme
843                    .sign_vote(
844                        NAMESPACE,
845                        VoteContext::Notarize {
846                            proposal: &proposal,
847                        },
848                    )
849                    .unwrap()
850            })
851            .collect();
852
853        assert!(schemes[0].assemble_certificate(votes).is_none());
854    }
855
856    #[test]
857    fn test_assemble_certificate_requires_quorum() {
858        assemble_certificate_requires_quorum::<MinPk>();
859        assemble_certificate_requires_quorum::<MinSig>();
860    }
861
862    fn verify_certificate<V: Variant>() {
863        let (schemes, verifier) = setup_signers::<V>(4, 19);
864        let quorum = quorum(schemes.len() as u32) as usize;
865        let proposal = sample_proposal(0, 9, 5);
866
867        let votes: Vec<_> = schemes
868            .iter()
869            .take(quorum)
870            .map(|scheme| {
871                scheme
872                    .sign_vote(
873                        NAMESPACE,
874                        VoteContext::Finalize {
875                            proposal: &proposal,
876                        },
877                    )
878                    .unwrap()
879            })
880            .collect();
881
882        let certificate = schemes[0]
883            .assemble_certificate(votes)
884            .expect("assemble certificate");
885
886        assert!(verifier.verify_certificate(
887            &mut thread_rng(),
888            NAMESPACE,
889            VoteContext::Finalize {
890                proposal: &proposal,
891            },
892            &certificate,
893        ));
894    }
895
896    #[test]
897    fn test_verify_certificate() {
898        verify_certificate::<MinPk>();
899        verify_certificate::<MinSig>();
900    }
901
902    fn verify_certificate_detects_corruption<V: Variant>() {
903        let (schemes, verifier) = setup_signers::<V>(4, 23);
904        let quorum = quorum(schemes.len() as u32) as usize;
905        let proposal = sample_proposal(0, 11, 6);
906
907        let votes: Vec<_> = schemes
908            .iter()
909            .take(quorum)
910            .map(|scheme| {
911                scheme
912                    .sign_vote(
913                        NAMESPACE,
914                        VoteContext::Notarize {
915                            proposal: &proposal,
916                        },
917                    )
918                    .unwrap()
919            })
920            .collect();
921
922        let certificate = schemes[0]
923            .assemble_certificate(votes)
924            .expect("assemble certificate");
925
926        assert!(verifier.verify_certificate(
927            &mut thread_rng(),
928            NAMESPACE,
929            VoteContext::Notarize {
930                proposal: &proposal,
931            },
932            &certificate,
933        ));
934
935        let mut corrupted = certificate.clone();
936        corrupted.vote_signature = corrupted.seed_signature;
937        assert!(!verifier.verify_certificate(
938            &mut thread_rng(),
939            NAMESPACE,
940            VoteContext::Notarize {
941                proposal: &proposal,
942            },
943            &corrupted,
944        ));
945    }
946
947    #[test]
948    fn test_verify_certificate_detects_corruption() {
949        verify_certificate_detects_corruption::<MinPk>();
950        verify_certificate_detects_corruption::<MinSig>();
951    }
952
953    fn certificate_codec_roundtrip<V: Variant>() {
954        let (schemes, _) = setup_signers::<V>(5, 29);
955        let quorum = quorum(schemes.len() as u32) as usize;
956        let proposal = sample_proposal(0, 13, 7);
957
958        let votes: Vec<_> = schemes
959            .iter()
960            .take(quorum)
961            .map(|scheme| {
962                scheme
963                    .sign_vote(
964                        NAMESPACE,
965                        VoteContext::Notarize {
966                            proposal: &proposal,
967                        },
968                    )
969                    .unwrap()
970            })
971            .collect();
972
973        let certificate = schemes[0]
974            .assemble_certificate(votes)
975            .expect("assemble certificate");
976
977        let encoded = certificate.encode();
978        let decoded =
979            Signature::<V>::decode_cfg(encoded.freeze(), &()).expect("decode certificate");
980        assert_eq!(decoded, certificate);
981    }
982
983    #[test]
984    fn test_certificate_codec_roundtrip() {
985        certificate_codec_roundtrip::<MinPk>();
986        certificate_codec_roundtrip::<MinSig>();
987    }
988
989    fn seed_codec_roundtrip<V: Variant>() {
990        let (schemes, _) = setup_signers::<V>(4, 5);
991        let quorum = quorum(schemes.len() as u32) as usize;
992        let proposal = sample_proposal(0, 1, 0);
993
994        let votes: Vec<_> = schemes
995            .iter()
996            .take(quorum)
997            .map(|scheme| {
998                scheme
999                    .sign_vote(
1000                        NAMESPACE,
1001                        VoteContext::Finalize {
1002                            proposal: &proposal,
1003                        },
1004                    )
1005                    .unwrap()
1006            })
1007            .collect();
1008
1009        let certificate = schemes[0]
1010            .assemble_certificate(votes)
1011            .expect("assemble certificate");
1012
1013        let seed = schemes[0]
1014            .seed(proposal.round, &certificate)
1015            .expect("extract seed");
1016
1017        let encoded = seed.encode();
1018        let decoded = Seed::<V>::decode_cfg(encoded, &()).expect("decode seed");
1019        assert_eq!(decoded, seed);
1020    }
1021
1022    #[test]
1023    fn test_seed_codec_roundtrip() {
1024        seed_codec_roundtrip::<MinPk>();
1025        seed_codec_roundtrip::<MinSig>();
1026    }
1027
1028    fn seed_verify<V: Variant>() {
1029        let (schemes, _) = setup_signers::<V>(4, 5);
1030        let quorum = quorum(schemes.len() as u32) as usize;
1031        let proposal = sample_proposal(0, 1, 0);
1032
1033        let votes: Vec<_> = schemes
1034            .iter()
1035            .take(quorum)
1036            .map(|scheme| {
1037                scheme
1038                    .sign_vote(
1039                        NAMESPACE,
1040                        VoteContext::Finalize {
1041                            proposal: &proposal,
1042                        },
1043                    )
1044                    .unwrap()
1045            })
1046            .collect();
1047
1048        let certificate = schemes[0]
1049            .assemble_certificate(votes)
1050            .expect("assemble certificate");
1051
1052        let seed = schemes[0]
1053            .seed(proposal.round, &certificate)
1054            .expect("extract seed");
1055
1056        assert!(seed.verify(&schemes[0], NAMESPACE));
1057
1058        let invalid_seed = schemes[0]
1059            .seed(
1060                Round::new(proposal.epoch(), proposal.view() + 1),
1061                &certificate,
1062            )
1063            .expect("extract seed");
1064
1065        assert!(!invalid_seed.verify(&schemes[0], NAMESPACE));
1066    }
1067
1068    #[test]
1069    fn test_seed_verify() {
1070        seed_verify::<MinPk>();
1071        seed_verify::<MinSig>();
1072    }
1073
1074    fn seedable<V: Variant>() {
1075        let (schemes, _) = setup_signers::<V>(4, 5);
1076        let proposal = sample_proposal(0, 1, 0);
1077
1078        let notarizes: Vec<_> = schemes
1079            .iter()
1080            .take(quorum(schemes.len() as u32) as usize)
1081            .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap())
1082            .collect();
1083
1084        let notarization = Notarization::from_notarizes(&schemes[0], &notarizes).unwrap();
1085
1086        let finalizes: Vec<_> = schemes
1087            .iter()
1088            .take(quorum(schemes.len() as u32) as usize)
1089            .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap())
1090            .collect();
1091
1092        let finalization = Finalization::from_finalizes(&schemes[0], &finalizes).unwrap();
1093
1094        assert_eq!(notarization.seed(), finalization.seed());
1095        assert!(notarization.seed().verify(&schemes[0], NAMESPACE));
1096    }
1097
1098    #[test]
1099    fn test_seedable() {
1100        seedable::<MinPk>();
1101        seedable::<MinSig>();
1102    }
1103
1104    fn scheme_clone_and_verifier<V: Variant>() {
1105        let (schemes, verifier) = setup_signers::<V>(4, 31);
1106        let signer = schemes[0].clone();
1107        let proposal = sample_proposal(0, 21, 9);
1108
1109        assert!(
1110            signer
1111                .sign_vote(
1112                    NAMESPACE,
1113                    VoteContext::Notarize {
1114                        proposal: &proposal,
1115                    },
1116                )
1117                .is_some(),
1118            "signer should produce votes"
1119        );
1120
1121        assert!(
1122            verifier
1123                .sign_vote(
1124                    NAMESPACE,
1125                    VoteContext::Notarize {
1126                        proposal: &proposal,
1127                    },
1128                )
1129                .is_none(),
1130            "verifier should not produce votes"
1131        );
1132    }
1133
1134    #[test]
1135    fn test_scheme_clone_and_verifier() {
1136        scheme_clone_and_verifier::<MinPk>();
1137        scheme_clone_and_verifier::<MinSig>();
1138    }
1139
1140    fn certificate_verifier_accepts_certificates<V: Variant>() {
1141        let (schemes, _) = setup_signers::<V>(4, 37);
1142        let quorum = quorum(schemes.len() as u32) as usize;
1143        let proposal = sample_proposal(0, 15, 8);
1144
1145        let votes: Vec<_> = schemes
1146            .iter()
1147            .take(quorum)
1148            .map(|scheme| {
1149                scheme
1150                    .sign_vote(
1151                        NAMESPACE,
1152                        VoteContext::Finalize {
1153                            proposal: &proposal,
1154                        },
1155                    )
1156                    .unwrap()
1157            })
1158            .collect();
1159
1160        let certificate = schemes[0]
1161            .assemble_certificate(votes)
1162            .expect("assemble certificate");
1163
1164        let certificate_verifier = Scheme::<V>::certificate_verifier(*schemes[0].identity());
1165        assert!(
1166            certificate_verifier
1167                .sign_vote(
1168                    NAMESPACE,
1169                    VoteContext::Finalize {
1170                        proposal: &proposal,
1171                    },
1172                )
1173                .is_none(),
1174            "certificate verifier should not produce votes"
1175        );
1176        assert!(certificate_verifier.verify_certificate(
1177            &mut thread_rng(),
1178            NAMESPACE,
1179            VoteContext::Finalize {
1180                proposal: &proposal,
1181            },
1182            &certificate,
1183        ));
1184    }
1185
1186    #[test]
1187    fn test_certificate_verifier_accepts_certificates() {
1188        certificate_verifier_accepts_certificates::<MinPk>();
1189        certificate_verifier_accepts_certificates::<MinSig>();
1190    }
1191
1192    fn certificate_verifier_panics_on_vote<V: Variant>() {
1193        let (schemes, _) = setup_signers::<V>(4, 37);
1194        let certificate_verifier = Scheme::<V>::certificate_verifier(*schemes[0].identity());
1195        let proposal = sample_proposal(0, 15, 8);
1196        let vote = schemes[1]
1197            .sign_vote(
1198                NAMESPACE,
1199                VoteContext::Finalize {
1200                    proposal: &proposal,
1201                },
1202            )
1203            .unwrap();
1204
1205        certificate_verifier.verify_vote(
1206            NAMESPACE,
1207            VoteContext::Finalize {
1208                proposal: &proposal,
1209            },
1210            &vote,
1211        );
1212    }
1213
1214    #[test]
1215    #[should_panic(expected = "can only be called for signer and verifier")]
1216    fn test_certificate_verifier_panics_on_vote_min_pk() {
1217        certificate_verifier_panics_on_vote::<MinPk>();
1218    }
1219
1220    #[test]
1221    #[should_panic(expected = "can only be called for signer and verifier")]
1222    fn test_certificate_verifier_panics_on_vote_min_sig() {
1223        certificate_verifier_panics_on_vote::<MinSig>();
1224    }
1225
1226    fn verify_certificate_returns_seed_randomness<V: Variant>() {
1227        let (schemes, _) = setup_signers::<V>(4, 43);
1228        let quorum = quorum(schemes.len() as u32) as usize;
1229        let proposal = sample_proposal(0, 19, 10);
1230
1231        let votes: Vec<_> = schemes
1232            .iter()
1233            .take(quorum)
1234            .map(|scheme| {
1235                scheme
1236                    .sign_vote(
1237                        NAMESPACE,
1238                        VoteContext::Notarize {
1239                            proposal: &proposal,
1240                        },
1241                    )
1242                    .unwrap()
1243            })
1244            .collect();
1245
1246        let certificate = schemes[0]
1247            .assemble_certificate(votes)
1248            .expect("assemble certificate");
1249
1250        let seed = schemes[0].seed(proposal.round, &certificate).unwrap();
1251        assert_eq!(seed.signature, certificate.seed_signature);
1252    }
1253
1254    #[test]
1255    fn test_verify_certificate_returns_seed_randomness() {
1256        verify_certificate_returns_seed_randomness::<MinPk>();
1257        verify_certificate_returns_seed_randomness::<MinSig>();
1258    }
1259
1260    fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1261        let (schemes, _) = setup_signers::<V>(4, 47);
1262        let quorum = quorum(schemes.len() as u32) as usize;
1263        let proposal = sample_proposal(0, 21, 11);
1264
1265        let votes: Vec<_> = schemes
1266            .iter()
1267            .take(quorum)
1268            .map(|scheme| {
1269                scheme
1270                    .sign_vote::<Sha256Digest>(
1271                        NAMESPACE,
1272                        VoteContext::Nullify {
1273                            round: proposal.round,
1274                        },
1275                    )
1276                    .unwrap()
1277            })
1278            .collect();
1279
1280        let certificate = schemes[0]
1281            .assemble_certificate(votes)
1282            .expect("assemble certificate");
1283
1284        let mut encoded = certificate.encode().freeze();
1285        let truncated = encoded.split_to(encoded.len() - 1);
1286        assert!(Signature::<V>::decode_cfg(truncated, &()).is_err());
1287    }
1288
1289    #[test]
1290    fn test_certificate_decode_rejects_length_mismatch() {
1291        certificate_decode_rejects_length_mismatch::<MinPk>();
1292        certificate_decode_rejects_length_mismatch::<MinSig>();
1293    }
1294
1295    fn sign_vote_partial_matches_share<V: Variant>() {
1296        let (schemes, _) = setup_signers::<V>(4, 53);
1297        let scheme = &schemes[0];
1298        let share = scheme.share().expect("has share");
1299
1300        let proposal = sample_proposal(0, 23, 12);
1301        let vote = scheme
1302            .sign_vote(
1303                NAMESPACE,
1304                VoteContext::Notarize {
1305                    proposal: &proposal,
1306                },
1307            )
1308            .unwrap();
1309
1310        let notarize_namespace = notarize_namespace(NAMESPACE);
1311        let notarize_message = proposal.encode();
1312        let expected_message = partial_sign_message::<V>(
1313            share,
1314            Some(notarize_namespace.as_ref()),
1315            notarize_message.as_ref(),
1316        )
1317        .value;
1318
1319        let seed_namespace = seed_namespace(NAMESPACE);
1320        let seed_message = proposal.round.encode();
1321        let expected_seed =
1322            partial_sign_message::<V>(share, Some(seed_namespace.as_ref()), seed_message.as_ref())
1323                .value;
1324
1325        assert_eq!(vote.signer, share.index);
1326        assert_eq!(vote.signature.vote_signature, expected_message);
1327        assert_eq!(vote.signature.seed_signature, expected_seed);
1328    }
1329
1330    #[test]
1331    fn test_sign_vote_partial_matches_share() {
1332        sign_vote_partial_matches_share::<MinPk>();
1333        sign_vote_partial_matches_share::<MinSig>();
1334    }
1335
1336    fn verify_certificate_detects_seed_corruption<V: Variant>() {
1337        let (schemes, verifier) = setup_signers::<V>(4, 59);
1338        let quorum = quorum(schemes.len() as u32) as usize;
1339        let proposal = sample_proposal(0, 25, 13);
1340
1341        let votes: Vec<_> = schemes
1342            .iter()
1343            .take(quorum)
1344            .map(|scheme| {
1345                scheme
1346                    .sign_vote::<Sha256Digest>(
1347                        NAMESPACE,
1348                        VoteContext::Nullify {
1349                            round: proposal.round,
1350                        },
1351                    )
1352                    .unwrap()
1353            })
1354            .collect();
1355
1356        let certificate = schemes[0]
1357            .assemble_certificate(votes)
1358            .expect("assemble certificate");
1359
1360        assert!(verifier.verify_certificate::<_, Sha256Digest>(
1361            &mut thread_rng(),
1362            NAMESPACE,
1363            VoteContext::Nullify {
1364                round: proposal.round,
1365            },
1366            &certificate,
1367        ));
1368
1369        let mut corrupted = certificate.clone();
1370        corrupted.seed_signature = corrupted.vote_signature;
1371        assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1372            &mut thread_rng(),
1373            NAMESPACE,
1374            VoteContext::Nullify {
1375                round: proposal.round,
1376            },
1377            &corrupted,
1378        ));
1379    }
1380
1381    #[test]
1382    fn test_verify_certificate_detects_seed_corruption() {
1383        verify_certificate_detects_seed_corruption::<MinPk>();
1384        verify_certificate_detects_seed_corruption::<MinSig>();
1385    }
1386}