wsts/state_machine/coordinator/
fire.rs

1use hashbrown::{HashMap, HashSet};
2use std::{collections::BTreeMap, time::Instant};
3use tracing::{debug, error, info, warn};
4
5use crate::{
6    common::{check_public_shares, PolyCommitment, PublicNonce, Signature, SignatureShare},
7    compute,
8    curve::{
9        ecdsa,
10        point::{Point, G},
11        scalar::Scalar,
12    },
13    errors::AggregatorError,
14    net::{
15        DkgBegin, DkgEnd, DkgEndBegin, DkgFailure, DkgPrivateBegin, DkgPrivateShares,
16        DkgPublicShares, DkgStatus, Message, NonceRequest, NonceResponse, Packet, Signable,
17        SignatureShareRequest, SignatureType,
18    },
19    state_machine::{
20        coordinator::{
21            Config, Coordinator as CoordinatorTrait, Error, SavedState, SignRoundInfo, State,
22        },
23        DkgError, OperationResult, SignError, StateMachine,
24    },
25    taproot::SchnorrProof,
26    traits::Aggregator as AggregatorTrait,
27    util::{decrypt, make_shared_secret_from_key},
28};
29
30/// The coordinator for the FIRE algorithm
31#[derive(Clone, Debug, PartialEq)]
32pub struct Coordinator<Aggregator: AggregatorTrait> {
33    /// common config fields
34    config: Config,
35    /// current DKG round ID
36    pub current_dkg_id: u64,
37    /// current signing round ID
38    pub current_sign_id: u64,
39    /// current signing iteration ID
40    pub current_sign_iter_id: u64,
41    dkg_public_shares: BTreeMap<u32, DkgPublicShares>,
42    dkg_private_shares: BTreeMap<u32, DkgPrivateShares>,
43    dkg_end_messages: BTreeMap<u32, DkgEnd>,
44    /// the current view of a successful DKG's participants' commitments
45    pub party_polynomials: HashMap<u32, PolyCommitment>,
46    signature_shares: BTreeMap<u32, Vec<SignatureShare>>,
47    message_nonces: BTreeMap<Vec<u8>, SignRoundInfo>,
48    /// aggregate public key
49    pub aggregate_public_key: Option<Point>,
50    signature: Option<Signature>,
51    schnorr_proof: Option<SchnorrProof>,
52    /// which signers we're currently waiting on for DKG
53    pub dkg_wait_signer_ids: HashSet<u32>,
54    /// the bytes that we're signing
55    pub message: Vec<u8>,
56    /// current state of the state machine
57    pub state: State,
58    /// Aggregator object
59    aggregator: Aggregator,
60    nonce_start: Option<Instant>,
61    dkg_public_start: Option<Instant>,
62    dkg_private_start: Option<Instant>,
63    dkg_end_start: Option<Instant>,
64    sign_start: Option<Instant>,
65    malicious_signer_ids: HashSet<u32>,
66    malicious_dkg_signer_ids: HashSet<u32>,
67    /// coordinator public key
68    pub coordinator_public_key: Option<ecdsa::PublicKey>,
69}
70
71impl<Aggregator: AggregatorTrait> Coordinator<Aggregator> {
72    /// Check the timeout
73    pub fn process_timeout(&mut self) -> Result<(Option<Packet>, Option<OperationResult>), Error> {
74        let now = Instant::now();
75        match self.state.clone() {
76            State::Idle => {}
77            State::DkgPublicDistribute => {}
78            State::DkgPublicGather => {
79                if let Some(start) = self.dkg_public_start {
80                    if let Some(timeout) = self.config.dkg_public_timeout {
81                        if now.duration_since(start) > timeout {
82                            // check dkg_threshold to determine if we can continue
83                            let dkg_size = self.compute_dkg_public_size()?;
84
85                            if self.config.dkg_threshold > dkg_size {
86                                error!("Timeout gathering DkgPublicShares for dkg round {} signing round {} iteration {}, dkg_threshold not met ({dkg_size}/{}), unable to continue", self.current_dkg_id, self.current_sign_id, self.current_sign_iter_id, self.config.dkg_threshold);
87                                let wait = self.dkg_wait_signer_ids.iter().copied().collect();
88                                return Ok((
89                                    None,
90                                    Some(OperationResult::DkgError(DkgError::DkgPublicTimeout(
91                                        wait,
92                                    ))),
93                                ));
94                            } else {
95                                // we hit the timeout but met the threshold, continue
96                                warn!("Timeout gathering DkgPublicShares for dkg round {} signing round {} iteration {}, dkg_threshold was met ({dkg_size}/{}), ", self.current_dkg_id, self.current_sign_id, self.current_sign_iter_id, self.config.dkg_threshold);
97                                self.public_shares_gathered()?;
98                                let packet = self.start_private_shares()?;
99                                return Ok((Some(packet), None));
100                            }
101                        }
102                    }
103                }
104            }
105            State::DkgPrivateDistribute => {}
106            State::DkgPrivateGather => {
107                if let Some(start) = self.dkg_private_start {
108                    if let Some(timeout) = self.config.dkg_private_timeout {
109                        if now.duration_since(start) > timeout {
110                            // check dkg_threshold to determine if we can continue
111                            let dkg_size = self.compute_dkg_private_size()?;
112
113                            if self.config.dkg_threshold > dkg_size {
114                                error!("Timeout gathering DkgPrivateShares for dkg round {} signing round {} iteration {}, dkg_threshold not met ({dkg_size}/{}), unable to continue", self.current_dkg_id, self.current_sign_id, self.current_sign_iter_id, self.config.dkg_threshold);
115                                let wait = self.dkg_wait_signer_ids.iter().copied().collect();
116                                return Ok((
117                                    None,
118                                    Some(OperationResult::DkgError(DkgError::DkgPrivateTimeout(
119                                        wait,
120                                    ))),
121                                ));
122                            } else {
123                                // we hit the timeout but met the threshold, continue
124                                warn!("Timeout gathering DkgPrivateShares for dkg round {} signing round {} iteration {}, dkg_threshold was met ({dkg_size}/{}), ", self.current_dkg_id, self.current_sign_id, self.current_sign_iter_id, self.config.dkg_threshold);
125                                self.private_shares_gathered()?;
126                                let packet = self.start_dkg_end()?;
127                                return Ok((Some(packet), None));
128                            }
129                        }
130                    }
131                }
132            }
133            State::DkgEndDistribute => {}
134            State::DkgEndGather => {
135                if let Some(start) = self.dkg_end_start {
136                    if let Some(timeout) = self.config.dkg_end_timeout {
137                        if now.duration_since(start) > timeout {
138                            error!("Timeout gathering DkgEnd for dkg round {} signing round {} iteration {}, unable to continue", self.current_dkg_id, self.current_sign_id, self.current_sign_iter_id);
139                            let wait = self.dkg_wait_signer_ids.iter().copied().collect();
140                            return Ok((
141                                None,
142                                Some(OperationResult::DkgError(DkgError::DkgEndTimeout(wait))),
143                            ));
144                        }
145                    }
146                }
147            }
148            State::NonceRequest(_signature_type) => {}
149            State::SigShareRequest(_signature_type) => {}
150            State::NonceGather(_signature_type) => {
151                if let Some(start) = self.nonce_start {
152                    if let Some(timeout) = self.config.nonce_timeout {
153                        if now.duration_since(start) > timeout {
154                            error!("Timeout gathering nonces for signing round {} iteration {}, unable to continue", self.current_sign_id, self.current_sign_iter_id);
155                            let recv = self
156                                .message_nonces
157                                .get(&self.message)
158                                .ok_or(Error::MissingMessageNonceInfo)?
159                                .sign_wait_signer_ids
160                                .iter()
161                                .copied()
162                                .collect();
163                            let mal = self.malicious_signer_ids.iter().copied().collect();
164                            return Ok((
165                                None,
166                                Some(OperationResult::SignError(SignError::NonceTimeout(
167                                    recv, mal,
168                                ))),
169                            ));
170                        }
171                    }
172                }
173            }
174            State::SigShareGather(signature_type) => {
175                if let Some(start) = self.sign_start {
176                    if let Some(timeout) = self.config.sign_timeout {
177                        if now.duration_since(start) > timeout {
178                            warn!("Timeout gathering signature shares for signing round {} iteration {}", self.current_sign_id, self.current_sign_iter_id);
179                            for signer_id in &self
180                                .message_nonces
181                                .get(&self.message)
182                                .ok_or(Error::MissingMessageNonceInfo)?
183                                .sign_wait_signer_ids
184                            {
185                                warn!("Mark signer {signer_id} as malicious");
186                                self.malicious_signer_ids.insert(*signer_id);
187                            }
188
189                            let num_malicious_keys: u32 =
190                                self.compute_num_key_ids(self.malicious_signer_ids.iter())?;
191
192                            if self.config.num_keys - num_malicious_keys < self.config.threshold {
193                                error!("Insufficient non-malicious signers, unable to continue");
194                                let mal = self.malicious_signer_ids.iter().copied().collect();
195                                return Ok((
196                                    None,
197                                    Some(OperationResult::SignError(
198                                        SignError::InsufficientSigners(mal),
199                                    )),
200                                ));
201                            }
202
203                            self.move_to(State::NonceRequest(signature_type))?;
204                            let packet = self.request_nonces(signature_type)?;
205                            return Ok((Some(packet), None));
206                        }
207                    }
208                }
209            }
210        }
211        Ok((None, None))
212    }
213
214    /// Process the message inside the passed packet
215    pub fn process_message(
216        &mut self,
217        packet: &Packet,
218    ) -> Result<(Option<Packet>, Option<OperationResult>), Error> {
219        if self.config.verify_packet_sigs {
220            let Some(coordinator_public_key) = self.coordinator_public_key else {
221                return Err(Error::MissingCoordinatorPublicKey);
222            };
223            if !packet.verify(&self.config.public_keys, &coordinator_public_key) {
224                return Err(Error::InvalidPacketSignature);
225            }
226        }
227        loop {
228            match self.state.clone() {
229                State::Idle => {
230                    // Did we receive a coordinator message?
231                    if let Message::DkgBegin(dkg_begin) = &packet.msg {
232                        if self.current_dkg_id == dkg_begin.dkg_id {
233                            // We have already processed this DKG round
234                            return Ok((None, None));
235                        }
236                        // use dkg_id from DkgBegin
237                        let packet = self.start_dkg_round(Some(dkg_begin.dkg_id))?;
238                        return Ok((Some(packet), None));
239                    } else if let Message::NonceRequest(nonce_request) = &packet.msg {
240                        if self.current_sign_id == nonce_request.sign_id {
241                            // We have already processed this sign round
242                            return Ok((None, None));
243                        }
244                        self.current_sign_iter_id = nonce_request.sign_iter_id.wrapping_sub(1);
245                        // use sign_id from NonceRequest
246                        let packet = self.start_signing_round(
247                            nonce_request.message.as_slice(),
248                            nonce_request.signature_type,
249                            Some(nonce_request.sign_id),
250                        )?;
251                        return Ok((Some(packet), None));
252                    }
253                    return Ok((None, None));
254                }
255                State::DkgPublicDistribute => {
256                    let packet = self.start_public_shares()?;
257                    return Ok((Some(packet), None));
258                }
259                State::DkgPublicGather => {
260                    self.gather_public_shares(packet)?;
261                    if self.state == State::DkgPublicGather {
262                        // We need more data
263                        return Ok((None, None));
264                    }
265                }
266                State::DkgPrivateDistribute => {
267                    let packet = self.start_private_shares()?;
268                    return Ok((Some(packet), None));
269                }
270                State::DkgPrivateGather => {
271                    self.gather_private_shares(packet)?;
272                    if self.state == State::DkgPrivateGather {
273                        // We need more data
274                        return Ok((None, None));
275                    }
276                }
277                State::DkgEndDistribute => {
278                    let packet = self.start_dkg_end()?;
279                    return Ok((Some(packet), None));
280                }
281                State::DkgEndGather => {
282                    if let Err(error) = self.gather_dkg_end(packet) {
283                        if let Error::DkgFailure {
284                            reported_failures,
285                            malicious_signers,
286                        } = error
287                        {
288                            return Ok((
289                                None,
290                                Some(OperationResult::DkgError(DkgError::DkgEndFailure {
291                                    reported_failures,
292                                    malicious_signers,
293                                })),
294                            ));
295                        } else {
296                            return Err(error);
297                        }
298                    }
299                    if self.state == State::DkgEndGather {
300                        // We need more data
301                        return Ok((None, None));
302                    } else if self.state == State::Idle {
303                        // We are done with the DKG round! Return the operation result
304                        return Ok((
305                            None,
306                            Some(OperationResult::Dkg(
307                                self.aggregate_public_key
308                                    .ok_or(Error::MissingAggregatePublicKey)?,
309                            )),
310                        ));
311                    }
312                }
313                State::NonceRequest(signature_type) => {
314                    let packet = self.request_nonces(signature_type)?;
315                    return Ok((Some(packet), None));
316                }
317                State::NonceGather(signature_type) => {
318                    self.gather_nonces(packet, signature_type)?;
319                    if self.state == State::NonceGather(signature_type) {
320                        // We need more data
321                        return Ok((None, None));
322                    }
323                }
324                State::SigShareRequest(signature_type) => {
325                    let packet = self.request_sig_shares(signature_type)?;
326                    return Ok((Some(packet), None));
327                }
328                State::SigShareGather(signature_type) => {
329                    if let Err(e) = self.gather_sig_shares(packet, signature_type) {
330                        return Ok((
331                            None,
332                            Some(OperationResult::SignError(SignError::Coordinator(e))),
333                        ));
334                    }
335                    if self.state == State::SigShareGather(signature_type) {
336                        // We need more data
337                        return Ok((None, None));
338                    } else if self.state == State::Idle {
339                        // We are done with the DKG round! Return the operation result
340                        if let SignatureType::Taproot(_) = signature_type {
341                            if let Some(schnorr_proof) = &self.schnorr_proof {
342                                return Ok((
343                                    None,
344                                    Some(OperationResult::SignTaproot(SchnorrProof {
345                                        r: schnorr_proof.r,
346                                        s: schnorr_proof.s,
347                                    })),
348                                ));
349                            } else {
350                                return Ok((
351                                    None,
352                                    Some(OperationResult::SignError(SignError::Coordinator(
353                                        Error::MissingSchnorrProof,
354                                    ))),
355                                ));
356                            }
357                        } else if let SignatureType::Schnorr = signature_type {
358                            if let Some(schnorr_proof) = &self.schnorr_proof {
359                                return Ok((
360                                    None,
361                                    Some(OperationResult::SignSchnorr(SchnorrProof {
362                                        r: schnorr_proof.r,
363                                        s: schnorr_proof.s,
364                                    })),
365                                ));
366                            } else {
367                                return Ok((
368                                    None,
369                                    Some(OperationResult::SignError(SignError::Coordinator(
370                                        Error::MissingSchnorrProof,
371                                    ))),
372                                ));
373                            }
374                        } else if let Some(signature) = &self.signature {
375                            return Ok((
376                                None,
377                                Some(OperationResult::Sign(Signature {
378                                    R: signature.R,
379                                    z: signature.z,
380                                })),
381                            ));
382                        } else {
383                            return Ok((
384                                None,
385                                Some(OperationResult::SignError(SignError::Coordinator(
386                                    Error::MissingSignature,
387                                ))),
388                            ));
389                        }
390                    }
391                }
392            }
393        }
394    }
395
396    /// Ask signers to send DKG public shares
397    pub fn start_public_shares(&mut self) -> Result<Packet, Error> {
398        self.dkg_public_shares.clear();
399        self.party_polynomials.clear();
400        self.dkg_wait_signer_ids = (0..self.config.num_signers).collect();
401        info!(
402            dkg_id = %self.current_dkg_id,
403            "Starting Public Share Distribution"
404        );
405        let dkg_begin = DkgBegin {
406            dkg_id: self.current_dkg_id,
407        };
408        let dkg_begin_packet = Packet {
409            sig: dkg_begin
410                .sign(&self.config.message_private_key)
411                .expect("Failed to sign DkgBegin"),
412            msg: Message::DkgBegin(dkg_begin),
413        };
414
415        self.move_to(State::DkgPublicGather)?;
416        self.dkg_public_start = Some(Instant::now());
417        Ok(dkg_begin_packet)
418    }
419
420    /// Ask signers to send DKG private shares
421    pub fn start_private_shares(&mut self) -> Result<Packet, Error> {
422        // only wait for signers that returned DkgPublicShares
423        self.dkg_wait_signer_ids = self
424            .dkg_public_shares
425            .keys()
426            .cloned()
427            .collect::<HashSet<u32>>();
428        info!(
429            dkg_id = %self.current_dkg_id,
430            "Starting Private Share Distribution"
431        );
432
433        let dkg_begin = DkgPrivateBegin {
434            dkg_id: self.current_dkg_id,
435            signer_ids: self.dkg_public_shares.keys().cloned().collect(),
436            key_ids: vec![],
437        };
438        let dkg_private_begin_msg = Packet {
439            sig: dkg_begin
440                .sign(&self.config.message_private_key)
441                .expect("Failed to sign DkgPrivateBegin"),
442            msg: Message::DkgPrivateBegin(dkg_begin),
443        };
444        self.move_to(State::DkgPrivateGather)?;
445        self.dkg_private_start = Some(Instant::now());
446        Ok(dkg_private_begin_msg)
447    }
448
449    /// Ask signers to compute shares and send DKG end
450    pub fn start_dkg_end(&mut self) -> Result<Packet, Error> {
451        // only wait for signers that returned DkgPublicShares
452        self.dkg_wait_signer_ids = self
453            .dkg_private_shares
454            .keys()
455            .cloned()
456            .collect::<HashSet<u32>>();
457        info!(
458            dkg_id = %self.current_dkg_id,
459            "Starting DkgEnd Distribution"
460        );
461
462        let dkg_end_begin = DkgEndBegin {
463            dkg_id: self.current_dkg_id,
464            signer_ids: self.dkg_private_shares.keys().cloned().collect(),
465            key_ids: vec![],
466        };
467        let dkg_end_begin_msg = Packet {
468            sig: dkg_end_begin
469                .sign(&self.config.message_private_key)
470                .expect("Failed to sign DkgPrivateBegin"),
471            msg: Message::DkgEndBegin(dkg_end_begin),
472        };
473        self.move_to(State::DkgEndGather)?;
474        self.dkg_end_start = Some(Instant::now());
475        Ok(dkg_end_begin_msg)
476    }
477
478    fn gather_public_shares(&mut self, packet: &Packet) -> Result<(), Error> {
479        if let Message::DkgPublicShares(dkg_public_shares) = &packet.msg {
480            if dkg_public_shares.dkg_id != self.current_dkg_id {
481                return Err(Error::BadDkgId(
482                    dkg_public_shares.dkg_id,
483                    self.current_dkg_id,
484                ));
485            }
486
487            // check that the signer_id exists in the config
488            let signer_public_keys = &self.config.public_keys.signers;
489            if !signer_public_keys.contains_key(&dkg_public_shares.signer_id) {
490                warn!(signer_id = %dkg_public_shares.signer_id, "No public key in config");
491                return Ok(());
492            };
493
494            let have_shares = self
495                .dkg_public_shares
496                .contains_key(&dkg_public_shares.signer_id);
497
498            if have_shares {
499                info!(signer_id = %dkg_public_shares.signer_id, "received duplicate DkgPublicShares");
500                return Ok(());
501            }
502
503            let waiting = self
504                .dkg_wait_signer_ids
505                .remove(&dkg_public_shares.signer_id);
506
507            if waiting {
508                self.dkg_public_shares
509                    .insert(dkg_public_shares.signer_id, dkg_public_shares.clone());
510                debug!(
511                    dkg_id = %dkg_public_shares.dkg_id,
512                    signer_id = %dkg_public_shares.signer_id,
513                    "DkgPublicShares received"
514                );
515            } else {
516                warn!(
517                    dkg_id = %dkg_public_shares.dkg_id,
518                    signer_id = %dkg_public_shares.signer_id,
519                    "Got DkgPublicShares from signer who we weren't waiting on"
520                );
521            }
522        }
523
524        if self.dkg_wait_signer_ids.is_empty() {
525            self.public_shares_gathered()?;
526        }
527        Ok(())
528    }
529
530    fn public_shares_gathered(&mut self) -> Result<(), Error> {
531        self.move_to(State::DkgPrivateDistribute)?;
532        Ok(())
533    }
534
535    fn gather_private_shares(&mut self, packet: &Packet) -> Result<(), Error> {
536        if let Message::DkgPrivateShares(dkg_private_shares) = &packet.msg {
537            if dkg_private_shares.dkg_id != self.current_dkg_id {
538                return Err(Error::BadDkgId(
539                    dkg_private_shares.dkg_id,
540                    self.current_dkg_id,
541                ));
542            }
543
544            // check that the signer_id exists in the config
545            let signer_public_keys = &self.config.public_keys.signers;
546            if !signer_public_keys.contains_key(&dkg_private_shares.signer_id) {
547                warn!(signer_id = %dkg_private_shares.signer_id, "No public key in config");
548                return Ok(());
549            };
550
551            let has_received_shares = self
552                .dkg_private_shares
553                .contains_key(&dkg_private_shares.signer_id);
554            if has_received_shares {
555                info!(signer_id = %dkg_private_shares.signer_id, "received duplicate DkgPrivateShares");
556                return Ok(());
557            }
558
559            let waiting = self
560                .dkg_wait_signer_ids
561                .remove(&dkg_private_shares.signer_id);
562
563            if waiting {
564                self.dkg_private_shares
565                    .insert(dkg_private_shares.signer_id, dkg_private_shares.clone());
566                info!(
567                    dkg_id = %dkg_private_shares.dkg_id,
568                    signer_id = %dkg_private_shares.signer_id,
569                    "DkgPrivateShares received"
570                );
571            } else {
572                warn!(
573                    dkg_id = %dkg_private_shares.dkg_id,
574                    signer_id = %dkg_private_shares.signer_id,
575                    "Got DkgPrivateShares from signer who we weren't waiting on"
576                );
577            }
578        }
579
580        if self.dkg_wait_signer_ids.is_empty() {
581            self.private_shares_gathered()?;
582        }
583        Ok(())
584    }
585
586    fn private_shares_gathered(&mut self) -> Result<(), Error> {
587        self.move_to(State::DkgEndDistribute)?;
588        Ok(())
589    }
590
591    fn gather_dkg_end(&mut self, packet: &Packet) -> Result<(), Error> {
592        debug!(
593            "DKG Round {}: waiting for Dkg End from signers {:?}",
594            self.current_dkg_id, self.dkg_wait_signer_ids
595        );
596        if let Message::DkgEnd(dkg_end) = &packet.msg {
597            if dkg_end.dkg_id != self.current_dkg_id {
598                return Err(Error::BadDkgId(dkg_end.dkg_id, self.current_dkg_id));
599            }
600            if self.dkg_wait_signer_ids.contains(&dkg_end.signer_id) {
601                self.dkg_wait_signer_ids.remove(&dkg_end.signer_id);
602                self.dkg_end_messages
603                    .insert(dkg_end.signer_id, dkg_end.clone());
604                debug!(
605                    dkg_id = %dkg_end.dkg_id,
606                    signer_id = %dkg_end.signer_id,
607                    waiting = ?self.dkg_wait_signer_ids,
608                    "DkgEnd received"
609                );
610            } else {
611                warn!(
612                    dkg_id = %dkg_end.dkg_id,
613                    signer_id = %dkg_end.signer_id,
614                    "Got DkgEnd from signer who we weren't waiting on"
615                );
616            }
617        }
618
619        let mut reported_failures = HashMap::new();
620        // this will be used to report signers who were malicious in this DKG round, as opposed to
621        // self.malicious_dkg_signer_ids which contains all DKG signers who were ever malicious
622        let mut malicious_signers = HashSet::new();
623        let threshold: usize = self
624            .config
625            .threshold
626            .try_into()
627            .map_err(Error::TryFromInt)?;
628        if self.dkg_wait_signer_ids.is_empty() {
629            // if there are any errors, mark signers malicious and retry
630            for (signer_id, dkg_end) in &self.dkg_end_messages {
631                if let DkgStatus::Failure(dkg_failure) = &dkg_end.status {
632                    warn!(%signer_id, ?dkg_failure, "DkgEnd failure");
633                    reported_failures.insert(*signer_id, dkg_failure.clone());
634
635                    match dkg_failure {
636                        DkgFailure::BadState => {
637                            // signer should not be in a bad state so treat as malicious
638                            malicious_signers.insert(*signer_id);
639                        }
640                        DkgFailure::Threshold => {
641                            // this shouldn't happen, maybe mark signer malicious?
642                        }
643                        DkgFailure::BadPublicShares(bad_shares) => {
644                            // bad_shares is a set of signer_ids
645                            for bad_signer_id in bad_shares {
646                                // verify public shares are bad
647                                let Some(dkg_public_shares) =
648                                    self.dkg_public_shares.get(bad_signer_id)
649                                else {
650                                    warn!("Signer {signer_id} reported BadPublicShares from {bad_signer_id} but there are no public shares from that signer, mark {signer_id} as malicious");
651                                    malicious_signers.insert(*signer_id);
652                                    continue;
653                                };
654                                let mut bad_party_ids = Vec::new();
655                                for (party_id, comm) in &dkg_public_shares.comms {
656                                    if !check_public_shares(
657                                        comm,
658                                        threshold,
659                                        &self.current_dkg_id.to_be_bytes(),
660                                    ) {
661                                        bad_party_ids.push(party_id);
662                                    }
663                                }
664
665                                // if none of the shares were bad sender was malicious
666                                if bad_party_ids.is_empty() {
667                                    warn!("Signer {signer_id} reported BadPublicShares from {bad_signer_id} but the shares were valid, mark {signer_id} as malicious");
668                                    malicious_signers.insert(*signer_id);
669                                } else {
670                                    warn!("Signer {signer_id} reported BadPublicShares from {bad_signer_id}, mark {bad_signer_id} as malicious");
671                                    malicious_signers.insert(*bad_signer_id);
672                                }
673                            }
674                        }
675                        DkgFailure::BadPrivateShares(bad_shares) => {
676                            // bad_shares is a map of signer_id to BadPrivateShare
677                            for (bad_signer_id, bad_private_share) in bad_shares {
678                                // verify the DH tuple proof first so we know the shared key is correct
679                                let Some(signer_key_ids) =
680                                    self.config.public_keys.signer_key_ids.get(signer_id)
681                                else {
682                                    warn!("No key IDs for signer_id {signer_id} DkgEnd");
683                                    continue;
684                                };
685                                let Some(signer_public_shares) =
686                                    self.dkg_public_shares.get(signer_id)
687                                else {
688                                    warn!("Signer {signer_id} reported BadPrivateShares from {bad_signer_id} but there are no public shares from {signer_id}");
689                                    continue;
690                                };
691                                let signer_public_key = signer_public_shares.kex_public_key;
692
693                                let Some(bad_signer_public_shares) =
694                                    self.dkg_public_shares.get(bad_signer_id)
695                                else {
696                                    warn!("Signer {signer_id} reported BadPrivateShares from {bad_signer_id} but there are no public shares from {bad_signer_id}, mark {signer_id} as malicious");
697                                    malicious_signers.insert(*signer_id);
698                                    continue;
699                                };
700                                let bad_signer_public_key = bad_signer_public_shares.kex_public_key;
701
702                                let mut is_bad = false;
703
704                                if bad_private_share.tuple_proof.verify(
705                                    &signer_public_key,
706                                    &bad_signer_public_key,
707                                    &bad_private_share.shared_key,
708                                ) {
709                                    // verify at least one bad private share for one of signer_id's key_ids
710                                    let shared_secret =
711                                        make_shared_secret_from_key(&bad_private_share.shared_key);
712
713                                    let polys = bad_signer_public_shares
714                                        .comms
715                                        .iter()
716                                        .cloned()
717                                        .collect::<HashMap<u32, PolyCommitment>>();
718                                    let Some(dkg_private_shares) =
719                                        self.dkg_private_shares.get(bad_signer_id)
720                                    else {
721                                        warn!("Signer {signer_id} reported BadPrivateShare from signer {bad_signer_id} who didn't send public shares, mark {signer_id} as malicious");
722                                        malicious_signers.insert(*signer_id);
723                                        continue;
724                                    };
725
726                                    for (src_party_id, key_shares) in &dkg_private_shares.shares {
727                                        let Some(poly) = polys.get(src_party_id) else {
728                                            warn!("Signer {signer_id} reported BadPrivateShares from {bad_signer_id} but the private shares from {bad_signer_id} dont have a polynomial for party {src_party_id}");
729                                            continue;
730                                        };
731                                        for key_id in signer_key_ids {
732                                            let Some(bytes) = key_shares.get(key_id) else {
733                                                warn!("DkgPrivateShares from party_id {src_party_id} did not include a share for key_id {key_id}");
734                                                continue;
735                                            };
736                                            match decrypt(&shared_secret, bytes) {
737                                                Ok(plain) => match Scalar::try_from(&plain[..]) {
738                                                    Ok(private_eval) => {
739                                                        let poly_eval = match compute::poly(
740                                                            &compute::id(*key_id),
741                                                            &poly.poly,
742                                                        ) {
743                                                            Ok(p) => p,
744                                                            Err(e) => {
745                                                                warn!("Failed to evaluate public poly from signer_id {bad_signer_id} to key_id {key_id}: {e:?}");
746                                                                is_bad = true;
747                                                                break;
748                                                            }
749                                                        };
750
751                                                        if private_eval * G != poly_eval {
752                                                            warn!("Invalid dkg private share from signer_id {bad_signer_id} to key_id {key_id}");
753
754                                                            is_bad = true;
755                                                            break;
756                                                        }
757                                                    }
758                                                    Err(e) => {
759                                                        warn!("Failed to parse Scalar for dkg private share from signer_id {bad_signer_id} to key_id {key_id}: {e:?}");
760
761                                                        is_bad = true;
762                                                        break;
763                                                    }
764                                                },
765                                                Err(e) => {
766                                                    warn!("Failed to decrypt dkg private share from signer_id {bad_signer_id} to key_id {key_id}: {e:?}");
767                                                    is_bad = true;
768                                                    break;
769                                                }
770                                            }
771                                        }
772                                    }
773                                } else {
774                                    warn!("TupleProof failed to verify, mark {signer_id} as malicious");
775                                    is_bad = false;
776                                }
777
778                                // if tuple proof failed or none of the shares were bad sender was malicious
779                                if !is_bad {
780                                    warn!("Signer {signer_id} reported BadPrivateShare from {bad_signer_id} but the shares were valid, mark {signer_id} as malicious");
781                                    malicious_signers.insert(*signer_id);
782                                } else {
783                                    warn!("Signer {signer_id} reported BadPrivateShare from {bad_signer_id}, mark {bad_signer_id} as malicious");
784                                    malicious_signers.insert(*bad_signer_id);
785                                }
786                            }
787                        }
788                        DkgFailure::MissingPublicShares(_) => {
789                            // this shouldn't happen, maybe mark signer malicious?
790                        }
791                        DkgFailure::MissingPrivateShares(_) => {
792                            // this shouldn't happen, maybe mark signer malicious?
793                        }
794                    }
795                }
796            }
797
798            for id in &malicious_signers {
799                self.malicious_dkg_signer_ids.insert(*id);
800            }
801
802            if reported_failures.is_empty() {
803                debug!("no dkg failures");
804                self.dkg_end_gathered()?;
805            } else {
806                // TODO: see if we have sufficient non-malicious signers to continue
807                warn!("got dkg failures");
808                return Err(Error::DkgFailure {
809                    reported_failures,
810                    malicious_signers,
811                });
812            }
813        }
814        Ok(())
815    }
816
817    fn dkg_end_gathered(&mut self) -> Result<(), Error> {
818        // Cache the polynomials used in DKG for the aggregator
819        for signer_id in self.dkg_private_shares.keys() {
820            if let Some(dkg_public_shares) = &self.dkg_public_shares.get(signer_id) {
821                for (party_id, comm) in &dkg_public_shares.comms {
822                    self.party_polynomials.insert(*party_id, comm.clone());
823                }
824            } else {
825                error!(
826                    signer_id = %signer_id,
827                    "No DkgPublicShares from signer who sent DkgPrivateShares"
828                );
829                return Err(Error::NoPublicSharesForSigner(*signer_id));
830            }
831        }
832
833        // Final sanity check on PolyComitments
834        let threshold: usize = self
835            .config
836            .threshold
837            .try_into()
838            .map_err(Error::TryFromInt)?;
839        let mut bad_ids = Vec::new();
840        for (party_id, comm) in &self.party_polynomials {
841            if !check_public_shares(comm, threshold, &self.current_dkg_id.to_be_bytes()) {
842                bad_ids.push(compute::id(*party_id));
843            }
844        }
845
846        if !bad_ids.is_empty() {
847            return Err(Error::Aggregator(AggregatorError::BadPolyCommitments(
848                bad_ids,
849            )));
850        }
851
852        // Calculate the aggregate public key
853        let key = self
854            .dkg_end_messages
855            .keys()
856            .flat_map(|signer_id| {
857                if let Some(dkg_public_shares) = self.dkg_public_shares.get(signer_id) {
858                    dkg_public_shares.comms.clone()
859                } else {
860                    warn!(
861                        signer_id = %signer_id,
862                        "No DkgPublicShares from signer who sent DkgEnd"
863                    );
864                    vec![]
865                }
866            })
867            .fold(Point::default(), |s, (_, comm)| {
868                if let Some(p) = comm.poly.first() {
869                    s + p
870                } else {
871                    warn!("Empty polynomial when computing aggregate public key");
872                    s
873                }
874            });
875
876        info!("Aggregate public key: {key}");
877        self.aggregate_public_key = Some(key);
878        self.move_to(State::Idle)
879    }
880
881    fn request_nonces(&mut self, signature_type: SignatureType) -> Result<Packet, Error> {
882        self.message_nonces.clear();
883        self.current_sign_iter_id = self.current_sign_iter_id.wrapping_add(1);
884        info!(
885            sign_id = %self.current_sign_id,
886            sign_iter_id = %self.current_sign_iter_id,
887            "Requesting Nonces"
888        );
889        let nonce_request = NonceRequest {
890            dkg_id: self.current_dkg_id,
891            sign_id: self.current_sign_id,
892            sign_iter_id: self.current_sign_iter_id,
893            message: self.message.clone(),
894            signature_type,
895        };
896        let nonce_request_msg = Packet {
897            sig: nonce_request
898                .sign(&self.config.message_private_key)
899                .expect("Failed to sign NonceRequest"),
900            msg: Message::NonceRequest(nonce_request),
901        };
902        self.move_to(State::NonceGather(signature_type))?;
903        self.nonce_start = Some(Instant::now());
904
905        Ok(nonce_request_msg)
906    }
907
908    fn gather_nonces(
909        &mut self,
910        packet: &Packet,
911        signature_type: SignatureType,
912    ) -> Result<(), Error> {
913        if let Message::NonceResponse(nonce_response) = &packet.msg {
914            if nonce_response.dkg_id != self.current_dkg_id {
915                return Err(Error::BadDkgId(nonce_response.dkg_id, self.current_dkg_id));
916            }
917            if nonce_response.sign_id != self.current_sign_id {
918                return Err(Error::BadSignId(
919                    nonce_response.sign_id,
920                    self.current_sign_id,
921                ));
922            }
923            if nonce_response.sign_iter_id != self.current_sign_iter_id {
924                return Err(Error::BadSignIterId(
925                    nonce_response.sign_iter_id,
926                    self.current_sign_iter_id,
927                ));
928            }
929
930            // check that the signer_id exists in the config
931            let signer_public_keys = &self.config.public_keys.signers;
932            if !signer_public_keys.contains_key(&nonce_response.signer_id) {
933                warn!(signer_id = %nonce_response.signer_id, "No public key in config");
934                return Ok(());
935            };
936
937            // check that the key_ids match the config
938            let Some(signer_key_ids) = self
939                .config
940                .public_keys
941                .signer_key_ids
942                .get(&nonce_response.signer_id)
943            else {
944                warn!(signer_id = %nonce_response.signer_id, "No keys IDs configured");
945                return Ok(());
946            };
947
948            let nonce_response_key_ids = nonce_response
949                .key_ids
950                .iter()
951                .cloned()
952                .collect::<HashSet<u32>>();
953            if *signer_key_ids != nonce_response_key_ids {
954                warn!(signer_id = %nonce_response.signer_id, "Nonce response key_ids didn't match config");
955                return Ok(());
956            }
957
958            for nonce in &nonce_response.nonces {
959                if !nonce.is_valid() {
960                    warn!(
961                        sign_id = %nonce_response.sign_id,
962                        sign_iter_id = %nonce_response.sign_iter_id,
963                        signer_id = %nonce_response.signer_id,
964                        "Received invalid nonce in NonceResponse"
965                    );
966                    return Ok(());
967                }
968            }
969
970            if self
971                .malicious_signer_ids
972                .contains(&nonce_response.signer_id)
973            {
974                warn!(
975                    sign_id = %nonce_response.sign_id,
976                    sign_iter_id = %nonce_response.sign_iter_id,
977                    signer_id = %nonce_response.signer_id,
978                    "Received malicious NonceResponse"
979                );
980                //return Err(Error::MaliciousSigner(nonce_response.signer_id));
981                return Ok(());
982            }
983
984            let nonce_info = self
985                .message_nonces
986                .entry(nonce_response.message.clone())
987                .or_default();
988
989            let have_nonces = nonce_info
990                .public_nonces
991                .contains_key(&nonce_response.signer_id);
992
993            if have_nonces {
994                info!(signer_id = %nonce_response.signer_id, "Received duplicate NonceResponse");
995                return Ok(());
996            }
997
998            nonce_info
999                .public_nonces
1000                .insert(nonce_response.signer_id, nonce_response.clone());
1001
1002            // ignore the passed key_ids
1003            for key_id in signer_key_ids {
1004                nonce_info.nonce_recv_key_ids.insert(*key_id);
1005            }
1006
1007            nonce_info
1008                .sign_wait_signer_ids
1009                .insert(nonce_response.signer_id);
1010            // Because of entry call, it is safe to unwrap here
1011            info!(
1012                sign_id = %nonce_response.sign_id,
1013                sign_iter_id = %nonce_response.sign_iter_id,
1014                signer_id = %nonce_response.signer_id,
1015                recv_keys = %nonce_info.nonce_recv_key_ids.len(),
1016                threshold = %self.config.threshold,
1017                "Received NonceResponse"
1018            );
1019            if nonce_info.nonce_recv_key_ids.len() >= self.config.threshold as usize {
1020                // We have a winning message!
1021                self.message.clone_from(&nonce_response.message);
1022                let aggregate_nonce = self.compute_aggregate_nonce();
1023                info!("Aggregate nonce: {aggregate_nonce}");
1024
1025                self.move_to(State::SigShareRequest(signature_type))?;
1026            }
1027        }
1028        Ok(())
1029    }
1030
1031    fn request_sig_shares(&mut self, signature_type: SignatureType) -> Result<Packet, Error> {
1032        self.signature_shares.clear();
1033        info!(
1034            sign_id = %self.current_sign_id,
1035            "Requesting Signature Shares"
1036        );
1037        let nonce_responses = self
1038            .message_nonces
1039            .get(&self.message)
1040            .ok_or(Error::MissingMessageNonceInfo)?
1041            .public_nonces
1042            .values()
1043            .cloned()
1044            .collect::<Vec<NonceResponse>>();
1045        let sig_share_request = SignatureShareRequest {
1046            dkg_id: self.current_dkg_id,
1047            sign_id: self.current_sign_id,
1048            sign_iter_id: self.current_sign_iter_id,
1049            nonce_responses,
1050            message: self.message.clone(),
1051            signature_type,
1052        };
1053        let sig_share_request_msg = Packet {
1054            sig: sig_share_request
1055                .sign(&self.config.message_private_key)
1056                .expect("Failed to sign SignatureShareRequest"),
1057            msg: Message::SignatureShareRequest(sig_share_request),
1058        };
1059        self.move_to(State::SigShareGather(signature_type))?;
1060        self.sign_start = Some(Instant::now());
1061
1062        Ok(sig_share_request_msg)
1063    }
1064
1065    fn gather_sig_shares(
1066        &mut self,
1067        packet: &Packet,
1068        signature_type: SignatureType,
1069    ) -> Result<(), Error> {
1070        let Message::SignatureShareResponse(sig_share_response) = &packet.msg else {
1071            return Ok(());
1072        };
1073
1074        let Some(response_info) = self.message_nonces.get_mut(&self.message) else {
1075            warn!(
1076                "Sign round {} SignatureShareResponse for round {} from signer {} no message nonces entry",
1077                self.current_sign_id, sig_share_response.sign_id, sig_share_response.signer_id,
1078            );
1079            return Ok(());
1080        };
1081
1082        let waiting = response_info
1083            .sign_wait_signer_ids
1084            .contains(&sig_share_response.signer_id);
1085
1086        if !waiting {
1087            warn!(
1088                "Sign round {} SignatureShareResponse for round {} from signer {} not in the wait list",
1089                self.current_sign_id, sig_share_response.sign_id, sig_share_response.signer_id,
1090            );
1091            return Ok(());
1092        }
1093
1094        if sig_share_response.dkg_id != self.current_dkg_id {
1095            return Err(Error::BadDkgId(
1096                sig_share_response.dkg_id,
1097                self.current_dkg_id,
1098            ));
1099        }
1100        if sig_share_response.sign_id != self.current_sign_id {
1101            return Err(Error::BadSignId(
1102                sig_share_response.sign_id,
1103                self.current_sign_id,
1104            ));
1105        }
1106
1107        // we were waiting on you, and you sent a packet for this sign round, so we won't take
1108        // another packet from you
1109        response_info
1110            .sign_wait_signer_ids
1111            .remove(&sig_share_response.signer_id);
1112
1113        // check that the signer_id exists in the config
1114        let signer_public_keys = &self.config.public_keys.signers;
1115        if !signer_public_keys.contains_key(&sig_share_response.signer_id) {
1116            warn!(signer_id = %sig_share_response.signer_id, "No public key in config");
1117            return Err(Error::MissingPublicKeyForSigner(
1118                sig_share_response.signer_id,
1119            ));
1120        };
1121
1122        // check that the key_ids match the config
1123        let Some(signer_key_ids) = self
1124            .config
1125            .public_keys
1126            .signer_key_ids
1127            .get(&sig_share_response.signer_id)
1128        else {
1129            warn!(signer_id = %sig_share_response.signer_id, "No keys IDs configured");
1130            return Err(Error::MissingKeyIDsForSigner(sig_share_response.signer_id));
1131        };
1132
1133        let mut sig_share_response_key_ids = HashSet::new();
1134        for sig_share in &sig_share_response.signature_shares {
1135            for key_id in &sig_share.key_ids {
1136                sig_share_response_key_ids.insert(*key_id);
1137            }
1138        }
1139
1140        if *signer_key_ids != sig_share_response_key_ids {
1141            warn!(signer_id = %sig_share_response.signer_id, "SignatureShareResponse key_ids didn't match config");
1142            return Err(Error::BadKeyIDsForSigner(sig_share_response.signer_id));
1143        }
1144
1145        let have_shares = self
1146            .signature_shares
1147            .contains_key(&sig_share_response.signer_id);
1148
1149        if have_shares {
1150            info!(signer_id = %sig_share_response.signer_id, "received duplicate SignatureShareResponse");
1151            // XXX should this be an error?  We should have already removed signer from wait set
1152            return Ok(());
1153        }
1154
1155        self.signature_shares.insert(
1156            sig_share_response.signer_id,
1157            sig_share_response.signature_shares.clone(),
1158        );
1159
1160        for sig_share in &sig_share_response.signature_shares {
1161            for key_id in &sig_share.key_ids {
1162                response_info.sign_recv_key_ids.insert(*key_id);
1163            }
1164        }
1165
1166        debug!(
1167            "Sign round {} SignatureShareResponse for round {} from signer {} ({}/{} key_ids). Waiting on {:?}",
1168            self.current_sign_id,
1169            sig_share_response.sign_id,
1170            sig_share_response.signer_id,
1171            response_info.sign_recv_key_ids.len(),
1172            response_info.nonce_recv_key_ids.len(),
1173            response_info.sign_wait_signer_ids
1174        );
1175
1176        let message_nonce = self
1177            .message_nonces
1178            .get(&self.message)
1179            .ok_or(Error::MissingMessageNonceInfo)?;
1180        if message_nonce.sign_wait_signer_ids.is_empty() {
1181            // Calculate the aggregate signature
1182            let nonce_responses = message_nonce
1183                .public_nonces
1184                .values()
1185                .cloned()
1186                .collect::<Vec<NonceResponse>>();
1187
1188            let nonces = nonce_responses
1189                .iter()
1190                .flat_map(|nr| nr.nonces.clone())
1191                .collect::<Vec<PublicNonce>>();
1192
1193            let key_ids = nonce_responses
1194                .iter()
1195                .flat_map(|nr| nr.key_ids.clone())
1196                .collect::<Vec<u32>>();
1197
1198            let shares = message_nonce
1199                .public_nonces
1200                .iter()
1201                .flat_map(|(i, _)| {
1202                    if let Some(shares) = self.signature_shares.get(i) {
1203                        shares.clone()
1204                    } else {
1205                        warn!(sign_id = %self.current_sign_id, signer_id = %i, "Have nonces but no signature shares from signer");
1206                        vec![]
1207                    }
1208                })
1209                .collect::<Vec<SignatureShare>>();
1210
1211            debug!(
1212                "aggregator.sign({}, {:?}, {:?}, {})",
1213                bs58::encode(&self.message).into_string(),
1214                nonces.len(),
1215                shares.len(),
1216                self.party_polynomials.len(),
1217            );
1218
1219            self.aggregator.init(&self.party_polynomials)?;
1220
1221            if let SignatureType::Taproot(merkle_root) = signature_type {
1222                let schnorr_proof = self.aggregator.sign_taproot(
1223                    &self.message,
1224                    &nonces,
1225                    &shares,
1226                    &key_ids,
1227                    merkle_root,
1228                )?;
1229                debug!("SchnorrProof ({}, {})", schnorr_proof.r, schnorr_proof.s);
1230                self.schnorr_proof = Some(schnorr_proof);
1231            } else if let SignatureType::Schnorr = signature_type {
1232                let schnorr_proof =
1233                    self.aggregator
1234                        .sign_schnorr(&self.message, &nonces, &shares, &key_ids)?;
1235                debug!("SchnorrProof ({}, {})", schnorr_proof.r, schnorr_proof.s);
1236                self.schnorr_proof = Some(schnorr_proof);
1237            } else {
1238                let signature = self
1239                    .aggregator
1240                    .sign(&self.message, &nonces, &shares, &key_ids)?;
1241                debug!("Signature ({}, {})", signature.R, signature.z);
1242                self.signature = Some(signature);
1243            }
1244
1245            self.move_to(State::Idle)?;
1246        }
1247        Ok(())
1248    }
1249
1250    #[allow(non_snake_case)]
1251    fn compute_aggregate_nonce(&self) -> Point {
1252        // XXX this needs to be key_ids for v1 and signer_ids for v2
1253        let public_nonces = self
1254            .message_nonces
1255            .get(&self.message)
1256            .cloned()
1257            .unwrap_or_default()
1258            .public_nonces;
1259        let party_ids = public_nonces
1260            .values()
1261            .cloned()
1262            .flat_map(|pn| pn.key_ids)
1263            .collect::<Vec<u32>>();
1264        let nonces = public_nonces
1265            .values()
1266            .cloned()
1267            .flat_map(|pn| pn.nonces)
1268            .collect::<Vec<PublicNonce>>();
1269        let (_, R) = compute::intermediate(&self.message, &party_ids, &nonces);
1270
1271        R
1272    }
1273
1274    fn compute_num_key_ids<'a, I>(&self, signer_ids: I) -> Result<u32, Error>
1275    where
1276        I: Iterator<Item = &'a u32>,
1277    {
1278        signer_ids
1279            .map(
1280                |signer_id| match self.config.public_keys.signer_key_ids.get(signer_id) {
1281                    Some(key_ids) => key_ids.len(),
1282                    None => {
1283                        error!("No key_ids for signer {signer_id}");
1284                        0usize
1285                    }
1286                },
1287            )
1288            .sum::<usize>()
1289            .try_into()
1290            .map_err(Error::TryFromInt)
1291    }
1292
1293    fn compute_dkg_public_size(&self) -> Result<u32, Error> {
1294        self.compute_num_key_ids(self.dkg_public_shares.keys())
1295    }
1296
1297    fn compute_dkg_private_size(&self) -> Result<u32, Error> {
1298        self.compute_num_key_ids(self.dkg_private_shares.keys())
1299    }
1300}
1301
1302impl<Aggregator: AggregatorTrait> StateMachine<State, Error> for Coordinator<Aggregator> {
1303    fn move_to(&mut self, state: State) -> Result<(), Error> {
1304        self.can_move_to(&state)?;
1305        self.state = state;
1306        Ok(())
1307    }
1308
1309    fn can_move_to(&self, state: &State) -> Result<(), Error> {
1310        let prev_state = &self.state;
1311        let accepted = match state {
1312            State::Idle => true,
1313            State::DkgPublicDistribute => prev_state == &State::Idle,
1314            State::DkgPublicGather => {
1315                prev_state == &State::DkgPublicDistribute || prev_state == &State::DkgPublicGather
1316            }
1317            State::DkgPrivateDistribute => prev_state == &State::DkgPublicGather,
1318            State::DkgPrivateGather => {
1319                prev_state == &State::DkgPrivateDistribute || prev_state == &State::DkgPrivateGather
1320            }
1321            State::DkgEndDistribute => prev_state == &State::DkgPrivateGather,
1322            State::DkgEndGather => prev_state == &State::DkgEndDistribute,
1323            State::NonceRequest(signature_type) => {
1324                prev_state == &State::Idle
1325                    || prev_state == &State::DkgEndGather
1326                    || prev_state == &State::SigShareGather(*signature_type)
1327            }
1328            State::NonceGather(signature_type) => {
1329                prev_state == &State::NonceRequest(*signature_type)
1330                    || prev_state == &State::NonceGather(*signature_type)
1331            }
1332            State::SigShareRequest(signature_type) => {
1333                prev_state == &State::NonceGather(*signature_type)
1334            }
1335            State::SigShareGather(signature_type) => {
1336                prev_state == &State::SigShareRequest(*signature_type)
1337                    || prev_state == &State::SigShareGather(*signature_type)
1338            }
1339        };
1340        if accepted {
1341            debug!("state change from {prev_state:?} to {state:?}");
1342            Ok(())
1343        } else {
1344            Err(Error::BadStateChange(format!(
1345                "{prev_state:?} to {state:?}"
1346            )))
1347        }
1348    }
1349}
1350
1351impl<Aggregator: AggregatorTrait> CoordinatorTrait for Coordinator<Aggregator> {
1352    /// Create a new coordinator
1353    fn new(config: Config) -> Self {
1354        Self {
1355            aggregator: Aggregator::new(config.num_keys, config.threshold),
1356            config,
1357            current_dkg_id: 0,
1358            current_sign_id: 0,
1359            current_sign_iter_id: 0,
1360            dkg_public_shares: Default::default(),
1361            dkg_private_shares: Default::default(),
1362            dkg_end_messages: Default::default(),
1363            party_polynomials: Default::default(),
1364            message_nonces: Default::default(),
1365            signature_shares: Default::default(),
1366            aggregate_public_key: None,
1367            signature: None,
1368            schnorr_proof: None,
1369            message: Default::default(),
1370            dkg_wait_signer_ids: Default::default(),
1371            state: State::Idle,
1372            dkg_public_start: None,
1373            dkg_private_start: None,
1374            dkg_end_start: None,
1375            nonce_start: None,
1376            sign_start: None,
1377            malicious_signer_ids: Default::default(),
1378            malicious_dkg_signer_ids: Default::default(),
1379            coordinator_public_key: None,
1380        }
1381    }
1382
1383    fn load(state: &SavedState) -> Self {
1384        Self {
1385            aggregator: Aggregator::new(state.config.num_keys, state.config.threshold),
1386            config: state.config.clone(),
1387            current_dkg_id: state.current_dkg_id,
1388            current_sign_id: state.current_sign_id,
1389            current_sign_iter_id: state.current_sign_iter_id,
1390            dkg_public_shares: state.dkg_public_shares.clone(),
1391            dkg_private_shares: state.dkg_private_shares.clone(),
1392            dkg_end_messages: state.dkg_end_messages.clone(),
1393            party_polynomials: state.party_polynomials.clone(),
1394            message_nonces: state.message_nonces.clone(),
1395            signature_shares: state.signature_shares.clone(),
1396            aggregate_public_key: state.aggregate_public_key,
1397            signature: state.signature.clone(),
1398            schnorr_proof: state.schnorr_proof.clone(),
1399            message: state.message.clone(),
1400            dkg_wait_signer_ids: state.dkg_wait_signer_ids.clone(),
1401            state: state.state.clone(),
1402            dkg_public_start: state.dkg_public_start,
1403            dkg_private_start: state.dkg_private_start,
1404            dkg_end_start: state.dkg_end_start,
1405            nonce_start: state.nonce_start,
1406            sign_start: state.sign_start,
1407            malicious_signer_ids: state.malicious_signer_ids.clone(),
1408            malicious_dkg_signer_ids: state.malicious_dkg_signer_ids.clone(),
1409            coordinator_public_key: state.coordinator_public_key,
1410        }
1411    }
1412
1413    fn save(&self) -> SavedState {
1414        SavedState {
1415            config: self.config.clone(),
1416            current_dkg_id: self.current_dkg_id,
1417            current_sign_id: self.current_sign_id,
1418            current_sign_iter_id: self.current_sign_iter_id,
1419            dkg_public_shares: self.dkg_public_shares.clone(),
1420            dkg_private_shares: self.dkg_private_shares.clone(),
1421            dkg_end_messages: self.dkg_end_messages.clone(),
1422            party_polynomials: self.party_polynomials.clone(),
1423            message_nonces: self.message_nonces.clone(),
1424            signature_shares: self.signature_shares.clone(),
1425            aggregate_public_key: self.aggregate_public_key,
1426            signature: self.signature.clone(),
1427            schnorr_proof: self.schnorr_proof.clone(),
1428            message: self.message.clone(),
1429            dkg_wait_signer_ids: self.dkg_wait_signer_ids.clone(),
1430            state: self.state.clone(),
1431            dkg_public_start: self.dkg_public_start,
1432            dkg_private_start: self.dkg_private_start,
1433            dkg_end_start: self.dkg_end_start,
1434            nonce_start: self.nonce_start,
1435            sign_start: self.sign_start,
1436            malicious_signer_ids: self.malicious_signer_ids.clone(),
1437            malicious_dkg_signer_ids: self.malicious_dkg_signer_ids.clone(),
1438            coordinator_public_key: self.coordinator_public_key,
1439        }
1440    }
1441
1442    /// Retrieve the config
1443    fn get_config(&self) -> Config {
1444        self.config.clone()
1445    }
1446
1447    #[cfg(any(test, feature = "testing"))]
1448    fn get_config_mut(&mut self) -> &mut Config {
1449        &mut self.config
1450    }
1451
1452    fn set_coordinator_public_key(&mut self, key: Option<ecdsa::PublicKey>) {
1453        self.coordinator_public_key = key;
1454    }
1455
1456    /// Set the aggregate key and polynomial commitments used to form that key.
1457    ///  Check if the polynomial commitments match the key
1458    fn set_key_and_party_polynomials(
1459        &mut self,
1460        aggregate_key: Point,
1461        party_polynomials: Vec<(u32, PolyCommitment)>,
1462    ) -> Result<(), Error> {
1463        let mut computed_key = Point::default();
1464        let mut bad_poly_commitments = vec![];
1465        for (i, comm) in &party_polynomials {
1466            if let Some(p) = comm.poly.first() {
1467                computed_key += p;
1468            } else {
1469                bad_poly_commitments.push(compute::id(*i));
1470            }
1471        }
1472        if !bad_poly_commitments.is_empty() {
1473            return Err(Error::Aggregator(AggregatorError::BadPolyCommitments(
1474                bad_poly_commitments,
1475            )));
1476        }
1477        if computed_key != aggregate_key {
1478            return Err(Error::AggregateKeyPolynomialMismatch(
1479                computed_key,
1480                aggregate_key,
1481            ));
1482        }
1483        let party_polynomials_len = party_polynomials.len();
1484        let party_polynomials = HashMap::from_iter(party_polynomials);
1485        if party_polynomials.len() != party_polynomials_len {
1486            return Err(Error::DuplicatePartyId);
1487        }
1488        self.aggregate_public_key = Some(aggregate_key);
1489        self.party_polynomials = party_polynomials;
1490        Ok(())
1491    }
1492
1493    /// Retrieve the aggregate public key
1494    fn get_aggregate_public_key(&self) -> Option<Point> {
1495        self.aggregate_public_key
1496    }
1497
1498    /// Set the aggregate public key
1499    fn set_aggregate_public_key(&mut self, aggregate_public_key: Option<Point>) {
1500        self.aggregate_public_key = aggregate_public_key;
1501    }
1502
1503    /// Retrieve the current message bytes being signed
1504    fn get_message(&self) -> Vec<u8> {
1505        self.message.clone()
1506    }
1507
1508    /// Retrive the current state
1509    fn get_state(&self) -> State {
1510        self.state.clone()
1511    }
1512
1513    /// Start a DKG round, with an optional `dkg_id`
1514    fn start_dkg_round(&mut self, dkg_id: Option<u64>) -> Result<Packet, Error> {
1515        if let Some(id) = dkg_id {
1516            self.current_dkg_id = id;
1517        } else {
1518            self.current_dkg_id = self.current_dkg_id.wrapping_add(1);
1519        }
1520
1521        info!("Starting DKG round {}", self.current_dkg_id);
1522        self.move_to(State::DkgPublicDistribute)?;
1523        self.start_public_shares()
1524    }
1525
1526    /// Process the timeouts, and if none of them fire then process the passed packet
1527    /// If a timeout does fire, then the coordinator state has changed; this means the
1528    /// packet is now stale and must be dropped
1529    fn process(
1530        &mut self,
1531        packet: &Packet,
1532    ) -> Result<(Option<Packet>, Option<OperationResult>), Error> {
1533        let (outbound_packet, operation_result) = self.process_timeout()?;
1534        if outbound_packet.is_some() || operation_result.is_some() {
1535            return Ok((outbound_packet, operation_result));
1536        }
1537
1538        self.process_message(packet)
1539    }
1540
1541    /// Start a signing round
1542    fn start_signing_round(
1543        &mut self,
1544        message: &[u8],
1545        signature_type: SignatureType,
1546        sign_id: Option<u64>,
1547    ) -> Result<Packet, Error> {
1548        // We cannot sign if we haven't first set DKG (either manually or via DKG round).
1549        if self.aggregate_public_key.is_none() {
1550            return Err(Error::MissingAggregatePublicKey);
1551        }
1552        self.message = message.to_vec();
1553        if let Some(id) = sign_id {
1554            self.current_sign_id = id;
1555        } else {
1556            self.current_sign_id = self.current_sign_id.wrapping_add(1);
1557        }
1558        info!("Starting signing round {}", self.current_sign_id);
1559        self.move_to(State::NonceRequest(signature_type))?;
1560        self.request_nonces(signature_type)
1561    }
1562
1563    // Reset internal state
1564    fn reset(&mut self) {
1565        self.state = State::Idle;
1566        self.dkg_public_shares.clear();
1567        self.dkg_private_shares.clear();
1568        self.dkg_end_messages.clear();
1569        self.party_polynomials.clear();
1570        self.message_nonces.clear();
1571        self.signature_shares.clear();
1572        self.dkg_wait_signer_ids.clear();
1573        self.nonce_start = None;
1574        self.sign_start = None;
1575    }
1576}
1577
1578#[cfg(test)]
1579/// Test module for coordinator functionality
1580pub mod test {
1581    use super::*;
1582    #[cfg(feature = "with_v1")]
1583    use crate::v1;
1584    use crate::{
1585        curve::{point::Point, scalar::Scalar},
1586        net::{
1587            DkgBegin, DkgFailure, DkgPrivateShares, DkgPublicShares, Message, NonceRequest, Packet,
1588            SignatureShareResponse, SignatureType,
1589        },
1590        schnorr::{self, ID},
1591        state_machine::{
1592            coordinator::{
1593                fire::Coordinator as FireCoordinator,
1594                test::{
1595                    bad_signature_share_request, check_signature_shares, coordinator_state_machine,
1596                    empty_private_shares, empty_public_shares, equal_after_save_load,
1597                    feedback_messages, feedback_mutated_messages, gen_nonces, invalid_nonce,
1598                    new_coordinator, run_dkg_sign, setup, setup_with_timeouts, start_dkg_round,
1599                    start_signing_round, verify_packet_sigs,
1600                },
1601                Config, Coordinator as CoordinatorTrait, State,
1602            },
1603            signer::Signer,
1604            DkgError, OperationResult, SignError,
1605        },
1606        traits::{Aggregator as AggregatorTrait, Signer as SignerTrait},
1607        util::create_rng,
1608        v2,
1609    };
1610    use hashbrown::HashMap;
1611    use std::{slice::from_ref, thread, time::Duration};
1612
1613    #[test]
1614    #[cfg(feature = "with_v1")]
1615    fn new_coordinator_v1() {
1616        new_coordinator::<FireCoordinator<v1::Aggregator>>();
1617    }
1618
1619    #[test]
1620    fn new_coordinator_v2() {
1621        new_coordinator::<FireCoordinator<v2::Aggregator>>();
1622    }
1623
1624    #[test]
1625    #[cfg(feature = "with_v1")]
1626    fn equal_after_save_load_v1() {
1627        equal_after_save_load::<FireCoordinator<v1::Aggregator>, v1::Signer>(2, 2);
1628    }
1629
1630    #[test]
1631    fn equal_after_save_load_v2() {
1632        equal_after_save_load::<FireCoordinator<v2::Aggregator>, v2::Signer>(2, 2);
1633    }
1634
1635    #[test]
1636    #[cfg(feature = "with_v1")]
1637    fn coordinator_state_machine_v1() {
1638        coordinator_state_machine::<FireCoordinator<v1::Aggregator>>();
1639    }
1640
1641    #[test]
1642    fn coordinator_state_machine_v2() {
1643        coordinator_state_machine::<FireCoordinator<v2::Aggregator>>();
1644    }
1645
1646    #[test]
1647    #[cfg(feature = "with_v1")]
1648    fn start_dkg_round_v1() {
1649        start_dkg_round::<FireCoordinator<v1::Aggregator>>(None);
1650        start_dkg_round::<FireCoordinator<v1::Aggregator>>(Some(12345u64));
1651    }
1652
1653    #[test]
1654    fn start_dkg_round_v2() {
1655        start_dkg_round::<FireCoordinator<v2::Aggregator>>(None);
1656        start_dkg_round::<FireCoordinator<v2::Aggregator>>(Some(12345u64));
1657    }
1658
1659    #[test]
1660    #[cfg(feature = "with_v1")]
1661    fn start_signing_round_v1() {
1662        start_signing_round::<FireCoordinator<v1::Aggregator>>(None);
1663        start_signing_round::<FireCoordinator<v1::Aggregator>>(Some(12345u64));
1664    }
1665
1666    #[test]
1667    fn start_signing_round_v2() {
1668        start_signing_round::<FireCoordinator<v2::Aggregator>>(None);
1669        start_signing_round::<FireCoordinator<v2::Aggregator>>(Some(12345u64));
1670    }
1671
1672    #[test]
1673    #[cfg(feature = "with_v1")]
1674    fn dkg_public_share_v1() {
1675        dkg_public_share::<v1::Aggregator, v1::Signer>();
1676    }
1677
1678    #[test]
1679    fn dkg_public_share_v2() {
1680        dkg_public_share::<v2::Aggregator, v2::Signer>();
1681    }
1682
1683    /// test basic insertion and detection of duplicates for DkgPublicShares
1684    #[allow(dead_code)]
1685    fn dkg_public_share<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
1686        let ctx = 0u64.to_be_bytes();
1687        let mut rng = create_rng();
1688        let (coordinators, _) = setup::<FireCoordinator<Aggregator>, Signer>(2, 1);
1689        let mut coordinator: FireCoordinator<Aggregator> = coordinators[0].clone();
1690
1691        coordinator.state = State::DkgPublicGather;
1692        let comms = vec![(
1693            0,
1694            PolyCommitment {
1695                id: ID::new(&Scalar::new(), &Scalar::new(), &ctx, &mut rng),
1696                poly: vec![],
1697            },
1698        )];
1699        let kex_private_key = Scalar::random(&mut rng);
1700        let kex_proof = DkgPublicShares::kex_prove(0, 0, &comms, &kex_private_key, &mut rng);
1701        let public_shares = DkgPublicShares {
1702            dkg_id: 0,
1703            signer_id: 0,
1704            comms: comms.clone(),
1705            kex_public_key: Point::from(kex_private_key),
1706            kex_proof: kex_proof.clone(),
1707        };
1708        let packet = Packet {
1709            msg: Message::DkgPublicShares(public_shares.clone()),
1710            sig: Default::default(),
1711        };
1712
1713        // check that shares are ignored if not waiting on that signer
1714        coordinator.dkg_wait_signer_ids.insert(1);
1715        coordinator.gather_public_shares(&packet).unwrap();
1716        assert_eq!(0, coordinator.dkg_public_shares.len());
1717
1718        coordinator.dkg_wait_signer_ids.insert(0);
1719        coordinator.gather_public_shares(&packet).unwrap();
1720        assert_eq!(1, coordinator.dkg_public_shares.len());
1721
1722        // check that a duplicate public share is ignored even if the state machine is tricked into waiting on it
1723        let dup_public_shares = DkgPublicShares {
1724            dkg_id: 0,
1725            signer_id: 0,
1726            comms: comms.clone(),
1727            kex_public_key: Point::from(kex_private_key),
1728            kex_proof: kex_proof.clone(),
1729        };
1730        let dup_packet = Packet {
1731            msg: Message::DkgPublicShares(dup_public_shares.clone()),
1732            sig: Default::default(),
1733        };
1734
1735        coordinator.dkg_wait_signer_ids.insert(0);
1736        coordinator.gather_public_shares(&dup_packet).unwrap();
1737        assert_eq!(1, coordinator.dkg_public_shares.len());
1738        assert_eq!(
1739            public_shares,
1740            coordinator
1741                .dkg_public_shares
1742                .iter()
1743                .next()
1744                .unwrap()
1745                .1
1746                .clone()
1747        );
1748    }
1749
1750    #[test]
1751    #[cfg(feature = "with_v1")]
1752    fn dkg_private_share_v1() {
1753        dkg_private_share::<v1::Aggregator, v1::Signer>();
1754    }
1755
1756    #[test]
1757    fn dkg_private_share_v2() {
1758        dkg_private_share::<v2::Aggregator, v2::Signer>();
1759    }
1760
1761    /// test basic insertion and detection of duplicates for DkgPrivateShares
1762    fn dkg_private_share<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
1763        let (coordinators, _) = setup::<FireCoordinator<Aggregator>, Signer>(2, 1);
1764        let mut coordinator: FireCoordinator<Aggregator> = coordinators[0].clone();
1765
1766        coordinator.state = State::DkgPrivateGather;
1767
1768        let private_share = DkgPrivateShares {
1769            dkg_id: 0,
1770            signer_id: 0,
1771            shares: vec![(1, HashMap::new())],
1772        };
1773        let packet = Packet {
1774            msg: Message::DkgPrivateShares(private_share.clone()),
1775            sig: Default::default(),
1776        };
1777
1778        // check that shares are ignored if not waiting on that signer
1779        coordinator.dkg_wait_signer_ids.insert(1);
1780        coordinator.gather_private_shares(&packet).unwrap();
1781        assert_eq!(0, coordinator.dkg_private_shares.len());
1782
1783        coordinator.dkg_wait_signer_ids.insert(0);
1784        coordinator.gather_private_shares(&packet).unwrap();
1785        assert_eq!(1, coordinator.dkg_private_shares.len());
1786
1787        // check that a duplicate private share is ignored even if the state machine is tricked into waiting for it
1788        let dup_private_share = DkgPrivateShares {
1789            dkg_id: 0,
1790            signer_id: 0,
1791            shares: vec![(1, HashMap::new())],
1792        };
1793        let packet = Packet {
1794            msg: Message::DkgPrivateShares(dup_private_share.clone()),
1795            sig: Default::default(),
1796        };
1797        coordinator.dkg_wait_signer_ids.insert(0);
1798        coordinator.gather_private_shares(&packet).unwrap();
1799        assert_eq!(1, coordinator.dkg_private_shares.len());
1800        assert_eq!(
1801            private_share,
1802            coordinator
1803                .dkg_private_shares
1804                .iter()
1805                .next()
1806                .unwrap()
1807                .1
1808                .clone()
1809        );
1810    }
1811
1812    #[test]
1813    #[cfg(feature = "with_v1")]
1814    fn nonce_response_v1() {
1815        nonce_response::<v1::Aggregator, v1::Signer>();
1816    }
1817
1818    #[test]
1819    fn nonce_response_v2() {
1820        nonce_response::<v2::Aggregator, v2::Signer>();
1821    }
1822
1823    /// test basic insertion and detection of duplicates for NonceResponse
1824    fn nonce_response<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
1825        let mut rng = create_rng();
1826        let (coordinators, _) = setup::<FireCoordinator<Aggregator>, Signer>(2, 1);
1827        let mut coordinator: FireCoordinator<Aggregator> = coordinators[0].clone();
1828        let signature_type = SignatureType::Frost;
1829        let message = vec![0u8];
1830        coordinator.state = State::NonceGather(signature_type);
1831
1832        let nonce_response = NonceResponse {
1833            dkg_id: 0,
1834            sign_id: 0,
1835            sign_iter_id: 0,
1836            signer_id: 0,
1837            key_ids: vec![1u32],
1838            nonces: vec![PublicNonce {
1839                D: Point::from(Scalar::random(&mut rng)),
1840                E: Point::from(Scalar::random(&mut rng)),
1841            }],
1842            message: message.clone(),
1843        };
1844        let packet = Packet {
1845            msg: Message::NonceResponse(nonce_response.clone()),
1846            sig: Default::default(),
1847        };
1848        coordinator.gather_nonces(&packet, signature_type).unwrap();
1849        assert_eq!(1, coordinator.message_nonces[&message].public_nonces.len());
1850
1851        // check that a duplicate private share is ignored
1852        let dup_nonce_response = NonceResponse {
1853            dkg_id: 0,
1854            sign_id: 0,
1855            sign_iter_id: 0,
1856            signer_id: 0,
1857            key_ids: vec![1u32],
1858            nonces: vec![PublicNonce {
1859                D: Point::from(Scalar::random(&mut rng)),
1860                E: Point::from(Scalar::random(&mut rng)),
1861            }],
1862            message: message.clone(),
1863        };
1864        let packet = Packet {
1865            msg: Message::NonceResponse(dup_nonce_response.clone()),
1866            sig: Default::default(),
1867        };
1868        coordinator.gather_nonces(&packet, signature_type).unwrap();
1869        assert_eq!(1, coordinator.message_nonces[&message].public_nonces.len());
1870        assert_eq!(
1871            nonce_response,
1872            coordinator.message_nonces[&message]
1873                .public_nonces
1874                .iter()
1875                .next()
1876                .unwrap()
1877                .1
1878                .clone()
1879        );
1880    }
1881
1882    #[test]
1883    #[cfg(feature = "with_v1")]
1884    fn sig_share_v1() {
1885        sig_share::<v1::Aggregator, v1::Signer>();
1886    }
1887
1888    #[test]
1889    fn sig_share_v2() {
1890        sig_share::<v2::Aggregator, v2::Signer>();
1891    }
1892
1893    /// test basic insertion and detection of duplicates for SignatureShareResponse
1894    #[allow(dead_code)]
1895    fn sig_share<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
1896        let mut rng = create_rng();
1897        let (coordinators, _) = setup::<FireCoordinator<Aggregator>, Signer>(2, 1);
1898        let mut coordinator = coordinators[0].clone();
1899        let signature_type = SignatureType::Frost;
1900
1901        coordinator.state = State::SigShareGather(signature_type);
1902
1903        let signature_share = SignatureShare {
1904            id: 1,
1905            z_i: Scalar::random(&mut rng),
1906            key_ids: vec![1],
1907        };
1908        let sig_share_response = SignatureShareResponse {
1909            dkg_id: 0,
1910            sign_id: 0,
1911            sign_iter_id: 0,
1912            signer_id: 0,
1913            signature_shares: vec![signature_share.clone()],
1914        };
1915        let packet = Packet {
1916            msg: Message::SignatureShareResponse(sig_share_response.clone()),
1917            sig: Default::default(),
1918        };
1919
1920        // check that a signature share is ignored due to not being on the wait list
1921        coordinator
1922            .gather_sig_shares(&packet, signature_type)
1923            .unwrap();
1924        assert_eq!(0, coordinator.signature_shares.len());
1925
1926        // add the signer to the wait list then verify that the signature share is accepted
1927        let response_info = coordinator
1928            .message_nonces
1929            .entry(coordinator.message.clone())
1930            .or_default();
1931        response_info.sign_wait_signer_ids.insert(0);
1932
1933        coordinator
1934            .gather_sig_shares(&packet, signature_type)
1935            .unwrap();
1936        assert_eq!(1, coordinator.signature_shares.len());
1937
1938        // check that a duplicate signature share is ignored even if on the wait list
1939        let response_info = coordinator
1940            .message_nonces
1941            .entry(coordinator.message.clone())
1942            .or_default();
1943        response_info.sign_wait_signer_ids.insert(0);
1944
1945        let dup_signature_share = SignatureShare {
1946            id: 1,
1947            z_i: Scalar::random(&mut rng),
1948            key_ids: vec![1],
1949        };
1950        let dup_sig_share_response = SignatureShareResponse {
1951            dkg_id: 0,
1952            sign_id: 0,
1953            sign_iter_id: 0,
1954            signer_id: 0,
1955            signature_shares: vec![dup_signature_share.clone()],
1956        };
1957        let packet = Packet {
1958            msg: Message::SignatureShareResponse(dup_sig_share_response.clone()),
1959            sig: Default::default(),
1960        };
1961        coordinator
1962            .gather_sig_shares(&packet, signature_type)
1963            .unwrap();
1964
1965        assert_eq!(1, coordinator.signature_shares.len());
1966        assert_eq!(
1967            vec![signature_share],
1968            coordinator
1969                .signature_shares
1970                .iter()
1971                .next()
1972                .unwrap()
1973                .1
1974                .clone()
1975        );
1976    }
1977
1978    #[test]
1979    #[cfg(feature = "with_v1")]
1980    fn start_public_shares_v1() {
1981        start_public_shares::<v1::Aggregator>();
1982    }
1983
1984    #[test]
1985    fn start_public_shares_v2() {
1986        start_public_shares::<v2::Aggregator>();
1987    }
1988
1989    fn start_public_shares<Aggregator: AggregatorTrait>() {
1990        let mut rng = create_rng();
1991        let config = Config::new(10, 40, 28, Scalar::random(&mut rng));
1992        let mut coordinator = FireCoordinator::<Aggregator>::new(config);
1993
1994        coordinator.state = State::DkgPublicDistribute; // Must be in this state before calling start public shares
1995
1996        let result = coordinator.start_public_shares().unwrap();
1997
1998        assert!(matches!(result.msg, Message::DkgBegin(_)));
1999        assert_eq!(coordinator.get_state(), State::DkgPublicGather);
2000        assert_eq!(coordinator.current_dkg_id, 0);
2001    }
2002
2003    #[test]
2004    #[cfg(feature = "with_v1")]
2005    fn start_private_shares_v1() {
2006        start_private_shares::<v1::Aggregator>();
2007    }
2008
2009    #[test]
2010    fn start_private_shares_v2() {
2011        start_private_shares::<v2::Aggregator>();
2012    }
2013
2014    fn start_private_shares<Aggregator: AggregatorTrait>() {
2015        let mut rng = create_rng();
2016        let config = Config::new(10, 40, 28, Scalar::random(&mut rng));
2017        let mut coordinator = FireCoordinator::<Aggregator>::new(config);
2018
2019        coordinator.state = State::DkgPrivateDistribute; // Must be in this state before calling start private shares
2020
2021        let message = coordinator.start_private_shares().unwrap();
2022        assert!(matches!(message.msg, Message::DkgPrivateBegin(_)));
2023        assert_eq!(coordinator.get_state(), State::DkgPrivateGather);
2024        assert_eq!(coordinator.current_dkg_id, 0);
2025    }
2026
2027    #[test]
2028    #[cfg(feature = "with_v1")]
2029    fn run_dkg_sign_v1() {
2030        for _ in 0..4 {
2031            run_dkg_sign::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
2032        }
2033    }
2034
2035    #[test]
2036    fn run_dkg_sign_v2() {
2037        for _ in 0..4 {
2038            run_dkg_sign::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
2039        }
2040    }
2041
2042    #[test]
2043    #[cfg(feature = "with_v1")]
2044    fn check_signature_shares_v1() {
2045        check_signature_shares::<FireCoordinator<v1::Aggregator>, v1::Signer>(
2046            5,
2047            2,
2048            SignatureType::Frost,
2049            vec![1, 2],
2050        );
2051        check_signature_shares::<FireCoordinator<v1::Aggregator>, v1::Signer>(
2052            5,
2053            2,
2054            SignatureType::Schnorr,
2055            vec![1, 2],
2056        );
2057        check_signature_shares::<FireCoordinator<v1::Aggregator>, v1::Signer>(
2058            5,
2059            2,
2060            SignatureType::Taproot(None),
2061            vec![1, 2],
2062        );
2063        check_signature_shares::<FireCoordinator<v1::Aggregator>, v1::Signer>(
2064            5,
2065            2,
2066            SignatureType::Taproot(Some([23u8; 32])),
2067            vec![1, 2],
2068        );
2069    }
2070
2071    #[test]
2072    fn check_signature_shares_v2() {
2073        check_signature_shares::<FireCoordinator<v2::Aggregator>, v2::Signer>(
2074            5,
2075            2,
2076            SignatureType::Frost,
2077            vec![0],
2078        );
2079        check_signature_shares::<FireCoordinator<v2::Aggregator>, v2::Signer>(
2080            5,
2081            2,
2082            SignatureType::Schnorr,
2083            vec![0],
2084        );
2085        check_signature_shares::<FireCoordinator<v2::Aggregator>, v2::Signer>(
2086            5,
2087            2,
2088            SignatureType::Taproot(None),
2089            vec![0],
2090        );
2091        check_signature_shares::<FireCoordinator<v2::Aggregator>, v2::Signer>(
2092            5,
2093            2,
2094            SignatureType::Taproot(Some([23u8; 32])),
2095            vec![0],
2096        );
2097    }
2098
2099    #[test]
2100    #[cfg(feature = "with_v1")]
2101    fn all_signers_dkg_v1() {
2102        all_signers_dkg::<v1::Aggregator, v1::Signer>(5, 2);
2103    }
2104
2105    #[test]
2106    fn all_signers_dkg_v2() {
2107        all_signers_dkg::<v2::Aggregator, v2::Signer>(5, 2);
2108    }
2109
2110    fn all_signers_dkg<Aggregator: AggregatorTrait, SignerType: SignerTrait>(
2111        num_signers: u32,
2112        keys_per_signer: u32,
2113    ) -> (Vec<FireCoordinator<Aggregator>>, Vec<Signer<SignerType>>) {
2114        let (mut coordinators, mut signers) =
2115            setup::<FireCoordinator<Aggregator>, SignerType>(num_signers, keys_per_signer);
2116
2117        // We have started a dkg round
2118        let message = coordinators
2119            .first_mut()
2120            .unwrap()
2121            .start_dkg_round(None)
2122            .unwrap();
2123        assert!(coordinators.first().unwrap().aggregate_public_key.is_none());
2124        assert_eq!(coordinators.first().unwrap().state, State::DkgPublicGather);
2125
2126        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinators
2127        let (outbound_messages, operation_results) =
2128            feedback_messages(&mut coordinators, &mut signers, &[message]);
2129        assert!(operation_results.is_empty());
2130        for coordinator in &coordinators {
2131            assert_eq!(coordinator.state, State::DkgPrivateGather);
2132        }
2133
2134        // Successfully got an Aggregate Public Key...
2135        assert_eq!(outbound_messages.len(), 1);
2136        assert!(
2137            matches!(&outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2138            "Expected DkgPrviateBegin message"
2139        );
2140        // Send the DKG Private Begin message to all signers and share their responses with the coordinators and signers
2141        let (outbound_messages, operation_results) =
2142            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2143        assert!(operation_results.is_empty());
2144        assert_eq!(outbound_messages.len(), 1);
2145        assert!(
2146            matches!(outbound_messages[0].msg, Message::DkgEndBegin(_)),
2147            "Expected DkgEndBegin message"
2148        );
2149
2150        // Send the DkgEndBegin message to all signers and share their responses with the coordinators and signers
2151        let (outbound_messages, operation_results) =
2152            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2153        assert!(outbound_messages.is_empty());
2154        assert_eq!(operation_results.len(), 1);
2155        let OperationResult::Dkg(point) = operation_results[0] else {
2156            panic!("Expected Dkg Operation result");
2157        };
2158        assert_ne!(point, Point::default());
2159        for coordinator in coordinators.iter() {
2160            assert_eq!(coordinator.get_aggregate_public_key(), Some(point));
2161            assert_eq!(coordinator.get_state(), State::Idle);
2162        }
2163        (coordinators, signers)
2164    }
2165
2166    #[test]
2167    #[cfg(feature = "with_v1")]
2168    fn missing_public_keys_dkg_v1() {
2169        missing_public_keys_dkg::<v1::Aggregator, v1::Signer>(10, 1);
2170    }
2171
2172    #[test]
2173    fn missing_public_keys_dkg_v2() {
2174        missing_public_keys_dkg::<v2::Aggregator, v2::Signer>(10, 1);
2175    }
2176
2177    fn missing_public_keys_dkg<Aggregator: AggregatorTrait, SignerType: SignerTrait>(
2178        num_signers: u32,
2179        keys_per_signer: u32,
2180    ) -> (Vec<FireCoordinator<Aggregator>>, Vec<Signer<SignerType>>) {
2181        let timeout = Duration::from_millis(1024);
2182        let expire = Duration::from_millis(1280);
2183        let (mut coordinators, signers) =
2184            setup_with_timeouts::<FireCoordinator<Aggregator>, SignerType>(
2185                num_signers,
2186                keys_per_signer,
2187                Some(timeout),
2188                Some(timeout),
2189                Some(timeout),
2190                Some(timeout),
2191                Some(timeout),
2192            );
2193
2194        // Start a DKG round where we will not allow all signers to recv DkgBegin, so they will not respond with DkgPublicShares
2195        let message = coordinators
2196            .first_mut()
2197            .unwrap()
2198            .start_dkg_round(None)
2199            .unwrap();
2200        assert!(coordinators.first().unwrap().aggregate_public_key.is_none());
2201        assert_eq!(coordinators.first().unwrap().state, State::DkgPublicGather);
2202
2203        let mut minimum_coordinators = coordinators.clone();
2204        let mut minimum_signers = signers.clone();
2205
2206        // Let us also remove that signers public key from the config including all of its key ids
2207        let mut removed_signer = minimum_signers.pop().expect("Failed to pop signer");
2208        let public_key = removed_signer
2209            .public_keys
2210            .signers
2211            .remove(&removed_signer.signer_id)
2212            .expect("Failed to remove public key");
2213        removed_signer
2214            .public_keys
2215            .key_ids
2216            .retain(|_k, pk| pk.to_bytes() != public_key.to_bytes());
2217
2218        for signer in minimum_signers.iter_mut() {
2219            // Overwrite all other signers to use the new public keys missing the removed signers public key
2220            signer.public_keys = removed_signer.public_keys.clone();
2221        }
2222
2223        // Send the DKG Begin message to minimum signers and gather responses by sharing with signers and coordinator
2224        let (outbound_messages, operation_results) = feedback_messages(
2225            &mut minimum_coordinators,
2226            &mut minimum_signers,
2227            from_ref(&message),
2228        );
2229
2230        assert!(outbound_messages.is_empty());
2231        assert!(operation_results.is_empty());
2232        assert_eq!(coordinators.first().unwrap().state, State::DkgPublicGather,);
2233
2234        // Sleep long enough to hit the timeout
2235        thread::sleep(expire);
2236
2237        let (outbound_messages, operation_results) = minimum_coordinators
2238            .first_mut()
2239            .unwrap()
2240            .process_timeout()
2241            .unwrap();
2242
2243        assert!(outbound_messages.is_some());
2244        assert!(operation_results.is_none());
2245        assert_eq!(
2246            minimum_coordinators.first().unwrap().state,
2247            State::DkgPrivateGather,
2248        );
2249        (minimum_coordinators, minimum_signers)
2250    }
2251
2252    #[test]
2253    #[cfg(feature = "with_v1")]
2254    fn minimum_signers_dkg_v1() {
2255        minimum_signers_dkg::<v1::Aggregator, v1::Signer>(10, 2);
2256    }
2257
2258    #[test]
2259    fn minimum_signers_dkg_v2() {
2260        minimum_signers_dkg::<v2::Aggregator, v2::Signer>(10, 2);
2261    }
2262
2263    fn minimum_signers_dkg<Aggregator: AggregatorTrait, SignerType: SignerTrait>(
2264        num_signers: u32,
2265        keys_per_signer: u32,
2266    ) -> (Vec<FireCoordinator<Aggregator>>, Vec<Signer<SignerType>>) {
2267        let timeout = Duration::from_millis(1024);
2268        let expire = Duration::from_millis(1280);
2269        let (coordinators, signers) = setup_with_timeouts::<FireCoordinator<Aggregator>, SignerType>(
2270            num_signers,
2271            keys_per_signer,
2272            Some(timeout),
2273            Some(timeout),
2274            Some(timeout),
2275            Some(timeout),
2276            Some(timeout),
2277        );
2278
2279        // Start a DKG round where we will not allow all signers to recv DkgBegin, so they will not respond with DkgPublicShares
2280
2281        // DKG threshold is 9/10, so need to remove 1
2282        let num_signers_to_remove = 1;
2283
2284        let mut minimum_coordinators = coordinators.clone();
2285        let mut minimum_signers = signers.clone();
2286
2287        minimum_signers.truncate(minimum_signers.len().saturating_sub(num_signers_to_remove));
2288
2289        let message = minimum_coordinators
2290            .first_mut()
2291            .unwrap()
2292            .start_dkg_round(None)
2293            .unwrap();
2294        assert!(minimum_coordinators
2295            .first()
2296            .unwrap()
2297            .aggregate_public_key
2298            .is_none());
2299        assert_eq!(
2300            minimum_coordinators.first().unwrap().state,
2301            State::DkgPublicGather
2302        );
2303
2304        // Send the DKG Begin message to minimum signers and gather responses by sharing with signers and coordinator
2305        let (outbound_messages, operation_results) = feedback_messages(
2306            &mut minimum_coordinators,
2307            &mut minimum_signers,
2308            from_ref(&message),
2309        );
2310
2311        assert!(outbound_messages.is_empty());
2312        assert!(operation_results.is_empty());
2313        assert_eq!(
2314            minimum_coordinators.first().unwrap().state,
2315            State::DkgPublicGather,
2316        );
2317
2318        // Sleep long enough to hit the timeout
2319        thread::sleep(expire);
2320
2321        let (outbound_messages, operation_results) = minimum_coordinators
2322            .first_mut()
2323            .unwrap()
2324            .process_timeout()
2325            .unwrap();
2326
2327        assert!(outbound_messages.is_some());
2328        assert!(operation_results.is_none());
2329        assert_eq!(
2330            minimum_coordinators.first().unwrap().state,
2331            State::DkgPrivateGather,
2332        );
2333
2334        // Run DKG again with fresh coordinator and signers, this time allow gathering DkgPublicShares but timeout getting DkgEnd
2335        let mut minimum_coordinators = coordinators.clone();
2336        let mut minimum_signers = signers.clone();
2337
2338        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinator
2339        let (outbound_messages, operation_results) =
2340            feedback_messages(&mut minimum_coordinators, &mut minimum_signers, &[message]);
2341        assert!(operation_results.is_empty());
2342        assert_eq!(
2343            minimum_coordinators.first().unwrap().state,
2344            State::DkgPrivateGather
2345        );
2346
2347        assert_eq!(outbound_messages.len(), 1);
2348        assert!(
2349            matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2350            "Expected DkgPrivateBegin message"
2351        );
2352
2353        // now remove signers so the set is minimum
2354        minimum_signers.truncate(minimum_signers.len().saturating_sub(num_signers_to_remove));
2355
2356        // Send the DKG Private Begin message to minimum signers and share their responses with the coordinator and signers
2357        let (outbound_messages, operation_results) = feedback_messages(
2358            &mut minimum_coordinators,
2359            &mut minimum_signers,
2360            &outbound_messages,
2361        );
2362        assert!(outbound_messages.is_empty());
2363        assert!(operation_results.is_empty());
2364        assert_eq!(
2365            minimum_coordinators.first().unwrap().state,
2366            State::DkgPrivateGather,
2367        );
2368
2369        // Sleep long enough to hit the timeout
2370        thread::sleep(expire);
2371
2372        let (outbound_message, operation_result) = minimum_coordinators
2373            .first_mut()
2374            .unwrap()
2375            .process_timeout()
2376            .unwrap();
2377
2378        assert!(outbound_message.is_some());
2379        assert!(operation_result.is_none());
2380        assert!(
2381            matches!(
2382                outbound_message.clone().unwrap().msg,
2383                Message::DkgEndBegin(_)
2384            ),
2385            "Expected DkgEndBegin message"
2386        );
2387        assert_eq!(
2388            minimum_coordinators.first().unwrap().state,
2389            State::DkgEndGather,
2390        );
2391
2392        // Send the DkgEndBegin message to all signers and share their responses with the coordinator and signers
2393        let (outbound_messages, operation_results) = feedback_messages(
2394            &mut minimum_coordinators,
2395            &mut minimum_signers,
2396            &[outbound_message.unwrap()],
2397        );
2398        assert!(outbound_messages.is_empty());
2399        assert_eq!(operation_results.len(), 1);
2400        let OperationResult::Dkg(point) = operation_results[0] else {
2401            panic!("Expected Dkg Operation result");
2402        };
2403        assert_ne!(point, Point::default());
2404        for coordinator in minimum_coordinators.iter() {
2405            assert_eq!(coordinator.get_aggregate_public_key(), Some(point));
2406            assert_eq!(coordinator.get_state(), State::Idle);
2407        }
2408
2409        (minimum_coordinators, minimum_signers)
2410    }
2411
2412    #[test]
2413    #[cfg(feature = "with_v1")]
2414    fn insufficient_signers_dkg_v1() {
2415        insufficient_signers_dkg::<v1::Aggregator, v1::Signer>();
2416    }
2417
2418    #[test]
2419    fn insufficient_signers_dkg_v2() {
2420        insufficient_signers_dkg::<v2::Aggregator, v2::Signer>();
2421    }
2422
2423    fn insufficient_signers_dkg<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
2424        let timeout = Duration::from_millis(1024);
2425        let expire = Duration::from_millis(1280);
2426        let num_signers = 10;
2427        let keys_per_signer = 2;
2428        let (coordinators, signers) = setup_with_timeouts::<FireCoordinator<Aggregator>, Signer>(
2429            num_signers,
2430            keys_per_signer,
2431            Some(timeout),
2432            Some(timeout),
2433            Some(timeout),
2434            Some(timeout),
2435            Some(timeout),
2436        );
2437
2438        // Start a DKG round where we will not allow all signers to recv DkgBegin, so they will not respond with DkgPublicShares
2439
2440        // DKG threshold is 9/10, so need to remove 2
2441        let num_signers_to_remove = 2;
2442
2443        let mut insufficient_coordinators = coordinators.clone();
2444        let mut insufficient_signers = signers.clone();
2445
2446        insufficient_signers.truncate(
2447            insufficient_signers
2448                .len()
2449                .saturating_sub(num_signers_to_remove),
2450        );
2451
2452        let message = insufficient_coordinators
2453            .first_mut()
2454            .unwrap()
2455            .start_dkg_round(None)
2456            .unwrap();
2457        assert!(insufficient_coordinators
2458            .first()
2459            .unwrap()
2460            .aggregate_public_key
2461            .is_none());
2462        assert_eq!(
2463            insufficient_coordinators.first().unwrap().state,
2464            State::DkgPublicGather
2465        );
2466
2467        // Send the DKG Begin message to insufficient signers and gather responses by sharing with signers and coordinator
2468        let (outbound_messages, operation_results) = feedback_messages(
2469            &mut insufficient_coordinators,
2470            &mut insufficient_signers,
2471            from_ref(&message),
2472        );
2473
2474        // Failed to get an aggregate public key
2475        assert!(outbound_messages.is_empty());
2476        assert!(operation_results.is_empty());
2477        for coordinator in &insufficient_coordinators {
2478            assert_eq!(coordinator.state, State::DkgPublicGather);
2479        }
2480
2481        // Sleep long enough to hit the timeout
2482        thread::sleep(expire);
2483
2484        let (outbound_message, operation_result) = insufficient_coordinators
2485            .first_mut()
2486            .unwrap()
2487            .process_timeout()
2488            .unwrap();
2489
2490        assert!(outbound_message.is_none());
2491        assert!(operation_result.is_some());
2492        assert_eq!(
2493            insufficient_coordinators.first().unwrap().state,
2494            State::DkgPublicGather,
2495        );
2496        assert!(
2497            matches!(
2498                operation_result.unwrap(),
2499                OperationResult::DkgError(DkgError::DkgPublicTimeout(_))
2500            ),
2501            "Expected OperationResult::DkgError(DkgError::DkgPublicTimeout"
2502        );
2503
2504        // Run DKG again with fresh coordinator and signers, this time allow gathering DkgPublicShares but timeout getting DkgEnd
2505        let mut insufficient_coordinator = coordinators.clone();
2506        let mut insufficient_signers = signers.clone();
2507
2508        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinator
2509        let (outbound_messages, operation_results) = feedback_messages(
2510            &mut insufficient_coordinator,
2511            &mut insufficient_signers,
2512            &[message],
2513        );
2514        assert!(operation_results.is_empty());
2515        assert_eq!(
2516            insufficient_coordinator.first().unwrap().state,
2517            State::DkgPrivateGather
2518        );
2519
2520        // Successfully got an Aggregate Public Key...
2521        assert_eq!(outbound_messages.len(), 1);
2522        assert!(
2523            matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2524            "Expected DkgPrivateBegin message"
2525        );
2526
2527        // now remove signers so the set is insufficient
2528        insufficient_signers.truncate(
2529            insufficient_signers
2530                .len()
2531                .saturating_sub(num_signers_to_remove),
2532        );
2533
2534        // Send the DKG Private Begin message to insufficient signers and share their responses with the coordinator and signers
2535        let (outbound_messages, operation_results) = feedback_messages(
2536            &mut insufficient_coordinator,
2537            &mut insufficient_signers,
2538            &outbound_messages,
2539        );
2540        assert!(outbound_messages.is_empty());
2541        assert!(operation_results.is_empty());
2542        assert_eq!(
2543            insufficient_coordinator.first().unwrap().state,
2544            State::DkgPrivateGather,
2545        );
2546
2547        // Sleep long enough to hit the timeout
2548        thread::sleep(expire);
2549
2550        let (outbound_message, operation_result) = insufficient_coordinator
2551            .first_mut()
2552            .unwrap()
2553            .process_timeout()
2554            .unwrap();
2555
2556        assert!(outbound_message.is_none());
2557        assert!(operation_result.is_some());
2558        assert_eq!(
2559            insufficient_coordinator.first().unwrap().state,
2560            State::DkgPrivateGather,
2561        );
2562        assert!(
2563            matches!(
2564                operation_result.unwrap(),
2565                OperationResult::DkgError(DkgError::DkgPrivateTimeout(_))
2566            ),
2567            "Expected OperationResult::DkgError(DkgError::DkgPrivateTimeout)"
2568        );
2569    }
2570
2571    #[test]
2572    #[cfg(feature = "with_v1")]
2573    fn malicious_signers_dkg_v1() {
2574        malicious_signers_dkg::<v1::Aggregator, v1::Signer>(5, 2);
2575    }
2576
2577    #[test]
2578    fn malicious_signers_dkg_v2() {
2579        malicious_signers_dkg::<v2::Aggregator, v2::Signer>(5, 2);
2580    }
2581
2582    fn malicious_signers_dkg<Aggregator: AggregatorTrait, SignerType: SignerTrait>(
2583        num_signers: u32,
2584        keys_per_signer: u32,
2585    ) -> (Vec<FireCoordinator<Aggregator>>, Vec<Signer<SignerType>>) {
2586        let (mut coordinators, mut signers) =
2587            setup::<FireCoordinator<Aggregator>, SignerType>(num_signers, keys_per_signer);
2588
2589        // We have started a dkg round
2590        let message = coordinators
2591            .first_mut()
2592            .unwrap()
2593            .start_dkg_round(None)
2594            .unwrap();
2595        assert!(coordinators.first().unwrap().aggregate_public_key.is_none());
2596        assert_eq!(coordinators.first().unwrap().state, State::DkgPublicGather);
2597
2598        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinators
2599        let (outbound_messages, operation_results) =
2600            feedback_messages(&mut coordinators, &mut signers, &[message]);
2601        assert!(operation_results.is_empty());
2602        for coordinator in &coordinators {
2603            assert_eq!(coordinator.state, State::DkgPrivateGather);
2604        }
2605
2606        assert_eq!(outbound_messages.len(), 1);
2607        assert!(
2608            matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2609            "Expected DkgPrivateBegin message"
2610        );
2611
2612        // Send the DKG Private Begin message to all signers and share their responses with the coordinators and signers, but mutate one signer's DkgPrivateShares so it is marked malicious
2613        let (outbound_messages, operation_results) = feedback_mutated_messages(
2614            &mut coordinators,
2615            &mut signers,
2616            &outbound_messages,
2617            |signer, msgs| {
2618                if signer.signer_id != 0 {
2619                    return msgs;
2620                }
2621                msgs.iter()
2622                    .map(|packet| {
2623                        let Message::DkgPrivateShares(shares) = &packet.msg else {
2624                            return packet.clone();
2625                        };
2626                        // mutate one of the shares
2627                        let sshares: Vec<(u32, HashMap<u32, Vec<u8>>)> = shares
2628                            .shares
2629                            .iter()
2630                            .map(|(src_party_id, share_map)| {
2631                                (
2632                                    *src_party_id,
2633                                    share_map
2634                                        .iter()
2635                                        .map(|(dst_key_id, bytes)| {
2636                                            let mut bytes = bytes.clone();
2637                                            bytes.insert(0, 234);
2638                                            (*dst_key_id, bytes)
2639                                        })
2640                                        .collect(),
2641                                )
2642                            })
2643                            .collect();
2644
2645                        Packet {
2646                            msg: Message::DkgPrivateShares(DkgPrivateShares {
2647                                dkg_id: shares.dkg_id,
2648                                signer_id: shares.signer_id,
2649                                shares: sshares.clone(),
2650                            }),
2651                            sig: vec![],
2652                        }
2653                    })
2654                    .collect()
2655            },
2656        );
2657        assert!(operation_results.is_empty());
2658        assert_eq!(outbound_messages.len(), 1);
2659        assert!(
2660            matches!(outbound_messages[0].msg, Message::DkgEndBegin(_)),
2661            "Expected DkgEndBegin message"
2662        );
2663
2664        // Send the DkgEndBegin message to all signers and share their responses with the coordinators and signers
2665        let (outbound_messages, operation_results) =
2666            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2667        assert!(outbound_messages.is_empty());
2668        assert_eq!(operation_results.len(), 1);
2669        let OperationResult::DkgError(DkgError::DkgEndFailure {
2670            reported_failures, ..
2671        }) = &operation_results[0]
2672        else {
2673            panic!("Expected OperationResult::DkgError(DkgError::DkgEndFailure");
2674        };
2675
2676        for (_signer_id, dkg_failure) in reported_failures {
2677            let DkgFailure::BadPrivateShares(bad_share_map) = dkg_failure else {
2678                panic!("Expected DkgFailure::BadPrivateShares");
2679            };
2680            for (bad_signer_id, _bad_private_share) in bad_share_map {
2681                assert_eq!(*bad_signer_id, 0u32);
2682            }
2683        }
2684        (coordinators, signers)
2685    }
2686
2687    #[test]
2688    #[cfg(feature = "with_v1")]
2689    fn bad_poly_length_dkg_v1() {
2690        bad_poly_length_dkg::<v1::Aggregator, v1::Signer>(5, 2);
2691    }
2692
2693    #[test]
2694    fn bad_poly_length_dkg_v2() {
2695        bad_poly_length_dkg::<v2::Aggregator, v2::Signer>(5, 2);
2696    }
2697
2698    fn bad_poly_length_dkg<Aggregator: AggregatorTrait, SignerType: SignerTrait>(
2699        num_signers: u32,
2700        keys_per_signer: u32,
2701    ) -> (Vec<FireCoordinator<Aggregator>>, Vec<Signer<SignerType>>) {
2702        let (mut coordinators, mut signers) =
2703            setup::<FireCoordinator<Aggregator>, SignerType>(num_signers, keys_per_signer);
2704
2705        // We have started a dkg round
2706        let message = coordinators
2707            .first_mut()
2708            .unwrap()
2709            .start_dkg_round(None)
2710            .unwrap();
2711        assert!(coordinators.first().unwrap().aggregate_public_key.is_none());
2712        assert_eq!(coordinators.first().unwrap().state, State::DkgPublicGather);
2713
2714        // Send the DkgBegin message to all signers and share their responses with the coordinators and signers, but mutate two signers' DkgPublicShares: make one polynomial larger than the threshold, and the other smaller
2715        let (outbound_messages, operation_results) = feedback_mutated_messages(
2716            &mut coordinators,
2717            &mut signers,
2718            &[message],
2719            |signer, msgs| {
2720                if signer.signer_id != 0 && signer.signer_id != 1 {
2721                    return msgs;
2722                }
2723                msgs.iter()
2724                    .map(|packet| {
2725                        let Message::DkgPublicShares(shares) = &packet.msg else {
2726                            return packet.clone();
2727                        };
2728                        let comms = shares
2729                            .comms
2730                            .iter()
2731                            .map(|(id, comm)| {
2732                                let mut c = comm.clone();
2733                                if signer.signer_id == 0 {
2734                                    c.poly.push(Point::new());
2735                                } else {
2736                                    c.poly.pop();
2737                                }
2738                                (*id, c)
2739                            })
2740                            .collect();
2741                        Packet {
2742                            msg: Message::DkgPublicShares(DkgPublicShares {
2743                                dkg_id: shares.dkg_id,
2744                                signer_id: shares.signer_id,
2745                                comms,
2746                                kex_public_key: Point::new(),
2747                                kex_proof: schnorr::Proof {
2748                                    R: Point::new(),
2749                                    s: Scalar::new(),
2750                                },
2751                            }),
2752                            sig: vec![],
2753                        }
2754                    })
2755                    .collect()
2756            },
2757        );
2758
2759        assert!(operation_results.is_empty());
2760        for coordinator in &coordinators {
2761            assert_eq!(coordinator.state, State::DkgPrivateGather);
2762        }
2763
2764        assert_eq!(outbound_messages.len(), 1);
2765        assert!(
2766            matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2767            "Expected DkgPrivateBegin message"
2768        );
2769
2770        let (outbound_messages, operation_results) =
2771            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2772        assert!(operation_results.is_empty());
2773        assert_eq!(outbound_messages.len(), 1);
2774        assert!(
2775            matches!(outbound_messages[0].msg, Message::DkgEndBegin(_)),
2776            "Expected DkgEndBegin message"
2777        );
2778
2779        // Send the DkgEndBegin message to all signers and share their responses with the coordinators and signers
2780        let (outbound_messages, operation_results) =
2781            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2782        assert!(outbound_messages.is_empty());
2783        assert_eq!(operation_results.len(), 1);
2784        let OperationResult::DkgError(DkgError::DkgEndFailure {
2785            reported_failures, ..
2786        }) = &operation_results[0]
2787        else {
2788            panic!("Expected OperationResult::DkgError(DkgError::DkgEndFailure)");
2789        };
2790
2791        for (_signer_id, dkg_failure) in reported_failures {
2792            let DkgFailure::BadPublicShares(bad_shares) = dkg_failure else {
2793                panic!("Expected DkgFailure::BadPublicShares");
2794            };
2795            for bad_signer_id in bad_shares {
2796                assert!(*bad_signer_id == 0u32 || *bad_signer_id == 1u32);
2797            }
2798        }
2799        (coordinators, signers)
2800    }
2801
2802    #[test]
2803    #[cfg(feature = "with_v1")]
2804    fn all_signers_sign_v1() {
2805        all_signers_sign::<v1::Aggregator, v1::Signer>();
2806    }
2807
2808    #[test]
2809    fn all_signers_sign_v2() {
2810        all_signers_sign::<v2::Aggregator, v2::Signer>();
2811    }
2812
2813    fn all_signers_sign<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
2814        let (mut coordinators, mut signers) = all_signers_dkg::<Aggregator, Signer>(5, 2);
2815
2816        // We have started a signing round
2817        let msg = "It was many and many a year ago, in a kingdom by the sea"
2818            .as_bytes()
2819            .to_vec();
2820        let signature_type = SignatureType::Frost;
2821        let message = coordinators
2822            .first_mut()
2823            .unwrap()
2824            .start_signing_round(&msg, signature_type, None)
2825            .unwrap();
2826        assert_eq!(
2827            coordinators.first().unwrap().state,
2828            State::NonceGather(signature_type)
2829        );
2830
2831        // Send the message to all signers and gather responses by sharing with all other signers and coordinator
2832        let (outbound_messages, operation_results) =
2833            feedback_messages(&mut coordinators, &mut signers, &[message]);
2834        assert!(operation_results.is_empty());
2835        for coordinator in &coordinators {
2836            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
2837        }
2838
2839        assert_eq!(outbound_messages.len(), 1);
2840        assert!(
2841            matches!(outbound_messages[0].msg, Message::SignatureShareRequest(_)),
2842            "Expected SignatureShareRequest message"
2843        );
2844        // Send the SignatureShareRequest message to all signers and share their responses with the coordinator and signers
2845        let (outbound_messages, operation_results) =
2846            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2847        assert!(outbound_messages.is_empty());
2848        assert_eq!(operation_results.len(), 1);
2849        let OperationResult::Sign(sig) = &operation_results[0] else {
2850            panic!("Expected Signature Operation result")
2851        };
2852        assert!(sig.verify(
2853            &coordinators
2854                .first()
2855                .unwrap()
2856                .aggregate_public_key
2857                .expect("No aggregate public key set!"),
2858            &msg
2859        ));
2860        for coordinator in &coordinators {
2861            assert_eq!(coordinator.state, State::Idle);
2862        }
2863    }
2864
2865    #[test]
2866    #[cfg(feature = "with_v1")]
2867    fn minimum_signers_sign_v1() {
2868        minimum_signers_sign::<v1::Aggregator, v1::Signer>();
2869    }
2870
2871    #[test]
2872    fn minimum_signers_sign_v2() {
2873        minimum_signers_sign::<v2::Aggregator, v2::Signer>();
2874    }
2875
2876    fn minimum_signers_sign<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
2877        let num_signers = 10;
2878        let keys_per_signer = 2;
2879
2880        let (mut coordinators, mut signers) =
2881            minimum_signers_dkg::<Aggregator, Signer>(num_signers, keys_per_signer);
2882        let config = coordinators.first().unwrap().get_config();
2883
2884        // Figure out how many signers we can remove and still be above the threshold
2885        let num_keys = config.num_keys as f64;
2886        let threshold = config.threshold as f64;
2887        let mut num_signers_to_remove =
2888            ((num_keys - threshold) / keys_per_signer as f64).floor() as usize;
2889        if num_signers as usize > signers.len() {
2890            num_signers_to_remove -= (num_signers - signers.len() as u32) as usize;
2891        }
2892
2893        signers.truncate(signers.len().saturating_sub(num_signers_to_remove));
2894
2895        // Start a signing round
2896        let msg = "It was many and many a year ago, in a kingdom by the sea"
2897            .as_bytes()
2898            .to_vec();
2899        let signature_type = SignatureType::Frost;
2900        let message = coordinators
2901            .first_mut()
2902            .unwrap()
2903            .start_signing_round(&msg, signature_type, None)
2904            .unwrap();
2905        assert_eq!(
2906            coordinators.first().unwrap().state,
2907            State::NonceGather(signature_type)
2908        );
2909
2910        // Send the message to all signers and gather responses by sharing with all other signers and coordinator
2911        let (outbound_messages, operation_results) =
2912            feedback_messages(&mut coordinators, &mut signers, &[message]);
2913        assert!(operation_results.is_empty());
2914        for coordinator in &coordinators {
2915            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
2916        }
2917
2918        assert_eq!(outbound_messages.len(), 1);
2919        assert!(
2920            matches!(outbound_messages[0].msg, Message::SignatureShareRequest(_)),
2921            "Expected SignatureShareRequest message"
2922        );
2923        // Send the SignatureShareRequest message to all signers and share their responses with the coordinator and signers
2924        let (outbound_messages, operation_results) =
2925            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
2926        assert!(outbound_messages.is_empty());
2927        assert_eq!(operation_results.len(), 1);
2928        let OperationResult::Sign(sig) = &operation_results[0] else {
2929            panic!("Expected Signature Operation result");
2930        };
2931        assert!(sig.verify(
2932            &coordinators
2933                .first()
2934                .unwrap()
2935                .aggregate_public_key
2936                .expect("No aggregate public key set!"),
2937            &msg
2938        ));
2939
2940        for coordinator in &coordinators {
2941            assert_eq!(coordinator.state, State::Idle);
2942        }
2943    }
2944
2945    #[test]
2946    #[cfg(feature = "with_v1")]
2947    fn missing_public_keys_sign_v1() {
2948        missing_public_keys_sign::<v1::Aggregator, v1::Signer>();
2949    }
2950
2951    #[test]
2952    fn missing_public_keys_sign_v2() {
2953        missing_public_keys_sign::<v2::Aggregator, v2::Signer>();
2954    }
2955
2956    fn missing_public_keys_sign<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
2957        let num_signers = 10;
2958        let keys_per_signer = 2;
2959
2960        let (mut coordinators, mut signers) =
2961            all_signers_dkg::<Aggregator, Signer>(num_signers, keys_per_signer);
2962
2963        // Let us also remove that signers public key from the config including all of its key ids
2964        let mut removed_signer = signers.pop().expect("Failed to pop signer");
2965        let public_key = removed_signer
2966            .public_keys
2967            .signers
2968            .remove(&removed_signer.signer_id)
2969            .expect("Failed to remove public key");
2970        removed_signer
2971            .public_keys
2972            .key_ids
2973            .retain(|_k, pk| pk.to_bytes() != public_key.to_bytes());
2974
2975        for signer in signers.iter_mut() {
2976            signer.public_keys = removed_signer.public_keys.clone();
2977        }
2978
2979        // Start a signing round
2980        let msg = "It was many and many a year ago, in a kingdom by the sea"
2981            .as_bytes()
2982            .to_vec();
2983        let signature_type = SignatureType::Frost;
2984        let message = coordinators
2985            .first_mut()
2986            .unwrap()
2987            .start_signing_round(&msg, signature_type, None)
2988            .unwrap();
2989        assert_eq!(
2990            coordinators.first().unwrap().state,
2991            State::NonceGather(signature_type)
2992        );
2993
2994        // Send the message to all signers and gather responses by sharing with all other signers and coordinator
2995        let (outbound_messages, operation_results) =
2996            feedback_messages(&mut coordinators, &mut signers, &[message]);
2997        assert!(operation_results.is_empty());
2998        for coordinator in &coordinators {
2999            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
3000        }
3001
3002        assert_eq!(outbound_messages.len(), 1);
3003        assert!(
3004            matches!(&outbound_messages[0].msg, Message::SignatureShareRequest(_)),
3005            "Expected SignatureShareRequest message"
3006        );
3007        // Send the SignatureShareRequest message to all signers and share their responses with the coordinator and signers
3008        let (outbound_messages, operation_results) =
3009            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3010        assert!(outbound_messages.is_empty());
3011        assert_eq!(operation_results.len(), 1);
3012        let OperationResult::Sign(sig) = &operation_results[0] else {
3013            panic!("Expected Signature Operation result");
3014        };
3015        assert!(sig.verify(
3016            &coordinators
3017                .first()
3018                .unwrap()
3019                .aggregate_public_key
3020                .expect("No aggregate public key set!"),
3021            &msg
3022        ));
3023
3024        for coordinator in &coordinators {
3025            assert_eq!(coordinator.state, State::Idle);
3026        }
3027    }
3028
3029    #[test]
3030    #[cfg(feature = "with_v1")]
3031    fn insufficient_signers_sign_v1() {
3032        insufficient_signers_sign::<v1::Aggregator, v1::Signer>();
3033    }
3034
3035    #[test]
3036    fn insufficient_signers_sign_v2() {
3037        insufficient_signers_sign::<v2::Aggregator, v2::Signer>();
3038    }
3039
3040    fn insufficient_signers_sign<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
3041        let num_signers = 5;
3042        let keys_per_signer = 2;
3043        let (mut coordinators, mut signers) =
3044            setup_with_timeouts::<FireCoordinator<Aggregator>, Signer>(
3045                num_signers,
3046                keys_per_signer,
3047                None,
3048                None,
3049                None,
3050                Some(Duration::from_millis(128)),
3051                Some(Duration::from_millis(128)),
3052            );
3053        let config = coordinators.first().unwrap().get_config();
3054
3055        // We have started a dkg round
3056        let message = coordinators
3057            .first_mut()
3058            .unwrap()
3059            .start_dkg_round(None)
3060            .unwrap();
3061        assert!(coordinators.first().unwrap().aggregate_public_key.is_none());
3062        assert_eq!(coordinators.first().unwrap().state, State::DkgPublicGather);
3063
3064        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinator
3065        let (outbound_messages, operation_results) =
3066            feedback_messages(&mut coordinators, &mut signers, &[message]);
3067        assert!(operation_results.is_empty());
3068        for coordinator in &coordinators {
3069            assert_eq!(coordinator.state, State::DkgPrivateGather);
3070        }
3071
3072        assert_eq!(outbound_messages.len(), 1);
3073        assert!(
3074            matches!(&outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
3075            "Expected DkgPrivateBegin message"
3076        );
3077
3078        // Send the DKG Private Begin message to all signers and share their responses with the coordinators and signers
3079        let (outbound_messages, operation_results) =
3080            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3081        assert!(operation_results.is_empty());
3082        assert_eq!(outbound_messages.len(), 1);
3083        assert!(
3084            matches!(&outbound_messages[0].msg, Message::DkgEndBegin(_)),
3085            "Expected DkgEndBegin message"
3086        );
3087
3088        // Send the DKG End Begin message to all signers and share their responses with the coordinator and signers
3089        let (outbound_messages, operation_results) =
3090            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3091        assert!(outbound_messages.is_empty());
3092        assert_eq!(operation_results.len(), 1);
3093        let OperationResult::Dkg(point) = operation_results[0] else {
3094            panic!("Expected Dkg Operationr result");
3095        };
3096        assert_ne!(point, Point::default());
3097        for coordinator in &coordinators {
3098            assert_eq!(coordinator.aggregate_public_key, Some(point));
3099            assert_eq!(coordinator.state, State::Idle);
3100        }
3101
3102        // Figure out how many signers we can remove and still be above the threshold
3103        let num_keys = config.num_keys as f64;
3104        let threshold = config.threshold as f64;
3105        let num_signers_to_remove =
3106            (((num_keys - threshold) / keys_per_signer as f64).floor() + 1_f64) as usize;
3107        let mut insufficient_coordinators = coordinators.clone();
3108        let mut insufficient_signers = signers.clone();
3109
3110        insufficient_signers.truncate(
3111            insufficient_signers
3112                .len()
3113                .saturating_sub(num_signers_to_remove),
3114        );
3115
3116        // Start a signing round with an insufficient number of signers
3117        let msg = "It was many and many a year ago, in a kingdom by the sea"
3118            .as_bytes()
3119            .to_vec();
3120        let signature_type = SignatureType::Frost;
3121        let message = insufficient_coordinators
3122            .first_mut()
3123            .unwrap()
3124            .start_signing_round(&msg, signature_type, None)
3125            .unwrap();
3126        assert_eq!(
3127            insufficient_coordinators.first().unwrap().state,
3128            State::NonceGather(signature_type)
3129        );
3130
3131        // Send the message to all signers and gather responses by sharing with all other signers and coordinator
3132        let (outbound_messages, operation_results) = feedback_messages(
3133            &mut insufficient_coordinators,
3134            &mut insufficient_signers,
3135            &[message],
3136        );
3137        assert!(operation_results.is_empty());
3138        for coordinator in &insufficient_coordinators {
3139            assert_eq!(coordinator.state, State::NonceGather(signature_type));
3140        }
3141
3142        assert!(outbound_messages.is_empty());
3143
3144        // Sleep long enough to hit the timeout
3145        thread::sleep(Duration::from_millis(256));
3146
3147        let (outbound_message, operation_result) = insufficient_coordinators
3148            .first_mut()
3149            .unwrap()
3150            .process_timeout()
3151            .unwrap();
3152
3153        assert!(outbound_message.is_none());
3154        assert!(operation_result.is_some());
3155        for coordinator in &insufficient_coordinators {
3156            assert_eq!(coordinator.state, State::NonceGather(signature_type));
3157        }
3158
3159        assert!(
3160            matches!(
3161                operation_result.unwrap(),
3162                OperationResult::SignError(SignError::NonceTimeout(..))
3163            ),
3164            "Expected OperationResult::SignError(SignError::NonceTimeout)"
3165        );
3166
3167        // Start a new signing round with a sufficient number of signers for nonces but not sig shares
3168        let mut insufficient_coordinators = coordinators.clone();
3169        let mut insufficient_signers = signers.clone();
3170
3171        let message = insufficient_coordinators
3172            .first_mut()
3173            .unwrap()
3174            .start_signing_round(&msg, signature_type, None)
3175            .unwrap();
3176        assert_eq!(
3177            insufficient_coordinators.first().unwrap().state,
3178            State::NonceGather(signature_type)
3179        );
3180
3181        // Send the message to all signers and gather responses by sharing with all other signers and insufficient_coordinator
3182        let (outbound_messages, operation_results) = feedback_messages(
3183            &mut insufficient_coordinators,
3184            &mut insufficient_signers,
3185            &[message],
3186        );
3187        assert!(operation_results.is_empty());
3188        for coordinator in &insufficient_coordinators {
3189            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
3190        }
3191
3192        assert_eq!(outbound_messages.len(), 1);
3193
3194        let mut malicious = Vec::new();
3195
3196        // now remove signers so the number is insufficient
3197        let num_signers_to_drain = insufficient_signers
3198            .len()
3199            .saturating_sub(num_signers_to_remove);
3200        malicious.extend(insufficient_signers.drain(num_signers_to_drain..));
3201
3202        // Send the SignatureShareRequest message to all signers and share their responses with the coordinator and signers
3203        let (outbound_messages, operation_results) = feedback_messages(
3204            &mut insufficient_coordinators,
3205            &mut insufficient_signers,
3206            &outbound_messages,
3207        );
3208        assert!(outbound_messages.is_empty());
3209        assert!(operation_results.is_empty());
3210
3211        for coordinator in &insufficient_coordinators {
3212            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
3213        }
3214
3215        // Sleep long enough to hit the timeout
3216        thread::sleep(Duration::from_millis(256));
3217
3218        let (outbound_message, operation_result) = insufficient_coordinators
3219            .first_mut()
3220            .unwrap()
3221            .process_timeout()
3222            .unwrap();
3223
3224        assert!(outbound_message.is_some());
3225        assert!(operation_result.is_none());
3226        assert_eq!(
3227            insufficient_coordinators.first().unwrap().state,
3228            State::NonceGather(signature_type)
3229        );
3230
3231        // put the malicious signers back in
3232        insufficient_signers.append(&mut malicious);
3233
3234        // Send the NonceRequest message to all signers and share their responses with the coordinator and signers
3235        let (outbound_messages, operation_results) = feedback_messages(
3236            &mut insufficient_coordinators,
3237            &mut insufficient_signers,
3238            &[outbound_message.unwrap()],
3239        );
3240        assert_eq!(outbound_messages.len(), 1);
3241        assert!(operation_results.is_empty());
3242
3243        for coordinator in &insufficient_coordinators {
3244            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
3245        }
3246
3247        // again remove signers so the number is insufficient
3248        let num_signers_to_drain = insufficient_signers
3249            .len()
3250            .saturating_sub(num_signers_to_remove);
3251        malicious.extend(insufficient_signers.drain(num_signers_to_drain..));
3252
3253        // Send the SignatureShareRequest message to all signers and share their responses with the coordinator and signers
3254        let (outbound_messages, operation_results) = feedback_messages(
3255            &mut insufficient_coordinators,
3256            &mut insufficient_signers,
3257            &outbound_messages,
3258        );
3259        assert!(outbound_messages.is_empty());
3260        assert!(operation_results.is_empty());
3261
3262        for coordinator in &insufficient_coordinators {
3263            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
3264        }
3265
3266        // Sleep long enough to hit the timeout
3267        thread::sleep(Duration::from_millis(256));
3268
3269        let (outbound_message, operation_result) = insufficient_coordinators
3270            .first_mut()
3271            .unwrap()
3272            .process_timeout()
3273            .unwrap();
3274
3275        assert!(outbound_message.is_none());
3276        assert!(operation_result.is_some());
3277        assert_eq!(
3278            insufficient_coordinators.first_mut().unwrap().state,
3279            State::SigShareGather(signature_type)
3280        );
3281        assert!(
3282            matches!(
3283                operation_result.unwrap(),
3284                OperationResult::SignError(SignError::InsufficientSigners(_))
3285            ),
3286            "Expected OperationResult::SignError(SignError::InsufficientSigners)"
3287        );
3288    }
3289
3290    #[test]
3291    #[cfg(feature = "with_v1")]
3292    fn multiple_nonce_request_messages_sign_v1() {
3293        multiple_nonce_request_messages::<v1::Aggregator, v1::Signer>();
3294    }
3295
3296    #[test]
3297    fn multiple_nonce_request_messages_sign_v2() {
3298        multiple_nonce_request_messages::<v2::Aggregator, v2::Signer>();
3299    }
3300
3301    fn multiple_nonce_request_messages<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
3302        let num_signers = 12;
3303        let keys_per_signer = 1;
3304        let (mut coordinators, mut signers) =
3305            all_signers_dkg::<Aggregator, Signer>(num_signers, keys_per_signer);
3306
3307        // Start a signing round
3308        let orig_msg = "It was many and many a year ago, in a kingdom by the sea"
3309            .as_bytes()
3310            .to_vec();
3311        let signature_type = SignatureType::Frost;
3312        let message = coordinators
3313            .first_mut()
3314            .unwrap()
3315            .start_signing_round(&orig_msg, signature_type, None)
3316            .unwrap();
3317
3318        let mut alt_packet = message.clone();
3319        assert_eq!(
3320            coordinators.first().unwrap().state,
3321            State::NonceGather(signature_type)
3322        );
3323
3324        // Send the original message to the first 1/4 of the signers and gather responses by sharing with the rest of the signers and the coordinators
3325        let signers_len = signers.len();
3326        let (outbound_messages, operation_results) = feedback_messages(
3327            &mut coordinators,
3328            &mut signers[0..signers_len / 4],
3329            &[message],
3330        );
3331
3332        let alt_message = "It was many and many a year ago, in a kingdom by the hill"
3333            .as_bytes()
3334            .to_vec();
3335        let Message::NonceRequest(nonce_request) = &mut alt_packet.msg else {
3336            panic!("Expected NonceRequest message");
3337        };
3338        nonce_request.message = alt_message.clone();
3339
3340        // Send the alternative message to the last 3/4 of signers and gather responses by sharing with the rest of the signers and the coordinators
3341        let (alt_outbound_messages, alt_operation_results) = feedback_messages(
3342            &mut coordinators,
3343            &mut signers[signers_len / 4..],
3344            &[alt_packet],
3345        );
3346
3347        assert!(operation_results.is_empty());
3348        assert!(alt_operation_results.is_empty());
3349        for coordinator in &coordinators {
3350            assert_eq!(coordinator.state, State::SigShareGather(signature_type));
3351        }
3352        // Assert that the first 1/4 signers did not receive a result
3353        assert!(outbound_messages.is_empty());
3354        assert_eq!(alt_outbound_messages.len(), 1);
3355        assert!(
3356            matches!(
3357                &alt_outbound_messages[0].msg,
3358                Message::SignatureShareRequest(_)
3359            ),
3360            "Expected SignatureShareRequest message"
3361        );
3362
3363        // Send the SignatureShareRequest message to all signers and share their responses with the coordinator and signers
3364        let (outbound_messages, operation_results) =
3365            feedback_messages(&mut coordinators, &mut signers, &alt_outbound_messages);
3366        assert!(outbound_messages.is_empty());
3367        assert_eq!(operation_results.len(), 1);
3368        let OperationResult::Sign(sig) = &operation_results[0] else {
3369            panic!("Expected Signature Operation result");
3370        };
3371        // Verify that the winning message was the alternative message that had majority vote
3372        assert!(sig.verify(
3373            &coordinators
3374                .first()
3375                .unwrap()
3376                .aggregate_public_key
3377                .expect("No aggregate public key set!"),
3378            &alt_message
3379        ));
3380
3381        for coordinator in &coordinators {
3382            assert_eq!(coordinator.state, State::Idle);
3383        }
3384    }
3385
3386    #[test]
3387    #[cfg(feature = "with_v1")]
3388    fn old_round_ids_are_ignored_v1() {
3389        old_round_ids_are_ignored::<v1::Aggregator, v1::Signer>();
3390    }
3391
3392    #[test]
3393    fn old_round_ids_are_ignored_v2() {
3394        old_round_ids_are_ignored::<v2::Aggregator, v2::Signer>();
3395    }
3396
3397    fn old_round_ids_are_ignored<Aggregator: AggregatorTrait, Signer: SignerTrait>() {
3398        let (mut coordinators, _) = setup::<FireCoordinator<Aggregator>, Signer>(3, 10);
3399        for coordinator in &mut coordinators {
3400            let id: u64 = 10;
3401            let old_id = id;
3402            coordinator.current_dkg_id = id;
3403            coordinator.current_sign_id = id;
3404            // Attempt to start an old DKG round
3405            let (packets, results) = coordinator
3406                .process(&Packet {
3407                    sig: vec![],
3408                    msg: Message::DkgBegin(DkgBegin { dkg_id: old_id }),
3409                })
3410                .unwrap();
3411            assert!(packets.is_none());
3412            assert!(results.is_none());
3413            assert_eq!(coordinator.state, State::Idle);
3414            assert_eq!(coordinator.current_dkg_id, id);
3415
3416            // Attempt to start the same DKG round
3417            let (packets, results) = coordinator
3418                .process(&Packet {
3419                    sig: vec![],
3420                    msg: Message::DkgBegin(DkgBegin { dkg_id: id }),
3421                })
3422                .unwrap();
3423            assert!(packets.is_none());
3424            assert!(results.is_none());
3425            assert_eq!(coordinator.state, State::Idle);
3426            assert_eq!(coordinator.current_dkg_id, id);
3427
3428            // Attempt to start an old Sign round
3429            let (packets, results) = coordinator
3430                .process(&Packet {
3431                    sig: vec![],
3432                    msg: Message::NonceRequest(NonceRequest {
3433                        dkg_id: id,
3434                        sign_id: old_id,
3435                        message: vec![],
3436                        sign_iter_id: id,
3437                        signature_type: SignatureType::Frost,
3438                    }),
3439                })
3440                .unwrap();
3441            assert!(packets.is_none());
3442            assert!(results.is_none());
3443            assert_eq!(coordinator.state, State::Idle);
3444            assert_eq!(coordinator.current_sign_id, id);
3445
3446            // Attempt to start the same Sign round
3447            let (packets, results) = coordinator
3448                .process(&Packet {
3449                    sig: vec![],
3450                    msg: Message::NonceRequest(NonceRequest {
3451                        dkg_id: id,
3452                        sign_id: id,
3453                        message: vec![],
3454                        sign_iter_id: id,
3455                        signature_type: SignatureType::Frost,
3456                    }),
3457                })
3458                .unwrap();
3459            assert!(packets.is_none());
3460            assert!(results.is_none());
3461            assert_eq!(coordinator.state, State::Idle);
3462            assert_eq!(coordinator.current_sign_id, id);
3463        }
3464    }
3465
3466    #[test]
3467    #[cfg(feature = "with_v1")]
3468    fn gen_nonces_v1() {
3469        gen_nonces::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 1);
3470    }
3471
3472    #[test]
3473    fn gen_nonces_v2() {
3474        gen_nonces::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 1);
3475    }
3476
3477    #[test]
3478    #[cfg(feature = "with_v1")]
3479    fn bad_signature_share_request_v1() {
3480        bad_signature_share_request::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
3481    }
3482
3483    #[test]
3484    fn bad_signature_share_request_v2() {
3485        bad_signature_share_request::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
3486    }
3487
3488    #[test]
3489    #[cfg(feature = "with_v1")]
3490    fn invalid_nonce_v1() {
3491        invalid_nonce::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
3492    }
3493
3494    #[test]
3495    fn invalid_nonce_v2() {
3496        invalid_nonce::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
3497    }
3498
3499    #[test]
3500    #[cfg(feature = "with_v1")]
3501    fn one_signer_bad_threshold_v1() {
3502        one_signer_bad_threshold::<v1::Aggregator, v1::Signer>();
3503    }
3504
3505    #[test]
3506    fn one_signer_bad_threshold_v2() {
3507        one_signer_bad_threshold::<v2::Aggregator, v2::Signer>();
3508    }
3509
3510    fn one_signer_bad_threshold<Aggregator: AggregatorTrait, SignerType: SignerTrait>() {
3511        let mut rng = create_rng();
3512        let (mut coordinators, mut signers) =
3513            setup::<FireCoordinator<Aggregator>, SignerType>(10, 1);
3514
3515        // persist one signer, change the threshold, reset polys
3516        let mut state = signers[0].save();
3517
3518        state.threshold -= 1;
3519        state.signer.threshold -= 1;
3520
3521        signers[0] = Signer::<SignerType>::load(&state);
3522
3523        signers[0].signer.reset_polys(&mut rng);
3524
3525        // We have started a dkg round
3526        let message = coordinators
3527            .first_mut()
3528            .unwrap()
3529            .start_dkg_round(None)
3530            .unwrap();
3531        assert!(coordinators
3532            .first_mut()
3533            .unwrap()
3534            .get_aggregate_public_key()
3535            .is_none());
3536        assert_eq!(
3537            coordinators.first_mut().unwrap().get_state(),
3538            State::DkgPublicGather
3539        );
3540
3541        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinator
3542        let (outbound_messages, operation_results) =
3543            feedback_messages(&mut coordinators, &mut signers, &[message]);
3544        assert!(operation_results.is_empty());
3545        for coordinator in coordinators.iter() {
3546            assert_eq!(coordinator.get_state(), State::DkgPrivateGather);
3547        }
3548
3549        assert_eq!(outbound_messages.len(), 1);
3550        assert!(
3551            matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
3552            "Expected DkgPrivateBegin message"
3553        );
3554
3555        // Send the DKG Private Begin message to all signers and share their responses with the coordinator and signers
3556        let (outbound_messages, operation_results) =
3557            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3558        assert!(operation_results.is_empty());
3559        assert_eq!(outbound_messages.len(), 1);
3560        assert!(
3561            matches!(outbound_messages[0].msg, Message::DkgEndBegin(_)),
3562            "Expected DkgEndBegin message"
3563        );
3564
3565        // Send the DkgEndBegin message to all signers and share their responses with the coordinator and signers
3566        let (outbound_messages, operation_results) =
3567            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3568        assert!(outbound_messages.is_empty());
3569        assert_eq!(operation_results.len(), 1);
3570        let OperationResult::DkgError(DkgError::DkgEndFailure {
3571            reported_failures,
3572            malicious_signers,
3573        }) = &operation_results[0]
3574        else {
3575            panic!(
3576                "Expected OperationResult::DkgError(DkgError::DkgEndFailure), got {:?}",
3577                operation_results[0]
3578            );
3579        };
3580        for i in 1..10 {
3581            match reported_failures.get(&i) {
3582                Some(DkgFailure::BadPublicShares(set)) => {
3583                    if set.len() != 1 {
3584                        panic!("signer {i} should have reported a single BadPublicShares");
3585                    } else if !set.contains(&0) {
3586                        panic!("signer {i} should have reported BadPublicShares from signer 0");
3587                    }
3588                }
3589                Some(failure) => {
3590                    panic!("signer {i} should have reported BadPublicShares, instead reported {failure:?}");
3591                }
3592                None => {
3593                    panic!("signer {i} should have reported BadPublicShares");
3594                }
3595            }
3596        }
3597
3598        match reported_failures.get(&0) {
3599            Some(DkgFailure::BadPublicShares(set)) => {
3600                if set.len() != 9 {
3601                    panic!("signer 0 should have reported BadPublicShares from all others");
3602                } else if set.contains(&0) {
3603                    panic!("signer 0 should not have reported BadPublicShares from signer 0");
3604                }
3605            }
3606            Some(failure) => {
3607                panic!(
3608                    "signer 0 should have reported BadPublicShares, instead reported {failure:?}"
3609                );
3610            }
3611            None => {
3612                panic!("signer 0 should have reported BadPublicShares");
3613            }
3614        }
3615
3616        if !malicious_signers.len() == 1 || !malicious_signers.contains(&0) {
3617            panic!(
3618                "Coordinator should have marked signer 0 as malicious, instead marked {malicious_signers:?}",
3619            );
3620        }
3621    }
3622
3623    #[test]
3624    #[cfg(feature = "with_v1")]
3625    fn bad_dkg_threshold_v1() {
3626        bad_dkg_threshold::<v1::Aggregator, v1::Signer>();
3627    }
3628
3629    #[test]
3630    fn bad_dkg_threshold_v2() {
3631        bad_dkg_threshold::<v2::Aggregator, v2::Signer>();
3632    }
3633
3634    fn bad_dkg_threshold<Aggregator: AggregatorTrait, SignerType: SignerTrait>() {
3635        let (mut coordinators, mut signers) =
3636            setup::<FireCoordinator<Aggregator>, SignerType>(10, 1);
3637
3638        // We have started a dkg round
3639        let message = coordinators
3640            .first_mut()
3641            .unwrap()
3642            .start_dkg_round(None)
3643            .unwrap();
3644        assert!(coordinators
3645            .first_mut()
3646            .unwrap()
3647            .get_aggregate_public_key()
3648            .is_none());
3649        assert_eq!(
3650            coordinators.first_mut().unwrap().get_state(),
3651            State::DkgPublicGather
3652        );
3653
3654        // Send the DKG Begin message to all signers and gather responses by sharing with all other signers and coordinator
3655        let (outbound_messages, operation_results) =
3656            feedback_messages(&mut coordinators, &mut signers, &[message]);
3657        assert!(operation_results.is_empty());
3658        for coordinator in coordinators.iter() {
3659            assert_eq!(coordinator.get_state(), State::DkgPrivateGather);
3660        }
3661
3662        assert_eq!(outbound_messages.len(), 1);
3663        assert!(
3664            matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
3665            "Expected DkgPrivateBegin message"
3666        );
3667
3668        // Send the DKG Private Begin message to all signers and share their responses with the coordinator and signers
3669        let (outbound_messages, operation_results) =
3670            feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3671        assert!(operation_results.is_empty());
3672        assert_eq!(outbound_messages.len(), 1);
3673        assert!(
3674            matches!(&outbound_messages[0].msg, Message::DkgEndBegin(_)),
3675            "Expected DkgEndBegin message"
3676        );
3677
3678        // alter the DkgEndBegin message
3679        let mut packet = outbound_messages[0].clone();
3680        if let Message::DkgEndBegin(ref mut dkg_end_begin) = packet.msg {
3681            dkg_end_begin.signer_ids = vec![0u32];
3682        }
3683
3684        // Send the DkgEndBegin message to all signers and share their responses with the coordinator and signers
3685        let (outbound_messages, operation_results) =
3686            feedback_messages(&mut coordinators, &mut signers, &[packet]);
3687        assert!(outbound_messages.is_empty());
3688        assert_eq!(operation_results.len(), 1);
3689        let OperationResult::DkgError(DkgError::DkgEndFailure {
3690            reported_failures, ..
3691        }) = &operation_results[0]
3692        else {
3693            panic!("Expected DkgEndFailure got {:?}", operation_results[0]);
3694        };
3695        for (signer_id, failure) in reported_failures {
3696            assert!(
3697                matches!(failure, DkgFailure::Threshold),
3698                "{signer_id} had wrong failure {failure:?}"
3699            );
3700        }
3701    }
3702
3703    #[test]
3704    #[cfg(feature = "with_v1")]
3705    fn empty_public_shares_v1() {
3706        empty_public_shares::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
3707    }
3708
3709    #[test]
3710    fn empty_public_shares_v2() {
3711        empty_public_shares::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
3712    }
3713
3714    #[test]
3715    #[cfg(feature = "with_v1")]
3716    fn empty_private_shares_v1() {
3717        empty_private_shares::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 2);
3718    }
3719
3720    #[test]
3721    fn empty_private_shares_v2() {
3722        empty_private_shares::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 2);
3723    }
3724
3725    #[test]
3726    #[cfg(feature = "with_v1")]
3727    fn verify_packet_sigs_v1() {
3728        verify_packet_sigs::<FireCoordinator<v1::Aggregator>, v1::Signer>();
3729    }
3730
3731    #[test]
3732    fn verify_packet_sigs_v2() {
3733        verify_packet_sigs::<FireCoordinator<v2::Aggregator>, v2::Signer>();
3734    }
3735}