Skip to main content

wsts/
v2.rs

1use hashbrown::{HashMap, HashSet};
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 validate_party_id(
615        signer_id: u32,
616        party_id: u32,
617        _signer_key_ids: &HashMap<u32, HashSet<u32>>,
618    ) -> bool {
619        signer_id == party_id
620    }
621
622    fn sign(
623        &self,
624        msg: &[u8],
625        signer_ids: &[u32],
626        key_ids: &[u32],
627        nonces: &[PublicNonce],
628    ) -> Vec<SignatureShare> {
629        vec![self.sign(msg, signer_ids, key_ids, nonces)]
630    }
631
632    fn sign_schnorr(
633        &self,
634        msg: &[u8],
635        signer_ids: &[u32],
636        key_ids: &[u32],
637        nonces: &[PublicNonce],
638    ) -> Vec<SignatureShare> {
639        vec![self.sign_with_tweak(msg, signer_ids, key_ids, nonces, Some(Scalar::from(0)))]
640    }
641
642    fn sign_taproot(
643        &self,
644        msg: &[u8],
645        signer_ids: &[u32],
646        key_ids: &[u32],
647        nonces: &[PublicNonce],
648        merkle_root: Option<[u8; 32]>,
649    ) -> Vec<SignatureShare> {
650        let tweak = compute::tweak(&self.group_key, merkle_root);
651        vec![self.sign_with_tweak(msg, signer_ids, key_ids, nonces, Some(tweak))]
652    }
653}
654
655/// Helper functions for tests
656pub mod test_helpers {
657    use crate::common::{PolyCommitment, PublicNonce};
658    use crate::errors::DkgError;
659    use crate::traits::Signer;
660    use crate::v2;
661    use crate::v2::SignatureShare;
662
663    use hashbrown::HashMap;
664    use rand_core::{CryptoRng, RngCore};
665
666    /// Run a distributed key generation round
667    pub fn dkg<RNG: RngCore + CryptoRng>(
668        signers: &mut [v2::Party],
669        rng: &mut RNG,
670    ) -> Result<HashMap<u32, PolyCommitment>, HashMap<u32, DkgError>> {
671        let mut polys: HashMap<u32, PolyCommitment> = Default::default();
672        for signer in signers.iter() {
673            if let Some(poly) = signer.get_poly_commitment(rng) {
674                polys.insert(signer.get_id(), poly);
675            }
676        }
677
678        // each party broadcasts their commitments
679        let mut broadcast_shares = Vec::new();
680        for party in signers.iter() {
681            broadcast_shares.push((party.party_id, party.get_shares()));
682        }
683
684        // each party collects its shares from the broadcasts
685        // maybe this should collect into a hashmap first?
686        let mut secret_errors = HashMap::new();
687        for party in signers.iter_mut() {
688            let mut party_shares = HashMap::new();
689            for key_id in party.key_ids.clone() {
690                let mut key_shares = HashMap::new();
691
692                for (id, shares) in &broadcast_shares {
693                    if let Some(share) = shares.get(&key_id) {
694                        key_shares.insert(*id, *share);
695                    }
696                }
697
698                party_shares.insert(key_id, key_shares);
699            }
700
701            if let Err(secret_error) = party.compute_secret(&party_shares, &polys) {
702                secret_errors.insert(party.party_id, secret_error);
703            }
704        }
705
706        if secret_errors.is_empty() {
707            Ok(polys)
708        } else {
709            Err(secret_errors)
710        }
711    }
712
713    /// Run a signing round for the passed `msg`
714    pub fn sign<RNG: RngCore + CryptoRng>(
715        msg: &[u8],
716        signers: &mut [v2::Party],
717        rng: &mut RNG,
718    ) -> (Vec<PublicNonce>, Vec<SignatureShare>, Vec<u32>) {
719        let party_ids: Vec<u32> = signers.iter().map(|s| s.party_id).collect();
720        let key_ids: Vec<u32> = signers.iter().flat_map(|s| s.key_ids.clone()).collect();
721        let nonces: Vec<PublicNonce> = signers.iter_mut().map(|s| s.gen_nonce(rng)).collect();
722        let shares = signers
723            .iter()
724            .map(|s| s.sign(msg, &party_ids, &key_ids, &nonces))
725            .collect();
726
727        (nonces, shares, key_ids)
728    }
729}
730
731#[cfg(test)]
732mod tests {
733    use hashbrown::{HashMap, HashSet};
734
735    use crate::util::create_rng;
736    use crate::{
737        traits::{
738            self, test_helpers::run_compute_secrets_missing_private_shares, Aggregator, Signer,
739        },
740        v2,
741    };
742
743    #[test]
744    fn party_save_load() {
745        let mut rng = create_rng();
746        let key_ids = [1, 2, 3];
747        let n: u32 = 10;
748        let t: u32 = 7;
749
750        let signer = v2::Party::new(0, &key_ids, 1, n, t, &mut rng);
751
752        let state = signer.save();
753        let loaded = v2::Party::load(&state);
754
755        assert_eq!(signer, loaded);
756    }
757
758    #[test]
759    fn clear_polys() {
760        let mut rng = create_rng();
761        let key_ids = [1, 2, 3];
762        let n: u32 = 10;
763        let t: u32 = 7;
764
765        let mut signer = v2::Party::new(0, &key_ids, 1, n, t, &mut rng);
766
767        assert_eq!(signer.get_poly_commitments(&mut rng).len(), 1);
768        assert_eq!(signer.get_shares().len(), usize::try_from(n).unwrap());
769
770        signer.clear_polys();
771
772        assert_eq!(signer.get_poly_commitments(&mut rng).len(), 0);
773        assert_eq!(signer.get_shares().len(), 0);
774    }
775
776    #[test]
777    fn aggregator_sign() {
778        let mut rng = create_rng();
779        let msg = "It was many and many a year ago".as_bytes();
780        let n_k: u32 = 10;
781        let t: u32 = 7;
782        let party_key_ids: Vec<Vec<u32>> = [
783            [1, 2, 3].to_vec(),
784            [4, 5].to_vec(),
785            [6, 7, 8].to_vec(),
786            [9, 10].to_vec(),
787        ]
788        .to_vec();
789        let n_p = party_key_ids.len().try_into().unwrap();
790        let mut signers: Vec<v2::Party> = party_key_ids
791            .iter()
792            .enumerate()
793            .map(|(pid, pkids)| {
794                v2::Party::new(pid.try_into().unwrap(), pkids, n_p, n_k, t, &mut rng)
795            })
796            .collect();
797
798        let comms = match traits::test_helpers::dkg(&mut signers, &mut rng) {
799            Ok(comms) => comms,
800            Err(secret_errors) => {
801                panic!("Got secret errors from DKG: {:?}", secret_errors);
802            }
803        };
804
805        // signers [0,1,3] who have t keys
806        {
807            let mut signers = [signers[0].clone(), signers[1].clone(), signers[3].clone()].to_vec();
808            let mut sig_agg = v2::Aggregator::new(n_k, t);
809
810            sig_agg.init(&comms).expect("aggregator init failed");
811
812            let (nonces, sig_shares, key_ids) = v2::test_helpers::sign(msg, &mut signers, &mut rng);
813            if let Err(e) = sig_agg.sign(msg, &nonces, &sig_shares, &key_ids) {
814                panic!("Aggregator sign failed: {:?}", e);
815            }
816        }
817    }
818
819    #[test]
820    /// Run a distributed key generation round with not enough shares
821    pub fn run_compute_secrets_missing_shares() {
822        run_compute_secrets_missing_private_shares::<v2::Signer>()
823    }
824
825    #[test]
826    /// Run DKG and aggregator init with a bad polynomial length
827    pub fn bad_polynomial_length() {
828        let gt = |t| t + 1;
829        let lt = |t| t - 1;
830        traits::test_helpers::bad_polynomial_length::<v2::Signer, _>(gt);
831        traits::test_helpers::bad_polynomial_length::<v2::Signer, _>(lt);
832    }
833
834    #[test]
835    /// Run DKG and aggregator init with a bad polynomial commitment
836    pub fn bad_polynomial_commitment() {
837        traits::test_helpers::bad_polynomial_commitment::<v2::Signer>();
838    }
839
840    #[test]
841    /// Check that party_ids can be properly validated
842    fn validate_party_id() {
843        let mut signer_key_ids = HashMap::new();
844        let mut key_ids = HashSet::new();
845
846        key_ids.insert(1);
847        signer_key_ids.insert(0, key_ids);
848
849        assert!(v2::Signer::validate_party_id(0, 0, &signer_key_ids));
850        assert!(!v2::Signer::validate_party_id(0, 1, &signer_key_ids));
851    }
852}