From 5dfdaef4944f7278604e9752b84ffdc772ce4ef5 Mon Sep 17 00:00:00 2001 From: Simon Rastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Mon, 25 May 2026 17:14:29 +0200 Subject: [PATCH 1/2] Deleting tests folder -- all the checks are already done --- .../src/frost/eddsa/test.rs | 18 +- .../src/frost/redjubjub/test.rs | 25 +-- crates/threshold-signatures/src/test_utils.rs | 5 +- .../src/test_utils/presign.rs | 25 ++- crates/threshold-signatures/tests/ckd.rs | 174 --------------- crates/threshold-signatures/tests/common.rs | 154 ------------- crates/threshold-signatures/tests/eddsa.rs | 185 ---------------- .../tests/robust_ecdsa.rs | 209 ------------------ 8 files changed, 50 insertions(+), 745 deletions(-) delete mode 100644 crates/threshold-signatures/tests/ckd.rs delete mode 100644 crates/threshold-signatures/tests/common.rs delete mode 100644 crates/threshold-signatures/tests/eddsa.rs delete mode 100644 crates/threshold-signatures/tests/robust_ecdsa.rs diff --git a/crates/threshold-signatures/src/frost/eddsa/test.rs b/crates/threshold-signatures/src/frost/eddsa/test.rs index 1691d34ff8..2fb89852ba 100644 --- a/crates/threshold-signatures/src/frost/eddsa/test.rs +++ b/crates/threshold-signatures/src/frost/eddsa/test.rs @@ -4,7 +4,10 @@ use crate::{ sign::{sign_v1, sign_v2}, KeygenOutput, PresignOutput, SignatureOption, }, - test_utils::{generate_participants, run_protocol, GenProtocol, MockCryptoRng}, + test_utils::{ + assert_frost_presignatures_well_formed, generate_participants, run_protocol, GenProtocol, + MockCryptoRng, + }, Participant, ReconstructionLowerBound, }; @@ -113,6 +116,19 @@ pub fn run_sign_v2( Ok(run_protocol(protocols)?) } +#[test] +fn check_presignatures_terms() { + let mut rng = MockCryptoRng::seed_from_u64(42); + let participants = generate_participants(5); + let threshold = 4; + let actual_signers = 4; + + let keys = crate::dkg::test::test_keygen::(&participants, threshold, &mut rng); + let presignatures = run_presign(&keys, threshold, actual_signers, rng).unwrap(); + + assert_frost_presignatures_well_formed(&presignatures); +} + #[test] #[allow(non_snake_case)] fn keygen_output__should_be_serializable() { diff --git a/crates/threshold-signatures/src/frost/redjubjub/test.rs b/crates/threshold-signatures/src/frost/redjubjub/test.rs index 980f1e752f..b84c38b47d 100644 --- a/crates/threshold-signatures/src/frost/redjubjub/test.rs +++ b/crates/threshold-signatures/src/frost/redjubjub/test.rs @@ -5,7 +5,8 @@ use crate::{ }; use crate::test_utils::{ - assert_public_key_invariant, build_frost_key_packages_with_dealer, generate_participants, + assert_frost_presignatures_well_formed, assert_public_key_invariant, + build_frost_key_packages_with_dealer, generate_participants, generate_participants_with_random_ids, one_coordinator_output, run_keygen, run_protocol, run_refresh, run_reshare, GenProtocol, MockCryptoRng, }; @@ -297,17 +298,10 @@ fn check_presignatures_terms() { let actual_signers = 10; let key_packages = build_frost_key_packages_with_dealer(max_signers, threshold, &mut rng); - // add the presignatures here let presignatures = run_presign(&key_packages, threshold as usize, actual_signers, rng).unwrap(); - for (i, (p1, presig1)) in presignatures.iter().enumerate() { - for (p2, presig2) in presignatures.iter().skip(i + 1) { - assert_ne!(p1, p2); - assert_ne!(presig1.nonces, presig2.nonces); - assert_eq!(presig1.commitments_map, presig2.commitments_map); - } - } + assert_frost_presignatures_well_formed(&presignatures); } #[test] @@ -319,17 +313,8 @@ fn check_presignatures_terms_with_less_active_participants() { let actual_signers = 8; let key_packages = build_frost_key_packages_with_dealer(max_signers, threshold, &mut rng); - // add the presignatures here let presignatures = run_presign(&key_packages, threshold as usize, actual_signers, rng).unwrap(); - for i in 0..presignatures.len() { - for j in (i + 1)..presignatures.len() { - let (p1, presig1) = &presignatures[i]; - let (p2, presig2) = &presignatures[j]; - - assert_ne!(p1, p2); - assert_ne!(presig1.nonces, presig2.nonces); - assert_eq!(presig1.commitments_map, presig2.commitments_map); - } - } + + assert_frost_presignatures_well_formed(&presignatures); } diff --git a/crates/threshold-signatures/src/test_utils.rs b/crates/threshold-signatures/src/test_utils.rs index a105d3e266..451b7bd048 100644 --- a/crates/threshold-signatures/src/test_utils.rs +++ b/crates/threshold-signatures/src/test_utils.rs @@ -36,7 +36,10 @@ pub use ckd::generate_ckd_app_package; pub use dkg::{assert_public_key_invariant, run_keygen, run_refresh, run_reshare}; pub use participant_simulation::Simulator; pub use participants::{generate_participants, generate_participants_with_random_ids}; -pub use presign::{ecdsa_generate_rerandpresig_args, frost_run_presignature}; +pub use presign::{ + assert_frost_presignatures_well_formed, ecdsa_generate_rerandpresig_args, + frost_run_presignature, +}; pub use protocol::{ assert_buffer_capacity, build_buffer_test, expected_buffer_by_role, run_and_assert_buffer_entries, diff --git a/crates/threshold-signatures/src/test_utils/presign.rs b/crates/threshold-signatures/src/test_utils/presign.rs index f8c230bc92..9c5626f603 100644 --- a/crates/threshold-signatures/src/test_utils/presign.rs +++ b/crates/threshold-signatures/src/test_utils/presign.rs @@ -1,8 +1,11 @@ -use frost_core::{Field, Group}; +use frost_core::round1::{SigningCommitments, SigningNonces}; +use frost_core::{Field, Group, Identifier}; use frost_secp256k1::Secp256K1Sha256; use k256::AffinePoint; use rand_core::CryptoRngCore; +use std::collections::BTreeMap; use std::error::Error; +use std::fmt::Debug; use crate::ecdsa::{RerandomizationArguments, Tweak}; use crate::frost; @@ -78,3 +81,23 @@ where Ok(run_protocol(protocols)?) } + +/// Asserts that a batch of FROST presignatures from a single run is well-formed. +/// +/// Every participant identifier is unique, every participant's secret nonces +/// are distinct, and every participant observes the same commitments map. +pub fn assert_frost_presignatures_well_formed( + presignatures: &[(Participant, frost::PresignOutput)], +) where + C: Ciphersuite + Send + 'static, + SigningNonces: PartialEq + Debug, + BTreeMap, SigningCommitments>: PartialEq + Debug, +{ + for (i, (p1, presig1)) in presignatures.iter().enumerate() { + for (p2, presig2) in presignatures.iter().skip(i + 1) { + assert_ne!(p1, p2); + assert_ne!(presig1.nonces, presig2.nonces); + assert_eq!(presig1.commitments_map, presig2.commitments_map); + } + } +} diff --git a/crates/threshold-signatures/tests/ckd.rs b/crates/threshold-signatures/tests/ckd.rs deleted file mode 100644 index fdb5983c60..0000000000 --- a/crates/threshold-signatures/tests/ckd.rs +++ /dev/null @@ -1,174 +0,0 @@ -mod common; - -use blstrs::G2Projective; -use rand_core::OsRng; - -use common::{choose_coordinator_at_random, generate_participants, run_keygen, run_reshare}; -use threshold_signatures::{ - confidential_key_derivation::{ - ciphersuite::{verify_signature, Field as _, G1Projective, Group as _}, - ckd_pv, - protocol::ckd, - AppId, CKDOutputOption, PublicVerificationKey, - }, - participants::Participant, -}; - -use crate::common::{run_protocol, GenProtocol}; -type C = threshold_signatures::confidential_key_derivation::BLS12381SHA256; -type Scalar = threshold_signatures::Scalar; - -#[test] -fn test_ckd() { - let mut rng = OsRng; - - // Create the app necessary items - let app_id = AppId::try_from(b"Near App").unwrap(); - let app_sk = Scalar::random(&mut rng); - let app_pk = G1Projective::generator() * app_sk; - - // create participants - let threshold = 2; - let participants = generate_participants(3); - - let keys = run_keygen(&participants, threshold.into()); - - assert!(keys.len() == participants.len()); - - let public_key = keys.get(&participants[0]).unwrap().public_key; - let coordinator = choose_coordinator_at_random(&participants); - - let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); - - for p in &participants { - let key_pair = keys.get(p).unwrap(); - - let protocol = ckd( - &participants, - coordinator, - *p, - key_pair.clone(), - app_id.clone(), - app_pk, - OsRng, - ) - .unwrap(); - - protocols.push((*p, Box::new(protocol))); - } - - let result = run_protocol(protocols).unwrap(); - - // test one single some for the coordinator - let mut some_iter = result.into_iter().filter(|(_, ckd)| ckd.is_some()); - - let ckd = some_iter - .next() - .map(|(_, c)| c.unwrap()) - .expect("Expected exactly one Some(CKDCoordinatorOutput)"); - assert!( - some_iter.next().is_none(), - "More than one Some(CKDCoordinatorOutput)" - ); - - // compute msk . H(app_id) - let confidential_key = ckd.unmask(app_sk); - assert!(verify_signature(&public_key, &app_id, &confidential_key).is_ok()); - - let participant_keys = keys.into_iter().collect::>(); - - let mut new_participants = participants.clone(); - new_participants.push(Participant::from(20u32)); - let new_threshold = 3; - - let new_keys = run_reshare( - &participants, - &public_key, - participant_keys.as_slice(), - threshold.into(), - new_threshold.into(), - &new_participants, - ); - let new_public_key = new_keys.get(&participants[0]).unwrap().public_key; - - assert_eq!(public_key, new_public_key); -} - -#[test] -fn test_ckd_pv() { - let mut rng = OsRng; - - // Create the app necessary items - let app_id = AppId::try_from(b"Near App").unwrap(); - let app_sk = Scalar::random(&mut rng); - let app_pk = PublicVerificationKey::new( - G1Projective::generator() * app_sk, - G2Projective::generator() * app_sk, - ); - - // create participants - let threshold = 2; - let participants = generate_participants(3); - - let keys = run_keygen(&participants, threshold.into()); - - assert!(keys.len() == participants.len()); - - let public_key = keys.get(&participants[0]).unwrap().public_key; - let coordinator = choose_coordinator_at_random(&participants); - - let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); - - for p in &participants { - let key_pair = keys.get(p).unwrap(); - - let protocol = ckd_pv( - &participants, - coordinator, - *p, - key_pair.clone(), - app_id.clone(), - app_pk.clone(), - OsRng, - ) - .unwrap(); - - protocols.push((*p, Box::new(protocol))); - } - - let result = run_protocol(protocols).unwrap(); - - // test one single some for the coordinator - let mut some_iter = result.into_iter().filter(|(_, ckd)| ckd.is_some()); - - let ckd = some_iter - .next() - .map(|(_, c)| c.unwrap()) - .expect("Expected exactly one Some(CKDCoordinatorOutput)"); - assert!( - some_iter.next().is_none(), - "More than one Some(CKDCoordinatorOutput)" - ); - - // compute msk . H(app_id) - let confidential_key = ckd.unmask(app_sk); - assert!(verify_signature(&public_key, &app_id, &confidential_key).is_ok()); - - let participant_keys = keys.into_iter().collect::>(); - - let mut new_participants = participants.clone(); - new_participants.push(Participant::from(20u32)); - let new_threshold = 3; - - let new_keys = run_reshare( - &participants, - &public_key, - participant_keys.as_slice(), - threshold.into(), - new_threshold.into(), - &new_participants, - ); - let new_public_key = new_keys.get(&participants[0]).unwrap().public_key; - - assert_eq!(public_key, new_public_key); -} diff --git a/crates/threshold-signatures/tests/common.rs b/crates/threshold-signatures/tests/common.rs deleted file mode 100644 index 508d1c0032..0000000000 --- a/crates/threshold-signatures/tests/common.rs +++ /dev/null @@ -1,154 +0,0 @@ -#![allow( - clippy::unwrap_used, - clippy::indexing_slicing, - clippy::missing_panics_doc -)] - -use std::collections::HashMap; - -use rand::seq::SliceRandom as _; -use rand_core::OsRng; - -use threshold_signatures::{ - self, - errors::ProtocolError, - frost_core::VerifyingKey, - keygen, - participants::Participant, - protocol::{Action, Protocol}, - reshare, Ciphersuite, Element, KeygenOutput, ReconstructionLowerBound, Scalar, -}; - -pub type GenProtocol = Vec<(Participant, Box>)>; - -pub fn generate_participants(number: u32) -> Vec { - (0..number).map(Participant::from).collect::>() -} - -pub fn choose_coordinator_at_random(participants: &[Participant]) -> Participant { - *participants - .choose(&mut OsRng) - .expect("participants list is not empty") -} - -/// Run a protocol to completion, synchronously. -/// -/// This works by executing each participant in order. -/// -/// The reason this function exists is as a convenient testing utility. -/// In practice each protocol participant is likely running on a different machine, -/// and so orchestrating the protocol would happen differently. -pub fn run_protocol( - mut ps: Vec<(Participant, Box>)>, -) -> Result, ProtocolError> { - let indices: HashMap = - ps.iter().enumerate().map(|(i, (p, _))| (*p, i)).collect(); - - let size = ps.len(); - let mut out = Vec::with_capacity(size); - while out.len() < size { - for i in 0..size { - while { - let action = ps[i].1.poke()?; - match action { - Action::Wait => false, - Action::SendMany(m) => { - for j in 0..size { - if i == j { - continue; - } - let from = ps[i].0; - ps[j].1.message(from, m.clone())?; - } - true - } - Action::SendPrivate(to, m) => { - let from = ps[i].0; - ps[indices[&to]].1.message(from, m)?; - true - } - Action::Return(r) => { - out.push((ps[i].0, r)); - false - } - } - } {} - } - } - - Ok(out) -} - -#[allow(clippy::missing_panics_doc)] -pub fn run_keygen( - participants: &[Participant], - threshold: ReconstructionLowerBound, -) -> HashMap> -where - Element: std::marker::Send, - Scalar: std::marker::Send, -{ - let protocols: GenProtocol> = participants - .iter() - .map(|p| { - let protocol: Box>> = - Box::new(keygen::(participants, *p, threshold, OsRng).unwrap()); - (*p, protocol) - }) - .collect(); - - run_protocol(protocols).unwrap().into_iter().collect() -} - -#[allow(clippy::missing_panics_doc)] -pub fn run_reshare( - participants: &[Participant], - pub_key: &VerifyingKey, - keys: &[(Participant, KeygenOutput)], - old_threshold: ReconstructionLowerBound, - new_threshold: ReconstructionLowerBound, - new_participants: &[Participant], -) -> HashMap> -where - Element: Send, - Scalar: Send, -{ - assert!(!new_participants.is_empty()); - let mut setup = vec![]; - - for new_participant in new_participants { - let mut is_break = false; - for (p, k) in keys { - if p == new_participant { - setup.push((*p, (Some(k.private_share), k.public_key))); - is_break = true; - break; - } - } - if !is_break { - setup.push((*new_participant, (None, *pub_key))); - } - } - - let protocols: GenProtocol> = setup - .iter() - .map(|(p, out)| { - let protocol: Box>> = Box::new( - reshare( - participants, - old_threshold, - out.0, - out.1, - new_participants, - new_threshold, - *p, - OsRng, - ) - .unwrap(), - ); - (*p, protocol) - }) - .collect(); - - run_protocol(protocols).unwrap().into_iter().collect() -} diff --git a/crates/threshold-signatures/tests/eddsa.rs b/crates/threshold-signatures/tests/eddsa.rs deleted file mode 100644 index 94c1502f42..0000000000 --- a/crates/threshold-signatures/tests/eddsa.rs +++ /dev/null @@ -1,185 +0,0 @@ -#![allow(clippy::unwrap_used)] -mod common; - -use common::{ - choose_coordinator_at_random, generate_participants, run_keygen, run_protocol, run_reshare, - GenProtocol, -}; - -use rand_core::OsRng; - -use threshold_signatures::{ - self, - frost::eddsa::{ - sign::{sign_v1, sign_v2}, - Ed25519Sha512, PresignOutput, SignatureOption, - }, - participants::Participant, - test_utils::frost_run_presignature, - ReconstructionLowerBound, -}; - -type C = Ed25519Sha512; -type KeygenOutput = threshold_signatures::KeygenOutput; - -fn run_sign_v1( - threshold: ReconstructionLowerBound, - participants: &[(Participant, KeygenOutput)], - coordinator: Participant, - msg_hash: &[u8], -) -> Vec<(Participant, SignatureOption)> { - let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); - - let participants_list: Vec = participants.iter().map(|(p, _)| *p).collect(); - for (p, keygen_output) in participants { - let protocol = sign_v1( - &participants_list, - threshold, - *p, - coordinator, - keygen_output.clone(), - msg_hash.to_vec(), - OsRng, - ) - .unwrap(); - - protocols.push((*p, Box::new(protocol))); - } - - run_protocol(protocols).unwrap() -} - -fn run_sign_v2( - threshold: ReconstructionLowerBound, - participants: &[(Participant, KeygenOutput)], - coordinator: Participant, - presig: &[(Participant, PresignOutput)], - msg_hash: &[u8], -) -> Vec<(Participant, SignatureOption)> { - let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); - - let participants_list: Vec = participants.iter().map(|(p, _)| *p).collect(); - for ((p, keygen_output), (participant_redundancy, presignature)) in - participants.iter().zip(presig.iter()) - { - assert_eq!(p, participant_redundancy); - let protocol = sign_v2( - &participants_list, - threshold, - *p, - coordinator, - keygen_output.clone(), - presignature.clone(), - msg_hash.to_vec(), - ) - .unwrap(); - - protocols.push((*p, Box::new(protocol))); - } - - run_protocol(protocols).unwrap() -} - -#[test] -fn test_run_sign_v1() { - let participants = generate_participants(5); - let threshold = 4; - let keys = run_keygen::(&participants, threshold.into()); - assert_eq!(keys.len(), participants.len()); - let public_key = keys.get(&participants[0]).unwrap().public_key; - - let msg_hash = *b"hello world"; - let coordinator = choose_coordinator_at_random(&participants); - let participant_keys = keys.into_iter().collect::>(); - let all_sigs = run_sign_v1( - threshold.into(), - participant_keys.as_slice(), - coordinator, - &msg_hash, - ); - - let signature = all_sigs - .into_iter() - .filter(|(p, sig)| *p == coordinator && sig.is_some()) - .collect::>() - .first() - .unwrap() - .1 - .unwrap(); - - assert!(public_key.verify(&msg_hash, &signature).is_ok()); - - let mut new_participants = participants.clone(); - new_participants.push(Participant::from(20u32)); - let new_threshold = 5; - - let new_keys = run_reshare( - &participants, - &public_key, - participant_keys.as_slice(), - threshold.into(), - new_threshold.into(), - &new_participants, - ); - let new_public_key = new_keys.get(&participants[0]).unwrap().public_key; - - assert_eq!(public_key, new_public_key); -} - -#[test] -fn test_run_presign() { - let participants = generate_participants(5); - let threshold = 4; - let actual_signers = 4; - let keys = run_keygen::(&participants, threshold.into()); - let keys: Vec<(Participant, KeygenOutput)> = keys.into_iter().collect(); - let presign = frost_run_presignature(&keys, threshold, actual_signers, OsRng).unwrap(); - for (i, (p1, presig1)) in presign.iter().enumerate() { - for (p2, presig2) in presign.iter().skip(i + 1) { - assert_ne!(p1, p2); - assert_ne!(presig1.nonces, presig2.nonces); - assert_eq!(presig1.commitments_map, presig2.commitments_map); - } - } -} - -#[test] -fn test_run_sign_v2() { - let participants = generate_participants(5); - let threshold = 4; - let actual_signers = 4; - let keys = run_keygen::(&participants, threshold.into()); - let mut keys: Vec<(Participant, KeygenOutput)> = keys.into_iter().collect(); - keys.sort_by_key(|(participant, _)| *participant); - - let public_key = keys[0].1.public_key; - // take away last participant - keys.pop(); - let msg_hash = *b"hello world"; - let presign = frost_run_presignature(&keys, threshold, actual_signers, OsRng).unwrap(); - let active_participants: Vec = presign - .iter() - .map(|(participant, _)| *participant) - .collect(); - let coordinator = choose_coordinator_at_random(&active_participants); - - let participant_keys = keys.into_iter().collect::>(); - let all_sigs = run_sign_v2( - threshold.into(), - participant_keys.as_slice(), - coordinator, - &presign, - &msg_hash, - ); - - let signature = all_sigs - .into_iter() - .filter(|(p, sig)| *p == coordinator && sig.is_some()) - .collect::>() - .first() - .unwrap() - .1 - .unwrap(); - - assert!(public_key.verify(&msg_hash, &signature).is_ok()); -} diff --git a/crates/threshold-signatures/tests/robust_ecdsa.rs b/crates/threshold-signatures/tests/robust_ecdsa.rs deleted file mode 100644 index 63d8f99fb5..0000000000 --- a/crates/threshold-signatures/tests/robust_ecdsa.rs +++ /dev/null @@ -1,209 +0,0 @@ -#![allow(clippy::unwrap_used, clippy::indexing_slicing)] -mod common; - -use common::{choose_coordinator_at_random, generate_participants, run_keygen, run_reshare}; -use std::collections::HashMap; - -use rand_core::{OsRng, RngCore}; - -use threshold_signatures::{ - self, - ecdsa::{ - robust_ecdsa::{ - presign::presign, sign::sign, PresignArguments, PresignOutput, - RerandomizedPresignOutput, - }, - RerandomizationArguments, Secp256K1Sha256, Signature, SignatureOption, - }, - frost_secp256k1::VerifyingKey, - participants::Participant, - Element, MaxMalicious, ParticipantList, -}; - -// Note: This is required to use Scalar::from_repr -use elliptic_curve::ff::PrimeField; - -use crate::common::{run_protocol, GenProtocol}; - -type C = Secp256K1Sha256; -type KeygenOutput = threshold_signatures::KeygenOutput; -type Scalar = threshold_signatures::Scalar; -type Tweak = threshold_signatures::Tweak; - -fn run_presign( - participants: HashMap, - max_malicious: MaxMalicious, -) -> Vec<(Participant, PresignOutput)> { - let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); - - let participant_list: Vec = participants.keys().copied().collect(); - - for (p, keygen_out) in participants { - let protocol = presign( - &participant_list, - p, - PresignArguments { - keygen_out, - max_malicious, - }, - OsRng, - ) - .unwrap(); - protocols.push((p, Box::new(protocol))); - } - - run_protocol(protocols).unwrap() -} - -fn run_sign( - participants_presign: Vec<(Participant, RerandomizedPresignOutput)>, - max_malicious: MaxMalicious, - coordinator: Participant, - public_key: Element, - msg_hash: [u8; 32], -) -> Vec<(Participant, SignatureOption)> { - let msg_hash = Scalar::from_repr(msg_hash.into()) - .into_option() - .expect("Couldn't construct k256 point"); - - let mut protocols: GenProtocol = - Vec::with_capacity(participants_presign.len()); - - let participants: Vec = participants_presign.iter().map(|(p, _)| *p).collect(); - for (p, presignature) in participants_presign { - let protocol = sign( - &participants, - coordinator, - max_malicious, - p, - public_key.to_affine(), - presignature, - msg_hash, - ) - .unwrap(); - - protocols.push((p, Box::new(protocol))); - } - - run_protocol(protocols).unwrap() -} - -fn run_sign_with_rerandomization( - participants_presign: &[(Participant, PresignOutput)], - max_malicious: MaxMalicious, - public_key: VerifyingKey, - msg_hash: [u8; 32], - tweak: [u8; 32], - entropy: [u8; 32], -) -> Signature { - let tweak = Tweak::new( - Scalar::from_repr(tweak.into()) - .into_option() - .expect("Couldn't construct k256 point"), - ); - - let big_r = participants_presign[0].1.big_r; - let participants = participants_presign - .iter() - .map(|(p, _)| *p) - .collect::>(); - - let derived_pk = tweak.derive_verifying_key(&public_key).to_element(); - let rerand_args = RerandomizationArguments::new( - derived_pk.to_affine(), - tweak, - msg_hash, - big_r, - ParticipantList::new(&participants).unwrap(), - entropy, - ); - - let rerand_participants_presign = participants_presign - .iter() - .map(|(p, presig)| { - RerandomizedPresignOutput::rerandomize_presign(presig, &rerand_args) - .map(|out| (*p, out)) - }) - .collect::>() - .unwrap(); - - let coordinator = choose_coordinator_at_random(&participants); - - // run sign instantiation with the necessary arguments - let all_sigs = run_sign( - rerand_participants_presign, - max_malicious, - coordinator, - derived_pk, - msg_hash, - ); - - let signature = all_sigs - .into_iter() - .filter(|(p, sig)| *p == coordinator && sig.is_some()) - .collect::>() - .first() - .unwrap() - .1 - .clone(); - signature.unwrap() -} - -#[test] -fn test_run_sign() { - let participants = generate_participants(11); - let max_malicious = 5; - let threshold = max_malicious + 1; - let keys = run_keygen(&participants, threshold.into()); - let public_key = keys.get(&participants[0]).unwrap().public_key; - assert_eq!(keys.len(), participants.len()); - let presign_result = run_presign(keys.clone(), max_malicious.into()); - - let msg_hash = *b"hello worldhello worldhello worl"; - // generate a random tweak - let mut tweak = [0u8; 32]; - OsRng.fill_bytes(&mut tweak); - // generate a random public entropy - let mut entropy = [0u8; 32]; - OsRng.fill_bytes(&mut entropy); - - let signature = run_sign_with_rerandomization( - &presign_result, - max_malicious.into(), - public_key, - msg_hash, - tweak, - entropy, - ); - - // Note: this interface to check a signature is clearly sub-optimal - let msg_hash_scalar = Scalar::from_repr(msg_hash.into()) - .into_option() - .expect("Couldn't construct k256 point"); - let tweak = Tweak::new( - Scalar::from_repr(tweak.into()) - .into_option() - .expect("Couldn't construct k256 point"), - ); - - let derived_pk = tweak.derive_verifying_key(&public_key).to_element(); - assert!(signature.verify(&derived_pk.to_affine(), &msg_hash_scalar)); - - let participant_keys = keys.into_iter().collect::>(); - - let mut new_participants = participants.clone(); - new_participants.push(Participant::from(20u32)); - let new_threshold = 6; - - let new_keys = run_reshare( - &participants, - &public_key, - participant_keys.as_slice(), - threshold.into(), - new_threshold.into(), - &new_participants, - ); - let new_public_key = new_keys.get(&participants[0]).unwrap().public_key; - - assert_eq!(public_key, new_public_key); -} From ac8d4396110104a0aad0af9ea1d9746175fdf5b1 Mon Sep 17 00:00:00 2001 From: Simon Rastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Mon, 25 May 2026 17:29:30 +0200 Subject: [PATCH 2/2] Nits from claude reviewer --- crates/threshold-signatures/src/test_utils/presign.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/threshold-signatures/src/test_utils/presign.rs b/crates/threshold-signatures/src/test_utils/presign.rs index 9c5626f603..a06d01ccf8 100644 --- a/crates/threshold-signatures/src/test_utils/presign.rs +++ b/crates/threshold-signatures/src/test_utils/presign.rs @@ -93,6 +93,11 @@ pub fn assert_frost_presignatures_well_formed( SigningNonces: PartialEq + Debug, BTreeMap, SigningCommitments>: PartialEq + Debug, { + assert!( + presignatures.len() >= 2, + "expected at least 2 presignatures to compare; got {}", + presignatures.len() + ); for (i, (p1, presig1)) in presignatures.iter().enumerate() { for (p2, presig2) in presignatures.iter().skip(i + 1) { assert_ne!(p1, p2);