1use 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#[derive(Clone, Debug)]
51pub enum Scheme<P: PublicKey, V: Variant> {
52 Signer {
53 participants: OrderedAssociated<P, V::Public>,
55 identity: V::Public,
57 share: Share,
59 },
60 Verifier {
61 participants: OrderedAssociated<P, V::Public>,
63 identity: V::Public,
65 },
66 CertificateVerifier {
67 identity: V::Public,
69 },
70}
71
72impl<P: PublicKey, V: Variant> Scheme<P, V> {
73 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 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 pub fn certificate_verifier(identity: V::Public) -> Self {
141 Self::CertificateVerifier { identity }
142 }
143
144 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 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 pub fn share(&self) -> Option<&Share> {
164 match self {
165 Scheme::Signer { share, .. } => Some(share),
166 _ => None,
167 }
168 }
169
170 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#[derive(Clone, Debug, PartialEq, Eq, Hash)]
182pub struct Signature<V: Variant> {
183 pub vote_signature: V::Signature,
185 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#[derive(Clone, Debug, PartialEq, Hash, Eq)]
216pub struct Seed<V: Variant> {
217 pub round: Round,
219 pub signature: V::Signature,
221}
222
223impl<V: Variant> Seed<V> {
224 pub fn new(round: Round, signature: V::Signature) -> Self {
226 Seed { round, signature }
227 }
228
229 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 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
289pub trait Seedable<V: Variant> {
291 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 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 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 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 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 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 ¬arize_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], ¬arizes).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}