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#[derive(Clone, Debug, PartialEq)]
32pub struct Coordinator<Aggregator: AggregatorTrait> {
33 config: Config,
35 pub current_dkg_id: u64,
37 pub current_sign_id: u64,
39 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 pub party_polynomials: HashMap<u32, PolyCommitment>,
46 signature_shares: BTreeMap<u32, Vec<SignatureShare>>,
47 message_nonces: BTreeMap<Vec<u8>, SignRoundInfo>,
48 pub aggregate_public_key: Option<Point>,
50 signature: Option<Signature>,
51 schnorr_proof: Option<SchnorrProof>,
52 pub dkg_wait_signer_ids: HashSet<u32>,
54 pub message: Vec<u8>,
56 pub state: State,
58 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 pub coordinator_public_key: Option<ecdsa::PublicKey>,
69}
70
71impl<Aggregator: AggregatorTrait> Coordinator<Aggregator> {
72 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 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 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 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 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 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 if let Message::DkgBegin(dkg_begin) = &packet.msg {
269 if self.current_dkg_id == dkg_begin.dkg_id {
270 return Ok((None, None));
272 }
273 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 return Ok((None, None));
280 }
281 self.current_sign_iter_id = nonce_request.sign_iter_id.wrapping_sub(1);
282 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 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 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 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 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 return Ok((None, None));
361 } else if self.state == State::Idle {
362 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 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 return Ok((None, None));
397 } else if self.state == State::Idle {
398 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 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 pub fn start_private_shares(&mut self) -> Result<Packet, Error> {
481 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 pub fn start_dkg_end(&mut self) -> Result<Packet, Error> {
510 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 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 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 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 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 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 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 malicious_signers.insert(*signer_id);
782 }
783 DkgFailure::Threshold => {
784 }
786 DkgFailure::BadPublicShares(bad_shares) => {
787 for bad_signer_id in bad_shares {
789 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 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 for (bad_signer_id, bad_private_share) in bad_shares {
821 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 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 !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 }
934 DkgFailure::MissingPrivateShares(_) => {
935 }
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 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 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 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 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 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 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 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 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 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 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 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 response_info
1262 .sign_wait_signer_ids
1263 .remove(&sig_share_response.signer_id);
1264
1265 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 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 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 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 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 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 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 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 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 fn get_aggregate_public_key(&self) -> Option<Point> {
1671 self.aggregate_public_key
1672 }
1673
1674 fn set_aggregate_public_key(&mut self, aggregate_public_key: Option<Point>) {
1676 self.aggregate_public_key = aggregate_public_key;
1677 }
1678
1679 fn get_message(&self) -> Vec<u8> {
1681 self.message.clone()
1682 }
1683
1684 fn get_state(&self) -> State {
1686 self.state.clone()
1687 }
1688
1689 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 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 fn start_signing_round(
1719 &mut self,
1720 message: &[u8],
1721 signature_type: SignatureType,
1722 sign_id: Option<u64>,
1723 ) -> Result<Packet, Error> {
1724 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 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)]
1755pub 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 #[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 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 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 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 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 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 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 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 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 #[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 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 coordinator
2121 .gather_sig_shares(&packet, signature_type)
2122 .unwrap();
2123 assert_eq!(0, coordinator.signature_shares.len());
2124
2125 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 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; 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; 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 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 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 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 assert_eq!(outbound_messages.len(), 1);
2349 assert!(
2350 matches!(&outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2351 "Expected DkgPrivateBegin message"
2352 );
2353 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 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 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 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 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 signer.public_keys = removed_signer.public_keys.clone();
2444 }
2445
2446 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 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 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 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 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 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 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 let mut minimum_coordinators = coordinators.clone();
2588 let mut minimum_signers = signers.clone();
2589
2590 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 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 minimum_signers.truncate(minimum_signers.len().saturating_sub(num_signers_to_remove));
2625
2626 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 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 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 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 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 let (outbound_messages, operation_results) = feedback_messages(
2756 &mut insufficient_coordinators,
2757 &mut insufficient_signers,
2758 from_ref(&message),
2759 );
2760
2761 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 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 let mut insufficient_coordinator = coordinators.clone();
2793 let mut insufficient_signers = signers.clone();
2794
2795 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 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 assert_eq!(outbound_messages.len(), 1);
2827 assert!(
2828 matches!(outbound_messages[0].msg, Message::DkgPrivateBegin(_)),
2829 "Expected DkgPrivateBegin message"
2830 );
2831
2832 insufficient_signers.truncate(
2834 insufficient_signers
2835 .len()
2836 .saturating_sub(num_signers_to_remove),
2837 );
2838
2839 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 for coordinator in &mut coordinators {
3234 coordinator.config.sign_threshold = coordinator.config.num_keys;
3235 }
3236
3237 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 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 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 let (outbound_messages, operation_results) =
3273 feedback_messages(&mut coordinators, &mut signers, &outbound_messages);
3274
3275 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 insufficient_signers.append(&mut malicious);
3690
3691 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 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 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 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 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 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 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!(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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}