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