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 pub fn summary(&self) -> DealerLogSummary<P> {
1015 match &self.results {
1016 DealerResult::TooManyReveals => DealerLogSummary::TooManyReveals,
1017 DealerResult::Ok(map) => {
1018 let (reveals, acks): (Vec<_>, Vec<_>) =
1019 map.iter_pairs().partition(|(_, a_r)| a_r.is_reveal());
1020 DealerLogSummary::Ok {
1021 acks: acks
1022 .into_iter()
1023 .map(|(p, _)| p.clone())
1024 .try_collect()
1025 .expect("map keys are deduped"),
1026 reveals: reveals
1027 .into_iter()
1028 .map(|(p, _)| p.clone())
1029 .try_collect()
1030 .expect("map keys are deduped"),
1031 }
1032 }
1033 }
1034 }
1035}
1036
1037#[derive(Clone, Debug)]
1041pub enum DealerLogSummary<P> {
1042 TooManyReveals,
1045 Ok { acks: Set<P>, reveals: Set<P> },
1047}
1048
1049#[cfg(feature = "arbitrary")]
1050impl<V: Variant, P: PublicKey> arbitrary::Arbitrary<'_> for DealerLog<V, P>
1051where
1052 P: for<'a> arbitrary::Arbitrary<'a>,
1053 V::Public: for<'a> arbitrary::Arbitrary<'a>,
1054 P::Signature: for<'a> arbitrary::Arbitrary<'a>,
1055{
1056 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1057 let pub_msg = u.arbitrary()?;
1058 let results = u.arbitrary()?;
1059 Ok(Self { pub_msg, results })
1060 }
1061}
1062
1063#[derive(Clone, Debug)]
1071pub struct SignedDealerLog<V: Variant, S: Signer> {
1072 dealer: S::PublicKey,
1073 log: DealerLog<V, S::PublicKey>,
1074 sig: S::Signature,
1075}
1076
1077impl<V: Variant, S: Signer> PartialEq for SignedDealerLog<V, S> {
1078 fn eq(&self, other: &Self) -> bool {
1079 self.dealer == other.dealer && self.log == other.log && self.sig == other.sig
1080 }
1081}
1082
1083impl<V: Variant, S: Signer> SignedDealerLog<V, S> {
1084 fn sign(sk: &S, info: &Info<V, S::PublicKey>, log: DealerLog<V, S::PublicKey>) -> Self {
1085 let sig = transcript_for_log(info, &log).sign(sk);
1086 Self {
1087 dealer: sk.public_key(),
1088 log,
1089 sig,
1090 }
1091 }
1092
1093 #[allow(clippy::type_complexity)]
1100 pub fn check(
1101 self,
1102 info: &Info<V, S::PublicKey>,
1103 ) -> Option<(S::PublicKey, DealerLog<V, S::PublicKey>)> {
1104 if !transcript_for_log(info, &self.log).verify(&self.dealer, &self.sig) {
1105 return None;
1106 }
1107 Some((self.dealer, self.log))
1108 }
1109}
1110
1111impl<V: Variant, S: Signer> EncodeSize for SignedDealerLog<V, S> {
1112 fn encode_size(&self) -> usize {
1113 self.dealer.encode_size() + self.log.encode_size() + self.sig.encode_size()
1114 }
1115}
1116
1117impl<V: Variant, S: Signer> Write for SignedDealerLog<V, S> {
1118 fn write(&self, buf: &mut impl bytes::BufMut) {
1119 self.dealer.write(buf);
1120 self.log.write(buf);
1121 self.sig.write(buf);
1122 }
1123}
1124
1125impl<V: Variant, S: Signer> Read for SignedDealerLog<V, S> {
1126 type Cfg = NonZeroU32;
1127
1128 fn read_cfg(
1129 buf: &mut impl bytes::Buf,
1130 cfg: &Self::Cfg,
1131 ) -> Result<Self, commonware_codec::Error> {
1132 Ok(Self {
1133 dealer: ReadExt::read(buf)?,
1134 log: Read::read_cfg(buf, cfg)?,
1135 sig: ReadExt::read(buf)?,
1136 })
1137 }
1138}
1139
1140#[cfg(feature = "arbitrary")]
1141impl<V: Variant, S: Signer> arbitrary::Arbitrary<'_> for SignedDealerLog<V, S>
1142where
1143 S::PublicKey: for<'a> arbitrary::Arbitrary<'a>,
1144 V::Public: for<'a> arbitrary::Arbitrary<'a>,
1145 S::Signature: for<'a> arbitrary::Arbitrary<'a>,
1146{
1147 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
1148 let dealer = u.arbitrary()?;
1149 let log = u.arbitrary()?;
1150 let sig = u.arbitrary()?;
1151 Ok(Self { dealer, log, sig })
1152 }
1153}
1154
1155fn transcript_for_round<V: Variant, P: PublicKey>(info: &Info<V, P>) -> Transcript {
1156 Transcript::resume(info.summary)
1157}
1158
1159fn transcript_for_ack<V: Variant, P: PublicKey>(
1160 transcript: &Transcript,
1161 dealer: &P,
1162 pub_msg: &DealerPubMsg<V>,
1163) -> Transcript {
1164 let mut out = transcript.fork(SIG_ACK);
1165 out.commit(dealer.encode());
1166 out.commit(pub_msg.encode());
1167 out
1168}
1169
1170fn transcript_for_log<V: Variant, P: PublicKey>(
1171 info: &Info<V, P>,
1172 log: &DealerLog<V, P>,
1173) -> Transcript {
1174 let mut out = transcript_for_round(info).fork(SIG_LOG);
1175 out.commit(log.encode());
1176 out
1177}
1178
1179pub struct Dealer<V: Variant, S: Signer> {
1180 me: S,
1181 info: Info<V, S::PublicKey>,
1182 pub_msg: DealerPubMsg<V>,
1183 results: Map<S::PublicKey, AckOrReveal<S::PublicKey>>,
1184 transcript: Transcript,
1185}
1186
1187impl<V: Variant, S: Signer> Dealer<V, S> {
1188 #[allow(clippy::type_complexity)]
1210 pub fn start<M: Faults>(
1211 mut rng: impl CryptoRngCore,
1212 info: Info<V, S::PublicKey>,
1213 me: S,
1214 share: Option<Share>,
1215 ) -> Result<(Self, DealerPubMsg<V>, Vec<(S::PublicKey, DealerPrivMsg)>), Error> {
1216 info.dealer_index(&me.public_key())?;
1218 let share = info.unwrap_or_random_share(
1219 &mut rng,
1220 share.map(|x| x.private.expose_unwrap()),
1225 )?;
1226 let my_poly = Poly::new_with_constant(&mut rng, info.degree::<M>(), share);
1227 let priv_msgs = info
1228 .players
1229 .iter()
1230 .map(|pk| {
1231 (
1232 pk.clone(),
1233 DealerPrivMsg::new(my_poly.eval_msm(
1234 &info.player_scalar(pk).expect("player should exist"),
1235 &Sequential,
1236 )),
1237 )
1238 })
1239 .collect::<Vec<_>>();
1240 let results: Map<_, _> = priv_msgs
1241 .clone()
1242 .into_iter()
1243 .map(|(pk, priv_msg)| (pk, AckOrReveal::Reveal(priv_msg)))
1244 .try_collect()
1245 .expect("players are unique");
1246 let commitment = Poly::commit(my_poly);
1247 let pub_msg = DealerPubMsg { commitment };
1248 let transcript = {
1249 let t = transcript_for_round(&info);
1250 transcript_for_ack(&t, &me.public_key(), &pub_msg)
1251 };
1252 let this = Self {
1253 me,
1254 info,
1255 pub_msg: pub_msg.clone(),
1256 results,
1257 transcript,
1258 };
1259 Ok((this, pub_msg, priv_msgs))
1260 }
1261
1262 pub fn receive_player_ack(
1267 &mut self,
1268 player: S::PublicKey,
1269 ack: PlayerAck<S::PublicKey>,
1270 ) -> Result<(), Error> {
1271 let res_mut = self
1272 .results
1273 .get_value_mut(&player)
1274 .ok_or(Error::UnknownPlayer)?;
1275 if self.transcript.verify(&player, &ack.sig) {
1276 *res_mut = AckOrReveal::Ack(ack);
1277 }
1278 Ok(())
1279 }
1280
1281 pub fn finalize<M: Faults>(self) -> SignedDealerLog<V, S> {
1285 let reveals = self
1286 .results
1287 .values()
1288 .iter()
1289 .filter(|x| x.is_reveal())
1290 .count() as u32;
1291 let results = if reveals > self.info.max_reveals::<M>() {
1293 DealerResult::TooManyReveals
1294 } else {
1295 DealerResult::Ok(self.results)
1296 };
1297 let log = DealerLog {
1298 pub_msg: self.pub_msg,
1299 results,
1300 };
1301 SignedDealerLog::sign(&self.me, &self.info, log)
1302 }
1303}
1304
1305#[allow(clippy::type_complexity)]
1306fn select<V: Variant, P: PublicKey, M: Faults>(
1307 info: &Info<V, P>,
1308 logs: BTreeMap<P, DealerLog<V, P>>,
1309) -> Result<Map<P, DealerLog<V, P>>, Error> {
1310 let required_commitments = info.required_commitments::<M>() as usize;
1311 let transcript = transcript_for_round(info);
1312 let out = logs
1313 .into_iter()
1314 .filter_map(|(dealer, log)| {
1315 info.dealer_index(&dealer).ok()?;
1316 if !info.check_dealer_pub_msg::<M>(&dealer, &log.pub_msg) {
1317 return None;
1318 }
1319 let results_iter = log.zip_players(&info.players)?;
1320 let transcript = transcript_for_ack(&transcript, &dealer, &log.pub_msg);
1321 let mut reveal_count = 0;
1322 let max_reveals = info.max_reveals::<M>();
1323 for (player, result) in results_iter {
1324 match result {
1325 AckOrReveal::Ack(ack) => {
1326 if !transcript.verify(player, &ack.sig) {
1327 return None;
1328 }
1329 }
1330 AckOrReveal::Reveal(priv_msg) => {
1331 reveal_count += 1;
1332 if reveal_count > max_reveals {
1333 return None;
1334 }
1335 if !info.check_dealer_priv_msg(player, &log.pub_msg, priv_msg) {
1336 return None;
1337 }
1338 }
1339 }
1340 }
1341 Some((dealer, log))
1342 })
1343 .take(required_commitments)
1344 .try_collect::<Map<_, _>>()
1345 .expect("logs has at most one entry per dealer");
1346 if out.len() < required_commitments {
1347 return Err(Error::DkgFailed);
1348 }
1349 Ok(out)
1350}
1351
1352struct ObserveInner<V: Variant, P: PublicKey> {
1353 output: Output<V, P>,
1354 weights: Option<Interpolator<P, Scalar>>,
1355}
1356
1357impl<V: Variant, P: PublicKey> ObserveInner<V, P> {
1358 fn reckon<M: Faults>(
1359 info: Info<V, P>,
1360 selected: Map<P, DealerLog<V, P>>,
1361 strategy: &impl Strategy,
1362 ) -> Result<Self, Error> {
1363 let max_faults = info.players.max_faults::<M>();
1365 let mut reveal_counts: BTreeMap<P, u32> = BTreeMap::new();
1366 let mut revealed = Vec::new();
1367 for log in selected.values() {
1368 let Some(iter) = log.zip_players(&info.players) else {
1369 continue;
1370 };
1371 for (player, result) in iter {
1372 if !result.is_reveal() {
1373 continue;
1374 }
1375 let count = reveal_counts.entry(player.clone()).or_insert(0);
1376 *count += 1;
1377 if *count == max_faults + 1 {
1378 revealed.push(player.clone());
1379 }
1380 }
1381 }
1382 let revealed: Set<P> = revealed
1383 .into_iter()
1384 .try_collect()
1385 .expect("players are unique");
1386
1387 let dealers: Set<P> = selected
1389 .keys()
1390 .iter()
1391 .cloned()
1392 .try_collect()
1393 .expect("selected dealers are unique");
1394
1395 let (public, weights) = if let Some(previous) = info.previous.as_ref() {
1397 let weights = previous
1398 .public()
1399 .mode()
1400 .subset_interpolator(previous.players(), selected.keys())
1401 .expect("the result of select should produce a valid subset");
1402 let commitments = selected
1403 .into_iter()
1404 .map(|(dealer, log)| (dealer, log.pub_msg.commitment))
1405 .try_collect::<Map<_, _>>()
1406 .expect("Map should have unique keys");
1407 let public = weights
1408 .interpolate(&commitments, strategy)
1409 .expect("select checks that enough points have been provided");
1410 if previous.public().public() != public.constant() {
1411 return Err(Error::DkgFailed);
1412 }
1413 (public, Some(weights))
1414 } else {
1415 let mut public = Poly::zero();
1416 for log in selected.values() {
1417 public += &log.pub_msg.commitment;
1418 }
1419 (public, None)
1420 };
1421 let n = info.players.len() as u32;
1422 let output = Output {
1423 summary: info.summary,
1424 public: Sharing::new(info.mode, NZU32!(n), public),
1425 dealers,
1426 players: info.players,
1427 revealed,
1428 };
1429 Ok(Self { output, weights })
1430 }
1431}
1432
1433pub fn observe<V: Variant, P: PublicKey, M: Faults>(
1442 info: Info<V, P>,
1443 logs: BTreeMap<P, DealerLog<V, P>>,
1444 strategy: &impl Strategy,
1445) -> Result<Output<V, P>, Error> {
1446 let selected = select::<V, P, M>(&info, logs)?;
1447 ObserveInner::<V, P>::reckon::<M>(info, selected, strategy).map(|x| x.output)
1448}
1449
1450pub struct Player<V: Variant, S: Signer> {
1456 me: S,
1457 me_pub: S::PublicKey,
1458 info: Info<V, S::PublicKey>,
1459 index: Participant,
1460 transcript: Transcript,
1461 view: BTreeMap<S::PublicKey, (DealerPubMsg<V>, DealerPrivMsg)>,
1462}
1463
1464impl<V: Variant, S: Signer> Player<V, S> {
1465 pub fn new(info: Info<V, S::PublicKey>, me: S) -> Result<Self, Error> {
1469 let me_pub = me.public_key();
1470 Ok(Self {
1471 index: info.player_index(&me_pub)?,
1472 me,
1473 me_pub,
1474 transcript: transcript_for_round(&info),
1475 info,
1476 view: BTreeMap::new(),
1477 })
1478 }
1479
1480 pub fn dealer_message<M: Faults>(
1487 &mut self,
1488 dealer: S::PublicKey,
1489 pub_msg: DealerPubMsg<V>,
1490 priv_msg: DealerPrivMsg,
1491 ) -> Option<PlayerAck<S::PublicKey>> {
1492 if self.view.contains_key(&dealer) {
1493 return None;
1494 }
1495 self.info.dealer_index(&dealer).ok()?;
1496 if !self.info.check_dealer_pub_msg::<M>(&dealer, &pub_msg) {
1497 return None;
1498 }
1499 if !self
1500 .info
1501 .check_dealer_priv_msg(&self.me_pub, &pub_msg, &priv_msg)
1502 {
1503 return None;
1504 }
1505 let sig = transcript_for_ack(&self.transcript, &dealer, &pub_msg).sign(&self.me);
1506 self.view.insert(dealer, (pub_msg, priv_msg));
1507 Some(PlayerAck { sig })
1508 }
1509
1510 pub fn finalize<M: Faults>(
1519 self,
1520 logs: BTreeMap<S::PublicKey, DealerLog<V, S::PublicKey>>,
1521 strategy: &impl Strategy,
1522 ) -> Result<(Output<V, S::PublicKey>, Share), Error> {
1523 let selected = select::<V, S::PublicKey, M>(&self.info, logs)?;
1524 let dealings = selected
1530 .iter_pairs()
1531 .map(|(dealer, log)| {
1532 let share = self
1533 .view
1534 .get(dealer)
1535 .map(|(_, priv_msg)| priv_msg.share.clone().expose_unwrap())
1536 .unwrap_or_else(|| {
1537 log.get_reveal(&self.me_pub).map_or_else(
1538 || {
1539 unreachable!(
1540 "select didn't check dealer reveal, or we're not a player?"
1541 )
1542 },
1543 |priv_msg| priv_msg.share.clone().expose_unwrap(),
1544 )
1545 });
1546 (dealer.clone(), share)
1547 })
1548 .try_collect::<Map<_, _>>()
1549 .expect("select produces at most one entry per dealer");
1550 let ObserveInner { output, weights } =
1551 ObserveInner::<V, S::PublicKey>::reckon::<M>(self.info, selected, strategy)?;
1552 let private = weights.map_or_else(
1553 || {
1554 let mut out = <Scalar as Additive>::zero();
1555 for s in dealings.values() {
1556 out += s;
1557 }
1558 out
1559 },
1560 |weights| {
1561 weights
1562 .interpolate(&dealings, strategy)
1563 .expect("select ensures that we can recover")
1564 },
1565 );
1566 let share = Share::new(self.index, Private::new(private));
1567 Ok((output, share))
1568 }
1569}
1570
1571pub type DealResult<V, P> = Result<(Output<V, P>, Map<P, Share>), Error>;
1573
1574pub fn deal<V: Variant, P: Clone + Ord, M: Faults>(
1576 mut rng: impl CryptoRngCore,
1577 mode: Mode,
1578 players: Set<P>,
1579) -> DealResult<V, P> {
1580 if players.is_empty() {
1581 return Err(Error::NumPlayers(0));
1582 }
1583 let n = NZU32!(players.len() as u32);
1584 let t = players.quorum::<M>();
1585 let private = Poly::new(&mut rng, t - 1);
1586 let shares: Map<_, _> = players
1587 .iter()
1588 .enumerate()
1589 .map(|(i, p)| {
1590 let participant = Participant::from_usize(i);
1591 let eval = private.eval_msm(
1592 &mode
1593 .scalar(n, participant)
1594 .expect("player index should be valid"),
1595 &Sequential,
1596 );
1597 let share = Share::new(participant, Private::new(eval));
1598 (p.clone(), share)
1599 })
1600 .try_collect()
1601 .expect("players are unique");
1602 let output = Output {
1603 summary: Summary::random(&mut rng),
1604 public: Sharing::new(mode, n, Poly::commit(private)),
1605 dealers: players.clone(),
1606 players,
1607 revealed: Set::default(),
1608 };
1609 Ok((output, shares))
1610}
1611
1612pub fn deal_anonymous<V: Variant, M: Faults>(
1618 rng: impl CryptoRngCore,
1619 mode: Mode,
1620 n: NonZeroU32,
1621) -> (Sharing<V>, Vec<Share>) {
1622 let players = (0..n.get()).try_collect().unwrap();
1623 let (output, shares) = deal::<V, _, M>(rng, mode, players).unwrap();
1624 (output.public().clone(), shares.values().to_vec())
1625}
1626
1627#[cfg(any(feature = "arbitrary", test))]
1628mod test_plan {
1629 use super::*;
1630 use crate::{
1631 bls12381::primitives::{
1632 ops::{self, threshold},
1633 variant::Variant,
1634 },
1635 ed25519, PublicKey,
1636 };
1637 use anyhow::anyhow;
1638 use bytes::BytesMut;
1639 use commonware_utils::{Faults, N3f1, TryCollect};
1640 use core::num::NonZeroI32;
1641 use rand::{rngs::StdRng, SeedableRng as _};
1642 use std::collections::BTreeSet;
1643
1644 fn apply_mask(bytes: &mut BytesMut, mask: &[u8]) -> bool {
1646 let mut modified = false;
1647 for (l, &r) in bytes.iter_mut().zip(mask.iter()) {
1648 modified |= r != 0;
1649 *l ^= r;
1650 }
1651 modified
1652 }
1653
1654 #[derive(Clone, Default, Debug)]
1655 pub struct Masks {
1656 pub info_summary: Vec<u8>,
1657 pub dealer: Vec<u8>,
1658 pub pub_msg: Vec<u8>,
1659 pub log: Vec<u8>,
1660 }
1661
1662 impl Masks {
1663 fn transcript_for_round<V: Variant, P: PublicKey>(
1664 &self,
1665 info: &Info<V, P>,
1666 ) -> anyhow::Result<(bool, Transcript)> {
1667 let mut summary_bs = info.summary.encode_mut();
1668 let modified = apply_mask(&mut summary_bs, &self.info_summary);
1669 let summary = Summary::read(&mut summary_bs)?;
1670 Ok((modified, Transcript::resume(summary)))
1671 }
1672
1673 fn transcript_for_player_ack<V: Variant, P: PublicKey>(
1674 &self,
1675 info: &Info<V, P>,
1676 dealer: &P,
1677 pub_msg: &DealerPubMsg<V>,
1678 ) -> anyhow::Result<(bool, Transcript)> {
1679 let (mut modified, transcript) = self.transcript_for_round(info)?;
1680 let mut transcript = transcript.fork(SIG_ACK);
1681
1682 let mut dealer_bs = dealer.encode_mut();
1683 modified |= apply_mask(&mut dealer_bs, &self.dealer);
1684 transcript.commit(&mut dealer_bs);
1685
1686 let mut pub_msg_bs = pub_msg.encode_mut();
1687 modified |= apply_mask(&mut pub_msg_bs, &self.pub_msg);
1688 transcript.commit(&mut pub_msg_bs);
1689
1690 Ok((modified, transcript))
1691 }
1692
1693 fn transcript_for_signed_dealer_log<V: Variant, P: PublicKey>(
1694 &self,
1695 info: &Info<V, P>,
1696 log: &DealerLog<V, P>,
1697 ) -> anyhow::Result<(bool, Transcript)> {
1698 let (mut modified, transcript) = self.transcript_for_round(info)?;
1699 let mut transcript = transcript.fork(SIG_LOG);
1700
1701 let mut log_bs = log.encode_mut();
1702 modified |= apply_mask(&mut log_bs, &self.log);
1703 transcript.commit(&mut log_bs);
1704
1705 Ok((modified, transcript))
1706 }
1707 }
1708
1709 #[derive(Debug, Default)]
1711 pub struct Round {
1712 dealers: Vec<u32>,
1713 players: Vec<u32>,
1714 no_acks: BTreeSet<(u32, u32)>,
1715 bad_shares: BTreeSet<(u32, u32)>,
1716 bad_player_sigs: BTreeMap<(u32, u32), Masks>,
1717 bad_reveals: BTreeSet<(u32, u32)>,
1718 bad_dealer_sigs: BTreeMap<u32, Masks>,
1719 replace_shares: BTreeSet<u32>,
1720 shift_degrees: BTreeMap<u32, NonZeroI32>,
1721 }
1722
1723 impl Round {
1724 pub fn new(dealers: Vec<u32>, players: Vec<u32>) -> Self {
1725 Self {
1726 dealers,
1727 players,
1728 ..Default::default()
1729 }
1730 }
1731
1732 pub fn no_ack(mut self, dealer: u32, player: u32) -> Self {
1733 self.no_acks.insert((dealer, player));
1734 self
1735 }
1736
1737 pub fn bad_share(mut self, dealer: u32, player: u32) -> Self {
1738 self.bad_shares.insert((dealer, player));
1739 self
1740 }
1741
1742 pub fn bad_player_sig(mut self, dealer: u32, player: u32, masks: Masks) -> Self {
1743 self.bad_player_sigs.insert((dealer, player), masks);
1744 self
1745 }
1746
1747 pub fn bad_reveal(mut self, dealer: u32, player: u32) -> Self {
1748 self.bad_reveals.insert((dealer, player));
1749 self
1750 }
1751
1752 pub fn bad_dealer_sig(mut self, dealer: u32, masks: Masks) -> Self {
1753 self.bad_dealer_sigs.insert(dealer, masks);
1754 self
1755 }
1756
1757 pub fn replace_share(mut self, dealer: u32) -> Self {
1758 self.replace_shares.insert(dealer);
1759 self
1760 }
1761
1762 pub fn shift_degree(mut self, dealer: u32, shift: NonZeroI32) -> Self {
1763 self.shift_degrees.insert(dealer, shift);
1764 self
1765 }
1766
1767 pub fn validate(
1770 &self,
1771 num_participants: u32,
1772 previous_players: Option<&[u32]>,
1773 ) -> anyhow::Result<()> {
1774 if self.dealers.is_empty() {
1775 return Err(anyhow!("dealers is empty"));
1776 }
1777 if self.players.is_empty() {
1778 return Err(anyhow!("players is empty"));
1779 }
1780 for &d in &self.dealers {
1782 if d >= num_participants {
1783 return Err(anyhow!("dealer {d} out of range [1, {num_participants}]"));
1784 }
1785 }
1786 for &p in &self.players {
1787 if p >= num_participants {
1788 return Err(anyhow!("player {p} out of range [1, {num_participants}]"));
1789 }
1790 }
1791
1792 if let Some(prev_players) = previous_players {
1794 for &d in &self.dealers {
1796 if !prev_players.contains(&d) {
1797 return Err(anyhow!("dealer {d} was not a player in previous round"));
1798 }
1799 }
1800 let required = N3f1::quorum(prev_players.len());
1802 if (self.dealers.len() as u32) < required {
1803 return Err(anyhow!(
1804 "not enough dealers: have {}, need {} (quorum of {} previous players)",
1805 self.dealers.len(),
1806 required,
1807 prev_players.len()
1808 ));
1809 }
1810 }
1811
1812 Ok(())
1813 }
1814
1815 fn bad(&self, previous_successful_round: bool, dealer: u32) -> bool {
1816 if self.replace_shares.contains(&dealer) && previous_successful_round {
1817 return true;
1818 }
1819 if let Some(shift) = self.shift_degrees.get(&dealer) {
1820 let degree = N3f1::quorum(self.players.len()) as i32 - 1;
1821 if (degree + shift.get()).max(0) != degree {
1827 return true;
1828 }
1829 }
1830 if self.bad_reveals.iter().any(|&(d, _)| d == dealer) {
1831 return true;
1832 }
1833 let revealed_players = self
1834 .bad_shares
1835 .iter()
1836 .copied()
1837 .chain(self.no_acks.iter().copied())
1838 .filter_map(|(d, p)| if d == dealer { Some(p) } else { None })
1839 .collect::<BTreeSet<_>>();
1840 revealed_players.len() as u32 > N3f1::max_faults(self.players.len())
1841 }
1842
1843 fn expect_failure(&self, previous_successful_round: Option<u32>) -> bool {
1845 let good_dealer_count = self
1846 .dealers
1847 .iter()
1848 .filter(|&&d| !self.bad(previous_successful_round.is_some(), d))
1849 .count();
1850 let required = previous_successful_round
1851 .map(N3f1::quorum)
1852 .unwrap_or_default()
1853 .max(N3f1::quorum(self.dealers.len())) as usize;
1854 good_dealer_count < required
1855 }
1856 }
1857
1858 #[derive(Debug)]
1860 pub struct Plan {
1861 num_participants: NonZeroU32,
1862 rounds: Vec<Round>,
1863 }
1864
1865 impl Plan {
1866 pub const fn new(num_participants: NonZeroU32) -> Self {
1867 Self {
1868 num_participants,
1869 rounds: Vec::new(),
1870 }
1871 }
1872
1873 pub fn with(mut self, round: Round) -> Self {
1874 self.rounds.push(round);
1875 self
1876 }
1877
1878 fn validate(&self) -> anyhow::Result<()> {
1880 let mut last_successful_players: Option<Vec<u32>> = None;
1881
1882 for round in &self.rounds {
1883 round.validate(
1884 self.num_participants.get(),
1885 last_successful_players.as_deref(),
1886 )?;
1887
1888 if !round.expect_failure(last_successful_players.as_ref().map(|x| x.len() as u32)) {
1890 last_successful_players = Some(round.players.clone());
1891 }
1892 }
1893 Ok(())
1894 }
1895
1896 pub fn run<V: Variant>(self, seed: u64) -> anyhow::Result<()> {
1898 self.validate()?;
1899
1900 let mut rng = StdRng::seed_from_u64(seed);
1901
1902 let keys = (0..self.num_participants.get())
1904 .map(|_| ed25519::PrivateKey::random(&mut rng))
1905 .collect::<Vec<_>>();
1906
1907 let pk_to_key_idx: BTreeMap<ed25519::PublicKey, u32> = keys
1910 .iter()
1911 .enumerate()
1912 .map(|(i, k)| (k.public_key(), i as u32))
1913 .collect();
1914
1915 let max_shift = self
1918 .rounds
1919 .iter()
1920 .flat_map(|r| r.shift_degrees.values())
1921 .map(|s| s.get())
1922 .max()
1923 .unwrap_or(0)
1924 .max(0) as u32;
1925 let max_read_size =
1926 NonZeroU32::new(self.num_participants.get() + max_shift).expect("non-zero");
1927
1928 let mut previous_output: Option<Output<V, ed25519::PublicKey>> = None;
1929 let mut shares: BTreeMap<ed25519::PublicKey, Share> = BTreeMap::new();
1930 let mut threshold_public_key: Option<V::Public> = None;
1931
1932 for (i_round, round) in self.rounds.into_iter().enumerate() {
1933 let previous_successful_round =
1934 previous_output.as_ref().map(|o| o.players.len() as u32);
1935
1936 let dealer_set = round
1937 .dealers
1938 .iter()
1939 .map(|&i| keys[i as usize].public_key())
1940 .try_collect::<Set<_>>()
1941 .unwrap();
1942 let player_set: Set<ed25519::PublicKey> = round
1943 .players
1944 .iter()
1945 .map(|&i| keys[i as usize].public_key())
1946 .try_collect()
1947 .unwrap();
1948
1949 let info = Info::new::<N3f1>(
1951 b"_COMMONWARE_CRYPTOGRAPHY_BLS12381_DKG_TEST",
1952 i_round as u64,
1953 previous_output.clone(),
1954 Default::default(),
1955 dealer_set.clone(),
1956 player_set.clone(),
1957 )?;
1958
1959 let mut players: Map<_, _> = round
1960 .players
1961 .iter()
1962 .map(|&i| {
1963 let sk = keys[i as usize].clone();
1964 let pk = sk.public_key();
1965 let player = Player::new(info.clone(), sk)?;
1966 Ok((pk, player))
1967 })
1968 .collect::<anyhow::Result<Vec<_>>>()?
1969 .try_into()
1970 .unwrap();
1971
1972 let mut dealer_logs = BTreeMap::new();
1974 for &i_dealer in &round.dealers {
1975 let sk = keys[i_dealer as usize].clone();
1976 let pk = sk.public_key();
1977 let share = match (shares.get(&pk), round.replace_shares.contains(&i_dealer)) {
1978 (None, _) => None,
1979 (Some(s), false) => Some(s.clone()),
1980 (Some(_), true) => Some(Share::new(
1981 Participant::new(i_dealer),
1982 Private::random(&mut rng),
1983 )),
1984 };
1985
1986 let (mut dealer, pub_msg, mut priv_msgs) =
1988 if let Some(shift) = round.shift_degrees.get(&i_dealer) {
1989 let degree = u32::try_from(info.degree::<N3f1>() as i32 + shift.get())
1991 .unwrap_or_default();
1992
1993 let share = info
1995 .unwrap_or_random_share(
1996 &mut rng,
1997 share.map(|s| s.private.expose_unwrap()),
1998 )
1999 .expect("Failed to generate dealer share");
2000
2001 let my_poly = Poly::new_with_constant(&mut rng, degree, share);
2002 let priv_msgs = info
2003 .players
2004 .iter()
2005 .map(|pk| {
2006 (
2007 pk.clone(),
2008 DealerPrivMsg::new(my_poly.eval_msm(
2009 &info.player_scalar(pk).expect("player should exist"),
2010 &Sequential,
2011 )),
2012 )
2013 })
2014 .collect::<Vec<_>>();
2015 let results: Map<_, _> = priv_msgs
2016 .iter()
2017 .map(|(pk, pm)| (pk.clone(), AckOrReveal::Reveal(pm.clone())))
2018 .try_collect()
2019 .unwrap();
2020 let commitment = Poly::commit(my_poly);
2021 let pub_msg = DealerPubMsg { commitment };
2022 let transcript = {
2023 let t = transcript_for_round(&info);
2024 transcript_for_ack(&t, &pk, &pub_msg)
2025 };
2026 let dealer = Dealer {
2027 me: sk.clone(),
2028 info: info.clone(),
2029 pub_msg: pub_msg.clone(),
2030 results,
2031 transcript,
2032 };
2033 (dealer, pub_msg, priv_msgs)
2034 } else {
2035 Dealer::start::<N3f1>(&mut rng, info.clone(), sk.clone(), share)?
2036 };
2037
2038 for (player, priv_msg) in &mut priv_msgs {
2040 let player_key_idx = pk_to_key_idx[player];
2041 if round.bad_shares.contains(&(i_dealer, player_key_idx)) {
2042 *priv_msg = DealerPrivMsg::new(Scalar::random(&mut rng));
2043 }
2044 }
2045 assert_eq!(priv_msgs.len(), players.len());
2046
2047 let mut num_reveals = players.len() as u32;
2049 for (player_pk, priv_msg) in priv_msgs {
2050 assert_eq!(priv_msg, ReadExt::read(&mut priv_msg.encode())?);
2052
2053 let i_player = players
2054 .index(&player_pk)
2055 .ok_or_else(|| anyhow!("unknown player: {:?}", &player_pk))?;
2056 let player_key_idx = pk_to_key_idx[&player_pk];
2057 let player = &mut players.values_mut()[usize::from(i_player)];
2058
2059 let ack =
2060 player.dealer_message::<N3f1>(pk.clone(), pub_msg.clone(), priv_msg);
2061 assert_eq!(ack, ReadExt::read(&mut ack.encode())?);
2062 if let Some(ack) = ack {
2063 let masks = round
2064 .bad_player_sigs
2065 .get(&(i_dealer, player_key_idx))
2066 .cloned()
2067 .unwrap_or_default();
2068 let (modified, transcript) =
2069 masks.transcript_for_player_ack(&info, &pk, &pub_msg)?;
2070 assert_eq!(transcript.verify(&player_pk, &ack.sig), !modified);
2071
2072 if !round.no_acks.contains(&(i_dealer, player_key_idx)) {
2074 dealer.receive_player_ack(player_pk, ack)?;
2075 num_reveals -= 1;
2076 }
2077 } else {
2078 assert!(
2079 round.bad_shares.contains(&(i_dealer, player_key_idx))
2080 || round.bad(previous_successful_round.is_some(), i_dealer)
2081 );
2082 }
2083 }
2084
2085 let signed_log = dealer.finalize::<N3f1>();
2087 assert_eq!(
2088 signed_log,
2089 Read::read_cfg(&mut signed_log.encode(), &max_read_size)?
2090 );
2091
2092 let masks = round
2094 .bad_dealer_sigs
2095 .get(&i_dealer)
2096 .cloned()
2097 .unwrap_or_default();
2098 let (modified, transcript) =
2099 masks.transcript_for_signed_dealer_log(&info, &signed_log.log)?;
2100 assert_eq!(transcript.verify(&pk, &signed_log.sig), !modified);
2101 let (found_pk, mut log) = signed_log
2102 .check(&info)
2103 .ok_or_else(|| anyhow!("signed log should verify"))?;
2104 assert_eq!(pk, found_pk);
2105 match &mut log.results {
2107 DealerResult::TooManyReveals => {
2108 assert!(num_reveals > info.max_reveals::<N3f1>());
2109 }
2110 DealerResult::Ok(results) => {
2111 assert_eq!(results.len(), players.len());
2112 for &i_player in &round.players {
2113 if !round.bad_reveals.contains(&(i_dealer, i_player)) {
2114 continue;
2115 }
2116 let player_pk = keys[i_player as usize].public_key();
2117 *results
2118 .get_value_mut(&player_pk)
2119 .ok_or_else(|| anyhow!("unknown player: {:?}", &player_pk))? =
2120 AckOrReveal::Reveal(DealerPrivMsg::new(Scalar::random(
2121 &mut rng,
2122 )));
2123 }
2124 }
2125 }
2126 dealer_logs.insert(pk, log);
2127 }
2128
2129 let selection = select::<_, _, N3f1>(&info, dealer_logs.clone());
2131 if let Ok(ref selection) = selection {
2132 let good_pks = selection
2133 .iter_pairs()
2134 .map(|(pk, _)| pk.clone())
2135 .collect::<BTreeSet<_>>();
2136 for &i_dealer in &round.dealers {
2137 if round.bad(previous_successful_round.is_some(), i_dealer) {
2138 assert!(!good_pks.contains(&keys[i_dealer as usize].public_key()));
2139 }
2140 }
2141 }
2142 let observe_result =
2144 observe::<_, _, N3f1>(info.clone(), dealer_logs.clone(), &Sequential);
2145 if round.expect_failure(previous_successful_round) {
2146 assert!(
2147 observe_result.is_err(),
2148 "Round {i_round} should have failed but succeeded",
2149 );
2150 continue;
2151 }
2152 let observer_output = observe_result?;
2153 let selection = selection.expect("select should succeed if observe succeeded");
2154
2155 let required_commitments = info.required_commitments::<N3f1>() as usize;
2158 let expected_dealers: Set<ed25519::PublicKey> = dealer_set
2159 .iter()
2160 .filter(|pk| {
2161 let i = keys.iter().position(|k| &k.public_key() == *pk).unwrap() as u32;
2162 !round.bad(previous_successful_round.is_some(), i)
2163 })
2164 .take(required_commitments)
2165 .cloned()
2166 .try_collect()
2167 .expect("dealers are unique");
2168 let expected_dealer_indices: BTreeSet<u32> = expected_dealers
2169 .iter()
2170 .filter_map(|pk| {
2171 keys.iter()
2172 .position(|k| &k.public_key() == pk)
2173 .map(|i| i as u32)
2174 })
2175 .collect();
2176 assert_eq!(
2177 observer_output.dealers(),
2178 &expected_dealers,
2179 "Output dealers should match expected good dealers"
2180 );
2181
2182 let selected_dealers: BTreeSet<u32> = selection
2184 .keys()
2185 .iter()
2186 .filter_map(|pk| {
2187 keys.iter()
2188 .position(|k| &k.public_key() == pk)
2189 .map(|i| i as u32)
2190 })
2191 .collect();
2192 assert_eq!(
2193 selected_dealers, expected_dealer_indices,
2194 "Selection should match expected dealers"
2195 );
2196 let selected_players: Set<ed25519::PublicKey> = round
2197 .players
2198 .iter()
2199 .map(|&i| keys[i as usize].public_key())
2200 .try_collect()
2201 .expect("players are unique");
2202
2203 let mut expected_reveals: BTreeMap<ed25519::PublicKey, u32> = BTreeMap::new();
2209 for &(dealer_idx, player_key_idx) in round.no_acks.union(&round.bad_shares) {
2210 if !selected_dealers.contains(&dealer_idx) {
2211 continue;
2212 }
2213 let pk = keys[player_key_idx as usize].public_key();
2214 if selected_players.position(&pk).is_none() {
2215 continue;
2216 }
2217 *expected_reveals.entry(pk).or_insert(0) += 1;
2218 }
2219
2220 let max_faults = selected_players.max_faults::<N3f1>();
2222 for player in player_set.iter() {
2223 let expected = expected_reveals.get(player).copied().unwrap_or(0) > max_faults;
2224 let actual = observer_output.revealed().position(player).is_some();
2225 assert_eq!(expected, actual, "Unexpected outcome for player {player:?} (expected={expected}, actual={actual})");
2226 }
2227
2228 for (player_pk, player) in players.into_iter() {
2230 let (player_output, share) = player
2231 .finalize::<N3f1>(dealer_logs.clone(), &Sequential)
2232 .expect("Player finalize should succeed");
2233
2234 assert_eq!(
2235 player_output, observer_output,
2236 "Player output should match observer output"
2237 );
2238
2239 let expected_public = observer_output
2241 .public
2242 .partial_public(share.index)
2243 .expect("share index should be valid");
2244 let actual_public = share.public::<V>();
2245 assert_eq!(
2246 expected_public, actual_public,
2247 "Share should match public polynomial"
2248 );
2249
2250 shares.insert(player_pk.clone(), share);
2251 }
2252
2253 let current_public = *observer_output.public().public();
2255 match threshold_public_key {
2256 None => threshold_public_key = Some(current_public),
2257 Some(tpk) => {
2258 assert_eq!(
2259 tpk, current_public,
2260 "Public key should remain constant across reshares"
2261 );
2262 }
2263 }
2264
2265 let test_message = format!("test message round {i_round}").into_bytes();
2267 let namespace = b"test";
2268
2269 let mut partial_sigs = Vec::new();
2270 for &i_player in &round.players {
2271 let share = &shares[&keys[i_player as usize].public_key()];
2272 let partial_sig = threshold::sign_message::<V>(share, namespace, &test_message);
2273
2274 threshold::verify_message::<V>(
2275 &observer_output.public,
2276 namespace,
2277 &test_message,
2278 &partial_sig,
2279 )
2280 .expect("Partial signature verification should succeed");
2281
2282 partial_sigs.push(partial_sig);
2283 }
2284
2285 let threshold = observer_output.quorum::<N3f1>();
2286 let threshold_sig = threshold::recover::<V, _, N3f1>(
2287 &observer_output.public,
2288 &partial_sigs[0..threshold as usize],
2289 &Sequential,
2290 )
2291 .expect("Should recover threshold signature");
2292
2293 ops::verify_message::<V>(
2295 threshold_public_key.as_ref().unwrap(),
2296 namespace,
2297 &test_message,
2298 &threshold_sig,
2299 )
2300 .expect("Threshold signature verification should succeed");
2301
2302 previous_output = Some(observer_output);
2304 }
2305 Ok(())
2306 }
2307 }
2308
2309 #[cfg(feature = "arbitrary")]
2310 mod impl_arbitrary {
2311 use super::*;
2312 use arbitrary::{Arbitrary, Unstructured};
2313 use core::ops::ControlFlow;
2314
2315 const MAX_NUM_PARTICIPANTS: u32 = 20;
2316 const MAX_ROUNDS: u32 = 10;
2317
2318 fn arbitrary_masks<'a>(u: &mut Unstructured<'a>) -> arbitrary::Result<Masks> {
2319 Ok(Masks {
2320 info_summary: Arbitrary::arbitrary(u)?,
2321 dealer: Arbitrary::arbitrary(u)?,
2322 pub_msg: Arbitrary::arbitrary(u)?,
2323 log: Arbitrary::arbitrary(u)?,
2324 })
2325 }
2326
2327 fn pick<'a, T>(
2332 u: &mut Unstructured<'a>,
2333 num: usize,
2334 mut data: Vec<T>,
2335 ) -> arbitrary::Result<Vec<T>> {
2336 let len = data.len();
2337 let num = num.min(len);
2338 for start in 0..num {
2340 data.swap(start, u.int_in_range(start..=len - 1)?);
2341 }
2342 data.truncate(num);
2343 Ok(data)
2344 }
2345
2346 fn arbitrary_round<'a>(
2347 u: &mut Unstructured<'a>,
2348 num_participants: u32,
2349 last_successful_players: Option<&Set<u32>>,
2350 ) -> arbitrary::Result<Round> {
2351 let dealers = if let Some(players) = last_successful_players {
2352 let to_pick = u.int_in_range(players.quorum::<N3f1>() as usize..=players.len())?;
2353 pick(u, to_pick, players.into_iter().copied().collect())?
2354 } else {
2355 let to_pick = u.int_in_range(1..=num_participants as usize)?;
2356 pick(u, to_pick, (0..num_participants).collect())?
2357 };
2358 let players = {
2359 let to_pick = u.int_in_range(1..=num_participants as usize)?;
2360 pick(u, to_pick, (0..num_participants).collect())?
2361 };
2362 let pairs = dealers
2363 .iter()
2364 .flat_map(|d| players.iter().map(|p| (*d, *p)))
2365 .collect::<Vec<_>>();
2366 let pick_pair_set = |u: &mut Unstructured<'a>| {
2367 let num = u.int_in_range(0..=pairs.len())?;
2368 if num == 0 {
2369 return Ok(BTreeSet::new());
2370 }
2371 Ok(pick(u, num, pairs.clone())?.into_iter().collect())
2372 };
2373 let pick_dealer_set = |u: &mut Unstructured<'a>| {
2374 let num = u.int_in_range(0..=dealers.len())?;
2375 if num == 0 {
2376 return Ok(BTreeSet::new());
2377 }
2378 Ok(pick(u, num, dealers.clone())?.into_iter().collect())
2379 };
2380 let round = Round {
2381 no_acks: pick_pair_set(u)?,
2382 bad_shares: pick_pair_set(u)?,
2383 bad_player_sigs: {
2384 let indices = pick_pair_set(u)?;
2385 indices
2386 .into_iter()
2387 .map(|k| Ok((k, arbitrary_masks(u)?)))
2388 .collect::<arbitrary::Result<_>>()?
2389 },
2390 bad_reveals: pick_pair_set(u)?,
2391 bad_dealer_sigs: {
2392 let indices = pick_dealer_set(u)?;
2393 indices
2394 .into_iter()
2395 .map(|k| Ok((k, arbitrary_masks(u)?)))
2396 .collect::<arbitrary::Result<_>>()?
2397 },
2398 replace_shares: pick_dealer_set(u)?,
2399 shift_degrees: {
2400 let indices = pick_dealer_set(u)?;
2401 indices
2402 .into_iter()
2403 .map(|k| {
2404 let expected = N3f1::quorum(players.len()) as i32 - 1;
2405 let shift = u.int_in_range(1..=expected.max(1))?;
2406 let shift = if bool::arbitrary(u)? { -shift } else { shift };
2407 Ok((k, NonZeroI32::new(shift).expect("checked to not be zero")))
2408 })
2409 .collect::<arbitrary::Result<_>>()?
2410 },
2411 dealers,
2412 players,
2413 };
2414 Ok(round)
2415 }
2416
2417 impl<'a> Arbitrary<'a> for Plan {
2418 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
2419 let num_participants = u.int_in_range(1..=MAX_NUM_PARTICIPANTS)?;
2420 let mut rounds = Vec::new();
2421 let mut last_successful_players: Option<Set<u32>> = None;
2422 u.arbitrary_loop(None, Some(MAX_ROUNDS), |u| {
2423 let round =
2424 arbitrary_round(u, num_participants, last_successful_players.as_ref())?;
2425 if !round
2426 .expect_failure(last_successful_players.as_ref().map(|x| x.len() as u32))
2427 {
2428 last_successful_players =
2429 Some(round.players.iter().copied().try_collect().unwrap());
2430 }
2431 rounds.push(round);
2432 Ok(ControlFlow::Continue(()))
2433 })?;
2434 let plan = Self {
2435 num_participants: NZU32!(num_participants),
2436 rounds,
2437 };
2438 plan.validate()
2439 .map_err(|_| arbitrary::Error::IncorrectFormat)?;
2440 Ok(plan)
2441 }
2442 }
2443 }
2444}
2445
2446#[cfg(feature = "arbitrary")]
2447pub use test_plan::Plan as FuzzPlan;
2448
2449#[cfg(test)]
2450mod test {
2451 use super::{test_plan::*, *};
2452 use crate::{bls12381::primitives::variant::MinPk, ed25519};
2453 use anyhow::anyhow;
2454 use commonware_math::algebra::Random;
2455 use commonware_utils::{test_rng, N3f1};
2456 use core::num::NonZeroI32;
2457
2458 #[test]
2459 fn single_round() -> anyhow::Result<()> {
2460 Plan::new(NZU32!(4))
2461 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2462 .run::<MinPk>(0)
2463 }
2464
2465 #[test]
2466 fn multiple_rounds() -> anyhow::Result<()> {
2467 Plan::new(NZU32!(4))
2468 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2469 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2470 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2471 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2472 .run::<MinPk>(0)
2473 }
2474
2475 #[test]
2476 fn changing_committee() -> anyhow::Result<()> {
2477 Plan::new(NonZeroU32::new(5).unwrap())
2478 .with(Round::new(vec![0, 1, 2], vec![1, 2, 3]))
2479 .with(Round::new(vec![1, 2, 3], vec![2, 3, 4]))
2480 .with(Round::new(vec![2, 3, 4], vec![3, 4, 0]))
2481 .with(Round::new(vec![3, 4, 0], vec![4, 0, 1]))
2482 .run::<MinPk>(0)
2483 }
2484
2485 #[test]
2486 fn missing_ack() -> anyhow::Result<()> {
2487 Plan::new(NonZeroU32::new(4).unwrap())
2489 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 0))
2490 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 1))
2491 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 2))
2492 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).no_ack(0, 3))
2493 .run::<MinPk>(0)
2494 }
2495
2496 #[test]
2497 fn increasing_decreasing_committee() -> anyhow::Result<()> {
2498 Plan::new(NonZeroU32::new(5).unwrap())
2499 .with(Round::new(vec![0, 1], vec![0, 1, 2]))
2500 .with(Round::new(vec![0, 1, 2], vec![0, 1, 2, 3]))
2501 .with(Round::new(vec![0, 1, 2], vec![0, 1]))
2502 .with(Round::new(vec![0, 1], vec![0, 1, 2, 3, 4]))
2503 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1]))
2504 .run::<MinPk>(0)
2505 }
2506
2507 #[test]
2508 fn bad_reveal_fails() -> anyhow::Result<()> {
2509 Plan::new(NonZeroU32::new(4).unwrap())
2510 .with(Round::new(vec![0], vec![0, 1, 2, 3]).bad_reveal(0, 1))
2511 .run::<MinPk>(0)
2512 }
2513
2514 #[test]
2515 fn bad_share() -> anyhow::Result<()> {
2516 Plan::new(NonZeroU32::new(4).unwrap())
2517 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).bad_share(0, 1))
2518 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).bad_share(0, 2))
2519 .run::<MinPk>(0)
2520 }
2521
2522 #[test]
2523 fn shift_degree_fails() -> anyhow::Result<()> {
2524 Plan::new(NonZeroU32::new(4).unwrap())
2525 .with(Round::new(vec![0], vec![0, 1, 2, 3]).shift_degree(
2526 0,
2527 NonZeroI32::new(1).ok_or_else(|| anyhow!("invalid NZI32"))?,
2528 ))
2529 .run::<MinPk>(0)
2530 }
2531
2532 #[test]
2533 fn replace_share_fails() -> anyhow::Result<()> {
2534 Plan::new(NonZeroU32::new(4).unwrap())
2535 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]))
2536 .with(Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3]).replace_share(0))
2537 .run::<MinPk>(0)
2538 }
2539
2540 #[test]
2541 fn too_many_reveals_dealer() -> anyhow::Result<()> {
2542 Plan::new(NonZeroU32::new(4).unwrap())
2543 .with(
2544 Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3])
2545 .no_ack(0, 0)
2546 .no_ack(0, 1),
2547 )
2548 .run::<MinPk>(0)
2549 }
2550
2551 #[test]
2552 fn too_many_reveals_player() -> anyhow::Result<()> {
2553 Plan::new(NonZeroU32::new(4).unwrap())
2554 .with(
2555 Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3])
2556 .no_ack(0, 0)
2557 .no_ack(1, 0)
2558 .no_ack(3, 0),
2559 )
2560 .run::<MinPk>(0)
2561 }
2562
2563 #[test]
2564 fn bad_sigs() -> anyhow::Result<()> {
2565 Plan::new(NonZeroU32::new(4).unwrap())
2566 .with(
2567 Round::new(vec![0, 1, 2, 3], vec![0, 1, 2, 3])
2568 .bad_dealer_sig(
2569 0,
2570 Masks {
2571 log: vec![0xFF; 8],
2572 ..Default::default()
2573 },
2574 )
2575 .bad_player_sig(
2576 0,
2577 1,
2578 Masks {
2579 pub_msg: vec![0xFF; 8],
2580 ..Default::default()
2581 },
2582 ),
2583 )
2584 .run::<MinPk>(0)
2585 }
2586
2587 #[test]
2588 fn issue_2745_regression() -> anyhow::Result<()> {
2589 Plan::new(NonZeroU32::new(6).unwrap())
2590 .with(
2591 Round::new(vec![0], vec![5, 1, 3, 0, 4])
2592 .no_ack(0, 5)
2593 .bad_share(0, 5),
2594 )
2595 .with(Round::new(vec![0, 1, 3, 4], vec![0]))
2596 .with(Round::new(vec![0], vec![0]))
2597 .run::<MinPk>(0)
2598 }
2599
2600 #[test]
2601 fn signed_dealer_log_commitment() -> Result<(), Error> {
2602 let sk = ed25519::PrivateKey::from_seed(0);
2603 let pk = sk.public_key();
2604 let info = Info::<MinPk, _>::new::<N3f1>(
2605 b"_COMMONWARE_CRYPTOGRAPHY_BLS12381_DKG_TEST",
2606 0,
2607 None,
2608 Default::default(),
2609 vec![sk.public_key()].try_into().unwrap(),
2610 vec![sk.public_key()].try_into().unwrap(),
2611 )?;
2612 let mut log0 = {
2613 let (dealer, _, _) =
2614 Dealer::start::<N3f1>(&mut test_rng(), info.clone(), sk.clone(), None)?;
2615 dealer.finalize::<N3f1>()
2616 };
2617 let mut log1 = {
2618 let (mut dealer, pub_msg, priv_msgs) =
2619 Dealer::start::<N3f1>(&mut test_rng(), info.clone(), sk.clone(), None)?;
2620 let mut player = Player::new(info.clone(), sk)?;
2621 let ack = player
2622 .dealer_message::<N3f1>(pk.clone(), pub_msg, priv_msgs[0].1.clone())
2623 .unwrap();
2624 dealer.receive_player_ack(pk, ack)?;
2625 dealer.finalize::<N3f1>()
2626 };
2627 std::mem::swap(&mut log0.log, &mut log1.log);
2628 assert!(log0.check(&info).is_none());
2629 assert!(log1.check(&info).is_none());
2630
2631 Ok(())
2632 }
2633
2634 #[test]
2635 fn test_dealer_priv_msg_redacted() {
2636 let mut rng = test_rng();
2637 let msg = DealerPrivMsg::new(Scalar::random(&mut rng));
2638 let debug = format!("{:?}", msg);
2639 assert!(debug.contains("REDACTED"));
2640 }
2641
2642 #[cfg(feature = "arbitrary")]
2643 mod conformance {
2644 use super::*;
2645 use commonware_codec::conformance::CodecConformance;
2646
2647 commonware_conformance::conformance_tests! {
2648 CodecConformance<Output<MinPk, ed25519::PublicKey>>,
2649 CodecConformance<DealerPubMsg<MinPk>>,
2650 CodecConformance<DealerPrivMsg>,
2651 CodecConformance<PlayerAck<ed25519::PublicKey>>,
2652 CodecConformance<AckOrReveal<ed25519::PublicKey>>,
2653 CodecConformance<DealerResult<ed25519::PublicKey>>,
2654 CodecConformance<DealerLog<MinPk, ed25519::PublicKey>>,
2655 CodecConformance<SignedDealerLog<MinPk, ed25519::PrivateKey>>,
2656 }
2657 }
2658}