wsts/
v2.rs

1use hashbrown::HashMap;
2use num_traits::{One, Zero};
3use polynomial::Polynomial;
4use rand_core::{CryptoRng, RngCore};
5use tracing::warn;
6
7use crate::{
8    common::{check_public_shares, Nonce, PolyCommitment, PublicNonce, Signature, SignatureShare},
9    compute,
10    curve::{
11        point::{Point, G},
12        scalar::Scalar,
13    },
14    errors::{AggregatorError, DkgError},
15    schnorr::ID,
16    taproot::SchnorrProof,
17    traits,
18    vss::VSS,
19};
20
21#[derive(Clone, Debug, Eq, PartialEq)]
22/// A WSTS party, which encapsulates a single polynomial, nonce, and one private key per key ID
23pub struct Party {
24    /// The party ID
25    pub party_id: u32,
26    /// The key IDs for this party
27    pub key_ids: Vec<u32>,
28    /// The public keys for this party, indexed by ID
29    num_keys: u32,
30    num_parties: u32,
31    threshold: u32,
32    f: Option<Polynomial<Scalar>>,
33    private_keys: HashMap<u32, Scalar>,
34    group_key: Point,
35    nonce: Nonce,
36}
37
38impl Party {
39    /// Construct a random Party with the passed party ID, key IDs, and parameters
40    pub fn new<RNG: RngCore + CryptoRng>(
41        party_id: u32,
42        key_ids: &[u32],
43        num_parties: u32,
44        num_keys: u32,
45        threshold: u32,
46        rng: &mut RNG,
47    ) -> Self {
48        Self {
49            party_id,
50            key_ids: key_ids.to_vec(),
51            num_keys,
52            num_parties,
53            threshold,
54            f: Some(VSS::random_poly(threshold - 1, rng)),
55            private_keys: Default::default(),
56            group_key: Point::zero(),
57            nonce: Nonce::zero(),
58        }
59    }
60
61    /// Generate and store a private nonce for a signing round
62    pub fn gen_nonce<RNG: RngCore + CryptoRng>(&mut self, rng: &mut RNG) -> PublicNonce {
63        self.nonce = Nonce::random(rng);
64        PublicNonce::from(&self.nonce)
65    }
66
67    /// Get a public commitment to the private polynomial
68    pub fn get_poly_commitment<RNG: RngCore + CryptoRng>(
69        &self,
70        rng: &mut RNG,
71    ) -> Option<PolyCommitment> {
72        if let Some(poly) = &self.f {
73            Some(PolyCommitment {
74                id: ID::new(&self.id(), &poly.data()[0], rng),
75                poly: (0..poly.data().len())
76                    .map(|i| &poly.data()[i] * G)
77                    .collect(),
78            })
79        } else {
80            warn!("get_poly_commitment called with no polynomial");
81            None
82        }
83    }
84
85    /// Get the shares of this party's private polynomial for all keys
86    pub fn get_shares(&self) -> HashMap<u32, Scalar> {
87        let mut shares = HashMap::new();
88        if let Some(poly) = &self.f {
89            for i in 1..self.num_keys + 1 {
90                shares.insert(i, poly.eval(compute::id(i)));
91            }
92        } else {
93            warn!("get_poly_commitment called with no polynomial");
94        }
95        shares
96    }
97
98    /// Compute this party's share of the group secret key, but first check that the data is valid
99    /// and consistent.  This raises an issue though: what if we have private_shares and
100    /// public_shares from different parties?
101    /// To resolve the ambiguity, assume that the public_shares represent the correct group of
102    /// parties.  
103    pub fn compute_secret(
104        &mut self,
105        private_shares: &HashMap<u32, HashMap<u32, Scalar>>,
106        public_shares: &HashMap<u32, PolyCommitment>,
107    ) -> Result<(), DkgError> {
108        self.private_keys.clear();
109        self.group_key = Point::zero();
110
111        let threshold: usize = self.threshold.try_into()?;
112
113        let mut bad_ids = Vec::new();
114        for (i, comm) in public_shares.iter() {
115            if !check_public_shares(comm, threshold) {
116                bad_ids.push(*i);
117            } else {
118                self.group_key += comm.poly[0];
119            }
120        }
121        if !bad_ids.is_empty() {
122            return Err(DkgError::BadPublicShares(bad_ids));
123        }
124
125        let mut missing_shares = Vec::new();
126        for dst_key_id in &self.key_ids {
127            for src_key_id in public_shares.keys() {
128                match private_shares.get(dst_key_id) {
129                    Some(shares) => {
130                        if shares.get(src_key_id).is_none() {
131                            missing_shares.push((*dst_key_id, *src_key_id));
132                        }
133                    }
134                    None => {
135                        missing_shares.push((*dst_key_id, *src_key_id));
136                    }
137                }
138            }
139        }
140        if !missing_shares.is_empty() {
141            return Err(DkgError::MissingPrivateShares(missing_shares));
142        }
143
144        let mut bad_shares = Vec::new();
145        for key_id in &self.key_ids {
146            if let Some(shares) = private_shares.get(key_id) {
147                for (sender, s) in shares {
148                    if let Some(comm) = public_shares.get(sender) {
149                        if s * G != compute::poly(&compute::id(*key_id), &comm.poly)? {
150                            bad_shares.push(*sender);
151                        }
152                    } else {
153                        warn!("unable to check private share from {}: no corresponding public share, even though we checked for it above", sender);
154                    }
155                }
156            } else {
157                warn!(
158                    "no private shares for key_id {}, even though we checked for it above",
159                    key_id
160                );
161            }
162        }
163        if !bad_shares.is_empty() {
164            return Err(DkgError::BadPrivateShares(bad_shares));
165        }
166
167        for key_id in &self.key_ids {
168            self.private_keys.insert(*key_id, Scalar::zero());
169            if let Some(shares) = private_shares.get(key_id) {
170                let secret = shares.values().sum();
171                self.private_keys.insert(*key_id, secret);
172            } else {
173                warn!(
174                    "no private shares for key_id {}, even though we checked for it above",
175                    key_id
176                );
177            }
178        }
179
180        Ok(())
181    }
182
183    /// Compute a Scalar from this party's ID
184    pub fn id(&self) -> Scalar {
185        compute::id(self.party_id)
186    }
187
188    /// Sign `msg` with this party's shares of the group private key, using the set of `party_ids`, `key_ids` and corresponding `nonces`
189    pub fn sign(
190        &self,
191        msg: &[u8],
192        party_ids: &[u32],
193        key_ids: &[u32],
194        nonces: &[PublicNonce],
195    ) -> SignatureShare {
196        self.sign_with_tweak(msg, party_ids, key_ids, nonces, None)
197    }
198
199    /// Sign `msg` with this party's shares of the group private key, using the set of `party_ids`, `key_ids` and corresponding `nonces` with a tweaked public key. The posible values for tweak are
200    /// None    - standard FROST signature
201    /// Some(0) - BIP-340 schnorr signature using 32-byte private key adjustments
202    /// Some(t) - BIP-340 schnorr signature with BIP-341 tweaked keys, using 32-byte private key adjustments
203    #[allow(non_snake_case)]
204    pub fn sign_with_tweak(
205        &self,
206        msg: &[u8],
207        party_ids: &[u32],
208        key_ids: &[u32],
209        nonces: &[PublicNonce],
210        tweak: Option<Scalar>,
211    ) -> SignatureShare {
212        // When using BIP-340 32-byte public keys, we have to invert the private key if the
213        // public key is odd.  But if we're also using BIP-341 tweaked keys, we have to do
214        // the same thing if the tweaked public key is odd.  In that case, only invert the
215        // public key if exactly one of the internal or tweaked public keys is odd
216        let mut cx_sign = Scalar::one();
217        let tweaked_public_key = if let Some(t) = tweak {
218            if t != Scalar::zero() {
219                let key = compute::tweaked_public_key_from_tweak(&self.group_key, t);
220                if key.has_even_y() ^ self.group_key.has_even_y() {
221                    cx_sign = -cx_sign;
222                }
223
224                key
225            } else {
226                if !self.group_key.has_even_y() {
227                    cx_sign = -cx_sign;
228                }
229                self.group_key
230            }
231        } else {
232            self.group_key
233        };
234        let (_, R) = compute::intermediate(msg, party_ids, nonces);
235        let c = compute::challenge(&tweaked_public_key, &R, msg);
236        let mut r = &self.nonce.d + &self.nonce.e * compute::binding(&self.id(), nonces, msg);
237        if tweak.is_some() && !R.has_even_y() {
238            r = -r;
239        }
240
241        let mut cx = Scalar::zero();
242        for key_id in self.key_ids.iter() {
243            cx += c * &self.private_keys[key_id] * compute::lambda(*key_id, key_ids);
244        }
245
246        cx = cx_sign * cx;
247
248        let z = r + cx;
249
250        SignatureShare {
251            id: self.party_id,
252            z_i: z,
253            key_ids: self.key_ids.clone(),
254        }
255    }
256}
257
258/// The group signature aggregator
259#[derive(Clone, Debug, PartialEq)]
260pub struct Aggregator {
261    /// The total number of keys
262    pub num_keys: u32,
263    /// The threshold of signing keys needed to construct a valid signature
264    pub threshold: u32,
265    /// The aggregate group polynomial; poly[0] is the group public key
266    pub poly: Vec<Point>,
267}
268
269impl Aggregator {
270    /// Aggregate the party signatures using a tweak.  The posible values for tweak are
271    /// None    - standard FROST signature
272    /// Some(0) - BIP-340 schnorr signature using 32-byte private key adjustments
273    /// Some(t) - BIP-340 schnorr signature with BIP-341 tweaked keys, using 32-byte private key adjustments
274    #[allow(non_snake_case)]
275    pub fn sign_with_tweak(
276        &mut self,
277        msg: &[u8],
278        nonces: &[PublicNonce],
279        sig_shares: &[SignatureShare],
280        _key_ids: &[u32],
281        tweak: Option<Scalar>,
282    ) -> Result<(Point, Signature), AggregatorError> {
283        if nonces.len() != sig_shares.len() {
284            return Err(AggregatorError::BadNonceLen(nonces.len(), sig_shares.len()));
285        }
286
287        let party_ids: Vec<u32> = sig_shares.iter().map(|ss| ss.id).collect();
288        let (_Rs, R) = compute::intermediate(msg, &party_ids, nonces);
289        let mut z = Scalar::zero();
290        let mut cx_sign = Scalar::one();
291        let aggregate_public_key = self.poly[0];
292        let tweaked_public_key = if let Some(t) = tweak {
293            if t != Scalar::zero() {
294                let key = compute::tweaked_public_key_from_tweak(&aggregate_public_key, t);
295                if !key.has_even_y() {
296                    cx_sign = -cx_sign;
297                }
298                key
299            } else {
300                aggregate_public_key
301            }
302        } else {
303            aggregate_public_key
304        };
305        let c = compute::challenge(&tweaked_public_key, &R, msg);
306        // optimistically try to create the aggregate signature without checking for bad keys or sig shares
307        for sig_share in sig_shares {
308            z += sig_share.z_i;
309        }
310
311        // The signature shares have already incorporated the private key adjustments, so we just have to add the tweak.  But the tweak itself needs to be adjusted if the tweaked public key is odd
312        if let Some(t) = tweak {
313            z += cx_sign * c * t;
314        }
315
316        let sig = Signature { R, z };
317
318        Ok((tweaked_public_key, sig))
319    }
320
321    /// Check the party signatures after a failed group signature. The posible values for tweak are
322    /// None    - standard FROST signature
323    /// Some(0) - BIP-340 schnorr signature using 32-byte private key adjustments
324    /// Some(t) - BIP-340 schnorr signature with BIP-341 tweaked keys, using 32-byte private key adjustments
325    #[allow(non_snake_case)]
326    pub fn check_signature_shares(
327        &mut self,
328        msg: &[u8],
329        nonces: &[PublicNonce],
330        sig_shares: &[SignatureShare],
331        key_ids: &[u32],
332        tweak: Option<Scalar>,
333    ) -> AggregatorError {
334        if nonces.len() != sig_shares.len() {
335            return AggregatorError::BadNonceLen(nonces.len(), sig_shares.len());
336        }
337
338        let party_ids: Vec<u32> = sig_shares.iter().map(|ss| ss.id).collect();
339        let (Rs, R) = compute::intermediate(msg, &party_ids, nonces);
340        let mut bad_party_keys = Vec::new();
341        let mut bad_party_sigs = Vec::new();
342        let aggregate_public_key = self.poly[0];
343        let tweaked_public_key = if let Some(t) = tweak {
344            if t != Scalar::zero() {
345                compute::tweaked_public_key_from_tweak(&aggregate_public_key, t)
346            } else {
347                aggregate_public_key
348            }
349        } else {
350            aggregate_public_key
351        };
352        let c = compute::challenge(&tweaked_public_key, &R, msg);
353        let mut r_sign = Scalar::one();
354        let mut cx_sign = Scalar::one();
355        if let Some(t) = tweak {
356            if !R.has_even_y() {
357                r_sign = -Scalar::one();
358            }
359            if t != Scalar::zero() {
360                if !tweaked_public_key.has_even_y() ^ !aggregate_public_key.has_even_y() {
361                    cx_sign = -Scalar::one();
362                }
363            } else if !aggregate_public_key.has_even_y() {
364                cx_sign = -Scalar::one();
365            }
366        }
367
368        for i in 0..sig_shares.len() {
369            let z_i = sig_shares[i].z_i;
370            let mut cx = Point::zero();
371
372            for key_id in &sig_shares[i].key_ids {
373                let kid = compute::id(*key_id);
374                let public_key = match compute::poly(&kid, &self.poly) {
375                    Ok(p) => p,
376                    Err(_) => {
377                        bad_party_keys.push(sig_shares[i].id);
378                        Point::zero()
379                    }
380                };
381
382                cx += compute::lambda(*key_id, key_ids) * c * public_key;
383            }
384
385            if z_i * G != (r_sign * Rs[i] + cx_sign * cx) {
386                bad_party_sigs.push(sig_shares[i].id);
387            }
388        }
389        if !bad_party_keys.is_empty() {
390            AggregatorError::BadPartyKeys(bad_party_keys)
391        } else if !bad_party_sigs.is_empty() {
392            AggregatorError::BadPartySigs(bad_party_sigs)
393        } else {
394            AggregatorError::BadGroupSig
395        }
396    }
397}
398
399impl traits::Aggregator for Aggregator {
400    /// Construct an Aggregator with the passed parameters
401    fn new(num_keys: u32, threshold: u32) -> Self {
402        Self {
403            num_keys,
404            threshold,
405            poly: Default::default(),
406        }
407    }
408
409    /// Initialize the Aggregator polynomial
410    fn init(&mut self, comms: &HashMap<u32, PolyCommitment>) -> Result<(), AggregatorError> {
411        let threshold: usize = self.threshold.try_into()?;
412        let mut poly = Vec::with_capacity(threshold);
413
414        for i in 0..poly.capacity() {
415            poly.push(Point::zero());
416            for (_, comm) in comms {
417                poly[i] += &comm.poly[i];
418            }
419        }
420
421        self.poly = poly;
422
423        Ok(())
424    }
425
426    /// Check and aggregate the party signatures
427    fn sign(
428        &mut self,
429        msg: &[u8],
430        nonces: &[PublicNonce],
431        sig_shares: &[SignatureShare],
432        key_ids: &[u32],
433    ) -> Result<Signature, AggregatorError> {
434        let (key, sig) = self.sign_with_tweak(msg, nonces, sig_shares, key_ids, None)?;
435
436        if sig.verify(&key, msg) {
437            Ok(sig)
438        } else {
439            Err(self.check_signature_shares(msg, nonces, sig_shares, key_ids, None))
440        }
441    }
442
443    /// Check and aggregate the party signatures
444    fn sign_schnorr(
445        &mut self,
446        msg: &[u8],
447        nonces: &[PublicNonce],
448        sig_shares: &[SignatureShare],
449        key_ids: &[u32],
450    ) -> Result<SchnorrProof, AggregatorError> {
451        let tweak = Scalar::from(0);
452        let (key, sig) = self.sign_with_tweak(msg, nonces, sig_shares, key_ids, Some(tweak))?;
453        let proof = SchnorrProof::new(&sig);
454
455        if proof.verify(&key.x(), msg) {
456            Ok(proof)
457        } else {
458            Err(self.check_signature_shares(msg, nonces, sig_shares, key_ids, Some(tweak)))
459        }
460    }
461
462    /// Check and aggregate the party signatures
463    fn sign_taproot(
464        &mut self,
465        msg: &[u8],
466        nonces: &[PublicNonce],
467        sig_shares: &[SignatureShare],
468        key_ids: &[u32],
469        merkle_root: Option<[u8; 32]>,
470    ) -> Result<SchnorrProof, AggregatorError> {
471        let tweak = compute::tweak(&self.poly[0], merkle_root);
472        let (key, sig) = self.sign_with_tweak(msg, nonces, sig_shares, key_ids, Some(tweak))?;
473        let proof = SchnorrProof::new(&sig);
474
475        if proof.verify(&key.x(), msg) {
476            Ok(proof)
477        } else {
478            Err(self.check_signature_shares(msg, nonces, sig_shares, key_ids, Some(tweak)))
479        }
480    }
481}
482
483/// Typedef so we can use the same tokens for v1 and v2
484pub type Signer = Party;
485
486impl traits::Signer for Party {
487    fn new<RNG: RngCore + CryptoRng>(
488        party_id: u32,
489        key_ids: &[u32],
490        num_signers: u32,
491        num_keys: u32,
492        threshold: u32,
493        rng: &mut RNG,
494    ) -> Self {
495        Party::new(party_id, key_ids, num_signers, num_keys, threshold, rng)
496    }
497
498    fn load(state: &traits::SignerState) -> Self {
499        // v2 signer contains single party
500        assert_eq!(state.parties.len(), 1);
501
502        let party_state = &state.parties[0].1;
503
504        Self {
505            party_id: state.id,
506            key_ids: state.key_ids.clone(),
507            num_keys: state.num_keys,
508            num_parties: state.num_parties,
509            threshold: state.threshold,
510            f: party_state.polynomial.clone(),
511            private_keys: party_state
512                .private_keys
513                .iter()
514                .map(|(k, v)| (*k, *v))
515                .collect(),
516            group_key: state.group_key,
517            nonce: party_state.nonce.clone(),
518        }
519    }
520
521    fn save(&self) -> traits::SignerState {
522        let party_state = traits::PartyState {
523            polynomial: self.f.clone(),
524            private_keys: self.private_keys.iter().map(|(k, v)| (*k, *v)).collect(),
525            nonce: self.nonce.clone(),
526        };
527        traits::SignerState {
528            id: self.party_id,
529            key_ids: self.key_ids.clone(),
530            num_keys: self.num_keys,
531            num_parties: self.num_parties,
532            threshold: self.threshold,
533            group_key: self.group_key,
534            parties: vec![(self.party_id, party_state)],
535        }
536    }
537
538    fn get_id(&self) -> u32 {
539        self.party_id
540    }
541
542    fn get_key_ids(&self) -> Vec<u32> {
543        self.key_ids.clone()
544    }
545
546    fn get_num_parties(&self) -> u32 {
547        self.num_parties
548    }
549
550    fn get_poly_commitments<RNG: RngCore + CryptoRng>(&self, rng: &mut RNG) -> Vec<PolyCommitment> {
551        if let Some(poly) = self.get_poly_commitment(rng) {
552            vec![poly.clone()]
553        } else {
554            vec![]
555        }
556    }
557
558    fn reset_polys<RNG: RngCore + CryptoRng>(&mut self, rng: &mut RNG) {
559        self.f = Some(VSS::random_poly(self.threshold - 1, rng));
560    }
561
562    fn clear_polys(&mut self) {
563        self.f = None;
564    }
565
566    fn get_shares(&self) -> HashMap<u32, HashMap<u32, Scalar>> {
567        let mut shares = HashMap::new();
568
569        shares.insert(self.party_id, self.get_shares());
570
571        shares
572    }
573
574    fn compute_secrets(
575        &mut self,
576        private_shares: &HashMap<u32, HashMap<u32, Scalar>>,
577        polys: &HashMap<u32, PolyCommitment>,
578    ) -> Result<(), HashMap<u32, DkgError>> {
579        // go through the shares, looking for this party's
580        let mut key_shares = HashMap::new();
581        for dest_key_id in self.get_key_ids() {
582            let mut shares = HashMap::new();
583            for (src_party_id, signer_shares) in private_shares.iter() {
584                if let Some(signer_share) = signer_shares.get(&dest_key_id) {
585                    shares.insert(*src_party_id, *signer_share);
586                }
587            }
588            key_shares.insert(dest_key_id, shares);
589        }
590
591        match self.compute_secret(&key_shares, polys) {
592            Ok(()) => Ok(()),
593            Err(dkg_error) => {
594                let mut dkg_errors = HashMap::new();
595                dkg_errors.insert(self.party_id, dkg_error);
596                Err(dkg_errors)
597            }
598        }
599    }
600
601    fn gen_nonces<RNG: RngCore + CryptoRng>(&mut self, rng: &mut RNG) -> Vec<PublicNonce> {
602        vec![self.gen_nonce(rng)]
603    }
604
605    fn compute_intermediate(
606        msg: &[u8],
607        signer_ids: &[u32],
608        _key_ids: &[u32],
609        nonces: &[PublicNonce],
610    ) -> (Vec<Point>, Point) {
611        compute::intermediate(msg, signer_ids, nonces)
612    }
613
614    fn sign(
615        &self,
616        msg: &[u8],
617        signer_ids: &[u32],
618        key_ids: &[u32],
619        nonces: &[PublicNonce],
620    ) -> Vec<SignatureShare> {
621        vec![self.sign(msg, signer_ids, key_ids, nonces)]
622    }
623
624    fn sign_schnorr(
625        &self,
626        msg: &[u8],
627        signer_ids: &[u32],
628        key_ids: &[u32],
629        nonces: &[PublicNonce],
630    ) -> Vec<SignatureShare> {
631        vec![self.sign_with_tweak(msg, signer_ids, key_ids, nonces, Some(Scalar::from(0)))]
632    }
633
634    fn sign_taproot(
635        &self,
636        msg: &[u8],
637        signer_ids: &[u32],
638        key_ids: &[u32],
639        nonces: &[PublicNonce],
640        merkle_root: Option<[u8; 32]>,
641    ) -> Vec<SignatureShare> {
642        let tweak = compute::tweak(&self.group_key, merkle_root);
643        vec![self.sign_with_tweak(msg, signer_ids, key_ids, nonces, Some(tweak))]
644    }
645}
646
647/// Helper functions for tests
648pub mod test_helpers {
649    use crate::common::{PolyCommitment, PublicNonce};
650    use crate::errors::DkgError;
651    use crate::traits::Signer;
652    use crate::v2;
653    use crate::v2::SignatureShare;
654
655    use hashbrown::HashMap;
656    use rand_core::{CryptoRng, RngCore};
657
658    /// Run a distributed key generation round
659    pub fn dkg<RNG: RngCore + CryptoRng>(
660        signers: &mut [v2::Party],
661        rng: &mut RNG,
662    ) -> Result<HashMap<u32, PolyCommitment>, HashMap<u32, DkgError>> {
663        let mut polys: HashMap<u32, PolyCommitment> = Default::default();
664        for signer in signers.iter() {
665            if let Some(poly) = signer.get_poly_commitment(rng) {
666                polys.insert(signer.get_id(), poly);
667            }
668        }
669
670        // each party broadcasts their commitments
671        let mut broadcast_shares = Vec::new();
672        for party in signers.iter() {
673            broadcast_shares.push((party.party_id, party.get_shares()));
674        }
675
676        // each party collects its shares from the broadcasts
677        // maybe this should collect into a hashmap first?
678        let mut secret_errors = HashMap::new();
679        for party in signers.iter_mut() {
680            let mut party_shares = HashMap::new();
681            for key_id in party.key_ids.clone() {
682                let mut key_shares = HashMap::new();
683
684                for (id, shares) in &broadcast_shares {
685                    if let Some(share) = shares.get(&key_id) {
686                        key_shares.insert(*id, *share);
687                    }
688                }
689
690                party_shares.insert(key_id, key_shares);
691            }
692
693            if let Err(secret_error) = party.compute_secret(&party_shares, &polys) {
694                secret_errors.insert(party.party_id, secret_error);
695            }
696        }
697
698        if secret_errors.is_empty() {
699            Ok(polys)
700        } else {
701            Err(secret_errors)
702        }
703    }
704
705    /// Run a signing round for the passed `msg`
706    pub fn sign<RNG: RngCore + CryptoRng>(
707        msg: &[u8],
708        signers: &mut [v2::Party],
709        rng: &mut RNG,
710    ) -> (Vec<PublicNonce>, Vec<SignatureShare>, Vec<u32>) {
711        let party_ids: Vec<u32> = signers.iter().map(|s| s.party_id).collect();
712        let key_ids: Vec<u32> = signers.iter().flat_map(|s| s.key_ids.clone()).collect();
713        let nonces: Vec<PublicNonce> = signers.iter_mut().map(|s| s.gen_nonce(rng)).collect();
714        let shares = signers
715            .iter()
716            .map(|s| s.sign(msg, &party_ids, &key_ids, &nonces))
717            .collect();
718
719        (nonces, shares, key_ids)
720    }
721}
722
723#[cfg(test)]
724mod tests {
725    use crate::util::create_rng;
726    use crate::{
727        traits::{
728            self, test_helpers::run_compute_secrets_missing_private_shares, Aggregator, Signer,
729        },
730        v2,
731    };
732
733    #[test]
734    fn party_save_load() {
735        let mut rng = create_rng();
736        let key_ids = [1, 2, 3];
737        let n: u32 = 10;
738        let t: u32 = 7;
739
740        let signer = v2::Party::new(0, &key_ids, 1, n, t, &mut rng);
741
742        let state = signer.save();
743        let loaded = v2::Party::load(&state);
744
745        assert_eq!(signer, loaded);
746    }
747
748    #[test]
749    fn clear_polys() {
750        let mut rng = create_rng();
751        let key_ids = [1, 2, 3];
752        let n: u32 = 10;
753        let t: u32 = 7;
754
755        let mut signer = v2::Party::new(0, &key_ids, 1, n, t, &mut rng);
756
757        assert_eq!(signer.get_poly_commitments(&mut rng).len(), 1);
758        assert_eq!(signer.get_shares().len(), usize::try_from(n).unwrap());
759
760        signer.clear_polys();
761
762        assert_eq!(signer.get_poly_commitments(&mut rng).len(), 0);
763        assert_eq!(signer.get_shares().len(), 0);
764    }
765
766    #[test]
767    fn aggregator_sign() {
768        let mut rng = create_rng();
769        let msg = "It was many and many a year ago".as_bytes();
770        let n_k: u32 = 10;
771        let t: u32 = 7;
772        let party_key_ids: Vec<Vec<u32>> = [
773            [1, 2, 3].to_vec(),
774            [4, 5].to_vec(),
775            [6, 7, 8].to_vec(),
776            [9, 10].to_vec(),
777        ]
778        .to_vec();
779        let n_p = party_key_ids.len().try_into().unwrap();
780        let mut signers: Vec<v2::Party> = party_key_ids
781            .iter()
782            .enumerate()
783            .map(|(pid, pkids)| {
784                v2::Party::new(pid.try_into().unwrap(), pkids, n_p, n_k, t, &mut rng)
785            })
786            .collect();
787
788        let comms = match traits::test_helpers::dkg(&mut signers, &mut rng) {
789            Ok(comms) => comms,
790            Err(secret_errors) => {
791                panic!("Got secret errors from DKG: {:?}", secret_errors);
792            }
793        };
794
795        // signers [0,1,3] who have t keys
796        {
797            let mut signers = [signers[0].clone(), signers[1].clone(), signers[3].clone()].to_vec();
798            let mut sig_agg = v2::Aggregator::new(n_k, t);
799
800            sig_agg.init(&comms).expect("aggregator init failed");
801
802            let (nonces, sig_shares, key_ids) = v2::test_helpers::sign(msg, &mut signers, &mut rng);
803            if let Err(e) = sig_agg.sign(msg, &nonces, &sig_shares, &key_ids) {
804                panic!("Aggregator sign failed: {:?}", e);
805            }
806        }
807    }
808
809    #[test]
810    /// Run a distributed key generation round with not enough shares
811    pub fn run_compute_secrets_missing_shares() {
812        run_compute_secrets_missing_private_shares::<v2::Signer>()
813    }
814
815    #[test]
816    /// Run DKG and aggregator init with a bad polynomial length
817    pub fn bad_polynomial_length() {
818        let gt = |t| t + 1;
819        let lt = |t| t - 1;
820        traits::test_helpers::bad_polynomial_length::<v2::Signer, _>(gt);
821        traits::test_helpers::bad_polynomial_length::<v2::Signer, _>(lt);
822    }
823
824    #[test]
825    /// Run DKG and aggregator init with a bad polynomial commitment
826    pub fn bad_polynomial_commitment() {
827        traits::test_helpers::bad_polynomial_commitment::<v2::Signer>();
828    }
829}