1use super::primitives::group::{Private, Share};
287use crate::{
288 bls12381::primitives::{
289 group::Scalar,
290 sharing::{Mode, Sharing},
291 variant::Variant,
292 },
293 transcript::{Summary, Transcript},
294 PublicKey, Secret, Signer,
295};
296use commonware_codec::{Encode, EncodeSize, RangeCfg, Read, ReadExt, Write};
297use commonware_math::{
298 algebra::{Additive, CryptoGroup, Random},
299 poly::{Interpolator, Poly},
300};
301use commonware_parallel::{Sequential, Strategy};
302#[cfg(feature = "arbitrary")]
303use commonware_utils::N3f1;
304use commonware_utils::{
305 ordered::{Map, Quorum, Set},
306 Faults, Participant, TryCollect, NZU32,
307};
308use core::num::NonZeroU32;
309use rand_core::CryptoRngCore;
310use std::collections::BTreeMap;
311use thiserror::Error;
312
313const NAMESPACE: &[u8] = b"_COMMONWARE_CRYPTOGRAPHY_BLS12381_DKG";
314const SIG_ACK: &[u8] = b"ack";
315const SIG_LOG: &[u8] = b"log";
316
317#[derive(Debug, Error)]
323pub enum Error {
324 #[error("missing dealer's share from the previous round")]
325 MissingDealerShare,
326 #[error("player is not present in the list of players")]
327 UnknownPlayer,
328 #[error("dealer is not present in the previous list of players")]
329 UnknownDealer(String),
330 #[error("invalid number of dealers: {0}")]
331 NumDealers(usize),
332 #[error("invalid number of players: {0}")]
333 NumPlayers(usize),
334 #[error("dkg failed for some reason")]
335 DkgFailed,
336}
337
338#[derive(Debug, Clone, PartialEq, Eq)]
340pub struct Output<V: Variant, P> {
341 summary: Summary,
342 public: Sharing<V>,
343 dealers: Set<P>,
344 players: Set<P>,
345 revealed: Set<P>,
346}
347
348impl<V: Variant, P: Ord> Output<V, P> {
349 fn share_commitment(&self, player: &P) -> Option<V::Public> {
350 self.public.partial_public(self.players.index(player)?).ok()
351 }
352
353 pub fn quorum<M: Faults>(&self) -> u32 {
355 self.players.quorum::<M>()
356 }
357
358 pub const fn public(&self) -> &Sharing<V> {
362 &self.public
363 }
364
365 pub const fn dealers(&self) -> &Set<P> {
367 &self.dealers
368 }
369
370 pub const fn players(&self) -> &Set<P> {
372 &self.players
373 }
374
375 pub const fn revealed(&self) -> &Set<P> {
379 &self.revealed
380 }
381}
382
383impl<V: Variant, P: PublicKey> EncodeSize for Output<V, P> {
384 fn encode_size(&self) -> usize {
385 self.summary.encode_size()
386 + self.public.encode_size()
387 + self.dealers.encode_size()
388 + self.players.encode_size()
389 + self.revealed.encode_size()
390 }
391}
392
393impl<V: Variant, P: PublicKey> Write for Output<V, P> {
394 fn write(&self, buf: &mut impl bytes::BufMut) {
395 self.summary.write(buf);
396 self.public.write(buf);
397 self.dealers.write(buf);
398 self.players.write(buf);
399 self.revealed.write(buf);
400 }
401}
402
403impl<V: Variant, P: PublicKey> Read for Output<V, P> {
404 type Cfg = NonZeroU32;
405
406 fn read_cfg(
407 buf: &mut impl bytes::Buf,
408 &max_participants: &Self::Cfg,
409 ) -> Result<Self, commonware_codec::Error> {
410 let max_participants_usize = max_participants.get() as usize;
411 Ok(Self {
412 summary: ReadExt::read(buf)?,
413 public: Read::read_cfg(buf, &max_participants)?,
414 dealers: Read::read_cfg(buf, &(RangeCfg::new(1..=max_participants_usize), ()))?, players: Read::read_cfg(buf, &(RangeCfg::new(1..=max_participants_usize), ()))?, revealed: Read::read_cfg(buf, &(RangeCfg::new(0..=max_participants_usize), ()))?, })
418 }
419}
420
421#[cfg(feature = "arbitrary")]
422impl<P: PublicKey, V: Variant> arbitrary::Arbitrary<'_> for Output<V, P>
423where
424 P: for<'a> arbitrary::Arbitrary<'a> + Ord,
425 V::Public: for<'a> arbitrary::Arbitrary<'a>,
426{
427 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
428 let summary = u.arbitrary()?;
429 let public: Sharing<V> = u.arbitrary()?;
430 let total = public.total().get() as usize;
431
432 let num_dealers = u.int_in_range(1..=total * 2)?;
433 let dealers = Set::try_from(
434 u.arbitrary_iter::<P>()?
435 .take(num_dealers)
436 .collect::<Result<Vec<_>, _>>()?,
437 )
438 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
439
440 let players = Set::try_from(
441 u.arbitrary_iter::<P>()?
442 .take(total)
443 .collect::<Result<Vec<_>, _>>()?,
444 )
445 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
446
447 let max_revealed = N3f1::max_faults(total) as usize;
448 let revealed = Set::from_iter_dedup(
449 players
450 .iter()
451 .filter(|_| u.arbitrary::<bool>().unwrap_or(false))
452 .take(max_revealed)
453 .cloned(),
454 );
455
456 Ok(Self {
457 summary,
458 public,
459 dealers,
460 players,
461 revealed,
462 })
463 }
464}
465
466#[derive(Debug, Clone)]
471pub struct Info<V: Variant, P: PublicKey> {
472 summary: Summary,
473 round: u64,
474 previous: Option<Output<V, P>>,
475 mode: Mode,
476 dealers: Set<P>,
477 players: Set<P>,
478}
479
480impl<V: Variant, P: PublicKey> PartialEq for Info<V, P> {
481 fn eq(&self, other: &Self) -> bool {
482 self.summary == other.summary
483 }
484}
485
486impl<V: Variant, P: PublicKey> Info<V, P> {
487 fn unwrap_or_random_share(
493 &self,
494 mut rng: impl CryptoRngCore,
495 share: Option<Scalar>,
496 ) -> Result<Scalar, Error> {
497 let out = match (self.previous.as_ref(), share) {
498 (None, None) => Scalar::random(&mut rng),
499 (_, Some(x)) => x,
500 (Some(_), None) => return Err(Error::MissingDealerShare),
501 };
502 Ok(out)
503 }
504
505 const fn num_players(&self) -> NonZeroU32 {
506 NZU32!(self.players.len() as u32)
508 }
509
510 fn degree<M: Faults>(&self) -> u32 {
511 self.players.quorum::<M>().saturating_sub(1)
512 }
513
514 fn required_commitments<M: Faults>(&self) -> u32 {
515 let dealer_quorum = self.dealers.quorum::<M>();
516 let prev_quorum = self
517 .previous
518 .as_ref()
519 .map(Output::quorum::<M>)
520 .unwrap_or(u32::MIN);
521 dealer_quorum.max(prev_quorum)
522 }
523
524 fn max_reveals<M: Faults>(&self) -> u32 {
525 self.players.max_faults::<M>()
526 }
527
528 fn player_index(&self, player: &P) -> Result<Participant, Error> {
529 self.players.index(player).ok_or(Error::UnknownPlayer)
530 }
531
532 fn dealer_index(&self, dealer: &P) -> Result<Participant, Error> {
533 self.dealers
534 .index(dealer)
535 .ok_or(Error::UnknownDealer(format!("{dealer:?}")))
536 }
537
538 fn player_scalar(&self, player: &P) -> Result<Scalar, Error> {
539 Ok(self
540 .mode
541 .scalar(self.num_players(), self.player_index(player)?)
542 .expect("player index should be < num_players"))
543 }
544
545 #[must_use]
546 fn check_dealer_pub_msg<M: Faults>(&self, dealer: &P, pub_msg: &DealerPubMsg<V>) -> bool {
547 if self.degree::<M>() != pub_msg.commitment.degree_exact() {
548 return false;
549 }
550 if let Some(previous) = self.previous.as_ref() {
551 let Some(share_commitment) = previous.share_commitment(dealer) else {
552 return false;
553 };
554 if *pub_msg.commitment.constant() != share_commitment {
555 return false;
556 }
557 }
558 true
559 }
560
561 #[must_use]
562 fn check_dealer_priv_msg(
563 &self,
564 player: &P,
565 pub_msg: &DealerPubMsg<V>,
566 priv_msg: &DealerPrivMsg,
567 ) -> bool {
568 let Ok(scalar) = self.player_scalar(player) else {
569 return false;
570 };
571 let expected = pub_msg.commitment.eval_msm(&scalar, &Sequential);
572 priv_msg
573 .share
574 .expose(|share| expected == V::Public::generator() * share)
575 }
576}
577
578impl<V: Variant, P: PublicKey> Info<V, P> {
579 pub fn new<M: Faults>(
589 namespace: &[u8],
590 round: u64,
591 previous: Option<Output<V, P>>,
592 mode: Mode,
593 dealers: Set<P>,
594 players: Set<P>,
595 ) -> Result<Self, Error> {
596 let participant_range = 1..u32::MAX as usize;
597 if !participant_range.contains(&dealers.len()) {
598 return Err(Error::NumDealers(dealers.len()));
599 }
600 if !participant_range.contains(&players.len()) {
601 return Err(Error::NumPlayers(players.len()));
602 }
603 if let Some(previous) = previous.as_ref() {
604 if let Some(unknown) = dealers
605 .iter()
606 .find(|d| previous.players.position(d).is_none())
607 {
608 return Err(Error::UnknownDealer(format!("{unknown:?}")));
609 }
610 if dealers.len() < previous.quorum::<M>() as usize {
611 return Err(Error::NumDealers(dealers.len()));
612 }
613 }
614 let summary = Transcript::new(NAMESPACE)
615 .commit(namespace)
616 .commit(round.encode())
617 .commit(previous.encode())
618 .commit(dealers.encode())
619 .commit(players.encode())
620 .summarize();
621 Ok(Self {
622 summary,
623 round,
624 previous,
625 mode,
626 dealers,
627 players,
628 })
629 }
630
631 pub const fn round(&self) -> u64 {
635 self.round
636 }
637}
638
639#[derive(Clone, Debug)]
640pub struct DealerPubMsg<V: Variant> {
641 commitment: Poly<V::Public>,
642}
643
644impl<V: Variant> PartialEq for DealerPubMsg<V> {
645 fn eq(&self, other: &Self) -> bool {
646 self.commitment == other.commitment
647 }
648}
649
650impl<V: Variant> Eq for DealerPubMsg<V> {}
651
652impl<V: Variant> EncodeSize for DealerPubMsg<V> {
653 fn encode_size(&self) -> usize {
654 self.commitment.encode_size()
655 }
656}
657
658impl<V: Variant> Write for DealerPubMsg<V> {
659 fn write(&self, buf: &mut impl bytes::BufMut) {
660 self.commitment.write(buf);
661 }
662}
663
664impl<V: Variant> Read for DealerPubMsg<V> {
665 type Cfg = NonZeroU32;
666
667 fn read_cfg(
668 buf: &mut impl bytes::Buf,
669 &max_size: &Self::Cfg,
670 ) -> Result<Self, commonware_codec::Error> {
671 Ok(Self {
672 commitment: Read::read_cfg(buf, &(RangeCfg::from(NZU32!(1)..=max_size), ()))?,
673 })
674 }
675}
676
677#[cfg(feature = "arbitrary")]
678impl<V: Variant> arbitrary::Arbitrary<'_> for DealerPubMsg<V>
679where
680 V::Public: for<'a> arbitrary::Arbitrary<'a>,
681{
682 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
683 let commitment = u.arbitrary()?;
684 Ok(Self { commitment })
685 }
686}
687
688#[derive(Clone, Debug, PartialEq, Eq)]
689pub struct DealerPrivMsg {
690 share: Secret<Scalar>,
691}
692
693impl DealerPrivMsg {
694 pub const fn new(share: Scalar) -> Self {
696 Self {
697 share: Secret::new(share),
698 }
699 }
700}
701
702impl EncodeSize for DealerPrivMsg {
703 fn encode_size(&self) -> usize {
704 self.share.expose(|share| share.encode_size())
705 }
706}
707
708impl Write for DealerPrivMsg {
709 fn write(&self, buf: &mut impl bytes::BufMut) {
710 self.share.expose(|share| share.write(buf));
711 }
712}
713
714impl Read for DealerPrivMsg {
715 type Cfg = ();
716
717 fn read_cfg(
718 buf: &mut impl bytes::Buf,
719 _cfg: &Self::Cfg,
720 ) -> Result<Self, commonware_codec::Error> {
721 Ok(Self::new(ReadExt::read(buf)?))
722 }
723}
724
725#[cfg(feature = "arbitrary")]
726impl arbitrary::Arbitrary<'_> for DealerPrivMsg {
727 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
728 Ok(Self::new(u.arbitrary()?))
729 }
730}
731
732#[derive(Clone, Debug)]
733pub struct PlayerAck<P: PublicKey> {
734 sig: P::Signature,
735}
736
737impl<P: PublicKey> PartialEq for PlayerAck<P> {
738 fn eq(&self, other: &Self) -> bool {
739 self.sig == other.sig
740 }
741}
742
743impl<P: PublicKey> EncodeSize for PlayerAck<P> {
744 fn encode_size(&self) -> usize {
745 self.sig.encode_size()
746 }
747}
748
749impl<P: PublicKey> Write for PlayerAck<P> {
750 fn write(&self, buf: &mut impl bytes::BufMut) {
751 self.sig.write(buf);
752 }
753}
754
755impl<P: PublicKey> Read for PlayerAck<P> {
756 type Cfg = ();
757
758 fn read_cfg(
759 buf: &mut impl bytes::Buf,
760 _cfg: &Self::Cfg,
761 ) -> Result<Self, commonware_codec::Error> {
762 Ok(Self {
763 sig: ReadExt::read(buf)?,
764 })
765 }
766}
767
768#[cfg(feature = "arbitrary")]
769impl<P: PublicKey> arbitrary::Arbitrary<'_> for PlayerAck<P>
770where
771 P::Signature: for<'a> arbitrary::Arbitrary<'a>,
772{
773 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
774 let sig = u.arbitrary()?;
775 Ok(Self { sig })
776 }
777}
778
779#[derive(Clone, PartialEq)]
780enum AckOrReveal<P: PublicKey> {
781 Ack(PlayerAck<P>),
782 Reveal(DealerPrivMsg),
783}
784
785impl<P: PublicKey> AckOrReveal<P> {
786 const fn is_reveal(&self) -> bool {
787 matches!(*self, Self::Reveal(_))
788 }
789}
790
791impl<P: PublicKey> std::fmt::Debug for AckOrReveal<P> {
792 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
793 match self {
794 Self::Ack(x) => write!(f, "Ack({x:?})"),
795 Self::Reveal(_) => write!(f, "Reveal(REDACTED)"),
796 }
797 }
798}
799
800impl<P: PublicKey> EncodeSize for AckOrReveal<P> {
801 fn encode_size(&self) -> usize {
802 1 + match self {
803 Self::Ack(x) => x.encode_size(),
804 Self::Reveal(x) => x.encode_size(),
805 }
806 }
807}
808
809impl<P: PublicKey> Write for AckOrReveal<P> {
810 fn write(&self, buf: &mut impl bytes::BufMut) {
811 match self {
812 Self::Ack(x) => {
813 0u8.write(buf);
814 x.write(buf);
815 }
816 Self::Reveal(x) => {
817 1u8.write(buf);
818 x.write(buf);
819 }
820 }
821 }
822}
823
824impl<P: PublicKey> Read for AckOrReveal<P> {
825 type Cfg = ();
826
827 fn read_cfg(
828 buf: &mut impl bytes::Buf,
829 _cfg: &Self::Cfg,
830 ) -> Result<Self, commonware_codec::Error> {
831 let tag = u8::read(buf)?;
832 match tag {
833 0 => Ok(Self::Ack(ReadExt::read(buf)?)),
834 1 => Ok(Self::Reveal(ReadExt::read(buf)?)),
835 x => Err(commonware_codec::Error::InvalidEnum(x)),
836 }
837 }
838}
839
840#[cfg(feature = "arbitrary")]
841impl<P: PublicKey> arbitrary::Arbitrary<'_> for AckOrReveal<P>
842where
843 P: for<'a> arbitrary::Arbitrary<'a>,
844 P::Signature: for<'a> arbitrary::Arbitrary<'a>,
845{
846 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
847 let choice = u.int_in_range(0..=1)?;
848 match choice {
849 0 => {
850 let ack = u.arbitrary()?;
851 Ok(Self::Ack(ack))
852 }
853 1 => {
854 let reveal = u.arbitrary()?;
855 Ok(Self::Reveal(reveal))
856 }
857 _ => unreachable!(),
858 }
859 }
860}
861
862#[derive(Clone, Debug)]
863enum DealerResult<P: PublicKey> {
864 Ok(Map<P, AckOrReveal<P>>),
865 TooManyReveals,
866}
867
868impl<P: PublicKey> PartialEq for DealerResult<P> {
869 fn eq(&self, other: &Self) -> bool {
870 match (self, other) {
871 (Self::Ok(x), Self::Ok(y)) => x == y,
872 (Self::TooManyReveals, Self::TooManyReveals) => true,
873 _ => false,
874 }
875 }
876}
877
878impl<P: PublicKey> EncodeSize for DealerResult<P> {
879 fn encode_size(&self) -> usize {
880 1 + match self {
881 Self::Ok(r) => r.encode_size(),
882 Self::TooManyReveals => 0,
883 }
884 }
885}
886
887impl<P: PublicKey> Write for DealerResult<P> {
888 fn write(&self, buf: &mut impl bytes::BufMut) {
889 match self {
890 Self::Ok(r) => {
891 0u8.write(buf);
892 r.write(buf);
893 }
894 Self::TooManyReveals => {
895 1u8.write(buf);
896 }
897 }
898 }
899}
900
901impl<P: PublicKey> Read for DealerResult<P> {
902 type Cfg = NonZeroU32;
903
904 fn read_cfg(
905 buf: &mut impl bytes::Buf,
906 &max_players: &Self::Cfg,
907 ) -> Result<Self, commonware_codec::Error> {
908 let tag = u8::read(buf)?;
909 match tag {
910 0 => Ok(Self::Ok(Read::read_cfg(
911 buf,
912 &(RangeCfg::from(0..=max_players.get() as usize), (), ()),
913 )?)),
914 1 => Ok(Self::TooManyReveals),
915 x => Err(commonware_codec::Error::InvalidEnum(x)),
916 }
917 }
918}
919
920#[cfg(feature = "arbitrary")]
921impl<P: PublicKey> arbitrary::Arbitrary<'_> for DealerResult<P>
922where
923 P: for<'a> arbitrary::Arbitrary<'a>,
924 P::Signature: for<'a> arbitrary::Arbitrary<'a>,
925{
926 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
927 let choice = u.int_in_range(0..=1)?;
928 match choice {
929 0 => {
930 use commonware_utils::TryFromIterator;
931 use std::collections::HashMap;
932
933 let base: HashMap<P, AckOrReveal<P>> = u.arbitrary()?;
934 let map = Map::try_from_iter(base.into_iter())
935 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
936
937 Ok(Self::Ok(map))
938 }
939 1 => Ok(Self::TooManyReveals),
940 _ => unreachable!(),
941 }
942 }
943}
944
945#[derive(Clone, Debug)]
946pub struct DealerLog<V: Variant, P: PublicKey> {
947 pub_msg: DealerPubMsg<V>,
948 results: DealerResult<P>,
949}
950
951impl<V: Variant, P: PublicKey> PartialEq for DealerLog<V, P> {
952 fn eq(&self, other: &Self) -> bool {
953 self.pub_msg == other.pub_msg && self.results == other.results
954 }
955}
956
957impl<V: Variant, P: PublicKey> EncodeSize for DealerLog<V, P> {
958 fn encode_size(&self) -> usize {
959 self.pub_msg.encode_size() + self.results.encode_size()
960 }
961}
962
963impl<V: Variant, P: PublicKey> Write for DealerLog<V, P> {
964 fn write(&self, buf: &mut impl bytes::BufMut) {
965 self.pub_msg.write(buf);
966 self.results.write(buf);
967 }
968}
969
970impl<V: Variant, P: PublicKey> Read for DealerLog<V, P> {
971 type Cfg = NonZeroU32;
972
973 fn read_cfg(
974 buf: &mut impl bytes::Buf,
975 cfg: &Self::Cfg,
976 ) -> Result<Self, commonware_codec::Error> {
977 Ok(Self {
978 pub_msg: Read::read_cfg(buf, cfg)?,
979 results: Read::read_cfg(buf, cfg)?,
980 })
981 }
982}
983
984impl<V: Variant, P: PublicKey> DealerLog<V, P> {
985 fn get_reveal(&self, player: &P) -> Option<&DealerPrivMsg> {
986 let DealerResult::Ok(results) = &self.results else {
987 return None;
988 };
989 match results.get_value(player) {
990 Some(AckOrReveal::Reveal(priv_msg)) => Some(priv_msg),
991 _ => None,
992 }
993 }
994
995 fn zip_players<'a, 'b>(
996 &'a self,
997 players: &'b Set<P>,
998 ) -> Option<impl Iterator<Item = (&'b P, &'a AckOrReveal<P>)>> {
999 match &self.results {
1000 DealerResult::TooManyReveals => None,
1001 DealerResult::Ok(results) => {
1002 if results.keys() != players {
1004 return None;
1005 }
1006 Some(players.iter().zip(results.values().iter()))
1007 }
1008 }
1009 }
1010}
1011
1012#[cfg(feature = "arbitrary")]
1013impl<V: Variant, P: PublicKey> arbitrary::Arbitrary<'_> for DealerLog<V, P>
1014where
1015 P: for<'a> arbitrary::Arbitrary<'a>,
1016 V::Public: for<'a> arbitrary::Arbitrary<'a>,
1017 P::Signature: for<'a> arbitrary::Arbitrary<'a>,
1018{
1019 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1020 let pub_msg = u.arbitrary()?;
1021 let results = u.arbitrary()?;
1022 Ok(Self { pub_msg, results })
1023 }
1024}
1025
1026#[derive(Clone, Debug)]
1034pub struct SignedDealerLog<V: Variant, S: Signer> {
1035 dealer: S::PublicKey,
1036 log: DealerLog<V, S::PublicKey>,
1037 sig: S::Signature,
1038}
1039
1040impl<V: Variant, S: Signer> PartialEq for SignedDealerLog<V, S> {
1041 fn eq(&self, other: &Self) -> bool {
1042 self.dealer == other.dealer && self.log == other.log && self.sig == other.sig
1043 }
1044}
1045
1046impl<V: Variant, S: Signer> SignedDealerLog<V, S> {
1047 fn sign(sk: &S, info: &Info<V, S::PublicKey>, log: DealerLog<V, S::PublicKey>) -> Self {
1048 let sig = transcript_for_log(info, &log).sign(sk);
1049 Self {
1050 dealer: sk.public_key(),
1051 log,
1052 sig,
1053 }
1054 }
1055
1056 #[allow(clippy::type_complexity)]
1063 pub fn check(
1064 self,
1065 info: &Info<V, S::PublicKey>,
1066 ) -> Option<(S::PublicKey, DealerLog<V, S::PublicKey>)> {
1067 if !transcript_for_log(info, &self.log).verify(&self.dealer, &self.sig) {
1068 return None;
1069 }
1070 Some((self.dealer, self.log))
1071 }
1072}
1073
1074impl<V: Variant, S: Signer> EncodeSize for SignedDealerLog<V, S> {
1075 fn encode_size(&self) -> usize {
1076 self.dealer.encode_size() + self.log.encode_size() + self.sig.encode_size()
1077 }
1078}
1079
1080impl<V: Variant, S: Signer> Write for SignedDealerLog<V, S> {
1081 fn write(&self, buf: &mut impl bytes::BufMut) {
1082 self.dealer.write(buf);
1083 self.log.write(buf);
1084 self.sig.write(buf);
1085 }
1086}
1087
1088impl<V: Variant, S: Signer> Read for SignedDealerLog<V, S> {
1089 type Cfg = NonZeroU32;
1090
1091 fn read_cfg(
1092 buf: &mut impl bytes::Buf,
1093 cfg: &Self::Cfg,
1094 ) -> Result<Self, commonware_codec::Error> {
1095 Ok(Self {
1096 dealer: ReadExt::read(buf)?,
1097 log: Read::read_cfg(buf, cfg)?,
1098 sig: ReadExt::read(buf)?,
1099 })
1100 }
1101}
1102
1103#[cfg(feature = "arbitrary")]
1104impl<V: Variant, S: Signer> arbitrary::Arbitrary<'_> for SignedDealerLog<V, S>
1105where
1106 S::PublicKey: for<'a> arbitrary::Arbitrary<'a>,
1107 V::Public: for<'a> arbitrary::Arbitrary<'a>,
1108 S::Signature: for<'a> arbitrary::Arbitrary<'a>,
1109{
1110 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1111 let dealer = u.arbitrary()?;
1112 let log = u.arbitrary()?;
1113 let sig = u.arbitrary()?;
1114 Ok(Self { dealer, log, sig })
1115 }
1116}
1117
1118fn transcript_for_round<V: Variant, P: PublicKey>(info: &Info<V, P>) -> Transcript {
1119 Transcript::resume(info.summary)
1120}
1121
1122fn transcript_for_ack<V: Variant, P: PublicKey>(
1123 transcript: &Transcript,
1124 dealer: &P,
1125 pub_msg: &DealerPubMsg<V>,
1126) -> Transcript {
1127 let mut out = transcript.fork(SIG_ACK);
1128 out.commit(dealer.encode());
1129 out.commit(pub_msg.encode());
1130 out
1131}
1132
1133fn transcript_for_log<V: Variant, P: PublicKey>(
1134 info: &Info<V, P>,
1135 log: &DealerLog<V, P>,
1136) -> Transcript {
1137 let mut out = transcript_for_round(info).fork(SIG_LOG);
1138 out.commit(log.encode());
1139 out
1140}
1141
1142pub struct Dealer<V: Variant, S: Signer> {
1143 me: S,
1144 info: Info<V, S::PublicKey>,
1145 pub_msg: DealerPubMsg<V>,
1146 results: Map<S::PublicKey, AckOrReveal<S::PublicKey>>,
1147 transcript: Transcript,
1148}
1149
1150impl<V: Variant, S: Signer> Dealer<V, S> {
1151 #[allow(clippy::type_complexity)]
1173 pub fn start<M: Faults>(
1174 mut rng: impl CryptoRngCore,
1175 info: Info<V, S::PublicKey>,
1176 me: S,
1177 share: Option<Share>,
1178 ) -> Result<(Self, DealerPubMsg<V>, Vec<(S::PublicKey, DealerPrivMsg)>), Error> {
1179 info.dealer_index(&me.public_key())?;
1181 let share = info.unwrap_or_random_share(
1182 &mut rng,
1183 share.map(|x| x.private.expose_unwrap()),
1188 )?;
1189 let my_poly = Poly::new_with_constant(&mut rng, info.degree::<M>(), share);
1190 let priv_msgs = info
1191 .players
1192 .iter()
1193 .map(|pk| {
1194 (
1195 pk.clone(),
1196 DealerPrivMsg::new(my_poly.eval_msm(
1197 &info.player_scalar(pk).expect("player should exist"),
1198 &Sequential,
1199 )),
1200 )
1201 })
1202 .collect::<Vec<_>>();
1203 let results: Map<_, _> = priv_msgs
1204 .clone()
1205 .into_iter()
1206 .map(|(pk, priv_msg)| (pk, AckOrReveal::Reveal(priv_msg)))
1207 .try_collect()
1208 .expect("players are unique");
1209 let commitment = Poly::commit(my_poly);
1210 let pub_msg = DealerPubMsg { commitment };
1211 let transcript = {
1212 let t = transcript_for_round(&info);
1213 transcript_for_ack(&t, &me.public_key(), &pub_msg)
1214 };
1215 let this = Self {
1216 me,
1217 info,
1218 pub_msg: pub_msg.clone(),
1219 results,
1220 transcript,
1221 };
1222 Ok((this, pub_msg, priv_msgs))
1223 }
1224
1225 pub fn receive_player_ack(
1230 &mut self,
1231 player: S::PublicKey,
1232 ack: PlayerAck<S::PublicKey>,
1233 ) -> Result<(), Error> {
1234 let res_mut = self
1235 .results
1236 .get_value_mut(&player)
1237 .ok_or(Error::UnknownPlayer)?;
1238 if self.transcript.verify(&player, &ack.sig) {
1239 *res_mut = AckOrReveal::Ack(ack);
1240 }
1241 Ok(())
1242 }
1243
1244 pub fn finalize<M: Faults>(self) -> SignedDealerLog<V, S> {
1248 let reveals = self
1249 .results
1250 .values()
1251 .iter()
1252 .filter(|x| x.is_reveal())
1253 .count() as u32;
1254 let results = if reveals > self.info.max_reveals::<M>() {
1256 DealerResult::TooManyReveals
1257 } else {
1258 DealerResult::Ok(self.results)
1259 };
1260 let log = DealerLog {
1261 pub_msg: self.pub_msg,
1262 results,
1263 };
1264 SignedDealerLog::sign(&self.me, &self.info, log)
1265 }
1266}
1267
1268#[allow(clippy::type_complexity)]
1269fn select<V: Variant, P: PublicKey, M: Faults>(
1270 info: &Info<V, P>,
1271 logs: BTreeMap<P, DealerLog<V, P>>,
1272) -> Result<Map<P, DealerLog<V, P>>, Error> {
1273 let required_commitments = info.required_commitments::<M>() as usize;
1274 let transcript = transcript_for_round(info);
1275 let out = logs
1276 .into_iter()
1277 .filter_map(|(dealer, log)| {
1278 info.dealer_index(&dealer).ok()?;
1279 if !info.check_dealer_pub_msg::<M>(&dealer, &log.pub_msg) {
1280 return None;
1281 }
1282 let results_iter = log.zip_players(&info.players)?;
1283 let transcript = transcript_for_ack(&transcript, &dealer, &log.pub_msg);
1284 let mut reveal_count = 0;
1285 let max_reveals = info.max_reveals::<M>();
1286 for (player, result) in results_iter {
1287 match result {
1288 AckOrReveal::Ack(ack) => {
1289 if !transcript.verify(player, &ack.sig) {
1290 return None;
1291 }
1292 }
1293 AckOrReveal::Reveal(priv_msg) => {
1294 reveal_count += 1;
1295 if reveal_count > max_reveals {
1296 return None;
1297 }
1298 if !info.check_dealer_priv_msg(player, &log.pub_msg, priv_msg) {
1299 return None;
1300 }
1301 }
1302 }
1303 }
1304 Some((dealer, log))
1305 })
1306 .take(required_commitments)
1307 .try_collect::<Map<_, _>>()
1308 .expect("logs has at most one entry per dealer");
1309 if out.len() < required_commitments {
1310 return Err(Error::DkgFailed);
1311 }
1312 Ok(out)
1313}
1314
1315struct ObserveInner<V: Variant, P: PublicKey> {
1316 output: Output<V, P>,
1317 weights: Option<Interpolator<P, Scalar>>,
1318}
1319
1320impl<V: Variant, P: PublicKey> ObserveInner<V, P> {
1321 fn reckon<M: Faults>(
1322 info: Info<V, P>,
1323 selected: Map<P, DealerLog<V, P>>,
1324 strategy: &impl Strategy,
1325 ) -> Result<Self, Error> {
1326 let max_faults = info.players.max_faults::<M>();
1328 let mut reveal_counts: BTreeMap<P, u32> = BTreeMap::new();
1329 let mut revealed = Vec::new();
1330 for log in selected.values() {
1331 let Some(iter) = log.zip_players(&info.players) else {
1332 continue;
1333 };
1334 for (player, result) in iter {
1335 if !result.is_reveal() {
1336 continue;
1337 }
1338 let count = reveal_counts.entry(player.clone()).or_insert(0);
1339 *count += 1;
1340 if *count == max_faults + 1 {
1341 revealed.push(player.clone());
1342 }
1343 }
1344 }
1345 let revealed: Set<P> = revealed
1346 .into_iter()
1347 .try_collect()
1348 .expect("players are unique");
1349
1350 let dealers: Set<P> = selected
1352 .keys()
1353 .iter()
1354 .cloned()
1355 .try_collect()
1356 .expect("selected dealers are unique");
1357
1358 let (public, weights) = if let Some(previous) = info.previous.as_ref() {
1360 let weights = previous
1361 .public()
1362 .mode()
1363 .subset_interpolator(previous.players(), selected.keys())
1364 .expect("the result of select should produce a valid subset");
1365 let commitments = selected
1366 .into_iter()
1367 .map(|(dealer, log)| (dealer, log.pub_msg.commitment))
1368 .try_collect::<Map<_, _>>()
1369 .expect("Map should have unique keys");
1370 let public = weights
1371 .interpolate(&commitments, strategy)
1372 .expect("select checks that enough points have been provided");
1373 if previous.public().public() != public.constant() {
1374 return Err(Error::DkgFailed);
1375 }
1376 (public, Some(weights))
1377 } else {
1378 let mut public = Poly::zero();
1379 for log in selected.values() {
1380 public += &log.pub_msg.commitment;
1381 }
1382 (public, None)
1383 };
1384 let n = info.players.len() as u32;
1385 let output = Output {
1386 summary: info.summary,
1387 public: Sharing::new(info.mode, NZU32!(n), public),
1388 dealers,
1389 players: info.players,
1390 revealed,
1391 };
1392 Ok(Self { output, weights })
1393 }
1394}
1395
1396pub fn observe<V: Variant, P: PublicKey, M: Faults>(
1405 info: Info<V, P>,
1406 logs: BTreeMap<P, DealerLog<V, P>>,
1407 strategy: &impl Strategy,
1408) -> Result<Output<V, P>, Error> {
1409 let selected = select::<V, P, M>(&info, logs)?;
1410 ObserveInner::<V, P>::reckon::<M>(info, selected, strategy).map(|x| x.output)
1411}
1412
1413pub struct Player<V: Variant, S: Signer> {
1419 me: S,
1420 me_pub: S::PublicKey,
1421 info: Info<V, S::PublicKey>,
1422 index: Participant,
1423 transcript: Transcript,
1424 view: BTreeMap<S::PublicKey, (DealerPubMsg<V>, DealerPrivMsg)>,
1425}
1426
1427impl<V: Variant, S: Signer> Player<V, S> {
1428 pub fn new(info: Info<V, S::PublicKey>, me: S) -> Result<Self, Error> {
1432 let me_pub = me.public_key();
1433 Ok(Self {
1434 index: info.player_index(&me_pub)?,
1435 me,
1436 me_pub,
1437 transcript: transcript_for_round(&info),
1438 info,
1439 view: BTreeMap::new(),
1440 })
1441 }
1442
1443 pub fn dealer_message<M: Faults>(
1450 &mut self,
1451 dealer: S::PublicKey,
1452 pub_msg: DealerPubMsg<V>,
1453 priv_msg: DealerPrivMsg,
1454 ) -> Option<PlayerAck<S::PublicKey>> {
1455 if self.view.contains_key(&dealer) {
1456 return None;
1457 }
1458 self.info.dealer_index(&dealer).ok()?;
1459 if !self.info.check_dealer_pub_msg::<M>(&dealer, &pub_msg) {
1460 return None;
1461 }
1462 if !self
1463 .info
1464 .check_dealer_priv_msg(&self.me_pub, &pub_msg, &priv_msg)
1465 {
1466 return None;
1467 }
1468 let sig = transcript_for_ack(&self.transcript, &dealer, &pub_msg).sign(&self.me);
1469 self.view.insert(dealer, (pub_msg, priv_msg));
1470 Some(PlayerAck { sig })
1471 }
1472
1473 pub fn finalize<M: Faults>(
1482 self,
1483 logs: BTreeMap<S::PublicKey, DealerLog<V, S::PublicKey>>,
1484 strategy: &impl Strategy,
1485 ) -> Result<(Output<V, S::PublicKey>, Share), Error> {
1486 let selected = select::<V, S::PublicKey, M>(&self.info, logs)?;
1487 let dealings = selected
1493 .iter_pairs()
1494 .map(|(dealer, log)| {
1495 let share = self
1496 .view
1497 .get(dealer)
1498 .map(|(_, priv_msg)| priv_msg.share.clone().expose_unwrap())
1499 .unwrap_or_else(|| {
1500 log.get_reveal(&self.me_pub).map_or_else(
1501 || {
1502 unreachable!(
1503 "select didn't check dealer reveal, or we're not a player?"
1504 )
1505 },
1506 |priv_msg| priv_msg.share.clone().expose_unwrap(),
1507 )
1508 });
1509 (dealer.clone(), share)
1510 })
1511 .try_collect::<Map<_, _>>()
1512 .expect("select produces at most one entry per dealer");
1513 let ObserveInner { output, weights } =
1514 ObserveInner::<V, S::PublicKey>::reckon::<M>(self.info, selected, strategy)?;
1515 let private = weights.map_or_else(
1516 || {
1517 let mut out = <Scalar as Additive>::zero();
1518 for s in dealings.values() {
1519 out += s;
1520 }
1521 out
1522 },
1523 |weights| {
1524 weights
1525 .interpolate(&dealings, strategy)
1526 .expect("select ensures that we can recover")
1527 },
1528 );
1529 let share = Share::new(self.index, Private::new(private));
1530 Ok((output, share))
1531 }
1532}
1533
1534pub type DealResult<V, P> = Result<(Output<V, P>, Map<P, Share>), Error>;
1536
1537pub fn deal<V: Variant, P: Clone + Ord, M: Faults>(
1539 mut rng: impl CryptoRngCore,
1540 mode: Mode,
1541 players: Set<P>,
1542) -> DealResult<V, P> {
1543 if players.is_empty() {
1544 return Err(Error::NumPlayers(0));
1545 }
1546 let n = NZU32!(players.len() as u32);
1547 let t = players.quorum::<M>();
1548 let private = Poly::new(&mut rng, t - 1);
1549 let shares: Map<_, _> = players
1550 .iter()
1551 .enumerate()
1552 .map(|(i, p)| {
1553 let participant = Participant::from_usize(i);
1554 let eval = private.eval_msm(
1555 &mode
1556 .scalar(n, participant)
1557 .expect("player index should be valid"),
1558 &Sequential,
1559 );
1560 let share = Share::new(participant, Private::new(eval));
1561 (p.clone(), share)
1562 })
1563 .try_collect()
1564 .expect("players are unique");
1565 let output = Output {
1566 summary: Summary::random(&mut rng),
1567 public: Sharing::new(mode, n, Poly::commit(private)),
1568 dealers: players.clone(),
1569 players,
1570 revealed: Set::default(),
1571 };
1572 Ok((output, shares))
1573}
1574
1575pub fn deal_anonymous<V: Variant, M: Faults>(
1581 rng: impl CryptoRngCore,
1582 mode: Mode,
1583 n: NonZeroU32,
1584) -> (Sharing<V>, Vec<Share>) {
1585 let players = (0..n.get()).try_collect().unwrap();
1586 let (output, shares) = deal::<V, _, M>(rng, mode, players).unwrap();
1587 (output.public().clone(), shares.values().to_vec())
1588}
1589
1590#[cfg(any(feature = "arbitrary", test))]
1591mod test_plan {
1592 use super::*;
1593 use crate::{
1594 bls12381::primitives::{
1595 ops::{self, threshold},
1596 variant::Variant,
1597 },
1598 ed25519, PublicKey,
1599 };
1600 use anyhow::anyhow;
1601 use bytes::BytesMut;
1602 use commonware_utils::{Faults, N3f1, TryCollect};
1603 use core::num::NonZeroI32;
1604 use rand::{rngs::StdRng, SeedableRng as _};
1605 use std::collections::BTreeSet;
1606
1607 fn apply_mask(bytes: &mut BytesMut, mask: &[u8]) -> bool {
1609 let mut modified = false;
1610 for (l, &r) in bytes.iter_mut().zip(mask.iter()) {
1611 modified |= r != 0;
1612 *l ^= r;
1613 }
1614 modified
1615 }
1616
1617 #[derive(Clone, Default, Debug)]
1618 pub struct Masks {
1619 pub info_summary: Vec<u8>,
1620 pub dealer: Vec<u8>,
1621 pub pub_msg: Vec<u8>,
1622 pub log: Vec<u8>,
1623 }
1624
1625 impl Masks {
1626 fn transcript_for_round<V: Variant, P: PublicKey>(
1627 &self,
1628 info: &Info<V, P>,
1629 ) -> anyhow::Result<(bool, Transcript)> {
1630 let mut summary_bs = info.summary.encode_mut();
1631 let modified = apply_mask(&mut summary_bs, &self.info_summary);
1632 let summary = Summary::read(&mut summary_bs)?;
1633 Ok((modified, Transcript::resume(summary)))
1634 }
1635
1636 fn transcript_for_player_ack<V: Variant, P: PublicKey>(
1637 &self,
1638 info: &Info<V, P>,
1639 dealer: &P,
1640 pub_msg: &DealerPubMsg<V>,
1641 ) -> anyhow::Result<(bool, Transcript)> {
1642 let (mut modified, transcript) = self.transcript_for_round(info)?;
1643 let mut transcript = transcript.fork(SIG_ACK);
1644
1645 let mut dealer_bs = dealer.encode_mut();
1646 modified |= apply_mask(&mut dealer_bs, &self.dealer);
1647 transcript.commit(&mut dealer_bs);
1648
1649 let mut pub_msg_bs = pub_msg.encode_mut();
1650 modified |= apply_mask(&mut pub_msg_bs, &self.pub_msg);
1651 transcript.commit(&mut pub_msg_bs);
1652
1653 Ok((modified, transcript))
1654 }
1655
1656 fn transcript_for_signed_dealer_log<V: Variant, P: PublicKey>(
1657 &self,
1658 info: &Info<V, P>,
1659 log: &DealerLog<V, P>,
1660 ) -> anyhow::Result<(bool, Transcript)> {
1661 let (mut modified, transcript) = self.transcript_for_round(info)?;
1662 let mut transcript = transcript.fork(SIG_LOG);
1663
1664 let mut log_bs = log.encode_mut();
1665 modified |= apply_mask(&mut log_bs, &self.log);
1666 transcript.commit(&mut log_bs);
1667
1668 Ok((modified, transcript))
1669 }
1670 }
1671
1672 #[derive(Debug, Default)]
1674 pub struct Round {
1675 dealers: Vec<u32>,
1676 players: Vec<u32>,
1677 no_acks: BTreeSet<(u32, u32)>,
1678 bad_shares: BTreeSet<(u32, u32)>,
1679 bad_player_sigs: BTreeMap<(u32, u32), Masks>,
1680 bad_reveals: BTreeSet<(u32, u32)>,
1681 bad_dealer_sigs: BTreeMap<u32, Masks>,
1682 replace_shares: BTreeSet<u32>,
1683 shift_degrees: BTreeMap<u32, NonZeroI32>,
1684 }
1685
1686 impl Round {
1687 pub fn new(dealers: Vec<u32>, players: Vec<u32>) -> Self {
1688 Self {
1689 dealers,
1690 players,
1691 ..Default::default()
1692 }
1693 }
1694
1695 pub fn no_ack(mut self, dealer: u32, player: u32) -> Self {
1696 self.no_acks.insert((dealer, player));
1697 self
1698 }
1699
1700 pub fn bad_share(mut self, dealer: u32, player: u32) -> Self {
1701 self.bad_shares.insert((dealer, player));
1702 self
1703 }
1704
1705 pub fn bad_player_sig(mut self, dealer: u32, player: u32, masks: Masks) -> Self {
1706 self.bad_player_sigs.insert((dealer, player), masks);
1707 self
1708 }
1709
1710 pub fn bad_reveal(mut self, dealer: u32, player: u32) -> Self {
1711 self.bad_reveals.insert((dealer, player));
1712 self
1713 }
1714
1715 pub fn bad_dealer_sig(mut self, dealer: u32, masks: Masks) -> Self {
1716 self.bad_dealer_sigs.insert(dealer, masks);
1717 self
1718 }
1719
1720 pub fn replace_share(mut self, dealer: u32) -> Self {
1721 self.replace_shares.insert(dealer);
1722 self
1723 }
1724
1725 pub fn shift_degree(mut self, dealer: u32, shift: NonZeroI32) -> Self {
1726 self.shift_degrees.insert(dealer, shift);
1727 self
1728 }
1729
1730 pub fn validate(
1733 &self,
1734 num_participants: u32,
1735 previous_players: Option<&[u32]>,
1736 ) -> anyhow::Result<()> {
1737 if self.dealers.is_empty() {
1738 return Err(anyhow!("dealers is empty"));
1739 }
1740 if self.players.is_empty() {
1741 return Err(anyhow!("players is empty"));
1742 }
1743 for &d in &self.dealers {
1745 if d >= num_participants {
1746 return Err(anyhow!("dealer {d} out of range [1, {num_participants}]"));
1747 }
1748 }
1749 for &p in &self.players {
1750 if p >= num_participants {
1751 return Err(anyhow!("player {p} out of range [1, {num_participants}]"));
1752 }
1753 }
1754
1755 if let Some(prev_players) = previous_players {
1757 for &d in &self.dealers {
1759 if !prev_players.contains(&d) {
1760 return Err(anyhow!("dealer {d} was not a player in previous round"));
1761 }
1762 }
1763 let required = N3f1::quorum(prev_players.len());
1765 if (self.dealers.len() as u32) < required {
1766 return Err(anyhow!(
1767 "not enough dealers: have {}, need {} (quorum of {} previous players)",
1768 self.dealers.len(),
1769 required,
1770 prev_players.len()
1771 ));
1772 }
1773 }
1774
1775 Ok(())
1776 }
1777
1778 fn bad(&self, previous_successful_round: bool, dealer: u32) -> bool {
1779 if self.replace_shares.contains(&dealer) && previous_successful_round {
1780 return true;
1781 }
1782 if let Some(shift) = self.shift_degrees.get(&dealer) {
1783 let degree = N3f1::quorum(self.players.len()) as i32 - 1;
1784 if (degree + shift.get()).max(0) != degree {
1790 return true;
1791 }
1792 }
1793 if self.bad_reveals.iter().any(|&(d, _)| d == dealer) {
1794 return true;
1795 }
1796 let revealed_players = self
1797 .bad_shares
1798 .iter()
1799 .copied()
1800 .chain(self.no_acks.iter().copied())
1801 .filter_map(|(d, p)| if d == dealer { Some(p) } else { None })
1802 .collect::<BTreeSet<_>>();
1803 revealed_players.len() as u32 > N3f1::max_faults(self.players.len())
1804 }
1805
1806 fn expect_failure(&self, previous_successful_round: Option<u32>) -> bool {
1808 let good_dealer_count = self
1809 .dealers
1810 .iter()
1811 .filter(|&&d| !self.bad(previous_successful_round.is_some(), d))
1812 .count();
1813 let required = previous_successful_round
1814 .map(N3f1::quorum)
1815 .unwrap_or_default()
1816 .max(N3f1::quorum(self.dealers.len())) as usize;
1817 good_dealer_count < required
1818 }
1819 }
1820
1821 #[derive(Debug)]
1823 pub struct Plan {
1824 num_participants: NonZeroU32,
1825 rounds: Vec<Round>,
1826 }
1827
1828 impl Plan {
1829 pub const fn new(num_participants: NonZeroU32) -> Self {
1830 Self {
1831 num_participants,
1832 rounds: Vec::new(),
1833 }
1834 }
1835
1836 pub fn with(mut self, round: Round) -> Self {
1837 self.rounds.push(round);
1838 self
1839 }
1840
1841 fn validate(&self) -> anyhow::Result<()> {
1843 let mut last_successful_players: Option<Vec<u32>> = None;
1844
1845 for round in &self.rounds {
1846 round.validate(
1847 self.num_participants.get(),
1848 last_successful_players.as_deref(),
1849 )?;
1850
1851 if !round.expect_failure(last_successful_players.as_ref().map(|x| x.len() as u32)) {
1853 last_successful_players = Some(round.players.clone());
1854 }
1855 }
1856 Ok(())
1857 }
1858
1859 pub fn run<V: Variant>(self, seed: u64) -> anyhow::Result<()> {
1861 self.validate()?;
1862
1863 let mut rng = StdRng::seed_from_u64(seed);
1864
1865 let keys = (0..self.num_participants.get())
1867 .map(|_| ed25519::PrivateKey::random(&mut rng))
1868 .collect::<Vec<_>>();
1869
1870 let pk_to_key_idx: BTreeMap<ed25519::PublicKey, u32> = keys
1873 .iter()
1874 .enumerate()
1875 .map(|(i, k)| (k.public_key(), i as u32))
1876 .collect();
1877
1878 let max_shift = self
1881 .rounds
1882 .iter()
1883 .flat_map(|r| r.shift_degrees.values())
1884 .map(|s| s.get())
1885 .max()
1886 .unwrap_or(0)
1887 .max(0) as u32;
1888 let max_read_size =
1889 NonZeroU32::new(self.num_participants.get() + max_shift).expect("non-zero");
1890
1891 let mut previous_output: Option<Output<V, ed25519::PublicKey>> = None;
1892 let mut shares: BTreeMap<ed25519::PublicKey, Share> = BTreeMap::new();
1893 let mut threshold_public_key: Option<V::Public> = None;
1894
1895 for (i_round, round) in self.rounds.into_iter().enumerate() {
1896 let previous_successful_round =
1897 previous_output.as_ref().map(|o| o.players.len() as u32);
1898
1899 let dealer_set = round
1900 .dealers
1901 .iter()
1902 .map(|&i| keys[i as usize].public_key())
1903 .try_collect::<Set<_>>()
1904 .unwrap();
1905 let player_set: Set<ed25519::PublicKey> = round
1906 .players
1907 .iter()
1908 .map(|&i| keys[i as usize].public_key())
1909 .try_collect()
1910 .unwrap();
1911
1912 let info = Info::new::<N3f1>(
1914 b"_COMMONWARE_CRYPTOGRAPHY_BLS12381_DKG_TEST",
1915 i_round as u64,
1916 previous_output.clone(),
1917 Default::default(),
1918 dealer_set.clone(),
1919 player_set.clone(),
1920 )?;
1921
1922 let mut players: Map<_, _> = round
1923 .players
1924 .iter()
1925 .map(|&i| {
1926 let sk = keys[i as usize].clone();
1927 let pk = sk.public_key();
1928 let player = Player::new(info.clone(), sk)?;
1929 Ok((pk, player))
1930 })
1931 .collect::<anyhow::Result<Vec<_>>>()?
1932 .try_into()
1933 .unwrap();
1934
1935 let mut dealer_logs = BTreeMap::new();
1937 for &i_dealer in &round.dealers {
1938 let sk = keys[i_dealer as usize].clone();
1939 let pk = sk.public_key();
1940 let share = match (shares.get(&pk), round.replace_shares.contains(&i_dealer)) {
1941 (None, _) => None,
1942 (Some(s), false) => Some(s.clone()),
1943 (Some(_), true) => Some(Share::new(
1944 Participant::new(i_dealer),
1945 Private::random(&mut rng),
1946 )),
1947 };
1948
1949 let (mut dealer, pub_msg, mut priv_msgs) =
1951 if let Some(shift) = round.shift_degrees.get(&i_dealer) {
1952 let degree = u32::try_from(info.degree::<N3f1>() as i32 + shift.get())
1954 .unwrap_or_default();
1955
1956 let share = info
1958 .unwrap_or_random_share(
1959 &mut rng,
1960 share.map(|s| s.private.expose_unwrap()),
1961 )
1962 .expect("Failed to generate dealer share");
1963
1964 let my_poly = Poly::new_with_constant(&mut rng, degree, share);
1965 let priv_msgs = info
1966 .players
1967 .iter()
1968 .map(|pk| {
1969 (
1970 pk.clone(),
1971 DealerPrivMsg::new(my_poly.eval_msm(
1972 &info.player_scalar(pk).expect("player should exist"),
1973 &Sequential,
1974 )),
1975 )
1976 })
1977 .collect::<Vec<_>>();
1978 let results: Map<_, _> = priv_msgs
1979 .iter()
1980 .map(|(pk, pm)| (pk.clone(), AckOrReveal::Reveal(pm.clone())))
1981 .try_collect()
1982 .unwrap();
1983 let commitment = Poly::commit(my_poly);
1984 let pub_msg = DealerPubMsg { commitment };
1985 let transcript = {
1986 let t = transcript_for_round(&info);
1987 transcript_for_ack(&t, &pk, &pub_msg)
1988 };
1989 let dealer = Dealer {
1990 me: sk.clone(),
1991 info: info.clone(),
1992 pub_msg: pub_msg.clone(),
1993 results,
1994 transcript,
1995 };
1996 (dealer, pub_msg, priv_msgs)
1997 } else {
1998 Dealer::start::<N3f1>(&mut rng, info.clone(), sk.clone(), share)?
1999 };
2000
2001 for (player, priv_msg) in &mut priv_msgs {
2003 let player_key_idx = pk_to_key_idx[player];
2004 if round.bad_shares.contains(&(i_dealer, player_key_idx)) {
2005 *priv_msg = DealerPrivMsg::new(Scalar::random(&mut rng));
2006 }
2007 }
2008 assert_eq!(priv_msgs.len(), players.len());
2009
2010 let mut num_reveals = players.len() as u32;
2012 for (player_pk, priv_msg) in priv_msgs {
2013 assert_eq!(priv_msg, ReadExt::read(&mut priv_msg.encode())?);
2015
2016 let i_player = players
2017 .index(&player_pk)
2018 .ok_or_else(|| anyhow!("unknown player: {:?}", &player_pk))?;
2019 let player_key_idx = pk_to_key_idx[&player_pk];
2020 let player = &mut players.values_mut()[usize::from(i_player)];
2021
2022 let ack =
2023 player.dealer_message::<N3f1>(pk.clone(), pub_msg.clone(), priv_msg);
2024 assert_eq!(ack, ReadExt::read(&mut ack.encode())?);
2025 if let Some(ack) = ack {
2026 let masks = round
2027 .bad_player_sigs
2028 .get(&(i_dealer, player_key_idx))
2029 .cloned()
2030 .unwrap_or_default();
2031 let (modified, transcript) =
2032 masks.transcript_for_player_ack(&info, &pk, &pub_msg)?;
2033 assert_eq!(transcript.verify(&player_pk, &ack.sig), !modified);
2034
2035 if !round.no_acks.contains(&(i_dealer, player_key_idx)) {
2037 dealer.receive_player_ack(player_pk, ack)?;
2038 num_reveals -= 1;
2039 }
2040 } else {
2041 assert!(
2042 round.bad_shares.contains(&(i_dealer, player_key_idx))
2043 || round.bad(previous_successful_round.is_some(), i_dealer)
2044 );
2045 }
2046 }
2047
2048 let signed_log = dealer.finalize::<N3f1>();
2050 assert_eq!(
2051 signed_log,
2052 Read::read_cfg(&mut signed_log.encode(), &max_read_size)?
2053 );
2054
2055 let masks = round
2057 .bad_dealer_sigs
2058 .get(&i_dealer)
2059 .cloned()
2060 .unwrap_or_default();
2061 let (modified, transcript) =
2062 masks.transcript_for_signed_dealer_log(&info, &signed_log.log)?;
2063 assert_eq!(transcript.verify(&pk, &signed_log.sig), !modified);
2064 let (found_pk, mut log) = signed_log
2065 .check(&info)
2066 .ok_or_else(|| anyhow!("signed log should verify"))?;
2067 assert_eq!(pk, found_pk);
2068 match &mut log.results {
2070 DealerResult::TooManyReveals => {
2071 assert!(num_reveals > info.max_reveals::<N3f1>());
2072 }
2073 DealerResult::Ok(results) => {
2074 assert_eq!(results.len(), players.len());
2075 for &i_player in &round.players {
2076 if !round.bad_reveals.contains(&(i_dealer, i_player)) {
2077 continue;
2078 }
2079 let player_pk = keys[i_player as usize].public_key();
2080 *results
2081 .get_value_mut(&player_pk)
2082 .ok_or_else(|| anyhow!("unknown player: {:?}", &player_pk))? =
2083 AckOrReveal::Reveal(DealerPrivMsg::new(Scalar::random(
2084 &mut rng,
2085 )));
2086 }
2087 }
2088 }
2089 dealer_logs.insert(pk, log);
2090 }
2091
2092 let selection = select::<_, _, N3f1>(&info, dealer_logs.clone());
2094 if let Ok(ref selection) = selection {
2095 let good_pks = selection
2096 .iter_pairs()
2097 .map(|(pk, _)| pk.clone())
2098 .collect::<BTreeSet<_>>();
2099 for &i_dealer in &round.dealers {
2100 if round.bad(previous_successful_round.is_some(), i_dealer) {
2101 assert!(!good_pks.contains(&keys[i_dealer as usize].public_key()));
2102 }
2103 }
2104 }
2105 let observe_result =
2107 observe::<_, _, N3f1>(info.clone(), dealer_logs.clone(), &Sequential);
2108 if round.expect_failure(previous_successful_round) {
2109 assert!(
2110 observe_result.is_err(),
2111 "Round {i_round} should have failed but succeeded",
2112 );
2113 continue;
2114 }
2115 let observer_output = observe_result?;
2116 let selection = selection.expect("select should succeed if observe succeeded");
2117
2118 let required_commitments = info.required_commitments::<N3f1>() as usize;
2121 let expected_dealers: Set<ed25519::PublicKey> = dealer_set
2122 .iter()
2123 .filter(|pk| {
2124 let i = keys.iter().position(|k| &k.public_key() == *pk).unwrap() as u32;
2125 !round.bad(previous_successful_round.is_some(), i)
2126 })
2127 .take(required_commitments)
2128 .cloned()
2129 .try_collect()
2130 .expect("dealers are unique");
2131 let expected_dealer_indices: BTreeSet<u32> = expected_dealers
2132 .iter()
2133 .filter_map(|pk| {
2134 keys.iter()
2135 .position(|k| &k.public_key() == pk)
2136 .map(|i| i as u32)
2137 })
2138 .collect();
2139 assert_eq!(
2140 observer_output.dealers(),
2141 &expected_dealers,
2142 "Output dealers should match expected good dealers"
2143 );
2144
2145 let selected_dealers: BTreeSet<u32> = selection
2147 .keys()
2148 .iter()
2149 .filter_map(|pk| {
2150 keys.iter()
2151 .position(|k| &k.public_key() == pk)
2152 .map(|i| i as u32)
2153 })
2154 .collect();
2155 assert_eq!(
2156 selected_dealers, expected_dealer_indices,
2157 "Selection should match expected dealers"
2158 );
2159 let selected_players: Set<ed25519::PublicKey> = round
2160 .players
2161 .iter()
2162 .map(|&i| keys[i as usize].public_key())
2163 .try_collect()
2164 .expect("players are unique");
2165
2166 let mut expected_reveals: BTreeMap<ed25519::PublicKey, u32> = BTreeMap::new();
2172 for &(dealer_idx, player_key_idx) in round.no_acks.union(&round.bad_shares) {
2173 if !selected_dealers.contains(&dealer_idx) {
2174 continue;
2175 }
2176 let pk = keys[player_key_idx as usize].public_key();
2177 if selected_players.position(&pk).is_none() {
2178 continue;
2179 }
2180 *expected_reveals.entry(pk).or_insert(0) += 1;
2181 }
2182
2183 let max_faults = selected_players.max_faults::<N3f1>();
2185 for player in player_set.iter() {
2186 let expected = expected_reveals.get(player).copied().unwrap_or(0) > max_faults;
2187 let actual = observer_output.revealed().position(player).is_some();
2188 assert_eq!(expected, actual, "Unexpected outcome for player {player:?} (expected={expected}, actual={actual})");
2189 }
2190
2191 for (player_pk, player) in players.into_iter() {
2193 let (player_output, share) = player
2194 .finalize::<N3f1>(dealer_logs.clone(), &Sequential)
2195 .expect("Player finalize should succeed");
2196
2197 assert_eq!(
2198 player_output, observer_output,
2199 "Player output should match observer output"
2200 );
2201
2202 let expected_public = observer_output
2204 .public
2205 .partial_public(share.index)
2206 .expect("share index should be valid");
2207 let actual_public = share.public::<V>();
2208 assert_eq!(
2209 expected_public, actual_public,
2210 "Share should match public polynomial"
2211 );
2212
2213 shares.insert(player_pk.clone(), share);
2214 }
2215
2216 let current_public = *observer_output.public().public();
2218 match threshold_public_key {
2219 None => threshold_public_key = Some(current_public),
2220 Some(tpk) => {
2221 assert_eq!(
2222 tpk, current_public,
2223 "Public key should remain constant across reshares"
2224 );
2225 }
2226 }
2227
2228 let test_message = format!("test message round {i_round}").into_bytes();
2230 let namespace = b"test";
2231
2232 let mut partial_sigs = Vec::new();
2233 for &i_player in &round.players {
2234 let share = &shares[&keys[i_player as usize].public_key()];
2235 let partial_sig = threshold::sign_message::<V>(share, namespace, &test_message);
2236
2237 threshold::verify_message::<V>(
2238 &observer_output.public,
2239 namespace,
2240 &test_message,
2241 &partial_sig,
2242 )
2243 .expect("Partial signature verification should succeed");
2244
2245 partial_sigs.push(partial_sig);
2246 }
2247
2248 let threshold = observer_output.quorum::<N3f1>();
2249 let threshold_sig = threshold::recover::<V, _, N3f1>(
2250 &observer_output.public,
2251 &partial_sigs[0..threshold as usize],
2252 &Sequential,
2253 )
2254 .expect("Should recover threshold signature");
2255
2256 ops::verify_message::<V>(
2258 threshold_public_key.as_ref().unwrap(),
2259 namespace,
2260 &test_message,
2261 &threshold_sig,
2262 )
2263 .expect("Threshold signature verification should succeed");
2264
2265 previous_output = Some(observer_output);
2267 }
2268 Ok(())
2269 }
2270 }
2271
2272 #[cfg(feature = "arbitrary")]
2273 mod impl_arbitrary {
2274 use super::*;
2275 use arbitrary::{Arbitrary, Unstructured};
2276 use core::ops::ControlFlow;
2277
2278 const MAX_NUM_PARTICIPANTS: u32 = 20;
2279 const MAX_ROUNDS: u32 = 10;
2280
2281 fn arbitrary_masks<'a>(u: &mut Unstructured<'a>) -> arbitrary::Result<Masks> {
2282 Ok(Masks {
2283 info_summary: Arbitrary::arbitrary(u)?,
2284 dealer: Arbitrary::arbitrary(u)?,
2285 pub_msg: Arbitrary::arbitrary(u)?,
2286 log: Arbitrary::arbitrary(u)?,
2287 })
2288 }
2289
2290 fn pick<'a, T>(
2295 u: &mut Unstructured<'a>,
2296 num: usize,
2297 mut data: Vec<T>,
2298 ) -> arbitrary::Result<Vec<T>> {
2299 let len = data.len();
2300 let num = num.min(len);
2301 for start in 0..num {
2303 data.swap(start, u.int_in_range(start..=len - 1)?);
2304 }
2305 data.truncate(num);
2306 Ok(data)
2307 }
2308
2309 fn arbitrary_round<'a>(
2310 u: &mut Unstructured<'a>,
2311 num_participants: u32,
2312 last_successful_players: Option<&Set<u32>>,
2313 ) -> arbitrary::Result<Round> {
2314 let dealers = if let Some(players) = last_successful_players {
2315 let to_pick = u.int_in_range(players.quorum::<N3f1>() as usize..=players.len())?;
2316 pick(u, to_pick, players.into_iter().copied().collect())?
2317 } else {
2318 let to_pick = u.int_in_range(1..=num_participants as usize)?;
2319 pick(u, to_pick, (0..num_participants).collect())?
2320 };
2321 let players = {
2322 let to_pick = u.int_in_range(1..=num_participants as usize)?;
2323 pick(u, to_pick, (0..num_participants).collect())?
2324 };
2325 let pairs = dealers
2326 .iter()
2327 .flat_map(|d| players.iter().map(|p| (*d, *p)))
2328 .collect::<Vec<_>>();
2329 let pick_pair_set = |u: &mut Unstructured<'a>| {
2330 let num = u.int_in_range(0..=pairs.len())?;
2331 if num == 0 {
2332 return Ok(BTreeSet::new());
2333 }
2334 Ok(pick(u, num, pairs.clone())?.into_iter().collect())
2335 };
2336 let pick_dealer_set = |u: &mut Unstructured<'a>| {
2337 let num = u.int_in_range(0..=dealers.len())?;
2338 if num == 0 {
2339 return Ok(BTreeSet::new());
2340 }
2341 Ok(pick(u, num, dealers.clone())?.into_iter().collect())
2342 };
2343 let round = Round {
2344 no_acks: pick_pair_set(u)?,
2345 bad_shares: pick_pair_set(u)?,
2346 bad_player_sigs: {
2347 let indices = pick_pair_set(u)?;
2348 indices
2349 .into_iter()
2350 .map(|k| Ok((k, arbitrary_masks(u)?)))
2351 .collect::<arbitrary::Result<_>>()?
2352 },
2353 bad_reveals: pick_pair_set(u)?,
2354 bad_dealer_sigs: {
2355 let indices = pick_dealer_set(u)?;
2356 indices
2357 .into_iter()
2358 .map(|k| Ok((k, arbitrary_masks(u)?)))
2359 .collect::<arbitrary::Result<_>>()?
2360 },
2361 replace_shares: pick_dealer_set(u)?,
2362 shift_degrees: {
2363 let indices = pick_dealer_set(u)?;
2364 indices
2365 .into_iter()
2366 .map(|k| {
2367 let expected = N3f1::quorum(players.len()) as i32 - 1;
2368 let shift = u.int_in_range(1..=expected.max(1))?;
2369 let shift = if bool::arbitrary(u)? { -shift } else { shift };
2370 Ok((k, NonZeroI32::new(shift).expect("checked to not be zero")))
2371 })
2372 .collect::<arbitrary::Result<_>>()?
2373 },
2374 dealers,
2375 players,
2376 };
2377 Ok(round)
2378 }
2379
2380 impl<'a> Arbitrary<'a> for Plan {
2381 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
2382 let num_participants = u.int_in_range(1..=MAX_NUM_PARTICIPANTS)?;
2383 let mut rounds = Vec::new();
2384 let mut last_successful_players: Option<Set<u32>> = None;
2385 u.arbitrary_loop(None, Some(MAX_ROUNDS), |u| {
2386 let round =
2387 arbitrary_round(u, num_participants, last_successful_players.as_ref())?;
2388 if !round
2389 .expect_failure(last_successful_players.as_ref().map(|x| x.len() as u32))
2390 {
2391 last_successful_players =
2392 Some(round.players.iter().copied().try_collect().unwrap());
2393 }
2394 rounds.push(round);
2395 Ok(ControlFlow::Continue(()))
2396 })?;
2397 let plan = Self {
2398 num_participants: NZU32!(num_participants),
2399 rounds,
2400 };
2401 plan.validate()
2402 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
2403 Ok(plan)
2404 }
2405 }
2406 }
2407}
2408
2409#[cfg(feature = "arbitrary")]
2410pub use test_plan::Plan as FuzzPlan;
2411
2412#[cfg(test)]
2413mod test {
2414 use super::{test_plan::*, *};
2415 use crate::{bls12381::primitives::variant::MinPk, ed25519};
2416 use anyhow::anyhow;
2417 use commonware_math::algebra::Random;
2418 use commonware_utils::{test_rng, N3f1};
2419 use core::num::NonZeroI32;
2420
2421 #[test]
2422 fn single_round() -> anyhow::Result<()> {
2423 Plan::new(NZU32!(4))
2424 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2425 .run::<MinPk>(0)
2426 }
2427
2428 #[test]
2429 fn multiple_rounds() -> anyhow::Result<()> {
2430 Plan::new(NZU32!(4))
2431 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2432 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2433 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2434 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2435 .run::<MinPk>(0)
2436 }
2437
2438 #[test]
2439 fn changing_committee() -> anyhow::Result<()> {
2440 Plan::new(NonZeroU32::new(5).unwrap())
2441 .with(Round::new(vec![0, 1, 2], vec![1, 2, 3]))
2442 .with(Round::new(vec![1, 2, 3], vec![2, 3, 4]))
2443 .with(Round::new(vec![2, 3, 4], vec![3, 4, 0]))
2444 .with(Round::new(vec![3, 4, 0], vec![4, 0, 1]))
2445 .run::<MinPk>(0)
2446 }
2447
2448 #[test]
2449 fn missing_ack() -> anyhow::Result<()> {
2450 Plan::new(NonZeroU32::new(4).unwrap())
2452 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 0))
2453 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 1))
2454 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 2))
2455 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 3))
2456 .run::<MinPk>(0)
2457 }
2458
2459 #[test]
2460 fn increasing_decreasing_committee() -> anyhow::Result<()> {
2461 Plan::new(NonZeroU32::new(5).unwrap())
2462 .with(Round::new(vec![0, 1], vec![0, 1, 2]))
2463 .with(Round::new(vec![0, 1, 2], vec![0, 1, 2, 3]))
2464 .with(Round::new(vec![0, 1, 2], vec![0, 1]))
2465 .with(Round::new(vec![0, 1], vec![0, 1, 2, 3, 4]))
2466 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1]))
2467 .run::<MinPk>(0)
2468 }
2469
2470 #[test]
2471 fn bad_reveal_fails() -> anyhow::Result<()> {
2472 Plan::new(NonZeroU32::new(4).unwrap())
2473 .with(Round::new(vec![0], vec![0, 1, 2, 3]).bad_reveal(0, 1))
2474 .run::<MinPk>(0)
2475 }
2476
2477 #[test]
2478 fn bad_share() -> anyhow::Result<()> {
2479 Plan::new(NonZeroU32::new(4).unwrap())
2480 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).bad_share(0, 1))
2481 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).bad_share(0, 2))
2482 .run::<MinPk>(0)
2483 }
2484
2485 #[test]
2486 fn shift_degree_fails() -> anyhow::Result<()> {
2487 Plan::new(NonZeroU32::new(4).unwrap())
2488 .with(Round::new(vec![0], vec![0, 1, 2, 3]).shift_degree(
2489 0,
2490 NonZeroI32::new(1).ok_or_else(|| anyhow!("invalid NZI32"))?,
2491 ))
2492 .run::<MinPk>(0)
2493 }
2494
2495 #[test]
2496 fn replace_share_fails() -> anyhow::Result<()> {
2497 Plan::new(NonZeroU32::new(4).unwrap())
2498 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2499 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).replace_share(0))
2500 .run::<MinPk>(0)
2501 }
2502
2503 #[test]
2504 fn too_many_reveals_dealer() -> anyhow::Result<()> {
2505 Plan::new(NonZeroU32::new(4).unwrap())
2506 .with(
2507 Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3])
2508 .no_ack(0, 0)
2509 .no_ack(0, 1),
2510 )
2511 .run::<MinPk>(0)
2512 }
2513
2514 #[test]
2515 fn too_many_reveals_player() -> anyhow::Result<()> {
2516 Plan::new(NonZeroU32::new(4).unwrap())
2517 .with(
2518 Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3])
2519 .no_ack(0, 0)
2520 .no_ack(1, 0)
2521 .no_ack(3, 0),
2522 )
2523 .run::<MinPk>(0)
2524 }
2525
2526 #[test]
2527 fn bad_sigs() -> anyhow::Result<()> {
2528 Plan::new(NonZeroU32::new(4).unwrap())
2529 .with(
2530 Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3])
2531 .bad_dealer_sig(
2532 0,
2533 Masks {
2534 log: vec![0xFF; 8],
2535 ..Default::default()
2536 },
2537 )
2538 .bad_player_sig(
2539 0,
2540 1,
2541 Masks {
2542 pub_msg: vec![0xFF; 8],
2543 ..Default::default()
2544 },
2545 ),
2546 )
2547 .run::<MinPk>(0)
2548 }
2549
2550 #[test]
2551 fn issue_2745_regression() -> anyhow::Result<()> {
2552 Plan::new(NonZeroU32::new(6).unwrap())
2553 .with(
2554 Round::new(vec![0], vec![5, 1, 3, 0, 4])
2555 .no_ack(0, 5)
2556 .bad_share(0, 5),
2557 )
2558 .with(Round::new(vec![0, 1, 3, 4], vec![0]))
2559 .with(Round::new(vec![0], vec![0]))
2560 .run::<MinPk>(0)
2561 }
2562
2563 #[test]
2564 fn signed_dealer_log_commitment() -> Result<(), Error> {
2565 let sk = ed25519::PrivateKey::from_seed(0);
2566 let pk = sk.public_key();
2567 let info = Info::<MinPk, _>::new::<N3f1>(
2568 b"_COMMONWARE_CRYPTOGRAPHY_BLS12381_DKG_TEST",
2569 0,
2570 None,
2571 Default::default(),
2572 vec![sk.public_key()].try_into().unwrap(),
2573 vec![sk.public_key()].try_into().unwrap(),
2574 )?;
2575 let mut log0 = {
2576 let (dealer, _, _) =
2577 Dealer::start::<N3f1>(&mut test_rng(), info.clone(), sk.clone(), None)?;
2578 dealer.finalize::<N3f1>()
2579 };
2580 let mut log1 = {
2581 let (mut dealer, pub_msg, priv_msgs) =
2582 Dealer::start::<N3f1>(&mut test_rng(), info.clone(), sk.clone(), None)?;
2583 let mut player = Player::new(info.clone(), sk)?;
2584 let ack = player
2585 .dealer_message::<N3f1>(pk.clone(), pub_msg, priv_msgs[0].1.clone())
2586 .unwrap();
2587 dealer.receive_player_ack(pk, ack)?;
2588 dealer.finalize::<N3f1>()
2589 };
2590 std::mem::swap(&mut log0.log, &mut log1.log);
2591 assert!(log0.check(&info).is_none());
2592 assert!(log1.check(&info).is_none());
2593
2594 Ok(())
2595 }
2596
2597 #[test]
2598 fn test_dealer_priv_msg_redacted() {
2599 let mut rng = test_rng();
2600 let msg = DealerPrivMsg::new(Scalar::random(&mut rng));
2601 let debug = format!("{:?}", msg);
2602 assert!(debug.contains("REDACTED"));
2603 }
2604
2605 #[cfg(feature = "arbitrary")]
2606 mod conformance {
2607 use super::*;
2608 use commonware_codec::conformance::CodecConformance;
2609
2610 commonware_conformance::conformance_tests! {
2611 CodecConformance<Output<MinPk, ed25519::PublicKey>>,
2612 CodecConformance<DealerPubMsg<MinPk>>,
2613 CodecConformance<DealerPrivMsg>,
2614 CodecConformance<PlayerAck<ed25519::PublicKey>>,
2615 CodecConformance<AckOrReveal<ed25519::PublicKey>>,
2616 CodecConformance<DealerResult<ed25519::PublicKey>>,
2617 CodecConformance<DealerLog<MinPk, ed25519::PublicKey>>,
2618 CodecConformance<SignedDealerLog<MinPk, ed25519::PrivateKey>>,
2619 }
2620 }
2621}