From baa6dfa8402e501339fdd4fbfefd82aa9972fc34 Mon Sep 17 00:00:00 2001 From: SimonRastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Tue, 26 May 2026 01:10:36 +0200 Subject: [PATCH 1/5] Enforcing strong typing and one explicit threshold: ReconstructionThreshold --- crates/node/src/config.rs | 22 ++- crates/node/src/coordinator.rs | 10 +- crates/node/src/indexer/participants.rs | 2 +- crates/node/src/key_events.rs | 16 +-- crates/node/src/p2p.rs | 2 +- crates/node/src/providers.rs | 6 +- crates/node/src/providers/ckd.rs | 6 +- .../node/src/providers/ckd/key_generation.rs | 10 +- .../node/src/providers/ckd/key_resharing.rs | 17 ++- crates/node/src/providers/ckd/sign.rs | 5 +- crates/node/src/providers/ecdsa.rs | 9 +- .../src/providers/ecdsa/key_generation.rs | 10 +- .../node/src/providers/ecdsa/key_resharing.rs | 17 ++- crates/node/src/providers/ecdsa/presign.rs | 11 +- crates/node/src/providers/ecdsa/sign.rs | 12 +- crates/node/src/providers/ecdsa/triple.rs | 17 ++- crates/node/src/providers/eddsa.rs | 6 +- .../src/providers/eddsa/key_generation.rs | 10 +- .../node/src/providers/eddsa/key_resharing.rs | 17 ++- crates/node/src/providers/eddsa/sign.rs | 10 +- crates/node/src/providers/robust_ecdsa.rs | 127 +----------------- .../src/providers/robust_ecdsa/presign.rs | 102 +++----------- .../node/src/providers/robust_ecdsa/sign.rs | 26 ++-- .../node/src/providers/verify_foreign_tx.rs | 6 +- .../benches/advanced_dkg.rs | 10 +- .../benches/advanced_eddsa_frost_sign_v1.rs | 14 +- .../benches/advanced_eddsa_frost_sign_v2.rs | 18 +-- .../benches/advanced_ot_based_ecdsa.rs | 26 ++-- .../benches/advanced_robust_ecdsa.rs | 17 +-- .../benches/bench_utils.rs | 6 +- .../benches/bench_utils/dkg.rs | 4 +- .../benches/bench_utils/frost_eddsa.rs | 12 +- .../benches/bench_utils/ot_based_ecdsa.rs | 8 +- .../benches/bench_utils/robust_ecdsa.rs | 12 +- crates/threshold-signatures/benches/ckd.rs | 8 +- .../benches/naive_ot_based_ecdsa.rs | 6 +- .../benches/naive_robust_ecdsa.rs | 5 +- .../benches/simulate_dkg.rs | 6 +- .../benches/simulate_ecdsa.rs | 33 +++-- .../benches/simulate_frost.rs | 10 +- crates/threshold-signatures/src/dkg.rs | 28 ++-- .../threshold-signatures/src/ecdsa/README.md | 6 +- .../src/ecdsa/ot_based_ecdsa.rs | 4 +- .../src/ecdsa/ot_based_ecdsa/sign.rs | 4 +- .../src/ecdsa/ot_based_ecdsa/test.rs | 10 +- .../src/ecdsa/ot_based_ecdsa/triples.rs | 4 +- .../ot_based_ecdsa/triples/generation.rs | 16 +-- .../src/ecdsa/ot_based_ecdsa/triples/test.rs | 4 +- .../src/ecdsa/robust_ecdsa.rs | 7 +- .../src/ecdsa/robust_ecdsa/presign.rs | 25 ++-- .../src/ecdsa/robust_ecdsa/sign.rs | 25 ++-- .../src/ecdsa/robust_ecdsa/test.rs | 21 +-- crates/threshold-signatures/src/frost.rs | 6 +- .../src/frost/eddsa/sign.rs | 20 +-- .../src/frost/eddsa/test.rs | 8 +- .../src/frost/redjubjub/sign.rs | 12 +- .../src/frost/redjubjub/test.rs | 6 +- crates/threshold-signatures/src/lib.rs | 10 +- .../src/test_utils/dkg.rs | 10 +- .../src/test_utils/presign.rs | 4 +- .../src/test_utils/snapshot.rs | 4 +- crates/threshold-signatures/src/thresholds.rs | 35 ++++- crates/threshold-signatures/tests/common.rs | 8 +- crates/threshold-signatures/tests/eddsa.rs | 6 +- .../tests/robust_ecdsa.rs | 29 ++-- 65 files changed, 415 insertions(+), 568 deletions(-) diff --git a/crates/node/src/config.rs b/crates/node/src/config.rs index bbf0553c13..14db523b07 100644 --- a/crates/node/src/config.rs +++ b/crates/node/src/config.rs @@ -1,5 +1,6 @@ use crate::primitives::ParticipantId; use mpc_node_config::{AuthConfig, ConfigFile}; +use mpc_primitives::ReconstructionThreshold; use anyhow::Context; use ed25519_dalek::{SigningKey, VerifyingKey}; @@ -62,11 +63,26 @@ impl MpcConfig { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct ParticipantsConfig { - /// The threshold for the MPC protocol. - pub threshold: u64, + /// The threshold for the MPC protocol — number of share-holders required + /// to reconstruct the secret. Wire format is a plain integer (the inner + /// type is `#[serde(transparent)]`), so this is wire-compatible with the + /// previous `u64` field. + pub threshold: ReconstructionThreshold, pub participants: Vec, } +impl ParticipantsConfig { + /// The threshold expressed in the form the threshold-signatures crypto + /// library consumes (a `usize`-backed `ReconstructionThreshold`). + pub fn ts_threshold( + &self, + ) -> anyhow::Result { + Ok(threshold_signatures::ReconstructionThreshold::from( + usize::try_from(self.threshold.inner())?, + )) + } +} + #[cfg(test)] impl ParticipantsConfig { pub fn change_participant_pk( @@ -563,7 +579,7 @@ pub mod tests { let participant = gen_participant(); let non_participant = gen_participant(); let bogus_config = ParticipantsConfig { - threshold: 3, + threshold: ReconstructionThreshold::new(3), participants: vec![participant.clone()], }; assert_matches!( diff --git a/crates/node/src/coordinator.rs b/crates/node/src/coordinator.rs index ccbd196194..f5c07469a0 100644 --- a/crates/node/src/coordinator.rs +++ b/crates/node/src/coordinator.rs @@ -38,7 +38,7 @@ use near_time::Clock; use std::collections::HashMap; use std::future::Future; use std::sync::{Arc, Mutex}; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; use threshold_signatures::{confidential_key_derivation, ecdsa, frost::eddsa}; use tokio::select; use tokio::sync::mpsc::unbounded_channel; @@ -292,8 +292,7 @@ where let (sender, receiver) = new_tls_mesh_network(&mpc_config, p2p_key).await?; let (network_client, channel_receiver, _handle) = run_network_client(Arc::new(sender), Box::new(receiver)); - let threshold: usize = mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = mpc_config.participants.ts_threshold()?; if mpc_config.is_leader_for_key_event() { keygen_leader( network_client, @@ -520,7 +519,7 @@ where sender .wait_for_ready( - running_mpc_config.participants.threshold.try_into()?, + usize::try_from(running_mpc_config.participants.threshold.inner())?, &running_participant_ids, ) .await?; @@ -718,11 +717,10 @@ where None }; - let new_threshold: usize = mpc_config.participants.threshold.try_into()?; let args = Arc::new(ResharingArgs { previous_keyset, existing_keyshares, - new_threshold: ReconstructionLowerBound::from(new_threshold), + new_threshold: mpc_config.participants.ts_threshold()?, old_participants: current_running_state.participants, }); diff --git a/crates/node/src/indexer/participants.rs b/crates/node/src/indexer/participants.rs index c0bf8ec592..56d645e1af 100644 --- a/crates/node/src/indexer/participants.rs +++ b/crates/node/src/indexer/participants.rs @@ -367,7 +367,7 @@ pub fn convert_participant_infos( } Ok(ParticipantsConfig { participants: converted, - threshold: threshold_parameters.threshold.0, + threshold: threshold_parameters.threshold.into(), }) } diff --git a/crates/node/src/key_events.rs b/crates/node/src/key_events.rs index 33f3e8c6cd..910a70500b 100644 --- a/crates/node/src/key_events.rs +++ b/crates/node/src/key_events.rs @@ -28,7 +28,7 @@ use near_mpc_crypto_types::{KeyForDomain, Keyset}; use std::sync::Arc; use std::time::Duration; use threshold_signatures::{ - confidential_key_derivation as ckd, frost_ed25519, frost_secp256k1, ReconstructionLowerBound, + confidential_key_derivation as ckd, frost_ed25519, frost_secp256k1, ReconstructionThreshold, }; use tokio::sync::{mpsc, watch, RwLock}; use tokio::time::timeout; @@ -47,7 +47,7 @@ pub async fn keygen_computation_inner( generated_keys: Vec, key_id: KeyEventId, domain: DomainConfig, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> anyhow::Result<()> { anyhow::ensure!(key_id.domain_id == domain.id, "Domain mismatch"); let keyshare_handle = keyshare_storage @@ -124,7 +124,7 @@ async fn keygen_computation( keyshare_storage: Arc>, chain_txn_sender: impl TransactionSender, key_id: KeyEventId, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> anyhow::Result<()> { let key_event = wait_for_contract_catchup(&mut contract_key_event_id, key_id).await; let inner = keygen_computation_inner( @@ -160,7 +160,7 @@ async fn keygen_computation( pub struct ResharingArgs { pub previous_keyset: Keyset, pub existing_keyshares: Option>, - pub new_threshold: ReconstructionLowerBound, + pub new_threshold: ReconstructionThreshold, pub old_participants: ParticipantsConfig, } @@ -416,7 +416,7 @@ pub async fn keygen_leader( keyshare_storage: Arc>, mut key_event_receiver: watch::Receiver, chain_txn_sender: impl TransactionSender, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> anyhow::Result<()> { loop { // Wait for all participants to be connected. Otherwise, computations are most likely going @@ -500,7 +500,7 @@ pub async fn keygen_follower( keyshare_storage: Arc>, key_event_receiver: watch::Receiver, chain_txn_sender: impl TransactionSender + 'static, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> anyhow::Result<()> { let mut tasks = AutoAbortTaskCollection::new(); loop { @@ -900,9 +900,9 @@ mod tests { Arc::new(ResharingArgs { previous_keyset: Keyset::new(EpochId::new(5), vec![]), existing_keyshares: None, - new_threshold: ReconstructionLowerBound::from(3), + new_threshold: ReconstructionThreshold::from(3), old_participants: ParticipantsConfig { - threshold: 3, + threshold: mpc_primitives::ReconstructionThreshold::new(3), participants: vec![], }, }) diff --git a/crates/node/src/p2p.rs b/crates/node/src/p2p.rs index 55f481cd4c..d67030a824 100644 --- a/crates/node/src/p2p.rs +++ b/crates/node/src/p2p.rs @@ -1026,7 +1026,7 @@ pub mod testing { let mut configs = Vec::new(); for (i, singing_key) in p2p_keypairs.into_iter().enumerate() { let participants = ParticipantsConfig { - threshold: threshold as u64, + threshold: mpc_primitives::ReconstructionThreshold::new(threshold as u64), participants: participants.clone(), }; diff --git a/crates/node/src/providers.rs b/crates/node/src/providers.rs index a0ff31a5e6..3eb654698e 100644 --- a/crates/node/src/providers.rs +++ b/crates/node/src/providers.rs @@ -21,7 +21,7 @@ pub use ecdsa::EcdsaSignatureProvider; pub use ecdsa::EcdsaTaskId; pub use robust_ecdsa::RobustEcdsaSignatureProvider; use std::sync::Arc; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; /// The interface that defines the requirements for a signing schema to be correctly used in the code. pub trait SignatureProvider { @@ -51,7 +51,7 @@ pub trait SignatureProvider { /// /// It drains `channel_receiver` until the required task is found, meaning these clients must not be run in parallel. async fn run_key_generation_client( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result; @@ -59,7 +59,7 @@ pub trait SignatureProvider { /// Both leaders and followers call this function. /// It drains `channel_receiver` until the required task is found, meaning these clients must not be run in parallel. async fn run_key_resharing_client( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, key_share: Option, public_key: Self::PublicKey, old_participants: &ParticipantsConfig, diff --git a/crates/node/src/providers/ckd.rs b/crates/node/src/providers/ckd.rs index 9447f9425c..39394769b4 100644 --- a/crates/node/src/providers/ckd.rs +++ b/crates/node/src/providers/ckd.rs @@ -11,7 +11,7 @@ use threshold_signatures::confidential_key_derivation::{ ElementG1, KeygenOutput, SigningShare, VerifyingKey, }; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; use mpc_node_config::ConfigFile; @@ -79,14 +79,14 @@ impl SignatureProvider for CKDProvider { } async fn run_key_generation_client( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { Self::run_key_generation_client_internal(threshold, channel).await } async fn run_key_resharing_client( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, key_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, diff --git a/crates/node/src/providers/ckd/key_generation.rs b/crates/node/src/providers/ckd/key_generation.rs index b6a58345a6..b2e4a5a726 100644 --- a/crates/node/src/providers/ckd/key_generation.rs +++ b/crates/node/src/providers/ckd/key_generation.rs @@ -6,11 +6,11 @@ use rand::rngs::OsRng; use threshold_signatures::confidential_key_derivation::KeygenOutput; use threshold_signatures::confidential_key_derivation::BLS12381SHA256; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; impl CKDProvider { pub(super) async fn run_key_generation_client_internal( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { let key = KeyGenerationComputation { threshold } @@ -25,7 +25,7 @@ impl CKDProvider { /// Runs the key generation protocol, returning the key generated. /// This protocol is identical for the leader and the followers. pub struct KeyGenerationComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, } #[async_trait::async_trait] @@ -68,7 +68,7 @@ mod tests { use threshold_signatures::frost_core::Group; use threshold_signatures::participants::Participant; use threshold_signatures::test_utils::generate_participants; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use threshold_signatures::{confidential_key_derivation as ckd, ParticipantList}; use tokio::sync::mpsc; @@ -120,7 +120,7 @@ mod tests { .ok_or_else(|| anyhow::anyhow!("No channel"))? }; let key = KeyGenerationComputation { - threshold: ReconstructionLowerBound::from(3), + threshold: ReconstructionThreshold::from(3), } .perform_leader_centric_computation(channel, std::time::Duration::from_secs(60)) .await?; diff --git a/crates/node/src/providers/ckd/key_resharing.rs b/crates/node/src/providers/ckd/key_resharing.rs index 90a01bd918..362486e93b 100644 --- a/crates/node/src/providers/ckd/key_resharing.rs +++ b/crates/node/src/providers/ckd/key_resharing.rs @@ -9,21 +9,20 @@ use threshold_signatures::confidential_key_derivation::KeygenOutput; use threshold_signatures::confidential_key_derivation::SigningShare; use threshold_signatures::confidential_key_derivation::{VerifyingKey, BLS12381SHA256}; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; impl CKDProvider { pub(super) async fn run_key_resharing_client_internal( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, channel: NetworkTaskChannel, ) -> anyhow::Result { - let old_threshold: usize = old_participants.threshold.try_into()?; let new_keyshare = KeyResharingComputation { threshold: new_threshold, old_participants: old_participants.participants.iter().map(|p| p.id).collect(), - old_threshold: ReconstructionLowerBound::from(old_threshold), + old_threshold: old_participants.ts_threshold()?, my_share, public_key, } @@ -49,9 +48,9 @@ impl CKDProvider { /// the old threshold; or /// - the threshold is larger than the number of participants. pub struct KeyResharingComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, old_participants: Vec, - old_threshold: ReconstructionLowerBound, + old_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, } @@ -109,7 +108,7 @@ mod tests { use threshold_signatures::frost_core::Group; use threshold_signatures::participants::Participant; use threshold_signatures::test_utils::{generate_participants_with_random_ids, run_keygen}; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use threshold_signatures::{confidential_key_derivation as ckd, ParticipantList}; use tokio::sync::mpsc; @@ -155,9 +154,9 @@ mod tests { .ok_or_else(|| anyhow::anyhow!("No channel"))? }; let key = KeyResharingComputation { - threshold: ReconstructionLowerBound::from(THRESHOLD), + threshold: ReconstructionThreshold::from(THRESHOLD), old_participants, - old_threshold: ReconstructionLowerBound::from(THRESHOLD), + old_threshold: ReconstructionThreshold::from(THRESHOLD), my_share: keyshare, public_key: pubkey, } diff --git a/crates/node/src/providers/ckd/sign.rs b/crates/node/src/providers/ckd/sign.rs index 10f24f0c21..cac1a1fdff 100644 --- a/crates/node/src/providers/ckd/sign.rs +++ b/crates/node/src/providers/ckd/sign.rs @@ -11,7 +11,7 @@ use threshold_signatures::{ VerifyingKey, }, participants::Participant, - ReconstructionLowerBound, + ReconstructionThreshold, }; use crate::metrics; @@ -29,8 +29,7 @@ impl CKDProvider { ) -> anyhow::Result<((ElementG1, ElementG1), VerifyingKey)> { let ckd_request = self.ckd_request_store.get(id).await?; - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = self.mpc_config.participants.ts_threshold()?; let running_participants: Vec<_> = self .mpc_config .participants diff --git a/crates/node/src/providers/ecdsa.rs b/crates/node/src/providers/ecdsa.rs index 1d4227a7aa..e768292f7f 100644 --- a/crates/node/src/providers/ecdsa.rs +++ b/crates/node/src/providers/ecdsa.rs @@ -29,7 +29,7 @@ use threshold_signatures::ecdsa::KeygenOutput; use threshold_signatures::ecdsa::Signature; use threshold_signatures::frost_secp256k1::keys::SigningShare; use threshold_signatures::frost_secp256k1::VerifyingKey; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; pub struct EcdsaSignatureProvider { config: Arc, @@ -159,14 +159,14 @@ impl SignatureProvider for EcdsaSignatureProvider { } async fn run_key_generation_client( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { EcdsaSignatureProvider::run_key_generation_client_internal(threshold, channel).await } async fn run_key_resharing_client( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, @@ -232,8 +232,7 @@ impl SignatureProvider for EcdsaSignatureProvider { } async fn spawn_background_tasks(self: Arc) -> anyhow::Result<()> { - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = self.mpc_config.participants.ts_threshold()?; let generate_triples = tracking::spawn( "generate triples", diff --git a/crates/node/src/providers/ecdsa/key_generation.rs b/crates/node/src/providers/ecdsa/key_generation.rs index 74f82319af..b7a948b91d 100644 --- a/crates/node/src/providers/ecdsa/key_generation.rs +++ b/crates/node/src/providers/ecdsa/key_generation.rs @@ -5,11 +5,11 @@ use crate::providers::ecdsa::{EcdsaSignatureProvider, KeygenOutput}; use rand::rngs::OsRng; use threshold_signatures::frost_secp256k1::Secp256K1Sha256; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; impl EcdsaSignatureProvider { pub(crate) async fn run_key_generation_client_internal( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { let key = KeyGenerationComputation { threshold } @@ -24,7 +24,7 @@ impl EcdsaSignatureProvider { /// Runs the key generation protocol, returning the key generated. /// This protocol is identical for the leader and the followers. pub struct KeyGenerationComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, } #[async_trait::async_trait] @@ -64,7 +64,7 @@ mod tests { use near_mpc_contract_interface::types::{AttemptId, EpochId, KeyEventId}; use std::sync::Arc; use threshold_signatures::test_utils::generate_participants; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use tokio::sync::mpsc; #[tokio::test] @@ -106,7 +106,7 @@ mod tests { .ok_or_else(|| anyhow::anyhow!("No channel"))? }; let key = KeyGenerationComputation { - threshold: ReconstructionLowerBound::from(3), + threshold: ReconstructionThreshold::from(3), } .perform_leader_centric_computation(channel, std::time::Duration::from_secs(60)) .await?; diff --git a/crates/node/src/providers/ecdsa/key_resharing.rs b/crates/node/src/providers/ecdsa/key_resharing.rs index 310f8bb709..ce1913daa1 100644 --- a/crates/node/src/providers/ecdsa/key_resharing.rs +++ b/crates/node/src/providers/ecdsa/key_resharing.rs @@ -8,21 +8,20 @@ use rand::rngs::OsRng; use threshold_signatures::frost_secp256k1::keys::SigningShare; use threshold_signatures::frost_secp256k1::{Secp256K1Sha256, VerifyingKey}; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; impl EcdsaSignatureProvider { pub(crate) async fn run_key_resharing_client_internal( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, channel: NetworkTaskChannel, ) -> anyhow::Result { - let old_threshold: usize = old_participants.threshold.try_into()?; let new_keyshare = KeyResharingComputation { threshold: new_threshold, old_participants: old_participants.participants.iter().map(|p| p.id).collect(), - old_threshold: ReconstructionLowerBound::from(old_threshold), + old_threshold: old_participants.ts_threshold()?, my_share, public_key, } @@ -48,9 +47,9 @@ impl EcdsaSignatureProvider { /// the old threshold; or /// - the threshold is larger than the number of participants. pub struct KeyResharingComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, old_participants: Vec, - old_threshold: ReconstructionLowerBound, + old_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, } @@ -105,7 +104,7 @@ mod tests { use std::sync::Arc; use threshold_signatures::frost_secp256k1::Secp256K1Sha256; use threshold_signatures::test_utils::{generate_participants_with_random_ids, run_keygen}; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use tokio::sync::mpsc; #[tokio::test] @@ -150,9 +149,9 @@ mod tests { .ok_or_else(|| anyhow::anyhow!("No channel"))? }; let key = KeyResharingComputation { - threshold: ReconstructionLowerBound::from(THRESHOLD), + threshold: ReconstructionThreshold::from(THRESHOLD), old_participants, - old_threshold: ReconstructionLowerBound::from(THRESHOLD), + old_threshold: ReconstructionThreshold::from(THRESHOLD), my_share: keyshare, public_key: pubkey, } diff --git a/crates/node/src/providers/ecdsa/presign.rs b/crates/node/src/providers/ecdsa/presign.rs index e04f6dd455..d3e5c746ed 100644 --- a/crates/node/src/providers/ecdsa/presign.rs +++ b/crates/node/src/providers/ecdsa/presign.rs @@ -23,7 +23,7 @@ use threshold_signatures::ecdsa::ot_based_ecdsa::{ presign::presign, PresignArguments, PresignOutput, }; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; #[derive(derive_more::Deref)] pub struct PresignatureStorage(DistributedAssetStorage); @@ -65,7 +65,7 @@ impl EcdsaSignatureProvider { /// so that needs to be separately handled. pub(super) async fn run_background_presignature_generation( client: Arc, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, config: Arc, triple_store: Arc, domain_id: DomainId, @@ -174,9 +174,8 @@ impl EcdsaSignatureProvider { id.validate_owned_by(channel.sender().get_leader())?; let domain_data = self.domain_data(domain_id)?; - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; FollowerPresignComputation { - threshold: ReconstructionLowerBound::from(threshold), + threshold: self.mpc_config.participants.ts_threshold()?, keygen_out: domain_data.keyshare, triple_store: self.triple_store.clone(), paired_triple_id, @@ -210,7 +209,7 @@ impl HasParticipants for PresignOutputWithParticipants { /// Performs an MPC presignature operation. This is shared for the initiator /// and for passive participants. pub struct PresignComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, triple0: TripleGenerationOutput, triple1: TripleGenerationOutput, keygen_out: KeygenOutput, @@ -250,7 +249,7 @@ impl MpcLeaderCentricComputation for PresignComputation { /// The difference is: we need to read the triples from the triple store (which may fail), /// and we need to write the presignature to the presignature store before completing. pub struct FollowerPresignComputation { - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, pub paired_triple_id: UniqueId, pub keygen_out: KeygenOutput, pub triple_store: Arc, diff --git a/crates/node/src/providers/ecdsa/sign.rs b/crates/node/src/providers/ecdsa/sign.rs index 175665679a..3a91831f17 100644 --- a/crates/node/src/providers/ecdsa/sign.rs +++ b/crates/node/src/providers/ecdsa/sign.rs @@ -19,7 +19,7 @@ use threshold_signatures::ecdsa::{RerandomizationArguments, Signature, Signature use threshold_signatures::frost_secp256k1::VerifyingKey; use threshold_signatures::participants::Participant; use threshold_signatures::ParticipantList; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; use tokio::time::timeout; impl EcdsaSignatureProvider { @@ -31,8 +31,7 @@ impl EcdsaSignatureProvider { ) -> anyhow::Result<(Signature, VerifyingKey)> { let domain_data = self.domain_data(sign_request.domain)?; let participants = presignature.participants.clone(); - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = self.mpc_config.participants.ts_threshold()?; let (signature, public_key) = SignComputation { keygen_out: domain_data.keyshare, @@ -90,8 +89,7 @@ impl EcdsaSignatureProvider { sign_request: SignatureRequest, ) -> anyhow::Result<()> { let domain_data = self.domain_data(sign_request.domain)?; - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = self.mpc_config.participants.ts_threshold()?; let participants = channel.participants().to_vec(); FollowerSignComputation { @@ -147,7 +145,7 @@ impl EcdsaSignatureProvider { /// The tweak allows key derivation pub struct SignComputation { pub keygen_out: KeygenOutput, - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, pub presign_out: PresignOutput, pub msg_hash: [u8; 32], pub tweak: Tweak, @@ -216,7 +214,7 @@ impl MpcLeaderCentricComputation<(SignatureOption, VerifyingKey)> for SignComput /// The difference is that the follower needs to look up the presignature, which may fail. pub struct FollowerSignComputation { pub keygen_out: KeygenOutput, - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, pub presignature_id: UniqueId, pub presignature_store: Arc, pub msg_hash: [u8; 32], diff --git a/crates/node/src/providers/ecdsa/triple.rs b/crates/node/src/providers/ecdsa/triple.rs index 3c11dd1351..bfcff67cb2 100644 --- a/crates/node/src/providers/ecdsa/triple.rs +++ b/crates/node/src/providers/ecdsa/triple.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use std::time::Duration; use threshold_signatures::ecdsa::ot_based_ecdsa::triples::TripleGenerationOutput; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; pub struct TripleStorage(DistributedAssetStorage); @@ -69,7 +69,7 @@ impl EcdsaSignatureProvider { mpc_config: Arc, config: Arc, triple_store: Arc, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> ! { let in_flight_generations = InFlightGenerationTracker::new(); let parallelism_limiter = Arc::new(tokio::sync::Semaphore::new(config.concurrency)); @@ -192,9 +192,8 @@ impl EcdsaSignatureProvider { "Unsupported batch size for triple generation" )); } - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; FollowerManyTripleGenerationComputation:: { - threshold: ReconstructionLowerBound::from(threshold), + threshold: self.mpc_config.participants.ts_threshold()?, out_triple_id_start: start, out_triple_store: self.triple_store.clone(), } @@ -222,7 +221,7 @@ impl HasParticipants for PairedTriple { /// Generates many cait-sith triples at once. This can significantly save the /// *number* of network messages. pub struct ManyTripleGenerationComputation { - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, } #[async_trait::async_trait] @@ -268,7 +267,7 @@ impl MpcLeaderCentricComputation> /// The follower version of the triple generation. The difference is that the follower will only /// complete the computation after successfully persisting the triples to storage. pub struct FollowerManyTripleGenerationComputation { - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, pub out_triple_store: Arc, pub out_triple_id_start: UniqueId, } @@ -325,7 +324,7 @@ mod tests { use std::collections::HashMap; use std::sync::Arc; use threshold_signatures::test_utils::generate_participants; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use tokio::sync::mpsc; const NUM_PARTICIPANTS: usize = 4; @@ -381,7 +380,7 @@ mod tests { panic!("Unexpected task id"); }; let triples = ManyTripleGenerationComputation:: { - threshold: ReconstructionLowerBound::from(THRESHOLD), + threshold: ReconstructionThreshold::from(THRESHOLD), } .perform_leader_centric_computation( channel, @@ -429,7 +428,7 @@ mod tests { let result = tracking::spawn( &format!("task {:?}", task_id), ManyTripleGenerationComputation:: { - threshold: ReconstructionLowerBound::from(THRESHOLD), + threshold: ReconstructionThreshold::from(THRESHOLD), } .perform_leader_centric_computation( channel, diff --git a/crates/node/src/providers/eddsa.rs b/crates/node/src/providers/eddsa.rs index c6b0bc7397..17e9875c28 100644 --- a/crates/node/src/providers/eddsa.rs +++ b/crates/node/src/providers/eddsa.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use threshold_signatures::frost::eddsa::KeygenOutput; use threshold_signatures::frost_ed25519::keys::SigningShare; use threshold_signatures::frost_ed25519::{Signature, VerifyingKey}; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; #[derive(Clone)] pub struct EddsaSignatureProvider { @@ -84,14 +84,14 @@ impl SignatureProvider for EddsaSignatureProvider { } async fn run_key_generation_client( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { Self::run_key_generation_client_internal(threshold, channel).await } async fn run_key_resharing_client( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, key_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, diff --git a/crates/node/src/providers/eddsa/key_generation.rs b/crates/node/src/providers/eddsa/key_generation.rs index 2926dc1d33..d6ec763006 100644 --- a/crates/node/src/providers/eddsa/key_generation.rs +++ b/crates/node/src/providers/eddsa/key_generation.rs @@ -6,11 +6,11 @@ use rand::rngs::OsRng; use threshold_signatures::frost::eddsa::KeygenOutput; use threshold_signatures::frost_ed25519::Ed25519Sha512; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; impl EddsaSignatureProvider { pub(super) async fn run_key_generation_client_internal( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { let key = KeyGenerationComputation { threshold } @@ -25,7 +25,7 @@ impl EddsaSignatureProvider { /// Runs the key generation protocol, returning the key generated. /// This protocol is identical for the leader and the followers. pub struct KeyGenerationComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, } #[async_trait::async_trait] @@ -66,7 +66,7 @@ mod tests { use std::sync::Arc; use threshold_signatures::frost::eddsa::KeygenOutput; use threshold_signatures::test_utils::generate_participants; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use tokio::sync::mpsc; #[tokio::test] @@ -108,7 +108,7 @@ mod tests { .ok_or_else(|| anyhow::anyhow!("No channel"))? }; let key = KeyGenerationComputation { - threshold: ReconstructionLowerBound::from(3), + threshold: ReconstructionThreshold::from(3), } .perform_leader_centric_computation(channel, std::time::Duration::from_secs(60)) .await?; diff --git a/crates/node/src/providers/eddsa/key_resharing.rs b/crates/node/src/providers/eddsa/key_resharing.rs index 3d73db01b1..1e6b901bce 100644 --- a/crates/node/src/providers/eddsa/key_resharing.rs +++ b/crates/node/src/providers/eddsa/key_resharing.rs @@ -9,21 +9,20 @@ use threshold_signatures::frost::eddsa::KeygenOutput; use threshold_signatures::frost_ed25519::keys::SigningShare; use threshold_signatures::frost_ed25519::{Ed25519Sha512, VerifyingKey}; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; impl EddsaSignatureProvider { pub(super) async fn run_key_resharing_client_internal( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, channel: NetworkTaskChannel, ) -> anyhow::Result { - let old_threshold: usize = old_participants.threshold.try_into()?; let new_keyshare = KeyResharingComputation { threshold: new_threshold, old_participants: old_participants.participants.iter().map(|p| p.id).collect(), - old_threshold: ReconstructionLowerBound::from(old_threshold), + old_threshold: old_participants.ts_threshold()?, my_share, public_key, } @@ -49,9 +48,9 @@ impl EddsaSignatureProvider { /// the old threshold; or /// - the threshold is larger than the number of participants. pub struct KeyResharingComputation { - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, old_participants: Vec, - old_threshold: ReconstructionLowerBound, + old_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, } @@ -107,7 +106,7 @@ mod tests { use std::sync::Arc; use threshold_signatures::frost_ed25519::Ed25519Sha512; use threshold_signatures::test_utils::{generate_participants_with_random_ids, run_keygen}; - use threshold_signatures::ReconstructionLowerBound; + use threshold_signatures::ReconstructionThreshold; use tokio::sync::mpsc; #[tokio::test] @@ -152,9 +151,9 @@ mod tests { .ok_or_else(|| anyhow::anyhow!("No channel"))? }; let key = KeyResharingComputation { - threshold: ReconstructionLowerBound::from(THRESHOLD), + threshold: ReconstructionThreshold::from(THRESHOLD), old_participants, - old_threshold: ReconstructionLowerBound::from(THRESHOLD), + old_threshold: ReconstructionThreshold::from(THRESHOLD), my_share: keyshare, public_key: pubkey, } diff --git a/crates/node/src/providers/eddsa/sign.rs b/crates/node/src/providers/eddsa/sign.rs index 3073e6d944..95b3dce41e 100644 --- a/crates/node/src/providers/eddsa/sign.rs +++ b/crates/node/src/providers/eddsa/sign.rs @@ -14,7 +14,7 @@ use threshold_signatures::frost_core::Scalar; use threshold_signatures::frost_ed25519::VerifyingKey; use threshold_signatures::frost_ed25519::{Ed25519Sha512, Signature}; use threshold_signatures::participants::Participant; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; use tokio::time::timeout; impl EddsaSignatureProvider { @@ -24,8 +24,7 @@ impl EddsaSignatureProvider { ) -> anyhow::Result<(Signature, VerifyingKey)> { let sign_request = self.sign_request_store.get(id).await?; - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = self.mpc_config.participants.ts_threshold()?; let running_participants: Vec<_> = self .mpc_config .participants @@ -95,8 +94,7 @@ impl EddsaSignatureProvider { .await??; metrics::MPC_NUM_PASSIVE_SIGN_REQUESTS_LOOKUP_SUCCEEDED.inc(); - let threshold: usize = self.mpc_config.participants.threshold.try_into()?; - let threshold = ReconstructionLowerBound::from(threshold); + let threshold = self.mpc_config.participants.ts_threshold()?; let Some(keygen_output) = self.keyshares.get(&sign_request.domain) else { anyhow::bail!("No keyshare for domain {:?}", sign_request.domain); @@ -136,7 +134,7 @@ impl EddsaSignatureProvider { /// The tweak allows key derivation pub struct SignComputation { pub keygen_output: KeygenOutput, - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, pub message: Vec, pub tweak: Tweak, } diff --git a/crates/node/src/providers/robust_ecdsa.rs b/crates/node/src/providers/robust_ecdsa.rs index c335012796..d350969b92 100644 --- a/crates/node/src/providers/robust_ecdsa.rs +++ b/crates/node/src/providers/robust_ecdsa.rs @@ -24,8 +24,7 @@ use threshold_signatures::ecdsa::KeygenOutput; use threshold_signatures::ecdsa::Signature; use threshold_signatures::frost_secp256k1::keys::SigningShare; use threshold_signatures::frost_secp256k1::VerifyingKey; -use threshold_signatures::MaxMalicious; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; pub struct RobustEcdsaSignatureProvider { config: Arc, @@ -144,47 +143,24 @@ impl SignatureProvider for RobustEcdsaSignatureProvider { } async fn run_key_generation_client( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, channel: NetworkTaskChannel, ) -> anyhow::Result { - let number_of_participants = channel.participants().len(); - let robust_ecdsa_threshold = - translate_threshold(threshold.value(), number_of_participants)?; - EcdsaSignatureProvider::run_key_generation_client_internal( - ReconstructionLowerBound::try_from(robust_ecdsa_threshold)?, - channel, - ) - .await + EcdsaSignatureProvider::run_key_generation_client_internal(threshold, channel).await } async fn run_key_resharing_client( - new_threshold: ReconstructionLowerBound, + new_threshold: ReconstructionThreshold, my_share: Option, public_key: VerifyingKey, old_participants: &ParticipantsConfig, channel: NetworkTaskChannel, ) -> anyhow::Result { - let number_of_participants = channel.participants().len(); - let new_robust_ecdsa_threshold = - translate_threshold(new_threshold.value(), number_of_participants)?; - - // This is a bad hack, but cannot think of a better way to solve it, as the struct - // comes directly from generic implementations, so probably this is the best place - // to do so anyway - let mut old_participants_patched = old_participants.clone(); - let old_translated = translate_threshold( - old_participants.threshold.try_into()?, - old_participants.participants.len(), - )?; - old_participants_patched.threshold = ReconstructionLowerBound::try_from(old_translated)? - .value() - .try_into()?; - EcdsaSignatureProvider::run_key_resharing_client_internal( - ReconstructionLowerBound::try_from(new_robust_ecdsa_threshold)?, + new_threshold, my_share, public_key, - &old_participants_patched, + old_participants, channel, ) .await @@ -254,94 +230,3 @@ impl SignatureProvider for RobustEcdsaSignatureProvider { Ok(()) } } - -/// Although currently the threshold is always equal to the number of signers, if in -/// the future we might want to change that invariant, for example to achieve -/// higher security guarantees for robust-ecdsa. In that case, -/// this function enforces that the number of signers and the threshold -/// computed below in `translate_threshold` stay consistent -pub(super) fn get_number_of_signers( - threshold: usize, - number_of_participants: usize, -) -> anyhow::Result { - anyhow::ensure!( - threshold <= number_of_participants, - "threshold ({threshold}) exceeds number of participants ({number_of_participants})" - ); - Ok(threshold) -} - -/// This function translates the current threshold from the contract -/// to the threshold expected by the robust-ecdsa scheme, which -/// is semantically different. -/// The function should be no longer needed when these issues are solved: -/// https://github.com/near/threshold-signatures/issues/255 -/// https://github.com/near/mpc/issues/1649 -pub(super) fn translate_threshold( - threshold: usize, - number_of_participants: usize, -) -> anyhow::Result { - let number_of_signers = get_number_of_signers(threshold, number_of_participants)?; - anyhow::ensure!(number_of_signers >= 5, "Robust ECDSA requires the threshold to be at least 2, which implies that the number of signers needs to be at least 5"); - Ok(MaxMalicious::from((number_of_signers - 1) / 2)) -} - -#[cfg(test)] -mod tests { - use super::*; - use rstest::rstest; - - // The resulting threshold for robust-ecdsa must always satisfy - // the underlying invariant that 2 * threshold + 1 <= number of signers - #[test] - fn test_translate_threshold() { - let max_size = 30; - for threshold in 5..max_size { - for number_of_participants in threshold..max_size { - let number_of_signers = - get_number_of_signers(threshold, number_of_participants).unwrap(); - let new_threshold = translate_threshold(threshold, number_of_participants) - .unwrap() - .value(); - assert!(2 * new_threshold < number_of_signers, "Failed for threshold={threshold}, number_of_participants={number_of_participants}"); - assert!(new_threshold >= (threshold - 1) / 2, "The new threshold should not decrease security more than necessary: new_threshold={new_threshold}, threshold={threshold}"); - } - } - } - - // Tests that the number of signers is below the threshold, - // guaranteeing that security is not reduced - #[test] - fn test_get_number_of_signers_not_lower_than_threshold() { - let max_size = 30; - for threshold in 5..max_size { - for number_of_participants in threshold..max_size { - let number_of_signers = - get_number_of_signers(threshold, number_of_participants).unwrap(); - assert!(threshold <= number_of_signers && number_of_signers <= number_of_participants, "Failed for threshold={threshold}, number_of_participants={number_of_participants}"); - } - } - } - - #[rstest] - #[case(0, 10, true, 0)] - #[case(1, 10, true, 0)] - #[case(2, 10, true, 0)] - #[case(3, 10, true, 0)] - #[case(4, 10, true, 0)] - #[case(5, 10, false, 2)] - #[case(6, 10, false, 2)] - #[case(7, 10, false, 3)] - fn test_translate_threshold_special_cases( - #[case] threshold: usize, - #[case] number_of_participants: usize, - #[case] is_err: bool, - #[case] expected_threshold: usize, - ) { - let result = translate_threshold(threshold, number_of_participants); - assert_eq!(result.is_err(), is_err); - if !is_err { - assert_eq!(result.unwrap(), MaxMalicious::from(expected_threshold)); - } - } -} diff --git a/crates/node/src/providers/robust_ecdsa/presign.rs b/crates/node/src/providers/robust_ecdsa/presign.rs index 2716f86b49..050ef0ca84 100644 --- a/crates/node/src/providers/robust_ecdsa/presign.rs +++ b/crates/node/src/providers/robust_ecdsa/presign.rs @@ -8,8 +8,7 @@ use crate::network::{MeshNetworkClient, NetworkTaskChannel}; use crate::primitives::{ParticipantId, UniqueId}; use crate::protocol::run_protocol; use crate::providers::robust_ecdsa::{ - get_number_of_signers, translate_threshold, KeygenOutput, RobustEcdsaSignatureProvider, - RobustEcdsaTaskId, + KeygenOutput, RobustEcdsaSignatureProvider, RobustEcdsaTaskId, }; use crate::providers::HasParticipants; use crate::tracking::AutoAbortTaskCollection; @@ -26,7 +25,7 @@ use threshold_signatures::ecdsa::robust_ecdsa::{ presign::presign, PresignArguments, PresignOutput, }; use threshold_signatures::participants::Participant; -use threshold_signatures::MaxMalicious; +use threshold_signatures::ReconstructionThreshold; #[derive(derive_more::Deref)] pub struct PresignatureStorage(DistributedAssetStorage); @@ -87,11 +86,16 @@ pub(super) async fn run_background_presignature_generation( .map(|p| p.id) .collect(); - let (num_signers, robust_ecdsa_threshold) = compute_thresholds( - mpc_config.participants.threshold, + let threshold = mpc_config + .participants + .ts_threshold() + .expect("governance threshold does not fit in usize"); + let num_signers = threshold.value(); + assert!( + num_signers <= running_participants.len(), + "threshold ({num_signers}) exceeds number of participants ({})", running_participants.len(), - ) - .expect("invalid governance threshold for robust-ECDSA"); + ); loop { progress_tracker.update_progress(); @@ -149,7 +153,7 @@ pub(super) async fn run_background_presignature_generation( let _in_flight = in_flight; let _semaphore_guard = parallelism_limiter.acquire().await?; let presignature = PresignComputation { - max_malicious: robust_ecdsa_threshold, + threshold, keygen_out, } .perform_leader_centric_computation( @@ -180,28 +184,6 @@ pub(super) async fn run_background_presignature_generation( } } -/// Computes `(num_signers, robust_ecdsa_threshold)` and validates the -/// `2 * max_malicious + 1 <= num_signers` invariant. Returns an error only if -/// the configured governance threshold is invalid for robust-ECDSA. -/// -/// TODO(#3164): once the node supports per-domain thresholds, this should -/// take the domain-specific threshold instead of the single governance threshold. -fn compute_thresholds( - governance_threshold: u64, - num_running_participants: usize, -) -> anyhow::Result<(usize, MaxMalicious)> { - let governance_threshold: usize = governance_threshold.try_into()?; - let num_signers = get_number_of_signers(governance_threshold, num_running_participants)?; - let robust_ecdsa_threshold = - translate_threshold(governance_threshold, num_running_participants)?; - anyhow::ensure!(robust_ecdsa_threshold - .value() - .checked_mul(2) - .and_then(|v| v.checked_add(1)) - .is_some_and(|v| v <= num_signers)); - Ok((num_signers, robust_ecdsa_threshold)) -} - impl RobustEcdsaSignatureProvider { pub(super) async fn run_presignature_generation_follower( &self, @@ -212,12 +194,8 @@ impl RobustEcdsaSignatureProvider { id.validate_owned_by(channel.sender().get_leader())?; let domain_data = self.domain_data(domain_id)?; - let number_of_participants = self.mpc_config.participants.participants.len(); - let threshold = self.mpc_config.participants.threshold.try_into()?; - let robust_ecdsa_threshold = translate_threshold(threshold, number_of_participants)?; - FollowerPresignComputation { - max_malicious: robust_ecdsa_threshold, + threshold: self.mpc_config.participants.ts_threshold()?, keygen_out: domain_data.keyshare, out_presignature_store: domain_data.presignature_store, out_presignature_id: id, @@ -249,7 +227,7 @@ impl HasParticipants for PresignOutputWithParticipants { /// Performs an MPC presignature operation. This is shared for the initiator /// and for passive participants. pub struct PresignComputation { - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, keygen_out: KeygenOutput, } @@ -268,7 +246,7 @@ impl MpcLeaderCentricComputation for PresignComputation { me.into(), PresignArguments { keygen_out: self.keygen_out, - max_malicious: self.max_malicious, + threshold: self.threshold, }, OsRng, )?; @@ -286,7 +264,7 @@ impl MpcLeaderCentricComputation for PresignComputation { /// The difference is: we need to read the triples from the triple store (which may fail), /// and we need to write the presignature to the presignature store before completing. pub struct FollowerPresignComputation { - pub max_malicious: MaxMalicious, + pub threshold: ReconstructionThreshold, pub keygen_out: KeygenOutput, pub out_presignature_store: Arc, @@ -297,7 +275,7 @@ pub struct FollowerPresignComputation { impl MpcLeaderCentricComputation<()> for FollowerPresignComputation { async fn compute(self, channel: &mut NetworkTaskChannel) -> anyhow::Result<()> { let presignature = PresignComputation { - max_malicious: self.max_malicious, + threshold: self.threshold, keygen_out: self.keygen_out, } .compute(channel) @@ -335,49 +313,3 @@ impl PresignatureGenerationProgressTracker { } } -#[cfg(test)] -#[expect(non_snake_case)] -mod tests { - use super::compute_thresholds; - - #[test] - fn compute_thresholds__should_succeed_for_valid_governance_threshold() { - // Given: in the current node, governance threshold == num_participants - let governance_threshold = 5u64; - let num_participants = 5; - - // When - let result = compute_thresholds(governance_threshold, num_participants); - - // Then - let (num_signers, robust_ecdsa_threshold) = result.unwrap(); - assert_eq!(num_signers, 5); - assert!(2 * robust_ecdsa_threshold.value() < num_signers); - } - - #[test] - fn compute_thresholds__should_err_when_governance_threshold_too_small_for_robust_ecdsa() { - // Given: robust-ECDSA requires the governance threshold to be at least 5 - let governance_threshold = 4u64; - let num_participants = 4; - - // When - let result = compute_thresholds(governance_threshold, num_participants); - - // Then - result.unwrap_err(); - } - - #[test] - fn compute_thresholds__should_err_when_governance_threshold_exceeds_participants() { - // Given - let governance_threshold = 8u64; - let num_participants = 5; - - // When - let result = compute_thresholds(governance_threshold, num_participants); - - // Then - result.unwrap_err(); - } -} diff --git a/crates/node/src/providers/robust_ecdsa/sign.rs b/crates/node/src/providers/robust_ecdsa/sign.rs index 67ba2819ba..f8e4d59123 100644 --- a/crates/node/src/providers/robust_ecdsa/sign.rs +++ b/crates/node/src/providers/robust_ecdsa/sign.rs @@ -4,8 +4,8 @@ use crate::network::NetworkTaskChannel; use crate::primitives::UniqueId; use crate::protocol::run_protocol; use crate::providers::robust_ecdsa::{ - translate_threshold, EcdsaMessageHash, KeygenOutput, PresignatureStorage, - RobustEcdsaSignatureProvider, RobustEcdsaTaskId, + EcdsaMessageHash, KeygenOutput, PresignatureStorage, RobustEcdsaSignatureProvider, + RobustEcdsaTaskId, }; use crate::types::SignatureId; use anyhow::Context; @@ -18,8 +18,8 @@ use threshold_signatures::ecdsa::robust_ecdsa::{PresignOutput, RerandomizedPresi use threshold_signatures::ecdsa::{RerandomizationArguments, Signature, SignatureOption}; use threshold_signatures::frost_secp256k1::VerifyingKey; use threshold_signatures::participants::Participant; -use threshold_signatures::MaxMalicious; use threshold_signatures::ParticipantList; +use threshold_signatures::ReconstructionThreshold; use tokio::time::timeout; impl RobustEcdsaSignatureProvider { @@ -38,9 +38,7 @@ impl RobustEcdsaSignatureProvider { }, presignature.participants, )?; - let number_of_participants = self.mpc_config.participants.participants.len(); - let threshold = self.mpc_config.participants.threshold.try_into()?; - let robust_ecdsa_threshold = translate_threshold(threshold, number_of_participants)?; + let threshold = self.mpc_config.participants.ts_threshold()?; let msg_hash = *sign_request .payload @@ -49,7 +47,7 @@ impl RobustEcdsaSignatureProvider { let (signature, public_key) = SignComputation { keygen_out: domain_data.keyshare, - max_malicious: robust_ecdsa_threshold, + threshold, presign_out: presignature.presignature, msg_hash: msg_hash.into(), tweak: sign_request.tweak, @@ -89,9 +87,7 @@ impl RobustEcdsaSignatureProvider { metrics::MPC_NUM_PASSIVE_SIGN_REQUESTS_LOOKUP_SUCCEEDED.inc(); let domain_data = self.domain_data(sign_request.domain)?; - let number_of_participants = self.mpc_config.participants.participants.len(); - let threshold = self.mpc_config.participants.threshold.try_into()?; - let robust_ecdsa_threshold = translate_threshold(threshold, number_of_participants)?; + let threshold = self.mpc_config.participants.ts_threshold()?; let msg_hash = *sign_request .payload @@ -101,7 +97,7 @@ impl RobustEcdsaSignatureProvider { let participants = channel.participants().to_vec(); FollowerSignComputation { keygen_out: domain_data.keyshare, - max_malicious: robust_ecdsa_threshold, + threshold, presignature_store: domain_data.presignature_store.clone(), presignature_id, msg_hash: msg_hash.into(), @@ -131,7 +127,7 @@ impl RobustEcdsaSignatureProvider { /// The tweak allows key derivation pub struct SignComputation { pub keygen_out: KeygenOutput, - pub max_malicious: MaxMalicious, + pub threshold: ReconstructionThreshold, pub presign_out: PresignOutput, pub msg_hash: EcdsaMessageHash, pub tweak: Tweak, @@ -179,7 +175,7 @@ impl MpcLeaderCentricComputation<(SignatureOption, VerifyingKey)> for SignComput let protocol = threshold_signatures::ecdsa::robust_ecdsa::sign::sign( &cs_participants, channel.sender().get_leader().into(), - self.max_malicious, + self.threshold, channel.my_participant_id().into(), derived_public_key, rerandomized_presignature, @@ -199,7 +195,7 @@ impl MpcLeaderCentricComputation<(SignatureOption, VerifyingKey)> for SignComput /// The difference is that the follower needs to look up the presignature, which may fail. pub struct FollowerSignComputation { pub keygen_out: KeygenOutput, - pub max_malicious: MaxMalicious, + pub threshold: ReconstructionThreshold, pub presignature_id: UniqueId, pub presignature_store: Arc, pub msg_hash: EcdsaMessageHash, @@ -216,7 +212,7 @@ impl MpcLeaderCentricComputation<()> for FollowerSignComputation { .presignature; SignComputation { keygen_out: self.keygen_out, - max_malicious: self.max_malicious, + threshold: self.threshold, presign_out, msg_hash: self.msg_hash, tweak: self.tweak, diff --git a/crates/node/src/providers/verify_foreign_tx.rs b/crates/node/src/providers/verify_foreign_tx.rs index f7e53b4b24..7040ae53ce 100644 --- a/crates/node/src/providers/verify_foreign_tx.rs +++ b/crates/node/src/providers/verify_foreign_tx.rs @@ -22,7 +22,7 @@ use std::sync::Arc; use threshold_signatures::ecdsa::{KeygenOutput, Signature}; use threshold_signatures::frost_secp256k1::keys::SigningShare; use threshold_signatures::frost_secp256k1::VerifyingKey; -use threshold_signatures::ReconstructionLowerBound; +use threshold_signatures::ReconstructionThreshold; /// Pre-built HTTP clients for each foreign chain, keyed in provider config order. /// @@ -132,7 +132,7 @@ where } async fn run_key_generation_client( - _threshold: ReconstructionLowerBound, + _threshold: ReconstructionThreshold, _channel: NetworkTaskChannel, ) -> anyhow::Result { anyhow::bail!( @@ -141,7 +141,7 @@ where } async fn run_key_resharing_client( - _new_threshold: ReconstructionLowerBound, + _new_threshold: ReconstructionThreshold, _key_share: Option, _public_key: VerifyingKey, _old_participants: &ParticipantsConfig, diff --git a/crates/threshold-signatures/benches/advanced_dkg.rs b/crates/threshold-signatures/benches/advanced_dkg.rs index fa0abb685a..b14cba9d7f 100644 --- a/crates/threshold-signatures/benches/advanced_dkg.rs +++ b/crates/threshold-signatures/benches/advanced_dkg.rs @@ -17,11 +17,11 @@ use threshold_signatures::{ test_utils::{ run_protocol_and_take_snapshots, run_simulated_protocol, MockCryptoRng, Simulator, }, - Ciphersuite, Element, KeygenOutput, ReconstructionLowerBound, Scalar, + Ciphersuite, Element, KeygenOutput, ReconstructionThreshold, Scalar, }; -fn threshold() -> ReconstructionLowerBound { - ReconstructionLowerBound::from(*MAX_MALICIOUS + 1) +fn threshold() -> ReconstructionThreshold { + ReconstructionThreshold::from(*MAX_MALICIOUS + 1) } fn participants_num() -> usize { @@ -82,7 +82,7 @@ struct DkgSetup { } /// Expensive one-time setup: runs the full N-party protocol to capture snapshots -fn setup_dkg_snapshot(threshold: ReconstructionLowerBound) -> DkgSetup +fn setup_dkg_snapshot(threshold: ReconstructionThreshold) -> DkgSetup where Element: Send, Scalar: Send, @@ -112,7 +112,7 @@ where /// Cheap per-sample setup: creates fresh protocol and clones the cached simulator fn prepare_simulated_dkg( setup: &DkgSetup, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> PreparedSimulatedDkg where Element: Send, diff --git a/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v1.rs b/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v1.rs index e15c4959bb..52b46cffe7 100644 --- a/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v1.rs +++ b/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v1.rs @@ -6,7 +6,7 @@ use rand_core::SeedableRng; mod bench_utils; use crate::bench_utils::{ analyze_received_sizes, ed25519_prepare_sign_v1, PreparedOutputs, MAX_MALICIOUS, - RECONSTRUCTION_LOWER_BOUND, SAMPLE_SIZE, + RECONSTRUCTION_THRESHOLD, SAMPLE_SIZE, }; use threshold_signatures::{ frost::eddsa::{sign::sign_v1, KeygenOutput, SignatureOption}, @@ -15,17 +15,17 @@ use threshold_signatures::{ test_utils::{ run_protocol_and_take_snapshots, run_simulated_protocol, MockCryptoRng, Simulator, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; type PreparedSimulatedSig = PreparedOutputs; /// Benches the signing protocol fn bench_sign(c: &mut Criterion) { - let num = RECONSTRUCTION_LOWER_BOUND.value(); + let num = RECONSTRUCTION_THRESHOLD.value(); let max_malicious = *MAX_MALICIOUS; - let setup = setup_sign_snapshot(*RECONSTRUCTION_LOWER_BOUND); + let setup = setup_sign_snapshot(*RECONSTRUCTION_THRESHOLD); let size = setup.cached_simulator.get_view_size(); let mut group = c.benchmark_group("sign"); @@ -34,7 +34,7 @@ fn bench_sign(c: &mut Criterion) { format!("frost_ed25519_sign_advanced_MAX_MALICIOUS_{max_malicious}_PARTICIPANTS_{num}"), |b| { b.iter_batched( - || prepare_simulated_sign(&setup, *RECONSTRUCTION_LOWER_BOUND), + || prepare_simulated_sign(&setup, *RECONSTRUCTION_THRESHOLD), |preps| run_simulated_protocol(preps.participant, preps.protocol, preps.simulator), criterion::BatchSize::SmallInput, ); @@ -56,7 +56,7 @@ struct SignSetup { } /// Expensive one-time setup: runs the full N-party protocol to capture snapshots -fn setup_sign_snapshot(threshold: ReconstructionLowerBound) -> SignSetup { +fn setup_sign_snapshot(threshold: ReconstructionThreshold) -> SignSetup { let mut rng = MockCryptoRng::seed_from_u64(41); let preps = ed25519_prepare_sign_v1(threshold, &mut rng); let (_, protocol_snapshot) = run_protocol_and_take_snapshots(preps.protocols) @@ -87,7 +87,7 @@ fn setup_sign_snapshot(threshold: ReconstructionLowerBound) -> SignSetup { /// Cheap per-sample setup: creates fresh sign protocol and clones the cached simulator fn prepare_simulated_sign( setup: &SignSetup, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> PreparedSimulatedSig { let real_protocol = sign_v1( &setup.participants, diff --git a/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v2.rs b/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v2.rs index 6e7cea1f2c..6190a4e11a 100644 --- a/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v2.rs +++ b/crates/threshold-signatures/benches/advanced_eddsa_frost_sign_v2.rs @@ -7,7 +7,7 @@ use rand_core::SeedableRng; mod bench_utils; use crate::bench_utils::{ analyze_received_sizes, ed25519_prepare_presign, ed25519_prepare_sign_v2, PreparedOutputs, - MAX_MALICIOUS, RECONSTRUCTION_LOWER_BOUND, SAMPLE_SIZE, + MAX_MALICIOUS, RECONSTRUCTION_THRESHOLD, SAMPLE_SIZE, }; use threshold_signatures::{ frost::eddsa::{ @@ -19,7 +19,7 @@ use threshold_signatures::{ run_protocol, run_protocol_and_take_snapshots, run_simulated_protocol, MockCryptoRng, Simulator, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; type PreparedPresig = PreparedOutputs; @@ -27,7 +27,7 @@ type PreparedSimulatedSig = PreparedOutputs; /// Benches the presigning protocol fn bench_presign(c: &mut Criterion) { - let num = RECONSTRUCTION_LOWER_BOUND.value(); + let num = RECONSTRUCTION_THRESHOLD.value(); let max_malicious = *MAX_MALICIOUS; let setup = setup_presign_snapshot(num); @@ -50,10 +50,10 @@ fn bench_presign(c: &mut Criterion) { /// Benches the signing protocol fn bench_sign(c: &mut Criterion) { - let num = RECONSTRUCTION_LOWER_BOUND.value(); + let num = RECONSTRUCTION_THRESHOLD.value(); let max_malicious = *MAX_MALICIOUS; - let setup = setup_sign_snapshot(*RECONSTRUCTION_LOWER_BOUND); + let setup = setup_sign_snapshot(*RECONSTRUCTION_THRESHOLD); let size = setup.cached_simulator.get_view_size(); let mut group = c.benchmark_group("sign"); @@ -62,7 +62,7 @@ fn bench_sign(c: &mut Criterion) { format!("frost_ed25519_sign_v2_advanced_MAX_MALICIOUS_{max_malicious}_PARTICIPANTS_{num}"), |b| { b.iter_batched( - || prepare_simulated_sign(&setup, *RECONSTRUCTION_LOWER_BOUND), + || prepare_simulated_sign(&setup, *RECONSTRUCTION_THRESHOLD), |preps| run_simulated_protocol(preps.participant, preps.protocol, preps.simulator), criterion::BatchSize::SmallInput, ); @@ -125,7 +125,7 @@ fn prepare_simulate_presign(setup: &PresignSetup) -> PreparedPresig { setup.real_participant, &PresignArguments { keygen_out: setup.keygen_out.clone(), - threshold: *RECONSTRUCTION_LOWER_BOUND, + threshold: *RECONSTRUCTION_THRESHOLD, }, setup.real_participant_rng.clone(), // provide the exact same randomness ) @@ -149,7 +149,7 @@ struct SignSetup { } /// Expensive one-time setup for sign: runs the full N-party protocol to capture snapshots -fn setup_sign_snapshot(threshold: ReconstructionLowerBound) -> SignSetup { +fn setup_sign_snapshot(threshold: ReconstructionThreshold) -> SignSetup { let mut rng = MockCryptoRng::seed_from_u64(41); let preps = ed25519_prepare_presign(threshold.value(), &mut rng); let result = run_protocol(preps.protocols).expect("Prepare sign should not fail"); @@ -182,7 +182,7 @@ fn setup_sign_snapshot(threshold: ReconstructionLowerBound) -> SignSetup { /// Cheap per-sample setup: creates fresh sign protocol and clones the cached simulator fn prepare_simulated_sign( setup: &SignSetup, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> PreparedSimulatedSig { let real_protocol = sign_v2( &setup.participants, diff --git a/crates/threshold-signatures/benches/advanced_ot_based_ecdsa.rs b/crates/threshold-signatures/benches/advanced_ot_based_ecdsa.rs index 52c02ec5bf..417f71bb04 100644 --- a/crates/threshold-signatures/benches/advanced_ot_based_ecdsa.rs +++ b/crates/threshold-signatures/benches/advanced_ot_based_ecdsa.rs @@ -9,7 +9,7 @@ use rand_core::SeedableRng; mod bench_utils; use crate::bench_utils::{ analyze_received_sizes, ot_ecdsa_prepare_presign, ot_ecdsa_prepare_sign, - ot_ecdsa_prepare_triples, PreparedOutputs, MAX_MALICIOUS, RECONSTRUCTION_LOWER_BOUND, + ot_ecdsa_prepare_triples, PreparedOutputs, MAX_MALICIOUS, RECONSTRUCTION_THRESHOLD, SAMPLE_SIZE, }; @@ -29,7 +29,7 @@ use threshold_signatures::{ run_protocol, run_protocol_and_take_snapshots, run_simulated_protocol, MockCryptoRng, Simulator, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; use k256::AffinePoint; @@ -72,7 +72,7 @@ fn bench_presign(c: &mut Criterion) { let max_malicious = *MAX_MALICIOUS; let mut rng = MockCryptoRng::seed_from_u64(42); - let preps = ot_ecdsa_prepare_triples(num, *RECONSTRUCTION_LOWER_BOUND, &mut rng); + let preps = ot_ecdsa_prepare_triples(num, *RECONSTRUCTION_THRESHOLD, &mut rng); let two_triples = run_protocol(preps.protocols).expect("Running triple preparations should succeed"); @@ -99,15 +99,15 @@ fn bench_sign(c: &mut Criterion) { let num = participants_num(); let max_malicious = *MAX_MALICIOUS; let mut rng = MockCryptoRng::seed_from_u64(42); - let preps = ot_ecdsa_prepare_triples(num, *RECONSTRUCTION_LOWER_BOUND, &mut rng); + let preps = ot_ecdsa_prepare_triples(num, *RECONSTRUCTION_THRESHOLD, &mut rng); let two_triples = run_protocol(preps.protocols).expect("Running triples preparation should succeed"); - let preps = ot_ecdsa_prepare_presign(&two_triples, *RECONSTRUCTION_LOWER_BOUND, &mut rng); + let preps = ot_ecdsa_prepare_presign(&two_triples, *RECONSTRUCTION_THRESHOLD, &mut rng); let result = run_protocol(preps.protocols).expect("Running presign preparation should succeed"); let pk = preps.key_packages[0].1.public_key; - let setup = setup_sign_snapshot(&result, *RECONSTRUCTION_LOWER_BOUND, pk); + let setup = setup_sign_snapshot(&result, *RECONSTRUCTION_THRESHOLD, pk); let size = setup.cached_simulator.get_view_size(); let mut group = c.benchmark_group("sign"); @@ -116,7 +116,7 @@ fn bench_sign(c: &mut Criterion) { format!("ot_ecdsa_sign_advanced_MAX_MALICIOUS_{max_malicious}_PARTICIPANTS_{num}"), |b| { b.iter_batched( - || prepare_simulated_sign(&setup, *RECONSTRUCTION_LOWER_BOUND), + || prepare_simulated_sign(&setup, *RECONSTRUCTION_THRESHOLD), |preps| run_simulated_protocol(preps.participant, preps.protocol, preps.simulator), criterion::BatchSize::SmallInput, ); @@ -139,7 +139,7 @@ struct TriplesSetup { fn setup_triples_snapshot(participant_num: usize) -> TriplesSetup { let mut rng = MockCryptoRng::seed_from_u64(42); - let preps = ot_ecdsa_prepare_triples(participant_num, *RECONSTRUCTION_LOWER_BOUND, &mut rng); + let preps = ot_ecdsa_prepare_triples(participant_num, *RECONSTRUCTION_THRESHOLD, &mut rng); let (_, protocol_snapshot) = run_protocol_and_take_snapshots(preps.protocols) .expect("Running protocol with snapshot should not have issues"); @@ -175,7 +175,7 @@ fn prepare_simulated_triples(setup: &TriplesSetup) -> PreparedSimulatedTriples { let real_protocol = generate_triple_many::<2>( &setup.participants, setup.real_participant, - *RECONSTRUCTION_LOWER_BOUND, + *RECONSTRUCTION_THRESHOLD, setup.real_participant_rng.clone(), ) .map(|prot| Box::new(prot) as Box>>) @@ -204,7 +204,7 @@ fn setup_presign_snapshot( two_triples: &[(Participant, Vec<(TripleShare, TriplePub)>)], ) -> PresignSetup { let mut rng = MockCryptoRng::seed_from_u64(40); - let preps = ot_ecdsa_prepare_presign(two_triples, *RECONSTRUCTION_LOWER_BOUND, &mut rng); + let preps = ot_ecdsa_prepare_presign(two_triples, *RECONSTRUCTION_THRESHOLD, &mut rng); let (_, protocol_snapshot) = run_protocol_and_take_snapshots(preps.protocols) .expect("Running protocol with snapshot should not have issues"); @@ -241,7 +241,7 @@ fn prepare_simulated_presign(setup: &PresignSetup) -> PreparedSimulatedPresig { triple0: (setup.share0.clone(), setup.pub0.clone()), triple1: (setup.share1.clone(), setup.pub1.clone()), keygen_out: setup.keygen_out.clone(), - threshold: *RECONSTRUCTION_LOWER_BOUND, + threshold: *RECONSTRUCTION_THRESHOLD, }, ) .map(|presig| Box::new(presig) as Box>) @@ -266,7 +266,7 @@ struct SignSetup { /// Expensive one-time setup for sign: runs the full N-party protocol to capture snapshots fn setup_sign_snapshot( result: &[(Participant, PresignOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, pk: VerifyingKey, ) -> SignSetup { let mut rng = MockCryptoRng::seed_from_u64(40); @@ -297,7 +297,7 @@ fn setup_sign_snapshot( /// Cheap per-sample setup: creates fresh sign protocol and clones the cached simulator fn prepare_simulated_sign( setup: &SignSetup, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> PreparedSimulatedSig { let real_protocol = sign( &setup.participants, diff --git a/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs b/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs index c2914eba6b..ddbddd4c72 100644 --- a/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs +++ b/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs @@ -8,7 +8,7 @@ use rand_core::SeedableRng; mod bench_utils; use crate::bench_utils::{ analyze_received_sizes, robust_ecdsa_prepare_presign, robust_ecdsa_prepare_sign, - PreparedOutputs, MAX_MALICIOUS, SAMPLE_SIZE, + PreparedOutputs, MAX_MALICIOUS, RECONSTRUCTION_THRESHOLD, SAMPLE_SIZE, }; use threshold_signatures::{ ecdsa::{ @@ -28,6 +28,7 @@ use threshold_signatures::{ use k256::AffinePoint; use threshold_signatures::ecdsa::Scalar; +use threshold_signatures::ReconstructionThreshold; type PreparedPresig = PreparedOutputs; type PreparedSimulatedSig = PreparedOutputs; @@ -69,7 +70,7 @@ fn bench_sign(c: &mut Criterion) { let result = run_protocol(preps.protocols).expect("Prepare sign should not fail"); let pk = preps.key_packages[0].1.public_key; - let setup = setup_sign_snapshot(&result, max_malicious, pk); + let setup = setup_sign_snapshot(&result, *RECONSTRUCTION_THRESHOLD, pk); let size = setup.cached_simulator.get_view_size(); let mut group = c.benchmark_group("sign"); @@ -78,7 +79,7 @@ fn bench_sign(c: &mut Criterion) { format!("robust_ecdsa_sign_advanced_MAX_MALICIOUS_{max_malicious}_PARTICIPANTS_{num}"), |b| { b.iter_batched( - || prepare_simulated_sign(&setup, max_malicious), + || prepare_simulated_sign(&setup, *RECONSTRUCTION_THRESHOLD), |preps| run_simulated_protocol(preps.participant, preps.protocol, preps.simulator), criterion::BatchSize::SmallInput, ); @@ -143,7 +144,7 @@ fn prepare_simulate_presign(setup: &PresignSetup) -> PreparedPresig { setup.real_participant, PresignArguments { keygen_out: setup.keygen_out.clone(), - max_malicious: (*MAX_MALICIOUS).into(), + threshold: *RECONSTRUCTION_THRESHOLD, }, setup.real_participant_rng.clone(), // provide the exact same randomness ) @@ -169,11 +170,11 @@ struct SignSetup { /// Expensive one-time setup for sign: runs the full N-party protocol to capture snapshots fn setup_sign_snapshot( result: &[(Participant, PresignOutput)], - max_malicious: usize, + threshold: ReconstructionThreshold, pk: VerifyingKey, ) -> SignSetup { let mut rng = MockCryptoRng::seed_from_u64(41); - let preps = robust_ecdsa_prepare_sign(result, max_malicious.into(), pk, &mut rng); + let preps = robust_ecdsa_prepare_sign(result, threshold, pk, &mut rng); let (_, protocol_snapshot) = run_protocol_and_take_snapshots(preps.protocols) .expect("Running protocol with snapshot should not have issues"); @@ -197,11 +198,11 @@ fn setup_sign_snapshot( } /// Cheap per-sample setup: creates fresh sign protocol and clones the cached simulator -fn prepare_simulated_sign(setup: &SignSetup, max_malicious: usize) -> PreparedSimulatedSig { +fn prepare_simulated_sign(setup: &SignSetup, threshold: ReconstructionThreshold) -> PreparedSimulatedSig { let real_protocol = sign( &setup.participants, setup.real_participant, - max_malicious, + threshold, setup.real_participant, setup.derived_pk, setup.presig.clone(), diff --git a/crates/threshold-signatures/benches/bench_utils.rs b/crates/threshold-signatures/benches/bench_utils.rs index f842b14e05..350bf958f3 100644 --- a/crates/threshold-signatures/benches/bench_utils.rs +++ b/crates/threshold-signatures/benches/bench_utils.rs @@ -28,7 +28,7 @@ use threshold_signatures::{ participants::Participant, protocol::Protocol, test_utils::Simulator, - ReconstructionLowerBound, + ReconstructionThreshold, }; // fix malicious number of participants @@ -47,8 +47,8 @@ pub static SAMPLE_SIZE: LazyLock = std::sync::LazyLock::new(|| { .unwrap_or(15) }); -pub static RECONSTRUCTION_LOWER_BOUND: LazyLock = - LazyLock::new(|| ReconstructionLowerBound::from(*MAX_MALICIOUS + 1)); +pub static RECONSTRUCTION_THRESHOLD: LazyLock = + LazyLock::new(|| ReconstructionThreshold::from(*MAX_MALICIOUS + 1)); /// This helps defining a generic type for the benchmarks prepared outputs pub struct PreparedOutputs { diff --git a/crates/threshold-signatures/benches/bench_utils/dkg.rs b/crates/threshold-signatures/benches/bench_utils/dkg.rs index 68e7bfc52e..4dc8840867 100644 --- a/crates/threshold-signatures/benches/bench_utils/dkg.rs +++ b/crates/threshold-signatures/benches/bench_utils/dkg.rs @@ -5,13 +5,13 @@ use threshold_signatures::{ participants::Participant, protocol::Protocol, test_utils::{generate_participants_with_random_ids, MockCryptoRng}, - Ciphersuite, KeygenOutput, ReconstructionLowerBound, + Ciphersuite, KeygenOutput, ReconstructionThreshold, }; /// Used to prepare DKG keygen protocols for benchmarking pub fn prepare_dkg( num_participants: usize, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> PreparedDkgPackage where diff --git a/crates/threshold-signatures/benches/bench_utils/frost_eddsa.rs b/crates/threshold-signatures/benches/bench_utils/frost_eddsa.rs index 7d92385857..cb0e9dde9e 100644 --- a/crates/threshold-signatures/benches/bench_utils/frost_eddsa.rs +++ b/crates/threshold-signatures/benches/bench_utils/frost_eddsa.rs @@ -10,7 +10,7 @@ use threshold_signatures::{ participants::Participant, protocol::Protocol, test_utils::{generate_participants_with_random_ids, run_keygen, MockCryptoRng}, - ReconstructionLowerBound, + ReconstructionThreshold, }; use super::{PreparedPresig, MAX_MALICIOUS}; @@ -23,7 +23,7 @@ pub type PresignProtocols = Vec<( pub fn ed25519_build_presign_protocols( participants: &[Participant], key_packages: &[(Participant, eddsa::KeygenOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> PresignProtocols { let mut protocols = Vec::with_capacity(participants.len()); @@ -52,7 +52,7 @@ pub fn ed25519_prepare_presign( ) -> FrostEd25519PreparedPresig { let participants = generate_participants_with_random_ids(num_participants, rng); let key_packages = run_keygen(&participants, *MAX_MALICIOUS + 1, rng); - let threshold = ReconstructionLowerBound::from(*MAX_MALICIOUS + 1); + let threshold = ReconstructionThreshold::from(*MAX_MALICIOUS + 1); let protocols = ed25519_build_presign_protocols(&participants, &key_packages, threshold, rng); FrostEd25519PreparedPresig { protocols, @@ -63,7 +63,7 @@ pub fn ed25519_prepare_presign( /// Used to prepare ed25519 signatures for benchmarking pub fn ed25519_prepare_sign_v1( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> FrostEd25519SigV1 { let num_participants = threshold.value(); @@ -107,7 +107,7 @@ pub fn ed25519_prepare_sign_v1( pub fn ed25519_prepare_sign_v2( result: &[(Participant, eddsa::PresignOutput)], key_packages: Vec<(Participant, eddsa::KeygenOutput)>, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> FrostEd25519SigV2 { let num_participants = threshold.value(); @@ -172,7 +172,7 @@ pub struct FrostEd25519SigV2 { } pub fn prepare_ckd( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> PreparedCkdPackage { let num_participants = threshold.value(); diff --git a/crates/threshold-signatures/benches/bench_utils/ot_based_ecdsa.rs b/crates/threshold-signatures/benches/bench_utils/ot_based_ecdsa.rs index 8e8f0152f7..54bdb3b410 100644 --- a/crates/threshold-signatures/benches/bench_utils/ot_based_ecdsa.rs +++ b/crates/threshold-signatures/benches/bench_utils/ot_based_ecdsa.rs @@ -15,7 +15,7 @@ use threshold_signatures::{ ecdsa_generate_rerandpresig_args, generate_participants_with_random_ids, run_keygen, MockCryptoRng, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; use super::{PreparedPresig, PreparedSig}; @@ -23,7 +23,7 @@ use super::{PreparedPresig, PreparedSig}; /// Used to prepare ot based ecdsa triples for benchmarking pub fn ot_ecdsa_prepare_triples( participant_num: usize, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> OTECDSAPreparedTriples { let mut protocols: Vec<(_, Box>)> = @@ -45,7 +45,7 @@ pub fn ot_ecdsa_prepare_triples /// Used to prepare ot based ecdsa presignatures for benchmarking pub fn ot_ecdsa_prepare_presign( two_triples: &[(Participant, Vec<(TripleShare, TriplePub)>)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: &mut R, ) -> OTECDSAPreparedPresig { let mut two_triples = two_triples.to_owned(); @@ -93,7 +93,7 @@ pub fn ot_ecdsa_prepare_presign /// Used to prepare ot based ecdsa signatures for benchmarking pub fn ot_ecdsa_prepare_sign( result: &[(Participant, ot_based_ecdsa::PresignOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, pk: frost_secp256k1::VerifyingKey, rng: &mut R, ) -> OTECDSAPreparedSig { diff --git a/crates/threshold-signatures/benches/bench_utils/robust_ecdsa.rs b/crates/threshold-signatures/benches/bench_utils/robust_ecdsa.rs index 02e3233210..fc89d6ee38 100644 --- a/crates/threshold-signatures/benches/bench_utils/robust_ecdsa.rs +++ b/crates/threshold-signatures/benches/bench_utils/robust_ecdsa.rs @@ -9,10 +9,10 @@ use threshold_signatures::{ ecdsa_generate_rerandpresig_args, generate_participants_with_random_ids, run_keygen, MockCryptoRng, }, - MaxMalicious, + ReconstructionThreshold, }; -use super::{PreparedPresig, PreparedSig, MAX_MALICIOUS}; +use super::{PreparedPresig, PreparedSig, RECONSTRUCTION_THRESHOLD}; /// Used to prepare robust ecdsa presignatures for benchmarking pub fn robust_ecdsa_prepare_presign( @@ -20,7 +20,7 @@ pub fn robust_ecdsa_prepare_presign RobustECDSAPreparedPresig { let participants = generate_participants_with_random_ids(num_participants, rng); - let key_packages = run_keygen(&participants, *MAX_MALICIOUS + 1, rng); + let key_packages = run_keygen(&participants, *RECONSTRUCTION_THRESHOLD, rng); let mut protocols: Vec<_> = Vec::with_capacity(participants.len()); for (p, keygen_out) in &key_packages { @@ -30,7 +30,7 @@ pub fn robust_ecdsa_prepare_presign( result: &[(Participant, robust_ecdsa::PresignOutput)], - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, pk: frost_secp256k1::VerifyingKey, rng: &mut R, ) -> RobustECDSASig { @@ -85,7 +85,7 @@ pub fn robust_ecdsa_prepare_sign( let protocol = robust_ecdsa::sign::sign( &participants, coordinator, - max_malicious, + threshold, p, derived_pk, presignature, diff --git a/crates/threshold-signatures/benches/ckd.rs b/crates/threshold-signatures/benches/ckd.rs index 6d431b098d..6a8a4b345c 100644 --- a/crates/threshold-signatures/benches/ckd.rs +++ b/crates/threshold-signatures/benches/ckd.rs @@ -16,13 +16,13 @@ use threshold_signatures::{ test_utils::{ run_protocol_and_take_snapshots, run_simulated_protocol, MockCryptoRng, Simulator, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; type PreparedSimulatedCkd = PreparedOutputs; -fn threshold() -> ReconstructionLowerBound { - ReconstructionLowerBound::from(*MAX_MALICIOUS + 1) +fn threshold() -> ReconstructionThreshold { + ReconstructionThreshold::from(*MAX_MALICIOUS + 1) } /// Benches the ckd protocol @@ -62,7 +62,7 @@ struct CkdSetup { } /// Expensive one-time setup: runs the full N-party protocol to capture snapshots -fn setup_ckd_snapshot(threshold: ReconstructionLowerBound) -> CkdSetup { +fn setup_ckd_snapshot(threshold: ReconstructionThreshold) -> CkdSetup { let mut rng = MockCryptoRng::seed_from_u64(41); let preps = prepare_ckd(threshold, &mut rng); let (_, protocol_snapshot) = run_protocol_and_take_snapshots(preps.protocols) diff --git a/crates/threshold-signatures/benches/naive_ot_based_ecdsa.rs b/crates/threshold-signatures/benches/naive_ot_based_ecdsa.rs index 9c3108d310..f0470e7eba 100644 --- a/crates/threshold-signatures/benches/naive_ot_based_ecdsa.rs +++ b/crates/threshold-signatures/benches/naive_ot_based_ecdsa.rs @@ -9,11 +9,11 @@ use crate::bench_utils::{ use rand_core::SeedableRng; use threshold_signatures::{ test_utils::{run_protocol, MockCryptoRng}, - ReconstructionLowerBound, + ReconstructionThreshold, }; -fn threshold() -> ReconstructionLowerBound { - ReconstructionLowerBound::from(*MAX_MALICIOUS + 1) +fn threshold() -> ReconstructionThreshold { + ReconstructionThreshold::from(*MAX_MALICIOUS + 1) } fn participants_num() -> usize { diff --git a/crates/threshold-signatures/benches/naive_robust_ecdsa.rs b/crates/threshold-signatures/benches/naive_robust_ecdsa.rs index 34dbd929f2..3287010f32 100644 --- a/crates/threshold-signatures/benches/naive_robust_ecdsa.rs +++ b/crates/threshold-signatures/benches/naive_robust_ecdsa.rs @@ -3,7 +3,8 @@ use criterion::{criterion_group, Criterion}; mod bench_utils; use crate::bench_utils::{ - robust_ecdsa_prepare_presign, robust_ecdsa_prepare_sign, MAX_MALICIOUS, SAMPLE_SIZE, + robust_ecdsa_prepare_presign, robust_ecdsa_prepare_sign, MAX_MALICIOUS, + RECONSTRUCTION_THRESHOLD, SAMPLE_SIZE, }; use rand_core::SeedableRng; use threshold_signatures::test_utils::{run_protocol, MockCryptoRng}; @@ -48,7 +49,7 @@ fn bench_sign(c: &mut Criterion) { format!("robust_ecdsa_sign_naive_MAX_MALICIOUS_{max_malicious}_PARTICIPANTS_{num}"), |b| { b.iter_batched( - || robust_ecdsa_prepare_sign(&result, max_malicious.into(), pk, &mut rng), + || robust_ecdsa_prepare_sign(&result, *RECONSTRUCTION_THRESHOLD, pk, &mut rng), |preps| run_protocol(preps.protocols), criterion::BatchSize::SmallInput, ); diff --git a/crates/threshold-signatures/benches/simulate_dkg.rs b/crates/threshold-signatures/benches/simulate_dkg.rs index 5a0ad9814a..07ef6d68c9 100644 --- a/crates/threshold-signatures/benches/simulate_dkg.rs +++ b/crates/threshold-signatures/benches/simulate_dkg.rs @@ -10,12 +10,12 @@ use threshold_signatures::{ frost_ed25519::Ed25519Sha512, frost_secp256k1::Secp256K1Sha256, test_utils::{bench_simulation, run_simulation, BenchConfig, MockCryptoRng, SimulationMetrics}, - Ciphersuite, Element, ReconstructionLowerBound, Scalar, + Ciphersuite, Element, ReconstructionThreshold, Scalar, }; fn main() { let config = BenchConfig::from_env(); - let threshold = ReconstructionLowerBound::from(config.threshold); + let threshold = ReconstructionThreshold::from(config.threshold); let n = config.num_participants; println!("Protocol simulation: DKG"); @@ -50,7 +50,7 @@ fn main() { fn run_dkg( n: usize, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, config: &BenchConfig, ) -> SimulationMetrics where diff --git a/crates/threshold-signatures/benches/simulate_ecdsa.rs b/crates/threshold-signatures/benches/simulate_ecdsa.rs index b43a263490..e72ecd2535 100644 --- a/crates/threshold-signatures/benches/simulate_ecdsa.rs +++ b/crates/threshold-signatures/benches/simulate_ecdsa.rs @@ -18,7 +18,7 @@ use threshold_signatures::{ bench_simulation, ecdsa_generate_rerandpresig_args, generate_participants_with_random_ids, run_keygen, run_simulation, BenchConfig, LatencyModel, MockCryptoRng, SimulationMetrics, }, - MaxMalicious, ReconstructionLowerBound, + ReconstructionThreshold, }; type TriplePair = ( @@ -29,8 +29,7 @@ type TriplesResult = Vec<(Participant, Vec)>; fn main() { let config = BenchConfig::from_env(); - let threshold = ReconstructionLowerBound::from(config.threshold); - let max_malicious = MaxMalicious::from(config.threshold - 1); + let threshold = ReconstructionThreshold::from(config.threshold); println!("Protocol simulation: ECDSA (Cait-Sith vs DamgardEtAl)"); println!( @@ -58,7 +57,7 @@ fn main() { robust_run_presign( &participants, &key_packages, - max_malicious, + threshold, &config.latency, &mut rng, ); @@ -75,7 +74,7 @@ fn main() { bench_damgard( &participants, &key_packages, - max_malicious, + threshold, coordinator, pk, &config, @@ -85,7 +84,7 @@ fn main() { fn bench_cait_sith( participants: &[Participant], key_packages: &[(Participant, ecdsa::KeygenOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, coordinator: Participant, pk: frost_secp256k1::VerifyingKey, config: &BenchConfig, @@ -146,7 +145,7 @@ fn bench_cait_sith( fn bench_damgard( participants: &[Participant], key_packages: &[(Participant, ecdsa::KeygenOutput)], - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, coordinator: Participant, pk: frost_secp256k1::VerifyingKey, config: &BenchConfig, @@ -155,7 +154,7 @@ fn bench_damgard( let (presign_outputs, _) = robust_run_presign( participants, key_packages, - max_malicious, + threshold, &config.latency, &mut presign_rng, ); @@ -167,7 +166,7 @@ fn bench_damgard( robust_run_presign( participants, key_packages, - max_malicious, + threshold, &config.latency, &mut rng, ) @@ -182,7 +181,7 @@ fn bench_damgard( robust_run_sign( participants, &presign_outputs, - max_malicious, + threshold, coordinator, pk, &config.latency, @@ -195,7 +194,7 @@ fn bench_damgard( fn ot_run_triples( participants: &[Participant], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, latency: &LatencyModel, rng: &mut MockCryptoRng, ) -> (TriplesResult, SimulationMetrics) { @@ -214,7 +213,7 @@ fn ot_run_presign( participants: &[Participant], two_triples: &[(Participant, Vec)], key_packages: &[(Participant, ecdsa::KeygenOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, latency: &LatencyModel, ) -> ( Vec<(Participant, ot_based_ecdsa::PresignOutput)>, @@ -250,7 +249,7 @@ fn ot_run_presign( fn ot_run_sign( participants: &[Participant], presign_outputs: &[(Participant, ot_based_ecdsa::PresignOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, coordinator: Participant, pk: frost_secp256k1::VerifyingKey, latency: &LatencyModel, @@ -301,7 +300,7 @@ fn ot_run_sign( fn robust_run_presign( participants: &[Participant], key_packages: &[(Participant, ecdsa::KeygenOutput)], - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, latency: &LatencyModel, rng: &mut MockCryptoRng, ) -> ( @@ -319,7 +318,7 @@ fn robust_run_presign( *p, robust_ecdsa::PresignArguments { keygen_out: keygen_out.clone(), - max_malicious, + threshold, }, rng_p, ) @@ -332,7 +331,7 @@ fn robust_run_presign( fn robust_run_sign( participants: &[Participant], presign_outputs: &[(Participant, robust_ecdsa::PresignOutput)], - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, coordinator: Participant, pk: frost_secp256k1::VerifyingKey, latency: &LatencyModel, @@ -365,7 +364,7 @@ fn robust_run_sign( let protocol = robust_ecdsa::sign::sign( participants, coordinator, - max_malicious, + threshold, p, derived_pk, presig, diff --git a/crates/threshold-signatures/benches/simulate_frost.rs b/crates/threshold-signatures/benches/simulate_frost.rs index cb5fcfaf61..9e16878a68 100644 --- a/crates/threshold-signatures/benches/simulate_frost.rs +++ b/crates/threshold-signatures/benches/simulate_frost.rs @@ -14,12 +14,12 @@ use threshold_signatures::{ bench_simulation, generate_participants_with_random_ids, run_simulation, BenchConfig, LatencyModel, MockCryptoRng, SimulationMetrics, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; fn main() { let config = BenchConfig::from_env(); - let threshold = ReconstructionLowerBound::from(config.threshold); + let threshold = ReconstructionThreshold::from(config.threshold); println!("Protocol simulation: EdDSA FROST signing"); println!( @@ -121,7 +121,7 @@ fn main() { fn run_presign( participants: &[Participant], key_packages: &[(Participant, eddsa::KeygenOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, latency: &LatencyModel, rng: &mut MockCryptoRng, ) -> (Vec<(Participant, eddsa::PresignOutput)>, SimulationMetrics) { @@ -134,7 +134,7 @@ fn run_presign( fn run_sign_v1( participants: &[Participant], key_packages: &[(Participant, eddsa::KeygenOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, coordinator: Participant, message: &[u8], latency: &LatencyModel, @@ -167,7 +167,7 @@ fn run_sign_v2( participants: &[Participant], key_packages: &[(Participant, eddsa::KeygenOutput)], presign_outputs: &[(Participant, eddsa::PresignOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, coordinator: Participant, message: &[u8], latency: &LatencyModel, diff --git a/crates/threshold-signatures/src/dkg.rs b/crates/threshold-signatures/src/dkg.rs index 3cadb836fb..14e6dacb76 100644 --- a/crates/threshold-signatures/src/dkg.rs +++ b/crates/threshold-signatures/src/dkg.rs @@ -9,7 +9,7 @@ use crate::participants::{Participant, ParticipantList, ParticipantMap}; use crate::protocol::{ echo_broadcast::do_broadcast, helpers::recv_from_others, internal::SharedChannel, }; -use crate::{KeygenOutput, ReconstructionLowerBound}; +use crate::{KeygenOutput, ReconstructionThreshold}; use frost_core::keys::{ CoefficientCommitment, SecretShare, SigningShare, VerifiableSecretSharingCommitment, @@ -172,7 +172,7 @@ fn internal_verify_proof_of_knowledge( fn verify_proof_of_knowledge( session_id: &HashOutput, domain_separator: &mut DomainSeparator, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, participant: Participant, old_participants: Option, commitment: &VerifiableSecretSharingCommitment, @@ -239,7 +239,7 @@ fn verify_commitment_hash( /// i.e. when the new participant sent a polynomial with a non-existant constant term /// such a participant would do so as the identity is not serializable fn insert_identity_if_missing( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, commitment_i: &VerifiableSecretSharingCommitment, ) -> VerifiableSecretSharingCommitment { // in case the participant was new and it sent a polynomial of length @@ -343,7 +343,7 @@ async fn do_keyshare( mut chan: SharedChannel, participants: ParticipantList, me: Participant, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, secret: Scalar, old_reshare_package: Option<(VerifyingKey, ParticipantList)>, rng: &mut impl CryptoRngCore, @@ -541,7 +541,7 @@ pub async fn do_keygen( chan: SharedChannel, participants: ParticipantList, me: Participant, - threshold: impl Into, + threshold: impl Into, mut rng: impl CryptoRngCore, ) -> Result, ProtocolError> { let threshold = threshold.into(); @@ -558,7 +558,7 @@ pub async fn do_keygen( pub fn assert_key_invariants( participants: &[Participant], me: Participant, - threshold: impl Into, + threshold: impl Into, ) -> Result { let threshold = usize::from(threshold.into()); // need enough participants @@ -601,7 +601,7 @@ pub async fn do_reshare( chan: SharedChannel, participants: ParticipantList, me: Participant, - threshold: impl Into, + threshold: impl Into, old_signing_key: Option>, old_public_key: VerifyingKey, old_participants: ParticipantList, @@ -638,9 +638,9 @@ pub async fn do_reshare( pub fn assert_reshare_keys_invariants( participants: &[Participant], me: Participant, - threshold: impl Into, + threshold: impl Into, old_signing_key: Option>, - old_threshold: impl Into, + old_threshold: impl Into, old_participants: &[Participant], ) -> Result<(ParticipantList, ParticipantList), InitializationError> { let threshold = usize::from(threshold.into()); @@ -686,7 +686,7 @@ pub mod test { GenOutput, }; use crate::{keygen, reshare, DKG_MAX_INCOMING_BUFFER_ENTRIES}; - use crate::{KeygenOutput, ReconstructionLowerBound}; + use crate::{KeygenOutput, ReconstructionThreshold}; use frost_core::{Field, Group}; use rand_core::{CryptoRngCore, SeedableRng}; use rstest::rstest; @@ -723,7 +723,7 @@ pub mod test { pub fn test_keygen( participants: &[Participant], - threshold: impl Into + Copy + Send + 'static, + threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> Vec<(Participant, KeygenOutput)> where @@ -767,7 +767,7 @@ pub mod test { pub fn test_refresh( participants: &[Participant], - threshold: impl Into + Copy + Send + 'static, + threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> Vec<(Participant, KeygenOutput)> where @@ -788,8 +788,8 @@ pub mod test { pub fn test_reshare( participants: &[Participant], - threshold0: impl Into + Copy + Send + 'static, - threshold1: impl Into + Copy + Send + 'static, + threshold0: impl Into + Copy + Send + 'static, + threshold1: impl Into + Copy + Send + 'static, rng: &mut R, ) -> Vec<(Participant, KeygenOutput)> where diff --git a/crates/threshold-signatures/src/ecdsa/README.md b/crates/threshold-signatures/src/ecdsa/README.md index b6314667b1..be1dd3858b 100644 --- a/crates/threshold-signatures/src/ecdsa/README.md +++ b/crates/threshold-signatures/src/ecdsa/README.md @@ -15,13 +15,13 @@ Both schemes share common types defined in this module: ### OT-based ECDSA (`ot_based_ecdsa/`) -Originally imported from the [Cait-Sith](https://github.com/cronokirby/cait-sith) library. Uses an **offline phase with two protocols** (triple generation + presigning) to enable efficient one-round online signing. Requires `N >= t` participants where `t = ReconstructionLowerBound`. +Originally imported from the [Cait-Sith](https://github.com/cronokirby/cait-sith) library. Uses an **offline phase with two protocols** (triple generation + presigning) to enable efficient one-round online signing. Requires `N >= t` participants where `t = ReconstructionThreshold`. See [`ot_based_ecdsa/README.md`](ot_based_ecdsa/README.md) for details. ### Robust ECDSA (`robust_ecdsa/`) -Implemented from scratch following \[[DJNPO20](https://eprint.iacr.org/2020/501)\] with minimal modifications. Avoids triple generation entirely -- the offline round consists of a single presigning protocol using degree-2t polynomials. Requires `N >= 2t+1` signers where `t = MaxMalicious`. +Implemented from scratch following \[[DJNPO20](https://eprint.iacr.org/2020/501)\] with minimal modifications. Avoids triple generation entirely -- the offline round consists of a single presigning protocol using degree-2t polynomials. Requires `N >= 2t+1` signers where `t = MaxMalicious` (derived from `ReconstructionThreshold` via `.max_malicious()`). See [`robust_ecdsa/README.md`](robust_ecdsa/README.md) for details. @@ -33,7 +33,7 @@ See [`robust_ecdsa/README.md`](robust_ecdsa/README.md) for details. | **Offline rounds** | 11+ | 3 | | **Sign rounds** | 1 | 1 | | **Triple requirement** | 2 triples per presignature | None | -| **Threshold parameter** | `ReconstructionLowerBound` | `MaxMalicious` | +| **Threshold parameter** | `ReconstructionThreshold` | `ReconstructionThreshold` | | **Scaling** | Less efficient with many participants | Better efficiency and bandwidth | See the [benchmark analysis](../../docs/benches/results.md) for detailed performance comparisons. diff --git a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa.rs b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa.rs index d5ab847893..8fb2191bb7 100644 --- a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa.rs +++ b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa.rs @@ -11,7 +11,7 @@ use crate::{ ot_based_ecdsa::triples::{TriplePub, TripleShare}, AffinePoint, KeygenOutput, RerandomizationArguments, Scalar, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; use serde::{Deserialize, Serialize}; use subtle::{Choice, ConstantTimeEq}; @@ -28,7 +28,7 @@ pub struct PresignArguments { /// This is of type `KeygenOutput` from Frost implementation pub keygen_out: KeygenOutput, /// The desired threshold for the presignature, which must match the original threshold - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, } /// The output of the presigning protocol. diff --git a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/sign.rs b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/sign.rs index 13432e7c47..d699355e92 100644 --- a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/sign.rs +++ b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/sign.rs @@ -4,7 +4,7 @@ use subtle::ConditionallySelectable; use super::RerandomizedPresignOutput; use crate::errors::{InitializationError, ProtocolError}; use crate::participants::{Participant, ParticipantList}; -use crate::ReconstructionLowerBound; +use crate::ReconstructionThreshold; use crate::{ ecdsa::{x_coordinate, AffinePoint, Scalar, Secp256K1Sha256, Signature, SignatureOption}, protocol::{ @@ -28,7 +28,7 @@ pub(crate) const OT_ECDSA_SIGN_MAX_INCOMING_PARTICIPANT_ENTRIES: usize = 0; pub fn sign( participants: &[Participant], coordinator: Participant, - threshold: impl Into, + threshold: impl Into, me: Participant, public_key: AffinePoint, presignature: RerandomizedPresignOutput, diff --git a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/test.rs b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/test.rs index db67c9637f..d60b508e20 100644 --- a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/test.rs +++ b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/test.rs @@ -9,7 +9,7 @@ use crate::test_utils::{ generate_participants_with_random_ids, run_keygen, run_protocol, run_refresh, run_reshare, run_sign, GenOutput, GenProtocol, MockCryptoRng, }; -use crate::{protocol::Protocol, Participant, ReconstructionLowerBound}; +use crate::{protocol::Protocol, Participant, ReconstructionThreshold}; use crate::crypto::hash::test::scalar_hash_secp256k1; use crate::ecdsa::{ @@ -25,7 +25,7 @@ use std::error::Error; /// This signing does not rerandomize the presignatures and tests only the core protocol pub fn run_sign_without_rerandomization( participants_presign: &[(Participant, PresignOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, public_key: Element, msg: &[u8], rng: &mut impl CryptoRngCore, @@ -78,7 +78,7 @@ pub fn run_sign_without_rerandomization( /// rerandomizing the presignatures pub fn run_sign_with_rerandomization( participants_presign: &[(Participant, PresignOutput)], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, public_key: Element, msg: &[u8], rng: &mut impl CryptoRngCore, @@ -153,7 +153,7 @@ pub fn run_presign( shares1: Vec, pub0: &TriplePub, pub1: &TriplePub, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> Vec<(Participant, PresignOutput)> { assert!(participants.len() == shares0.len()); assert!(participants.len() == shares1.len()); @@ -461,7 +461,7 @@ fn test_robustness_with_rerandomization() { fn test_robustness(run_sign: F, rng: &mut R) where - F: Fn(&[(Participant, PresignOutput)], ReconstructionLowerBound, Element, &[u8], &mut R) -> T, + F: Fn(&[(Participant, PresignOutput)], ReconstructionThreshold, Element, &[u8], &mut R) -> T, { let participants_count = 7; let mut participants = generate_participants_with_random_ids(participants_count, rng); diff --git a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples.rs b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples.rs index c2e53b8e03..e648ff8699 100644 --- a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples.rs +++ b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples.rs @@ -45,7 +45,7 @@ use zeroize::ZeroizeOnDrop; use crate::{ ecdsa::{AffinePoint, Scalar}, participants::Participant, - ReconstructionLowerBound, + ReconstructionThreshold, }; /// Represents the public part of a triple. @@ -61,7 +61,7 @@ pub struct TriplePub { /// The participants in generating this triple. pub participants: Vec, /// The threshold which will be able to reconstruct it. - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, } /// Represents a share of a triple. diff --git a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/generation.rs b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/generation.rs index da66c93366..677b1bcbbd 100644 --- a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/generation.rs +++ b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/generation.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use crate::participants::{Participant, ParticipantList, ParticipantMap}; -use crate::thresholds::ReconstructionLowerBound; +use crate::thresholds::ReconstructionThreshold; use crate::{ crypto::{ commitment::{commit, Commitment}, @@ -32,7 +32,7 @@ use super::{multiplication::multiplication_many, TriplePub, TripleShare}; /// LABEL, NAME, Participants, threshold fn create_transcript( participants: &ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> Result { let mut transcript = Transcript::new(NEAR_TRIPLE_GENERATION_LABEL); @@ -73,7 +73,7 @@ async fn do_generation( comms: Comms, participants: ParticipantList, me: Participant, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, rng: impl CryptoRngCore, ) -> Result { let mut triple = do_generation_many::<1>(comms, participants, me, threshold, rng).await?; @@ -125,7 +125,7 @@ async fn do_generation_many( comms: Comms, participants: ParticipantList, me: Participant, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, mut rng: impl CryptoRngCore, ) -> Result { if N == 0 { @@ -707,8 +707,8 @@ async fn do_generation_many( /// provided to this function. fn validate_triple_inputs( participants: &[Participant], - threshold: impl Into, -) -> Result<(ParticipantList, ReconstructionLowerBound), InitializationError> { + threshold: impl Into, +) -> Result<(ParticipantList, ReconstructionThreshold), InitializationError> { let threshold = threshold.into(); let threshold_value = threshold.value(); if participants.len() < 2 { @@ -781,7 +781,7 @@ pub fn triple_generation_max_incoming_buffer_entries( pub fn generate_triple( participants: &[Participant], me: Participant, - threshold: impl Into, + threshold: impl Into, rng: impl CryptoRngCore + Send + 'static, ) -> Result, InitializationError> { let (participants, threshold) = validate_triple_inputs(participants, threshold)?; @@ -797,7 +797,7 @@ pub fn generate_triple( pub fn generate_triple_many( participants: &[Participant], me: Participant, - threshold: impl Into, + threshold: impl Into, rng: impl CryptoRngCore + Send + 'static, ) -> Result, InitializationError> { let (participants, threshold) = validate_triple_inputs(participants, threshold)?; diff --git a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/test.rs b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/test.rs index 8e85c3563e..c7ee57da20 100644 --- a/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/test.rs +++ b/crates/threshold-signatures/src/ecdsa/ot_based_ecdsa/triples/test.rs @@ -9,7 +9,7 @@ use super::{ use crate::{ ecdsa::{Field, Polynomial, ProjectivePoint, Secp256K1ScalarField}, test_utils::MockCryptoRng, - ReconstructionLowerBound, + ReconstructionThreshold, }; use crate::errors::ProtocolError; @@ -25,7 +25,7 @@ use crate::test_utils::run_two_party_protocol; pub fn deal( rng: &mut impl CryptoRngCore, participants: &[Participant], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> Result<(TriplePub, Vec), ProtocolError> { let a = Secp256K1ScalarField::random(&mut *rng); let b = Secp256K1ScalarField::random(&mut *rng); diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa.rs index 468304da48..6812bdc15f 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa.rs @@ -6,7 +6,7 @@ mod test; use crate::{ ecdsa::{AffinePoint, KeygenOutput, RerandomizationArguments, Scalar}, errors::ProtocolError, - MaxMalicious, + ReconstructionThreshold, }; use serde::{Deserialize, Serialize}; use subtle::{Choice, ConstantTimeEq}; @@ -17,8 +17,9 @@ pub struct PresignArguments { /// The output of key generation, i.e. our share of the secret key, and the public key package. /// This is of type `KeygenOutput` from Frost implementation pub keygen_out: KeygenOutput, - /// The desired threshold for the presignature, which must match the original threshold - pub max_malicious: MaxMalicious, + /// The desired threshold for the presignature, which must match the original threshold. + /// Used internally as `MaxMalicious = ReconstructionThreshold - 1`. + pub threshold: ReconstructionThreshold, } /// The output of the presigning protocol. diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs index 7ab49d8bab..5f2196334b 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs @@ -52,32 +52,32 @@ pub fn presign( }); } - if args.max_malicious.value() > participants.len() { + let max_malicious = args.threshold.max_malicious(); + if max_malicious.value() > participants.len() { return Err(InitializationError::BadParameters( - "max_malicious must be less than or equals to participant count".to_string(), + "ReconstructionThreshold - 1 must be less than or equals to participant count".to_string(), )); } - let robust_ecdsa_threshold = args - .max_malicious + let robust_ecdsa_threshold = max_malicious .value() .checked_mul(2) .and_then(|v| v.checked_add(1)) .ok_or_else(|| { InitializationError::BadParameters( - "2*max_malicious+1 must be less than usize::MAX".to_string(), + "2*(ReconstructionThreshold - 1)+1 must be less than usize::MAX".to_string(), ) })?; if robust_ecdsa_threshold > participants.len() { return Err(InitializationError::BadParameters( - "2*max_malicious+1 must be less than or equals to participant count".to_string(), + "2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count".to_string(), )); } // To prevent split-view attacks documented in docs/ecdsa/robust_ecdsa/signing.md if participants.len() != robust_ecdsa_threshold { return Err(InitializationError::BadParameters( - "the number of participants during presigning must be exactly 2*max_malicious+1 to avoid split view attacks".to_string(), + "the number of participants during presigning must be exactly 2*(ReconstructionThreshold - 1)+1 to avoid split view attacks".to_string(), )); } @@ -86,8 +86,8 @@ pub fn presign( Ok(make_protocol(ctx, fut)) } -/// /!\ Warning: the threshold in this scheme is the exactly the -/// same as the max number of malicious parties. +/// /!\ Warning: the polynomial degree in this scheme equals +/// `ReconstructionThreshold - 1` (the max number of malicious parties). #[allow(clippy::too_many_lines)] async fn do_presign( mut chan: SharedChannel, @@ -97,7 +97,7 @@ async fn do_presign( mut rng: impl CryptoRngCore, ) -> Result { let rng = &mut rng; - let threshold = args.max_malicious.value(); + let threshold = args.threshold.max_malicious().value(); // Round 1 let degree = threshold .checked_mul(2) @@ -406,6 +406,7 @@ mod test { generate_participants, generate_test_keys, make_keygen_output, run_protocol, GenProtocol, MockCryptoRng, }; + use crate::thresholds::MaxMalicious; use rstest::rstest; #[test] @@ -430,7 +431,7 @@ mod test { *p, PresignArguments { keygen_out, - max_malicious: max_malicious.into(), + threshold: MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), }, rng_p, ) @@ -470,7 +471,7 @@ mod test { p, PresignArguments { keygen_out, - max_malicious: max_malicious.into(), + threshold: MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), }, rng_p, ) diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs index 7527df7735..00efeb1d16 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs @@ -12,7 +12,7 @@ use crate::{ internal::{make_protocol, Comms, SharedChannel}, Protocol, }, - MaxMalicious, + ReconstructionThreshold, }; use frost_core::serialization::SerializableScalar; use subtle::ConditionallySelectable; @@ -32,14 +32,14 @@ pub(crate) const ROBUST_ECDSA_SIGN_MAX_INCOMING_PARTICIPANT_ENTRIES: usize = 0; /// setting if different subsets of participants sign different `(msg_hash, tweak)` /// values using shares derived from the same presignature (i.e., different /// rerandomization inputs for the same presignature). -/// To reduce risk in this implementation, require `N1 = N2 = 2 * max_malicious + 1`, +/// To reduce risk in this implementation, require `N1 = N2 = 2*(ReconstructionThreshold - 1) + 1`, /// ensure all participants agree on `(msg_hash, tweak, participants)` when creating /// `RerandomizedPresignOutput`, never reuse a presignature, and do not sign with /// `msg_hash == 0`. pub fn sign( participants: &[Participant], coordinator: Participant, - max_malicious: impl Into, + threshold: impl Into, me: Participant, public_key: AffinePoint, presignature: RerandomizedPresignOutput, @@ -70,20 +70,20 @@ pub fn sign( }); } - // ensure number of participants during the signing phase is >= 2 * max_malicious + 1 + // ensure number of participants during the signing phase is >= 2*(ReconstructionThreshold - 1) + 1 + let max_malicious = threshold.into().max_malicious(); let robust_ecdsa_threshold = max_malicious - .into() .value() .checked_mul(2) .and_then(|v| v.checked_add(1)) .ok_or_else(|| { InitializationError::BadParameters( - "2*threshold+1 must be less than usize::MAX".to_string(), + "2*(ReconstructionThreshold - 1)+1 must be less than usize::MAX".to_string(), ) })?; if robust_ecdsa_threshold > participants.len() { return Err(InitializationError::BadParameters( - "2*max_malicious+1 must be less than or equals to participant count".to_string(), + "2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count".to_string(), )); } @@ -91,7 +91,7 @@ pub fn sign( // documented in docs/ecdsa/robust_ecdsa/signing.md if participants.len() != robust_ecdsa_threshold { return Err(InitializationError::BadParameters( - "the number of participants during signing must be exactly 2*max_malicious+1 to avoid split view attacks".to_string(), + "the number of participants during signing must be exactly 2*(ReconstructionThreshold - 1)+1 to avoid split view attacks".to_string(), )); } if bool::from(msg_hash.is_zero()) { @@ -231,6 +231,7 @@ mod test { use crate::test_utils::{ assert_buffer_capacity, expected_buffer_by_role, generate_participants, MockCryptoRng, }; + use crate::thresholds::MaxMalicious; use rstest::rstest; type PresigSimulationOutput = (Scalar, Polynomial, Polynomial, Polynomial, ProjectivePoint); @@ -307,7 +308,7 @@ mod test { let (_, sig) = run_sign_without_rerandomization( &participants_presign, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key, msg, &mut rng, @@ -379,7 +380,7 @@ mod test { let result = crate::ecdsa::robust_ecdsa::test::run_sign_without_rerandomization( &presignatures, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key, &msg, &mut rng, @@ -424,7 +425,7 @@ mod test { let result = crate::ecdsa::robust_ecdsa::test::run_sign_without_rerandomization( &presignatures, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key, &msg, &mut rng, @@ -435,7 +436,7 @@ mod test { Err(err) => { let text = err.to_string(); assert!( - text.contains("bad parameters: 2*max_malicious+1 must be less than or equals to participant count"), + text.contains("bad parameters: 2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count"), "unexpected error type: {text}" ); } diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs index e71c3d3075..cf2bd77521 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs @@ -16,6 +16,7 @@ use crate::test_utils::{ run_sign, GenOutput, GenProtocol, MockCryptoRng, }; use crate::thresholds::MaxMalicious; +use crate::ReconstructionThreshold; use rand::seq::SliceRandom as _; use rand_core::{CryptoRngCore, SeedableRng}; @@ -24,7 +25,7 @@ use rand_core::{CryptoRngCore, SeedableRng}; /// This signing does not rerandomize the presignatures and tests only the core protocol pub fn run_sign_without_rerandomization( participants_presign: &[(Participant, PresignOutput)], - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, public_key: Element, msg: &[u8], rng: &mut impl CryptoRngCore, @@ -51,7 +52,7 @@ pub fn run_sign_without_rerandomization( sign( participants, coordinator, - max_malicious, + threshold, me, pk, rerand_presig, @@ -76,6 +77,7 @@ pub fn run_sign_with_rerandomization( msg: &[u8], rng: &mut impl CryptoRngCore, ) -> Result<(Tweak, Participant, Signature), Box> { + let threshold = max_malicious.into().reconstruction_threshold().unwrap(); // hash the message into secp256k1 field let msg_hash = scalar_hash_secp256k1(msg); @@ -130,7 +132,7 @@ pub fn run_sign_with_rerandomization( sign( participants, coordinator, - max_malicious, + threshold, me, pk, presignature, @@ -152,6 +154,7 @@ pub fn run_presign( let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); let participant_list: Vec = participants.iter().map(|(p, _)| *p).collect(); + let threshold = max_malicious.into().reconstruction_threshold().unwrap(); for (p, keygen_out) in participants { let rng_p = R::seed_from_u64(rng.next_u64()); @@ -160,7 +163,7 @@ pub fn run_presign( p, PresignArguments { keygen_out, - max_malicious: max_malicious.into(), + threshold, }, rng_p, ) @@ -188,7 +191,7 @@ fn test_refresh() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key.to_element(), msg, &mut rng, @@ -236,7 +239,7 @@ fn test_reshare_sign_more_participants() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key.to_element(), msg, &mut rng, @@ -279,7 +282,7 @@ fn test_reshare_sign_less_participants() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key.to_element(), msg, &mut rng, @@ -302,7 +305,7 @@ fn test_e2e() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key.to_element(), msg, &mut rng, @@ -328,7 +331,7 @@ fn test_e2e_random_identifiers() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - max_malicious.into(), + MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), public_key.to_element(), msg, &mut rng, diff --git a/crates/threshold-signatures/src/frost.rs b/crates/threshold-signatures/src/frost.rs index e2979a7bd8..851360fc0b 100644 --- a/crates/threshold-signatures/src/frost.rs +++ b/crates/threshold-signatures/src/frost.rs @@ -16,7 +16,7 @@ use crate::{ internal::{make_protocol, Comms, SharedChannel}, Protocol, }, - Ciphersuite, KeygenOutput, ReconstructionLowerBound, + Ciphersuite, KeygenOutput, ReconstructionThreshold, }; pub mod eddsa; @@ -27,7 +27,7 @@ pub struct PresignArguments { /// The output of key generation, i.e. our share of the secret key, and the public key package. pub keygen_out: KeygenOutput, /// The threshold for the scheme - pub threshold: ReconstructionLowerBound, + pub threshold: ReconstructionThreshold, } /// The output of the presigning protocol. @@ -126,7 +126,7 @@ async fn do_presign( /// Verifies that the sign inputs are valid pub fn assert_sign_inputs( participants: &[Participant], - threshold: impl Into, + threshold: impl Into, me: Participant, coordinator: Participant, ) -> Result { diff --git a/crates/threshold-signatures/src/frost/eddsa/sign.rs b/crates/threshold-signatures/src/frost/eddsa/sign.rs index cbaebfe113..4f03b302d6 100644 --- a/crates/threshold-signatures/src/frost/eddsa/sign.rs +++ b/crates/threshold-signatures/src/frost/eddsa/sign.rs @@ -9,7 +9,7 @@ use crate::{ internal::{make_protocol, Comms, SharedChannel}, Protocol, }, - Participant, ParticipantList, ReconstructionLowerBound, + Participant, ParticipantList, ReconstructionThreshold, }; use frost_ed25519::{ @@ -48,7 +48,7 @@ pub(crate) const EDDSA_SIGN_V2_MAX_INCOMING_PARTICIPANT_ENTRIES: usize = 0; /// For reference, see how RFC 8032 handles "pre-hashing". pub fn sign_v1( participants: &[Participant], - threshold: impl Into, + threshold: impl Into, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -75,7 +75,7 @@ pub fn sign_v1( pub fn sign_v2( participants: &[Participant], - threshold: impl Into + Copy, + threshold: impl Into + Copy, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -111,7 +111,7 @@ pub fn sign_v2( async fn do_sign_coordinator_v1( mut chan: SharedChannel, participants: ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, keygen_output: KeygenOutput, message: Vec, @@ -191,7 +191,7 @@ async fn do_sign_coordinator_v1( async fn do_sign_coordinator_v2( mut chan: SharedChannel, participants: ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, keygen_output: KeygenOutput, presignature: &PresignOutput, @@ -247,7 +247,7 @@ async fn do_sign_coordinator_v2( /// For reference, see how RFC 8032 handles "pre-hashing". async fn do_sign_participant_v1( mut chan: SharedChannel, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -326,7 +326,7 @@ async fn do_sign_participant_v1( /// For reference, see how RFC 8032 handles "pre-hashing". fn do_sign_participant_v2( mut chan: SharedChannel, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, coordinator: Participant, keygen_output: &KeygenOutput, @@ -363,7 +363,7 @@ fn do_sign_participant_v2( /// A function that takes a signing share and a keygenOutput /// and construct a public key package used for frost signing fn construct_key_package( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, signing_share: SigningShare, verifying_key: &VerifyingKey, @@ -386,7 +386,7 @@ fn construct_key_package( async fn fut_wrapper_v1( chan: SharedChannel, participants: ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -422,7 +422,7 @@ async fn fut_wrapper_v1( async fn fut_wrapper_v2( chan: SharedChannel, participants: ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, diff --git a/crates/threshold-signatures/src/frost/eddsa/test.rs b/crates/threshold-signatures/src/frost/eddsa/test.rs index 1691d34ff8..97bd8db138 100644 --- a/crates/threshold-signatures/src/frost/eddsa/test.rs +++ b/crates/threshold-signatures/src/frost/eddsa/test.rs @@ -5,7 +5,7 @@ use crate::{ KeygenOutput, PresignOutput, SignatureOption, }, test_utils::{generate_participants, run_protocol, GenProtocol, MockCryptoRng}, - Participant, ReconstructionLowerBound, + Participant, ReconstructionThreshold, }; use std::error::Error; @@ -21,7 +21,7 @@ pub fn run_sign_v1( participants: &[(Participant, KeygenOutput)], actual_signers: usize, coordinator: Participant, - threshold: impl Into + Copy + 'static, + threshold: impl Into + Copy + 'static, msg_hash: HashOutput, rng: &mut impl CryptoRngCore, ) -> Result, Box> { @@ -59,7 +59,7 @@ pub fn run_sign_v1( pub fn run_presign( participants: &[(Participant, KeygenOutput)], - threshold: impl Into + Copy, + threshold: impl Into + Copy, actual_signers: usize, rng: impl CryptoRngCore + Send + Clone + 'static, ) -> Result, Box> { @@ -70,7 +70,7 @@ pub fn run_sign_v2( participants: &[(Participant, KeygenOutput)], actual_signers: usize, coordinator: Participant, - threshold: impl Into + Copy + 'static, + threshold: impl Into + Copy + 'static, msg_hash: HashOutput, rng: impl CryptoRngCore + Send + Clone + 'static, ) -> Result, Box> { diff --git a/crates/threshold-signatures/src/frost/redjubjub/sign.rs b/crates/threshold-signatures/src/frost/redjubjub/sign.rs index 6f2a709cce..40c5c3be14 100644 --- a/crates/threshold-signatures/src/frost/redjubjub/sign.rs +++ b/crates/threshold-signatures/src/frost/redjubjub/sign.rs @@ -9,7 +9,7 @@ use crate::{ internal::{make_protocol, Comms, SharedChannel}, Protocol, }, - ReconstructionLowerBound, + ReconstructionThreshold, }; use reddsa::frost::redjubjub::{ @@ -44,7 +44,7 @@ pub(crate) const REDJUBJUB_SIGN_MAX_INCOMING_PARTICIPANT_ENTRIES: usize = 1; #[allow(clippy::too_many_arguments)] pub fn sign( participants: &[Participant], - threshold: impl Into, + threshold: impl Into, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -75,7 +75,7 @@ pub fn sign( async fn fut_wrapper( chan: SharedChannel, participants: ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -136,7 +136,7 @@ async fn fut_wrapper( async fn do_sign_coordinator( mut chan: SharedChannel, participants: ParticipantList, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, keygen_output: KeygenOutput, presignature: &PresignOutput, @@ -202,7 +202,7 @@ async fn do_sign_coordinator( /// For reference, see how RFC 8032 handles "pre-hashing". async fn do_sign_participant( mut chan: SharedChannel, - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, coordinator: Participant, keygen_output: KeygenOutput, @@ -249,7 +249,7 @@ async fn do_sign_participant( /// A function that takes a signing share and a keygenOutput /// and construct a public key package used for frost signing fn construct_key_package( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, me: Participant, keygen_output: &KeygenOutput, ) -> Result { diff --git a/crates/threshold-signatures/src/frost/redjubjub/test.rs b/crates/threshold-signatures/src/frost/redjubjub/test.rs index 980f1e752f..1cb14dfb50 100644 --- a/crates/threshold-signatures/src/frost/redjubjub/test.rs +++ b/crates/threshold-signatures/src/frost/redjubjub/test.rs @@ -1,7 +1,7 @@ use crate::{ crypto::hash::{hash, HashOutput}, frost::redjubjub::{sign::sign, KeygenOutput, PresignOutput, SignatureOption}, - Participant, ReconstructionLowerBound, + Participant, ReconstructionThreshold, }; use crate::test_utils::{ @@ -23,7 +23,7 @@ type C = JubjubBlake2b512; pub fn run_presign( participants: &[(Participant, KeygenOutput)], - threshold: impl Into + Copy, + threshold: impl Into + Copy, actual_signers: usize, rng: impl CryptoRngCore + Send + Clone + 'static, ) -> Result, Box> { @@ -36,7 +36,7 @@ pub fn run_sign_with_presign( participants: &[(Participant, KeygenOutput)], actual_signers: usize, coordinator: Participant, - threshold: impl Into + Copy + 'static, + threshold: impl Into + Copy + 'static, msg_hash: HashOutput, ) -> Result, Box> { let mut rng = MockCryptoRng::seed_from_u64(644_221); diff --git a/crates/threshold-signatures/src/lib.rs b/crates/threshold-signatures/src/lib.rs index 260e3b30ae..61303e70f9 100644 --- a/crates/threshold-signatures/src/lib.rs +++ b/crates/threshold-signatures/src/lib.rs @@ -36,7 +36,7 @@ use crate::errors::InitializationError; use crate::participants::Participant; use crate::protocol::internal::{make_protocol, Comms}; use crate::protocol::Protocol; -pub use crate::thresholds::{MaxMalicious, ReconstructionLowerBound}; +pub use crate::thresholds::ReconstructionThreshold; use rand_core::CryptoRngCore; use std::marker::Send; @@ -96,7 +96,7 @@ pub(crate) const DKG_MAX_INCOMING_BUFFER_ENTRIES: usize = 5; pub fn keygen( participants: &[Participant], me: Participant, - threshold: impl Into + Send + Copy + 'static, + threshold: impl Into + Send + Copy + 'static, rng: impl CryptoRngCore + Send + 'static, ) -> Result>, InitializationError> where @@ -113,11 +113,11 @@ where #[allow(clippy::too_many_arguments)] pub fn reshare( old_participants: &[Participant], - old_threshold: impl Into + Send + 'static, + old_threshold: impl Into + Send + 'static, old_signing_key: Option>, old_public_key: VerifyingKey, new_participants: &[Participant], - new_threshold: impl Into + Copy + Send + 'static, + new_threshold: impl Into + Copy + Send + 'static, me: Participant, rng: impl CryptoRngCore + Send + 'static, ) -> Result>, InitializationError> @@ -153,7 +153,7 @@ pub fn refresh( old_signing_key: Option>, old_public_key: VerifyingKey, old_participants: &[Participant], - old_threshold: impl Into + Copy + Send + 'static, + old_threshold: impl Into + Copy + Send + 'static, me: Participant, rng: impl CryptoRngCore + Send + 'static, ) -> Result>, InitializationError> diff --git a/crates/threshold-signatures/src/test_utils/dkg.rs b/crates/threshold-signatures/src/test_utils/dkg.rs index 54069cecd2..07169d0b7f 100644 --- a/crates/threshold-signatures/src/test_utils/dkg.rs +++ b/crates/threshold-signatures/src/test_utils/dkg.rs @@ -3,7 +3,7 @@ use rand_core::CryptoRngCore; use crate::participants::Participant; use crate::test_utils::{run_protocol, GenOutput, GenProtocol}; -use crate::thresholds::ReconstructionLowerBound; +use crate::thresholds::ReconstructionThreshold; use crate::{keygen, refresh, reshare, Ciphersuite, Element, KeygenOutput, Scalar, VerifyingKey}; // +++++++++++++++++ DKG Functions +++++++++++++++++ // @@ -15,7 +15,7 @@ type DKGGenProtocol = GenProtocol>; /// If the protocol succeeds, returns a sorted vector based on participants id pub fn run_keygen( participants: &[Participant], - threshold: impl Into + Copy + Send + 'static, + threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> GenOutput where @@ -38,7 +38,7 @@ where pub fn run_refresh( participants: &[Participant], keys: &[(Participant, KeygenOutput)], - threshold: impl Into + Copy + Send + 'static, + threshold: impl Into + Copy + Send + 'static, rng: &mut R, ) -> GenOutput where @@ -70,8 +70,8 @@ pub fn run_reshare, keys: &[(Participant, KeygenOutput)], - old_threshold: impl Into + Copy + Send + 'static, - new_threshold: impl Into + Copy + Send + 'static, + old_threshold: impl Into + Copy + Send + 'static, + new_threshold: impl Into + Copy + Send + 'static, new_participants: &[Participant], rng: &mut R, ) -> GenOutput diff --git a/crates/threshold-signatures/src/test_utils/presign.rs b/crates/threshold-signatures/src/test_utils/presign.rs index f8c230bc92..9a6e08ef53 100644 --- a/crates/threshold-signatures/src/test_utils/presign.rs +++ b/crates/threshold-signatures/src/test_utils/presign.rs @@ -8,7 +8,7 @@ use crate::ecdsa::{RerandomizationArguments, Tweak}; use crate::frost; use crate::test_utils::{random_32_bytes, run_protocol, GenProtocol}; use crate::{ - Ciphersuite, Participant, ParticipantList, ReconstructionLowerBound, Scalar, VerifyingKey, + Ciphersuite, Participant, ParticipantList, ReconstructionThreshold, Scalar, VerifyingKey, }; // +++++++++++++++++ ECDSA Presignature Rerandomization +++++++++++++++++ // @@ -45,7 +45,7 @@ pub fn ecdsa_generate_rerandpresig_args( type BoxErr = Box; pub fn frost_run_presignature( participants: &[(Participant, crate::KeygenOutput)], - threshold: impl Into + Copy, + threshold: impl Into + Copy, actual_signers: usize, mut rng: impl CryptoRngCore + Send + Clone + 'static, ) -> Result)>, BoxErr> diff --git a/crates/threshold-signatures/src/test_utils/snapshot.rs b/crates/threshold-signatures/src/test_utils/snapshot.rs index d4c804a078..ca807ddd2d 100644 --- a/crates/threshold-signatures/src/test_utils/snapshot.rs +++ b/crates/threshold-signatures/src/test_utils/snapshot.rs @@ -219,7 +219,9 @@ mod test { *p, PresignArguments { keygen_out, - max_malicious: max_malicious.into(), + threshold: crate::thresholds::MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), }, rng_p, ) diff --git a/crates/threshold-signatures/src/thresholds.rs b/crates/threshold-signatures/src/thresholds.rs index 621267f123..19ab6a8983 100644 --- a/crates/threshold-signatures/src/thresholds.rs +++ b/crates/threshold-signatures/src/thresholds.rs @@ -2,31 +2,54 @@ use derive_more::{From, Into}; use serde::{Deserialize, Serialize}; use thiserror::Error; +/// Number of share-holders required to reconstruct the secret (`t` in a t-of-n +/// scheme). The single public threshold type exposed by this crate. #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, From, Into, )] -pub struct MaxMalicious(usize); +pub struct ReconstructionThreshold(usize); +/// Maximum number of malicious parties tolerated by a protocol. The module +/// `thresholds` is private, so the only callers are inside this crate — +/// external code stays in [`ReconstructionThreshold`] terms. #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, From, Into, )] -pub struct ReconstructionLowerBound(usize); +pub struct MaxMalicious(usize); -// ----- MaxMalicious conversions ----- impl MaxMalicious { pub fn value(self) -> usize { self.0 } + + /// Number of share-holders needed to reconstruct the secret under this + /// max-malicious bound. Inverse of [`ReconstructionThreshold::max_malicious`]. + /// Fails only if `MaxMalicious == usize::MAX`. + #[allow(dead_code)] // used only by test code under cfg(test) + pub fn reconstruction_threshold(self) -> Result { + ReconstructionThreshold::try_from(self) + } } -impl ReconstructionLowerBound { +impl ReconstructionThreshold { pub fn value(self) -> usize { self.0 } + + /// Maximum number of malicious parties this protocol tolerates, derived + /// by the standard relation `MaxMalicious = ReconstructionThreshold - 1`. + /// + /// Returns a crate-private type — robust ECDSA needs this internally; + /// external callers stay in `ReconstructionThreshold` terms. + /// + /// Panics if `ReconstructionThreshold == 0`; constructors only produce values ≥ 1. + pub fn max_malicious(self) -> MaxMalicious { + MaxMalicious(self.0 - 1) + } } -/// Lower bound to reconstruct the secret is `MaxMalicious` + 1. -impl TryFrom for ReconstructionLowerBound { +/// Standard relation: `ReconstructionThreshold = MaxMalicious + 1`. +impl TryFrom for ReconstructionThreshold { type Error = ThresholdError; fn try_from(m: MaxMalicious) -> Result { diff --git a/crates/threshold-signatures/tests/common.rs b/crates/threshold-signatures/tests/common.rs index 508d1c0032..451d3fcd64 100644 --- a/crates/threshold-signatures/tests/common.rs +++ b/crates/threshold-signatures/tests/common.rs @@ -16,7 +16,7 @@ use threshold_signatures::{ keygen, participants::Participant, protocol::{Action, Protocol}, - reshare, Ciphersuite, Element, KeygenOutput, ReconstructionLowerBound, Scalar, + reshare, Ciphersuite, Element, KeygenOutput, ReconstructionThreshold, Scalar, }; pub type GenProtocol = Vec<(Participant, Box>)>; @@ -82,7 +82,7 @@ pub fn run_protocol( #[allow(clippy::missing_panics_doc)] pub fn run_keygen( participants: &[Participant], - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, ) -> HashMap> where Element: std::marker::Send, @@ -105,8 +105,8 @@ pub fn run_reshare( participants: &[Participant], pub_key: &VerifyingKey, keys: &[(Participant, KeygenOutput)], - old_threshold: ReconstructionLowerBound, - new_threshold: ReconstructionLowerBound, + old_threshold: ReconstructionThreshold, + new_threshold: ReconstructionThreshold, new_participants: &[Participant], ) -> HashMap> where diff --git a/crates/threshold-signatures/tests/eddsa.rs b/crates/threshold-signatures/tests/eddsa.rs index 94c1502f42..b9b6d375c6 100644 --- a/crates/threshold-signatures/tests/eddsa.rs +++ b/crates/threshold-signatures/tests/eddsa.rs @@ -16,14 +16,14 @@ use threshold_signatures::{ }, participants::Participant, test_utils::frost_run_presignature, - ReconstructionLowerBound, + ReconstructionThreshold, }; type C = Ed25519Sha512; type KeygenOutput = threshold_signatures::KeygenOutput; fn run_sign_v1( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, participants: &[(Participant, KeygenOutput)], coordinator: Participant, msg_hash: &[u8], @@ -50,7 +50,7 @@ fn run_sign_v1( } fn run_sign_v2( - threshold: ReconstructionLowerBound, + threshold: ReconstructionThreshold, participants: &[(Participant, KeygenOutput)], coordinator: Participant, presig: &[(Participant, PresignOutput)], diff --git a/crates/threshold-signatures/tests/robust_ecdsa.rs b/crates/threshold-signatures/tests/robust_ecdsa.rs index 63d8f99fb5..b2379a5d21 100644 --- a/crates/threshold-signatures/tests/robust_ecdsa.rs +++ b/crates/threshold-signatures/tests/robust_ecdsa.rs @@ -17,7 +17,7 @@ use threshold_signatures::{ }, frost_secp256k1::VerifyingKey, participants::Participant, - Element, MaxMalicious, ParticipantList, + Element, ParticipantList, ReconstructionThreshold, }; // Note: This is required to use Scalar::from_repr @@ -32,7 +32,7 @@ type Tweak = threshold_signatures::Tweak; fn run_presign( participants: HashMap, - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, ) -> Vec<(Participant, PresignOutput)> { let mut protocols: GenProtocol = Vec::with_capacity(participants.len()); @@ -44,7 +44,7 @@ fn run_presign( p, PresignArguments { keygen_out, - max_malicious, + threshold, }, OsRng, ) @@ -57,7 +57,7 @@ fn run_presign( fn run_sign( participants_presign: Vec<(Participant, RerandomizedPresignOutput)>, - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, coordinator: Participant, public_key: Element, msg_hash: [u8; 32], @@ -74,7 +74,7 @@ fn run_sign( let protocol = sign( &participants, coordinator, - max_malicious, + threshold, p, public_key.to_affine(), presignature, @@ -90,7 +90,7 @@ fn run_sign( fn run_sign_with_rerandomization( participants_presign: &[(Participant, PresignOutput)], - max_malicious: MaxMalicious, + threshold: ReconstructionThreshold, public_key: VerifyingKey, msg_hash: [u8; 32], tweak: [u8; 32], @@ -132,7 +132,7 @@ fn run_sign_with_rerandomization( // run sign instantiation with the necessary arguments let all_sigs = run_sign( rerand_participants_presign, - max_malicious, + threshold, coordinator, derived_pk, msg_hash, @@ -152,12 +152,11 @@ fn run_sign_with_rerandomization( #[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 threshold = ReconstructionThreshold::from(6_usize); + let keys = run_keygen(&participants, threshold); 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 presign_result = run_presign(keys.clone(), threshold); let msg_hash = *b"hello worldhello worldhello worl"; // generate a random tweak @@ -169,7 +168,7 @@ fn test_run_sign() { let signature = run_sign_with_rerandomization( &presign_result, - max_malicious.into(), + threshold, public_key, msg_hash, tweak, @@ -193,14 +192,14 @@ fn test_run_sign() { let mut new_participants = participants.clone(); new_participants.push(Participant::from(20u32)); - let new_threshold = 6; + let new_threshold = ReconstructionThreshold::from(6_usize); let new_keys = run_reshare( &participants, &public_key, participant_keys.as_slice(), - threshold.into(), - new_threshold.into(), + threshold, + new_threshold, &new_participants, ); let new_public_key = new_keys.get(&participants[0]).unwrap().public_key; From da94f8a2083256e468f04b9195afe930b3aeba62 Mon Sep 17 00:00:00 2001 From: SimonRastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Tue, 26 May 2026 01:14:19 +0200 Subject: [PATCH 2/5] Cargo fmt --- crates/node/src/config.rs | 4 +--- .../src/providers/robust_ecdsa/presign.rs | 1 - .../benches/advanced_robust_ecdsa.rs | 5 ++++- .../src/ecdsa/robust_ecdsa/presign.rs | 14 +++++++++---- .../src/ecdsa/robust_ecdsa/sign.rs | 15 ++++++++++---- .../src/ecdsa/robust_ecdsa/test.rs | 20 ++++++++++++++----- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/crates/node/src/config.rs b/crates/node/src/config.rs index 14db523b07..a9e6839d39 100644 --- a/crates/node/src/config.rs +++ b/crates/node/src/config.rs @@ -74,9 +74,7 @@ pub struct ParticipantsConfig { impl ParticipantsConfig { /// The threshold expressed in the form the threshold-signatures crypto /// library consumes (a `usize`-backed `ReconstructionThreshold`). - pub fn ts_threshold( - &self, - ) -> anyhow::Result { + pub fn ts_threshold(&self) -> anyhow::Result { Ok(threshold_signatures::ReconstructionThreshold::from( usize::try_from(self.threshold.inner())?, )) diff --git a/crates/node/src/providers/robust_ecdsa/presign.rs b/crates/node/src/providers/robust_ecdsa/presign.rs index 050ef0ca84..8028c7a5a3 100644 --- a/crates/node/src/providers/robust_ecdsa/presign.rs +++ b/crates/node/src/providers/robust_ecdsa/presign.rs @@ -312,4 +312,3 @@ impl PresignatureGenerationProgressTracker { )) } } - diff --git a/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs b/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs index ddbddd4c72..8acd5ecc34 100644 --- a/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs +++ b/crates/threshold-signatures/benches/advanced_robust_ecdsa.rs @@ -198,7 +198,10 @@ fn setup_sign_snapshot( } /// Cheap per-sample setup: creates fresh sign protocol and clones the cached simulator -fn prepare_simulated_sign(setup: &SignSetup, threshold: ReconstructionThreshold) -> PreparedSimulatedSig { +fn prepare_simulated_sign( + setup: &SignSetup, + threshold: ReconstructionThreshold, +) -> PreparedSimulatedSig { let real_protocol = sign( &setup.participants, setup.real_participant, diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs index 5f2196334b..02bc897aac 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs @@ -55,7 +55,8 @@ pub fn presign( let max_malicious = args.threshold.max_malicious(); if max_malicious.value() > participants.len() { return Err(InitializationError::BadParameters( - "ReconstructionThreshold - 1 must be less than or equals to participant count".to_string(), + "ReconstructionThreshold - 1 must be less than or equals to participant count" + .to_string(), )); } @@ -70,7 +71,8 @@ pub fn presign( })?; if robust_ecdsa_threshold > participants.len() { return Err(InitializationError::BadParameters( - "2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count".to_string(), + "2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count" + .to_string(), )); } @@ -431,7 +433,9 @@ mod test { *p, PresignArguments { keygen_out, - threshold: MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + threshold: MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), }, rng_p, ) @@ -471,7 +475,9 @@ mod test { p, PresignArguments { keygen_out, - threshold: MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + threshold: MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), }, rng_p, ) diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs index 00efeb1d16..205b14d67a 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs @@ -83,7 +83,8 @@ pub fn sign( })?; if robust_ecdsa_threshold > participants.len() { return Err(InitializationError::BadParameters( - "2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count".to_string(), + "2*(ReconstructionThreshold - 1)+1 must be less than or equals to participant count" + .to_string(), )); } @@ -308,7 +309,9 @@ mod test { let (_, sig) = run_sign_without_rerandomization( &participants_presign, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key, msg, &mut rng, @@ -380,7 +383,9 @@ mod test { let result = crate::ecdsa::robust_ecdsa::test::run_sign_without_rerandomization( &presignatures, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key, &msg, &mut rng, @@ -425,7 +430,9 @@ mod test { let result = crate::ecdsa::robust_ecdsa::test::run_sign_without_rerandomization( &presignatures, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key, &msg, &mut rng, diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs index cf2bd77521..d94ee968a6 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/test.rs @@ -191,7 +191,9 @@ fn test_refresh() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key.to_element(), msg, &mut rng, @@ -239,7 +241,9 @@ fn test_reshare_sign_more_participants() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key.to_element(), msg, &mut rng, @@ -282,7 +286,9 @@ fn test_reshare_sign_less_participants() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key.to_element(), msg, &mut rng, @@ -305,7 +311,9 @@ fn test_e2e() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key.to_element(), msg, &mut rng, @@ -331,7 +339,9 @@ fn test_e2e_random_identifiers() -> Result<(), Box> { let msg = b"hello world"; run_sign_without_rerandomization( &presign_result, - MaxMalicious::from(max_malicious).reconstruction_threshold().unwrap(), + MaxMalicious::from(max_malicious) + .reconstruction_threshold() + .unwrap(), public_key.to_element(), msg, &mut rng, From 349b0242e2dff3603240d7962e26caf2c7d2cec1 Mon Sep 17 00:00:00 2001 From: Simon Rastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Tue, 26 May 2026 13:22:14 +0200 Subject: [PATCH 3/5] Fix bugs --- crates/node/src/coordinator.rs | 1 - crates/node/src/indexer/fake.rs | 6 +++++- crates/node/src/indexer/participants.rs | 5 +++-- crates/node/src/key_events.rs | 2 +- crates/node/src/providers/ckd/sign.rs | 1 - crates/node/src/tests/resharing.rs | 8 ++++---- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/node/src/coordinator.rs b/crates/node/src/coordinator.rs index f5c07469a0..d26bcd966d 100644 --- a/crates/node/src/coordinator.rs +++ b/crates/node/src/coordinator.rs @@ -38,7 +38,6 @@ use near_time::Clock; use std::collections::HashMap; use std::future::Future; use std::sync::{Arc, Mutex}; -use threshold_signatures::ReconstructionThreshold; use threshold_signatures::{confidential_key_derivation, ecdsa, frost::eddsa}; use tokio::select; use tokio::sync::mpsc::unbounded_channel; diff --git a/crates/node/src/indexer/fake.rs b/crates/node/src/indexer/fake.rs index 882f0d1567..d439363a49 100644 --- a/crates/node/src/indexer/fake.rs +++ b/crates/node/src/indexer/fake.rs @@ -418,7 +418,11 @@ fn participants_config_to_threshold_parameters( ) .expect("Failed to insert participant"); } - ThresholdParameters::new(participants, Threshold::new(participants_config.threshold)).unwrap() + ThresholdParameters::new( + participants, + Threshold::new(participants_config.threshold.inner()), + ) + .unwrap() } /// Runs the fake indexer's shared state and logic. There's one instance of this per test. diff --git a/crates/node/src/indexer/participants.rs b/crates/node/src/indexer/participants.rs index 56d645e1af..acb082bd13 100644 --- a/crates/node/src/indexer/participants.rs +++ b/crates/node/src/indexer/participants.rs @@ -401,7 +401,8 @@ mod tests { use near_indexer_primitives::types::AccountId; use near_mpc_contract_interface::types::AccountId as DtoAccountId; use near_mpc_contract_interface::types::{ - ParticipantId, ParticipantInfo, Participants, Threshold, ThresholdParameters, + ParticipantId, ParticipantInfo, Participants, ReconstructionThreshold, Threshold, + ThresholdParameters, }; use std::collections::HashMap; @@ -493,7 +494,7 @@ mod tests { }; let converted = convert_participant_infos(params, None).unwrap(); - assert_eq!(converted.threshold, 3); + assert_eq!(converted.threshold, ReconstructionThreshold::new(3)); for (i, p) in converted.participants.iter().enumerate() { assert!(p.near_account_id == account_ids[i]); let expected_pk: near_sdk::PublicKey = account_id_to_pk[&account_ids[i]].clone().into(); diff --git a/crates/node/src/key_events.rs b/crates/node/src/key_events.rs index 910a70500b..eb68855d90 100644 --- a/crates/node/src/key_events.rs +++ b/crates/node/src/key_events.rs @@ -900,7 +900,7 @@ mod tests { Arc::new(ResharingArgs { previous_keyset: Keyset::new(EpochId::new(5), vec![]), existing_keyshares: None, - new_threshold: ReconstructionThreshold::from(3), + new_threshold: threshold_signatures::ReconstructionThreshold::from(3usize), old_participants: ParticipantsConfig { threshold: mpc_primitives::ReconstructionThreshold::new(3), participants: vec![], diff --git a/crates/node/src/providers/ckd/sign.rs b/crates/node/src/providers/ckd/sign.rs index cac1a1fdff..edd11487d6 100644 --- a/crates/node/src/providers/ckd/sign.rs +++ b/crates/node/src/providers/ckd/sign.rs @@ -11,7 +11,6 @@ use threshold_signatures::{ VerifyingKey, }, participants::Participant, - ReconstructionThreshold, }; use crate::metrics; diff --git a/crates/node/src/tests/resharing.rs b/crates/node/src/tests/resharing.rs index 6c0dc38578..ede7b05076 100644 --- a/crates/node/src/tests/resharing.rs +++ b/crates/node/src/tests/resharing.rs @@ -177,7 +177,7 @@ async fn test_key_resharing_multistage() { let mut participants_1 = setup.participants.clone(); participants_1.participants.pop(); participants_1.participants.pop(); - participants_1.threshold = 3; + participants_1.threshold = ReconstructionThreshold::new(3); let domain = DomainConfig { id: DomainId(0), @@ -227,7 +227,7 @@ async fn test_key_resharing_multistage() { // Have the fifth node join. let mut participants_2 = setup.participants.clone(); participants_2.participants.pop(); - participants_2.threshold = 3; + participants_2.threshold = ReconstructionThreshold::new(3); setup .indexer .contract_mut() @@ -292,7 +292,7 @@ async fn test_key_resharing_multistage() { // Have the first node quit. let mut participants_3 = setup.participants.clone(); participants_3.participants.remove(0); - participants_3.threshold = 3; + participants_3.threshold = ReconstructionThreshold::new(3); setup .indexer .contract_mut() @@ -327,7 +327,7 @@ async fn test_key_resharing_multistage() { let mut participants_4 = setup.participants.clone(); participants_4.participants.remove(0); participants_4.participants.remove(0); - participants_4.threshold = 3; + participants_4.threshold = ReconstructionThreshold::new(3); setup .indexer .contract_mut() From 9208be366fc05424a17aad40648966fe345cb824 Mon Sep 17 00:00:00 2001 From: SimonRastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Tue, 26 May 2026 15:05:10 +0200 Subject: [PATCH 4/5] no-op From 0b2334d35955a9ab08e83d852db98630655bc8d7 Mon Sep 17 00:00:00 2001 From: Simon Rastikian <43679791+SimonRastikian@users.noreply.github.com> Date: Wed, 27 May 2026 10:08:10 +0200 Subject: [PATCH 5/5] No more failing --- .../src/ecdsa/robust_ecdsa/presign.rs | 12 +++- .../src/ecdsa/robust_ecdsa/sign.rs | 6 +- crates/threshold-signatures/src/thresholds.rs | 55 ++++++++++++++++++- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs index 02bc897aac..60ed9374d4 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/presign.rs @@ -52,7 +52,11 @@ pub fn presign( }); } - let max_malicious = args.threshold.max_malicious(); + let max_malicious = args.threshold.max_malicious().map_err(|_| { + InitializationError::BadParameters( + "ReconstructionThreshold must be > 0 to derive MaxMalicious".to_string(), + ) + })?; if max_malicious.value() > participants.len() { return Err(InitializationError::BadParameters( "ReconstructionThreshold - 1 must be less than or equals to participant count" @@ -99,7 +103,11 @@ async fn do_presign( mut rng: impl CryptoRngCore, ) -> Result { let rng = &mut rng; - let threshold = args.threshold.max_malicious().value(); + let threshold = args + .threshold + .max_malicious() + .map_err(|_| ProtocolError::IntegerOverflow)? + .value(); // Round 1 let degree = threshold .checked_mul(2) diff --git a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs index 205b14d67a..ce008f488f 100644 --- a/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs +++ b/crates/threshold-signatures/src/ecdsa/robust_ecdsa/sign.rs @@ -71,7 +71,11 @@ pub fn sign( } // ensure number of participants during the signing phase is >= 2*(ReconstructionThreshold - 1) + 1 - let max_malicious = threshold.into().max_malicious(); + let max_malicious = threshold.into().max_malicious().map_err(|_| { + InitializationError::BadParameters( + "ReconstructionThreshold must be > 0 to derive MaxMalicious".to_string(), + ) + })?; let robust_ecdsa_threshold = max_malicious .value() .checked_mul(2) diff --git a/crates/threshold-signatures/src/thresholds.rs b/crates/threshold-signatures/src/thresholds.rs index 19ab6a8983..5781095f3c 100644 --- a/crates/threshold-signatures/src/thresholds.rs +++ b/crates/threshold-signatures/src/thresholds.rs @@ -42,9 +42,14 @@ impl ReconstructionThreshold { /// Returns a crate-private type — robust ECDSA needs this internally; /// external callers stay in `ReconstructionThreshold` terms. /// - /// Panics if `ReconstructionThreshold == 0`; constructors only produce values ≥ 1. - pub fn max_malicious(self) -> MaxMalicious { - MaxMalicious(self.0 - 1) + /// Fails with [`ThresholdError::IntegerOverflow`] if `ReconstructionThreshold == 0`. + /// The auto-derived `From` and `Deserialize` impls accept 0, so this + /// validation is required at use-time. + pub fn max_malicious(self) -> Result { + self.0 + .checked_sub(1) + .map(MaxMalicious) + .ok_or(ThresholdError::IntegerOverflow) } } @@ -64,3 +69,47 @@ pub enum ThresholdError { #[error("integer overflow")] IntegerOverflow, } + +#[cfg(test)] +#[expect(non_snake_case)] +mod tests { + use super::*; + + #[test] + fn max_malicious__round_trips_via_reconstruction_threshold() { + // Given: every non-zero reconstruction threshold in a representative range + for n in 1usize..=64 { + let t = ReconstructionThreshold::from(n); + + // When: derive MaxMalicious then map back via the inverse + let recovered = t.max_malicious().unwrap().reconstruction_threshold().unwrap(); + + // Then: the round-trip is the identity + assert_eq!(recovered, t, "round-trip failed for n={n}"); + } + } + + #[test] + fn max_malicious__should_err_when_reconstruction_threshold_is_zero() { + // Given: the unsafe construction path (auto-derived From) admits zero + let t = ReconstructionThreshold::from(0usize); + + // When + let result = t.max_malicious(); + + // Then: the underflow is reported instead of silently wrapping + assert_eq!(result, Err(ThresholdError::IntegerOverflow)); + } + + #[test] + fn reconstruction_threshold__should_err_on_overflow_at_usize_max() { + // Given: MaxMalicious holds usize::MAX (only reachable via the unsafe ctor) + let m = MaxMalicious::from(usize::MAX); + + // When + let result = m.reconstruction_threshold(); + + // Then + assert_eq!(result, Err(ThresholdError::IntegerOverflow)); + } +}