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