use rand::SeedableRng; use rand_core::CryptoRngCore; #[cfg(test)] use frost_core::{Group, keys::SigningShare}; #[cfg(test)] use crate::crypto::polynomials::Polynomial; use crate::participants::Participant; #[cfg(test)] use crate::test_utils::participants::generate_participants_with_random_ids; use crate::test_utils::{GenOutput, GenProtocol, run_protocol}; use crate::thresholds::ReconstructionThreshold; use crate::{Ciphersuite, KeygenOutput, VerifyingKey, keygen, refresh, reshare}; // +++++++++++++++++ DKG Functions +++++++++++++++++ // type DKGGenProtocol = GenProtocol>; /// Runs distributed keygen /// If the protocol succeeds, returns a sorted vector based on participants id /// Runs distributed keygen /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_keygen( participants: &[Participant], threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> GenOutput { let mut protocols: DKGGenProtocol = Vec::with_capacity(participants.len()); for p in participants { let rng_p = R::seed_from_u64(rng.next_u64()); let protocol = keygen::(participants, *p, threshold, rng_p).unwrap(); protocols.push((*p, Box::new(protocol))); } run_protocol(protocols).unwrap() } /// Runs distributed refresh /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_refresh( participants: &[Participant], keys: &[(Participant, KeygenOutput)], threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> GenOutput { let mut protocols: DKGGenProtocol = Vec::with_capacity(participants.len()); for (p, out) in keys { let rng_p = R::seed_from_u64(rng.next_u64()); let protocol = refresh::( Some(out.private_share), out.public_key, participants, threshold, *p, rng_p, ) .unwrap(); protocols.push((*p, Box::new(protocol))); } run_protocol(protocols).unwrap() } /// Runs distributed reshare /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_reshare( participants: &[Participant], pub_key: &VerifyingKey, keys: &[(Participant, KeygenOutput)], old_threshold: impl Into + Copy + Send + 'static, new_threshold: impl Into + Copy + Send + 'static, new_participants: &[Participant], rng: &mut R, ) -> GenOutput { assert!(!new_participants.is_empty()); let mut setup = vec![]; for new_participant in new_participants { let mut is_break = false; for (p, k) in keys { if p == new_participant { setup.push((*p, (Some(k.private_share), k.public_key))); is_break = true; break; } } if !is_break { setup.push((*new_participant, (None, *pub_key))); } } let mut protocols: DKGGenProtocol = Vec::with_capacity(participants.len()); for (p, out) in &setup { let rng_p = R::seed_from_u64(rng.next_u64()); let protocol = reshare( participants, old_threshold, out.0, out.1, new_participants, new_threshold, *p, rng_p, ) .unwrap(); protocols.push((*p, Box::new(protocol))); } run_protocol(protocols).unwrap() } /// Assert that each participant has the same view of the public key pub fn assert_public_key_invariant( participants: &[(Participant, KeygenOutput)], ) { let vk = participants.first().unwrap().1.public_key; if participants .iter() .any(|(_, key_pair)| key_pair.public_key != vk) { panic!("public key package is not the same for all participants"); } } /// Generates a random polynomial of given degree and derives the corresponding /// public verifying key. Returns both the polynomial (for per-participant share /// derivation) and the verifying key. #[cfg(test)] pub fn generate_test_keys( degree: usize, rng: &mut impl CryptoRngCore, ) -> (Polynomial, VerifyingKey) { let f = Polynomial::::generate_polynomial(None, degree, rng).unwrap(); let secret = f.eval_at_zero().unwrap().0; ( f, VerifyingKey::new(::generator() * secret), ) } /// Constructs a [`KeygenOutput`] for a single participant from a shared /// polynomial and public verifying key. #[cfg(test)] pub fn make_keygen_output( f: &Polynomial, pk: &VerifyingKey, p: Participant, ) -> KeygenOutput { KeygenOutput { private_share: SigningShare::new(f.eval_at_participant(p).unwrap().0), public_key: *pk, } } /// Centralized key generation for testing: generates random participant IDs /// and creates `KeygenOutput` for each using polynomial evaluation. #[cfg(test)] pub fn build_frost_key_packages_with_dealer( max_signers: u16, min_signers: u16, rng: &mut impl CryptoRngCore, ) -> GenOutput { let participants = generate_participants_with_random_ids(max_signers as usize, rng); let (f, pk) = generate_test_keys::((min_signers - 1) as usize, rng); participants .iter() .map(|p| (*p, make_keygen_output(&f, &pk, *p))) .collect() }