From fa03e96bd1ac943c2c73f2414541a38d5fd8a77e Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 10 Feb 2026 14:50:01 +0100 Subject: [PATCH 1/3] refactor: update h in small committee --- circuits/lib/src/configs/committee/small.nr | 2 +- crates/zk-helpers/src/ciphernodes_committee.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/circuits/lib/src/configs/committee/small.nr b/circuits/lib/src/configs/committee/small.nr index 93dc81fc3d..9d037d65f7 100644 --- a/circuits/lib/src/configs/committee/small.nr +++ b/circuits/lib/src/configs/committee/small.nr @@ -12,4 +12,4 @@ pub global N_PARTIES: u32 = 5; /// Threshold. pub global T: u32 = 2; /// Number of honest parties. -pub global H: u32 = 3; +pub global H: u32 = 5; diff --git a/crates/zk-helpers/src/ciphernodes_committee.rs b/crates/zk-helpers/src/ciphernodes_committee.rs index 0c690fec1d..b1c921e36f 100644 --- a/crates/zk-helpers/src/ciphernodes_committee.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -33,7 +33,7 @@ impl CiphernodesCommitteeSize { match self { CiphernodesCommitteeSize::Small => CiphernodesCommittee { n: 5, - h: 3, + h: 5, threshold: 2, }, _ => unreachable!(), @@ -41,12 +41,12 @@ impl CiphernodesCommitteeSize { // @todo add the other committee sizes // CiphernodesCommitteeSize::Medium => CiphernodesCommittee { // n: 5, - // h: 3, + // h: 5, // threshold: 2, // }, // CiphernodesCommitteeSize::Large => CiphernodesCommittee { // n: 5, - // h: 3, + // h: 5, // threshold: 2, // }, } From 168b6c452b36962780006b642722ca1239a323a6 Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 10 Feb 2026 16:17:49 +0100 Subject: [PATCH 2/3] refactor: improve error handling in sample generation --- crates/zk-helpers/src/bin/zk_cli.rs | 11 +- .../zk-helpers/src/circuits/dkg/pk/codegen.rs | 2 +- .../src/circuits/dkg/pk/computation.rs | 20 +-- .../zk-helpers/src/circuits/dkg/pk/sample.rs | 13 +- .../circuits/dkg/share_computation/codegen.rs | 3 +- .../dkg/share_computation/computation.rs | 9 +- .../circuits/dkg/share_computation/sample.rs | 41 ++++-- .../circuits/dkg/share_encryption/circuit.rs | 4 +- .../circuits/dkg/share_encryption/codegen.rs | 6 +- .../dkg/share_encryption/computation.rs | 13 +- .../circuits/dkg/share_encryption/sample.rs | 53 +++++--- .../decrypted_shares_aggregation/codegen.rs | 3 +- .../computation.rs | 3 +- .../decrypted_shares_aggregation/sample.rs | 126 +++++++++++++----- .../threshold/user_data_encryption/codegen.rs | 3 +- .../threshold/user_data_encryption/sample.rs | 21 +-- .../threshold/user_data_encryption/utils.rs | 6 +- crates/zk-helpers/src/utils.rs | 13 +- crates/zk-prover/tests/local_e2e_tests.rs | 6 +- 19 files changed, 239 insertions(+), 117 deletions(-) diff --git a/crates/zk-helpers/src/bin/zk_cli.rs b/crates/zk-helpers/src/bin/zk_cli.rs index 774dacc15c..6cbab46d49 100644 --- a/crates/zk-helpers/src/bin/zk_cli.rs +++ b/crates/zk-helpers/src/bin/zk_cli.rs @@ -262,7 +262,7 @@ fn main() -> Result<()> { let committee = CiphernodesCommitteeSize::Small.values(); let artifacts = match circuit_name { name if name == ::NAME => { - let sample = PkCircuitInput::generate_sample(preset); + let sample = PkCircuitInput::generate_sample(preset)?; let circuit = PkCircuit; circuit.codegen(preset, &sample)? @@ -272,7 +272,8 @@ fn main() -> Result<()> { preset, committee, dkg_input_type, - ); + )?; + let circuit = ShareComputationCircuit; circuit.codegen(preset, &sample)? } @@ -284,13 +285,13 @@ fn main() -> Result<()> { dkg_input_type, sd.z, sd.lambda, - ); + )?; let circuit = ShareEncryptionCircuit; circuit.codegen(preset, &sample)? } name if name == ::NAME => { - let sample = UserDataEncryptionCircuitInput::generate_sample(preset); + let sample = UserDataEncryptionCircuitInput::generate_sample(preset)?; let circuit = UserDataEncryptionCircuit; circuit.codegen(preset, &sample)? @@ -328,7 +329,7 @@ fn main() -> Result<()> { let sample = DecryptedSharesAggregationCircuitInput::generate_sample( preset, CiphernodesCommitteeSize::Small.values(), - ); + )?; let circuit = DecryptedSharesAggregationCircuit; circuit.codegen(preset, &sample)? diff --git a/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs b/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs index 0ba7b3c5ef..e5bc47ffd2 100644 --- a/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/pk/codegen.rs @@ -81,7 +81,7 @@ mod tests { #[test] fn test_toml_generation_and_structure() { let (_, dkg_params) = build_pair_for_preset(BfvPreset::InsecureThreshold512).unwrap(); - let sample = PkCircuitInput::generate_sample(BfvPreset::InsecureThreshold512); + let sample = PkCircuitInput::generate_sample(BfvPreset::InsecureThreshold512).unwrap(); let artifacts = PkCircuit .codegen(BfvPreset::InsecureThreshold512, &sample) diff --git a/crates/zk-helpers/src/circuits/dkg/pk/computation.rs b/crates/zk-helpers/src/circuits/dkg/pk/computation.rs index eb51262a84..18f59a40f1 100644 --- a/crates/zk-helpers/src/circuits/dkg/pk/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/pk/computation.rs @@ -11,6 +11,7 @@ use crate::circuits::dkg::pk::circuit::PkCircuit; use crate::circuits::dkg::pk::circuit::PkCircuitInput; +use crate::compute_max_modulus; use crate::crt_polynomial_to_toml_json; use crate::get_zkp_modulus; use crate::utils::compute_modulus_bit; @@ -90,12 +91,13 @@ impl Computation for Configs { build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; let moduli = dkg_params.moduli().to_vec(); + let l = moduli.len(); let bounds = Bounds::compute(preset, &())?; let bits = Bits::compute(preset, &())?; Ok(Configs { n: dkg_params.degree(), - l: moduli.len(), + l, moduli, bits, bounds, @@ -127,19 +129,11 @@ impl Computation for Bounds { let (_, dkg_params) = build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; - let mut pk_bound_max = BigUint::from(0u32); - - for &qi in dkg_params.moduli() { - let qi_bound: BigUint = (&BigUint::from(qi) - 1u32) / 2u32; - - if qi_bound > pk_bound_max { - pk_bound_max = qi_bound; - } - } + let moduli = dkg_params.moduli(); + let max_mod = compute_max_modulus(moduli); + let pk_bound = (BigUint::from(max_mod) - 1u32) / 2u32; - Ok(Bounds { - pk_bound: pk_bound_max, - }) + Ok(Bounds { pk_bound }) } } diff --git a/crates/zk-helpers/src/circuits/dkg/pk/sample.rs b/crates/zk-helpers/src/circuits/dkg/pk/sample.rs index f43f6cb988..045fc83343 100644 --- a/crates/zk-helpers/src/circuits/dkg/pk/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/pk/sample.rs @@ -7,6 +7,7 @@ //! Sample data generation for the pk circuit: committee and DKG public key only. use crate::dkg::pk::PkCircuitInput; +use crate::CircuitsErrors; use e3_fhe_params::build_pair_for_preset; use e3_fhe_params::BfvPreset; use fhe::bfv::{PublicKey, SecretKey}; @@ -14,16 +15,18 @@ use rand::thread_rng; impl PkCircuitInput { /// Generates sample data for the pk circuit. - pub fn generate_sample(preset: BfvPreset) -> Self { - let (_, dkg_params) = build_pair_for_preset(preset).unwrap(); + pub fn generate_sample(preset: BfvPreset) -> Result { + let (_, dkg_params) = build_pair_for_preset(preset).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)) + })?; let mut rng = thread_rng(); let dkg_secret_key = SecretKey::random(&dkg_params, &mut rng); let dkg_public_key = PublicKey::new(&dkg_secret_key, &mut rng); - Self { + Ok(Self { public_key: dkg_public_key, - } + }) } } @@ -34,7 +37,7 @@ mod tests { #[test] fn test_generate_pk_sample() { - let sample = PkCircuitInput::generate_sample(BfvPreset::InsecureThreshold512); + let sample = PkCircuitInput::generate_sample(BfvPreset::InsecureThreshold512).unwrap(); assert_eq!(sample.public_key.c.c.len(), 2); } diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs index 4eaf55f418..6f8c3e4866 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/codegen.rs @@ -158,7 +158,8 @@ mod tests { BfvPreset::InsecureThreshold512, committee, DkgInputType::SecretKey, - ); + ) + .unwrap(); let artifacts = ShareComputationCircuit .codegen(BfvPreset::InsecureThreshold512, &sample) diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs index dd8408b866..885537b706 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs @@ -276,7 +276,8 @@ mod tests { BfvPreset::InsecureThreshold512, committee, DkgInputType::SecretKey, - ); + ) + .unwrap(); let bounds = Bounds::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); let bits = Bits::compute(BfvPreset::InsecureThreshold512, &bounds).unwrap(); let expected_sk_bits = calculate_bit_width(BigInt::from(bounds.sk_bound.clone())); @@ -291,7 +292,8 @@ mod tests { BfvPreset::InsecureThreshold512, committee, DkgInputType::SmudgingNoise, - ); + ) + .unwrap(); let witness = Witness::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); let degree = witness.secret_crt.limb(0).coefficients().len(); let num_moduli = witness.secret_crt.limbs.len(); @@ -315,7 +317,8 @@ mod tests { BfvPreset::InsecureThreshold512, committee, DkgInputType::SecretKey, - ); + ) + .unwrap(); let constants = Configs::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs index 582f554143..3ccd3ca61f 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/sample.rs @@ -28,19 +28,25 @@ impl ShareComputationCircuitInput { preset: BfvPreset, committee: CiphernodesCommittee, dkg_input_type: DkgInputType, - ) -> Self { - let (threshold_params, _) = build_pair_for_preset(preset).unwrap(); - let sd = preset.search_defaults().unwrap(); + ) -> Result { + let (threshold_params, _) = build_pair_for_preset(preset).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)) + })?; + let sd = preset + .search_defaults() + .ok_or_else(|| CircuitsErrors::Sample("Preset has no search defaults".into()))?; let mut rng = thread_rng(); let trbfv = TRBFV::new(committee.n, committee.threshold, threshold_params.clone()) - .unwrap_or_else(|e| panic!("Failed to create TRBFV: {:?}", e)); + .map_err(|e| CircuitsErrors::Sample(format!("Failed to create TRBFV: {:?}", e)))?; let mut share_manager = ShareManager::new(committee.n, committee.threshold, threshold_params.clone()); let parity_matrix = compute_parity_matrix(threshold_params.moduli(), committee.n, committee.threshold) - .unwrap_or_else(|e| panic!("Failed to compute parity matrix: {}", e)); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to compute parity matrix: {:?}", e)) + })?; let (secret, secret_sss) = match dkg_input_type { DkgInputType::SecretKey => { @@ -48,11 +54,18 @@ impl ShareComputationCircuitInput { let sk_poly = share_manager .coeffs_to_poly_level0(threshold_secret_key.coeffs.clone().as_ref()) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!( + "Failed to convert secret key to poly: {:?}", + e + )) + })?; let sk_sss_u64 = share_manager .generate_secret_shares_from_poly(sk_poly.clone(), rng) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to generate secret shares: {:?}", e)) + })?; let secret_sss: SecretShares = sk_sss_u64 .into_iter() @@ -66,7 +79,9 @@ impl ShareComputationCircuitInput { .collect(); let mut secret_crt = CrtPolynomial::from_mod_q_polynomial(&sk_coeffs, threshold_params.moduli()); - secret_crt.center(threshold_params.moduli()).unwrap(); + secret_crt.center(threshold_params.moduli()).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to center secret CRT: {:?}", e)) + })?; (secret_crt, secret_sss) } @@ -100,14 +115,14 @@ impl ShareComputationCircuitInput { } }; - Self { + Ok(Self { dkg_input_type, n_parties: committee.n as u32, threshold: committee.threshold as u32, secret, secret_sss, parity_matrix, - } + }) } } @@ -125,7 +140,8 @@ mod tests { BfvPreset::InsecureThreshold512, committee.clone(), DkgInputType::SecretKey, - ); + ) + .unwrap(); assert_eq!(sample.n_parties, committee.n as u32); assert_eq!(sample.threshold, committee.threshold as u32); assert_eq!(sample.dkg_input_type, DkgInputType::SecretKey); @@ -140,7 +156,8 @@ mod tests { BfvPreset::InsecureThreshold512, committee.clone(), DkgInputType::SmudgingNoise, - ); + ) + .unwrap(); assert_eq!(sample.n_parties, committee.n as u32); assert_eq!(sample.threshold, committee.threshold as u32); assert_eq!(sample.dkg_input_type, DkgInputType::SmudgingNoise); diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs index 4f4a13340c..ec3bcd3bf5 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/circuit.rs @@ -20,8 +20,8 @@ use fhe_math::rq::Poly; pub struct ShareEncryptionCircuit; impl Circuit for ShareEncryptionCircuit { - const NAME: &'static str = "dkg-share-encryption"; - const PREFIX: &'static str = "DKG_SHARE_ENCRYPTION"; + const NAME: &'static str = "share-encryption"; + const PREFIX: &'static str = "SHARE_ENCRYPTION"; const SUPPORTED_PARAMETER: ParameterType = ParameterType::DKG; /// None: circuit accepts runtime-varying input type (SecretKey or SmudgingNoise). const DKG_INPUT_TYPE: Option = None; diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs index d8b4130f87..6ec6af8dc4 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs @@ -203,7 +203,8 @@ mod tests { DkgInputType::SecretKey, sd.z, sd.lambda, - ); + ) + .unwrap(); let artifacts = ShareEncryptionCircuit .codegen(BfvPreset::InsecureThreshold512, &sample) .unwrap(); @@ -225,7 +226,8 @@ mod tests { DkgInputType::SecretKey, sd.z, sd.lambda, - ); + ) + .unwrap(); let artifacts = ShareEncryptionCircuit .codegen(BfvPreset::InsecureThreshold512, &sample) diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs index 074d4c70e8..fcd032ae2a 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs @@ -161,13 +161,13 @@ impl Computation for Configs { let q_mod_t = center(&BigInt::from(q_mod_t_uint), &t); let q_mod_t_mod_p = reduce(&q_mod_t, &p); - let k0is = compute_k0is(&moduli, dkg_params.plaintext())?; + let k0is = compute_k0is(&moduli, plaintext)?; let bounds = Bounds::compute(preset, input)?; let bits = Bits::compute(preset, &bounds)?; Ok(Configs { - t: dkg_params.plaintext() as usize, + t: plaintext as usize, q_mod_t: q_mod_t_mod_p, moduli, k0is, @@ -762,7 +762,8 @@ mod tests { DkgInputType::SecretKey, sd.z, sd.lambda, - ); + ) + .unwrap(); let bounds = Bounds::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); let bits = Bits::compute(BfvPreset::InsecureThreshold512, &bounds).unwrap(); @@ -784,7 +785,8 @@ mod tests { DkgInputType::SecretKey, sd.z, sd.lambda, - ); + ) + .unwrap(); let constants = Configs::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); let json = constants.to_json().unwrap(); @@ -808,7 +810,8 @@ mod tests { DkgInputType::SecretKey, sd.z, sd.lambda, - ); + ) + .unwrap(); let witness = Witness::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); // witness.message is plaintext coefficients (reversed, as used in circuit) diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs index d150c0515d..73d72343ae 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs @@ -28,15 +28,18 @@ impl ShareEncryptionCircuitInput { dkg_input_type: DkgInputType, num_ciphertexts: u128, // z in the search defaults lambda: u32, - ) -> Self { - let (threshold_params, dkg_params) = build_pair_for_preset(preset).unwrap(); + ) -> Result { + let (threshold_params, dkg_params) = build_pair_for_preset(preset).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)) + })?; let mut rng = thread_rng(); let dkg_secret_key = SecretKey::random(&dkg_params, &mut rng); let dkg_public_key = PublicKey::new(&dkg_secret_key, &mut rng); - let trbfv = TRBFV::new(committee.n, committee.threshold, threshold_params.clone()).unwrap(); + let trbfv = TRBFV::new(committee.n, committee.threshold, threshold_params.clone()) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to create TRBFV: {:?}", e)))?; let mut share_manager = ShareManager::new(committee.n, committee.threshold, threshold_params.clone()); @@ -46,11 +49,18 @@ impl ShareEncryptionCircuitInput { let sk_poly = share_manager .coeffs_to_poly_level0(threshold_secret_key.coeffs.clone().as_ref()) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!( + "Failed to convert secret key to poly: {:?}", + e + )) + })?; let sk_sss_u64 = share_manager .generate_secret_shares_from_poly(sk_poly.clone(), &mut rng) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to generate secret shares: {:?}", e)) + })?; sk_sss_u64[0].row(0).to_vec() } @@ -63,25 +73,36 @@ impl ShareEncryptionCircuitInput { e )) }) - .unwrap(); - let esi_poly = share_manager.bigints_to_poly(&esi_coeffs).unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!( + "Failed to generate smudging error: {:?}", + e + )) + })?; + let esi_poly = share_manager.bigints_to_poly(&esi_coeffs).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to convert error to poly: {:?}", e)) + })?; let esi_sss_u64 = share_manager .generate_secret_shares_from_poly(esi_poly.clone(), &mut rng.clone()) .map_err(|e| { CircuitsErrors::Sample(format!("Failed to generate error shares: {:?}", e)) }) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to generate error shares: {:?}", e)) + })?; esi_sss_u64[0].row(0).to_vec() } }; - let pt = Plaintext::try_encode(&share_row, Encoding::poly(), &dkg_params).unwrap(); + let pt = Plaintext::try_encode(&share_row, Encoding::poly(), &dkg_params) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to encode plaintext: {:?}", e)))?; - let (_ct, u_rns, e0_rns, e1_rns) = - dkg_public_key.try_encrypt_extended(&pt, &mut rng).unwrap(); + let (_ct, u_rns, e0_rns, e1_rns) = dkg_public_key + .try_encrypt_extended(&pt, &mut rng) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to encrypt extended: {:?}", e)))?; - ShareEncryptionCircuitInput { + Ok(ShareEncryptionCircuitInput { plaintext: pt, ciphertext: _ct, public_key: dkg_public_key, @@ -89,7 +110,7 @@ impl ShareEncryptionCircuitInput { u_rns, e0_rns, e1_rns, - } + }) } } @@ -109,7 +130,8 @@ mod tests { DkgInputType::SecretKey, sd.z, sd.lambda, - ); + ) + .unwrap(); assert_eq!(sample.public_key.c.c.len(), 2); assert_eq!( @@ -141,7 +163,8 @@ mod tests { DkgInputType::SmudgingNoise, sd.z, sd.lambda, - ); + ) + .unwrap(); assert_eq!(sample.public_key.c.c.len(), 2); assert_eq!(sample.ciphertext.c.len(), 2); diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs index c176ea6ecb..f20ad2379a 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/codegen.rs @@ -112,7 +112,8 @@ mod tests { fn test_codegen_with_sample() { let preset = BfvPreset::InsecureThreshold512; let committee = CiphernodesCommitteeSize::Small.values(); - let input = DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee); + let input = + DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee).unwrap(); let circuit = DecryptedSharesAggregationCircuit; let artifacts = circuit.codegen(preset, &input).unwrap(); diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs index d5f68eb87b..9a34198b28 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs @@ -410,7 +410,8 @@ mod tests { let preset = BfvPreset::InsecureThreshold512; let committee = CiphernodesCommitteeSize::Small.values(); let input = - DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee.clone()); + DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee.clone()) + .unwrap(); let out = DecryptedSharesAggregationCircuit::compute(preset, &input).unwrap(); diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs index 1e1c469adb..2e55192036 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs @@ -12,6 +12,7 @@ use crate::circuits::computation::Computation; use crate::threshold::decrypted_shares_aggregation::computation::Configs; +use crate::CircuitsErrors; use crate::{ threshold::decrypted_shares_aggregation::DecryptedSharesAggregationCircuitInput, CiphernodesCommittee, @@ -41,54 +42,84 @@ impl DecryptedSharesAggregationCircuitInput { /// Generates sample data for the decrypted shares aggregation circuit: /// TRBFV setup, parties with sk/pk shares and smudging error shares, share collection /// and aggregation, encryption of a message, T+1 decryption shares, and threshold decrypt. - pub fn generate_sample(preset: BfvPreset, committee: CiphernodesCommittee) -> Self { - let (threshold_params, _) = build_pair_for_preset(preset).unwrap(); - - let sd = preset.search_defaults().unwrap(); + pub fn generate_sample( + preset: BfvPreset, + committee: CiphernodesCommittee, + ) -> Result { + let (threshold_params, _) = build_pair_for_preset(preset).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)) + })?; + + let sd = preset + .search_defaults() + .ok_or_else(|| CircuitsErrors::Sample("Preset has no search defaults".into()))?; let num_parties = committee.n; let threshold = committee.threshold; let degree = threshold_params.degree(); let num_moduli = threshold_params.moduli().len(); - let trbfv = TRBFV::new(num_parties, threshold, threshold_params.clone()).unwrap(); + let trbfv = TRBFV::new(num_parties, threshold, threshold_params.clone()) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to create TRBFV: {:?}", e)))?; let mut rng = OsRng; let mut thread_rng = rand::thread_rng(); - let crp = CommonRandomPoly::new(&threshold_params, &mut rng).unwrap(); + let crp = CommonRandomPoly::new(&threshold_params, &mut rng) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to create CRP: {:?}", e)))?; let ctx = threshold_params.ctx_at_level(0).unwrap(); let mut parties: Vec = (0..num_parties) - .map(|_| { + .map(|_| -> Result { let sk_share = SecretKey::random(&threshold_params, &mut rng); - let pk_share = - PublicKeyShare::new(&sk_share, crp.clone(), &mut thread_rng).unwrap(); + let pk_share = PublicKeyShare::new(&sk_share, crp.clone(), &mut thread_rng) + .map_err(|e| { + CircuitsErrors::Sample(format!( + "Failed to create public key share: {:?}", + e + )) + })?; let mut share_manager = ShareManager::new(num_parties, threshold, threshold_params.clone()); let sk_poly = share_manager .coeffs_to_poly_level0(sk_share.coeffs.as_ref()) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!( + "Failed to convert secret key to poly: {:?}", + e + )) + })?; let sk_sss = share_manager .generate_secret_shares_from_poly(sk_poly, &mut rng) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to generate secret shares: {:?}", e)) + })?; let esi_coeffs = trbfv .generate_smudging_error(sd.z as usize, sd.lambda as usize, &mut rng) - .unwrap(); - let esi_poly = share_manager.bigints_to_poly(&esi_coeffs).unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!( + "Failed to generate smudging error: {:?}", + e + )) + })?; + let esi_poly = share_manager.bigints_to_poly(&esi_coeffs).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to convert error to poly: {:?}", e)) + })?; let esi_sss = share_manager .generate_secret_shares_from_poly(esi_poly, &mut rng) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to generate error shares: {:?}", e)) + })?; let sk_sss_collected = Vec::with_capacity(num_parties); let es_sss_collected = Vec::with_capacity(num_parties); let sk_poly_sum = Poly::zero(&ctx, Representation::PowerBasis); let es_poly_sum = Poly::zero(&ctx, Representation::PowerBasis); - Party { + Ok(Party { pk_share, sk_sss, esi_sss, @@ -96,9 +127,9 @@ impl DecryptedSharesAggregationCircuitInput { es_sss_collected, sk_poly_sum, es_poly_sum, - } + }) }) - .collect(); + .collect::, _>>()?; // Collect shares: for each party i, sk_sss_collected is one Array2 per sender j // (same as Vec). Each Array2 has shape (num_moduli, degree): row m = share from j for modulus m. @@ -114,11 +145,14 @@ impl DecryptedSharesAggregationCircuitInput { .collect::>() }) .collect(); - Array2::from_shape_vec((num_moduli, degree), data) - .map_err(|e| format!("sk_sss_collected shape: {:?}", e)) + Array2::from_shape_vec((num_moduli, degree), data).map_err(|e| { + CircuitsErrors::Sample(format!("sk_sss_collected shape: {:?}", e)) + }) }) .collect::, _>>() - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to collect sk_sss_collected: {:?}", e)) + })?; parties[i].es_sss_collected = (0..num_parties) .map(|j| { let data: Vec = (0..num_moduli) @@ -130,11 +164,14 @@ impl DecryptedSharesAggregationCircuitInput { .collect::>() }) .collect(); - Array2::from_shape_vec((num_moduli, degree), data) - .map_err(|e| format!("es_sss_collected shape: {:?}", e)) + Array2::from_shape_vec((num_moduli, degree), data).map_err(|e| { + CircuitsErrors::Sample(format!("es_sss_collected shape: {:?}", e)) + }) }) .collect::, _>>() - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to collect es_sss_collected: {:?}", e)) + })?; } // Aggregate collected shares to get sk_poly_sum and es_poly_sum per party @@ -142,10 +179,14 @@ impl DecryptedSharesAggregationCircuitInput { let share_manager = ShareManager::new(num_parties, threshold, threshold_params.clone()); party.sk_poly_sum = share_manager .aggregate_collected_shares(&party.sk_sss_collected) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to aggregate collected shares: {:?}", e)) + })?; party.es_poly_sum = share_manager .aggregate_collected_shares(&party.es_sss_collected) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to aggregate collected shares: {:?}", e)) + })?; } // Aggregate public key @@ -156,10 +197,13 @@ impl DecryptedSharesAggregationCircuitInput { .iter() .cloned() .aggregate() - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to aggregate public key: {:?}", e)) + })?; // Build message: max_msg_non_zero_coeffs from config, tiled from CRISP-style pattern, pad to degree - let configs = Configs::compute(preset, &()).unwrap(); + let configs = Configs::compute(preset, &()) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to compute configs: {:?}", e)))?; let n = configs.max_msg_non_zero_coeffs; let pattern: Vec = vec![ 2, 1, 5, 2, 1, 2, 3, 2, 4, 3, 3, 3, 2, 3, 3, 1, 2, 3, 4, 6, 1, 5, 1, 1, 2, 1, 2, @@ -167,8 +211,11 @@ impl DecryptedSharesAggregationCircuitInput { let mut message: Vec = (0..n).map(|i| pattern[i % pattern.len()]).collect(); message.resize(degree, 0); - let pt = Plaintext::try_encode(&message, Encoding::poly(), &threshold_params).unwrap(); - let ciphertext = public_key.try_encrypt(&pt, &mut thread_rng).unwrap(); + let pt = Plaintext::try_encode(&message, Encoding::poly(), &threshold_params) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to encode plaintext: {:?}", e)))?; + let ciphertext = public_key + .try_encrypt(&pt, &mut thread_rng) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to encrypt: {:?}", e)))?; let ciphertext = Arc::new(ciphertext); @@ -185,7 +232,9 @@ impl DecryptedSharesAggregationCircuitInput { party.sk_poly_sum.clone(), party.es_poly_sum.clone(), ) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to compute decryption share: {:?}", e)) + })?; d_share_polys.push(d_share); } @@ -199,16 +248,19 @@ impl DecryptedSharesAggregationCircuitInput { reconstructing_parties.clone(), Arc::clone(&ciphertext), ) - .unwrap(); + .map_err(|e| { + CircuitsErrors::Sample(format!("Failed to decrypt from shares: {:?}", e)) + })?; - let message_vec = Vec::::try_decode(&plaintext, Encoding::poly()).unwrap(); + let message_vec = Vec::::try_decode(&plaintext, Encoding::poly()) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to decode plaintext: {:?}", e)))?; - DecryptedSharesAggregationCircuitInput { + Ok(DecryptedSharesAggregationCircuitInput { committee, d_share_polys, reconstructing_parties, message_vec, - } + }) } } @@ -230,7 +282,8 @@ mod tests { let preset = BfvPreset::InsecureThreshold512; let committee = CiphernodesCommitteeSize::Small.values(); - let sample = DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee); + let sample = + DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee).unwrap(); let witness = Witness::compute(preset, &sample).unwrap(); assert_eq!( @@ -253,7 +306,8 @@ mod tests { use crate::threshold::decrypted_shares_aggregation::computation::Configs; let preset = BfvPreset::InsecureThreshold512; let committee = CiphernodesCommitteeSize::Small.values(); - let sample = DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee); + let sample = + DecryptedSharesAggregationCircuitInput::generate_sample(preset, committee).unwrap(); let witness = Witness::compute(preset, &sample).unwrap(); let configs = Configs::compute(preset, &()).unwrap(); let n = configs.max_msg_non_zero_coeffs; diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs index fbee84ad23..8a27945c2b 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs @@ -212,7 +212,8 @@ mod tests { #[test] fn test_toml_generation_and_structure() { let sample = - UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512); + UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512) + .unwrap(); let artifacts = UserDataEncryptionCircuit .codegen(BfvPreset::InsecureThreshold512, &sample) .unwrap(); diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/sample.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/sample.rs index 63b9785555..7d3eee2a4e 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/sample.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/sample.rs @@ -9,7 +9,9 @@ //! [`Sample`] produces a random BFV key pair and plaintext; the public key and plaintext are used as input //! for codegen and tests. -use crate::threshold::user_data_encryption::circuit::UserDataEncryptionCircuitInput; +use crate::{ + threshold::user_data_encryption::circuit::UserDataEncryptionCircuitInput, CircuitsErrors, +}; use e3_fhe_params::{build_pair_for_preset, BfvPreset}; use fhe::bfv::{Encoding, Plaintext, PublicKey, SecretKey}; use fhe_traits::FheEncoder; @@ -17,21 +19,23 @@ use rand::thread_rng; impl UserDataEncryptionCircuitInput { /// Generates a random secret key, public key, and plaintext for the given BFV parameters. - pub fn generate_sample(preset: BfvPreset) -> Self { - let (threshold_params, _) = build_pair_for_preset(preset).unwrap(); + pub fn generate_sample(preset: BfvPreset) -> Result { + let (threshold_params, _) = build_pair_for_preset(preset).map_err(|e| { + CircuitsErrors::Sample(format!("Failed to build pair for preset: {:?}", e)) + })?; let mut rng = thread_rng(); let secret_key = SecretKey::random(&threshold_params, &mut rng); let public_key = PublicKey::new(&secret_key, &mut rng); - let plaintext = - Plaintext::try_encode(&[1u64], Encoding::poly(), &threshold_params).unwrap(); + let plaintext = Plaintext::try_encode(&[1u64], Encoding::poly(), &threshold_params) + .map_err(|e| CircuitsErrors::Sample(format!("Failed to encode plaintext: {:?}", e)))?; - Self { + Ok(Self { public_key, plaintext, - } + }) } } @@ -43,7 +47,8 @@ mod tests { #[test] fn test_generate_sample() { let sample = - UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512); + UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512) + .unwrap(); assert_eq!(sample.public_key.c.c.len(), 2); assert_eq!( diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs index d289074aad..76718258a1 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs @@ -172,7 +172,8 @@ mod tests { fn test_bfv_public_key_to_greco() { let (threshold_params, _) = build_pair_for_preset(BfvPreset::InsecureThreshold512).unwrap(); let sample = - UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512); + UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512) + .unwrap(); let witness = Witness::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); @@ -190,7 +191,8 @@ mod tests { let (threshold_params, _) = build_pair_for_preset(BfvPreset::InsecureThreshold512).unwrap(); let sample = - UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512); + UserDataEncryptionCircuitInput::generate_sample(BfvPreset::InsecureThreshold512) + .unwrap(); let witness = Witness::compute(BfvPreset::InsecureThreshold512, &sample).unwrap(); diff --git a/crates/zk-helpers/src/utils.rs b/crates/zk-helpers/src/utils.rs index 89d430a626..e7d6df7b56 100644 --- a/crates/zk-helpers/src/utils.rs +++ b/crates/zk-helpers/src/utils.rs @@ -160,12 +160,23 @@ pub fn calculate_bit_width(bound: BigInt) -> u32 { /// The bit width of ring elements (coefficients bounded by the coefficient modulus) pub fn compute_modulus_bit(params: &BfvParameters) -> u32 { let moduli = params.moduli(); - let modulus = BigInt::from(moduli.iter().copied().max().unwrap()); + let modulus = BigInt::from(compute_max_modulus(moduli)); let bound = (modulus - BigInt::from(1)) / BigInt::from(2); calculate_bit_width(bound) } +/// Computes the maximum modulus from a vector of moduli. +/// +/// # Arguments +/// * `moduli` - Vector of moduli +/// +/// # Returns +/// The maximum modulus +pub fn compute_max_modulus(moduli: &[u64]) -> u64 { + moduli.iter().copied().max().unwrap() +} + /// Computes the bit width of the message. /// /// # Arguments diff --git a/crates/zk-prover/tests/local_e2e_tests.rs b/crates/zk-prover/tests/local_e2e_tests.rs index e51d2c4746..686e49c0a6 100644 --- a/crates/zk-prover/tests/local_e2e_tests.rs +++ b/crates/zk-prover/tests/local_e2e_tests.rs @@ -300,7 +300,7 @@ async fn test_pk_bfv_proof_generation() { .unwrap(); let preset = BfvPreset::InsecureThreshold512; - let sample = PkCircuitInput::generate_sample(preset); + let sample = PkCircuitInput::generate_sample(preset).unwrap(); let prover = ZkProver::new(&backend); let circuit = PkCircuit; @@ -342,7 +342,7 @@ async fn test_pk_bfv_proof_verification() { .unwrap(); let preset = BfvPreset::InsecureThreshold512; - let sample = PkCircuitInput::generate_sample(preset); + let sample = PkCircuitInput::generate_sample(preset).unwrap(); let prover = ZkProver::new(&backend); let circuit = PkCircuit; @@ -386,7 +386,7 @@ async fn test_pk_bfv_commitment_consistency() { .unwrap(); let preset = BfvPreset::InsecureThreshold512; - let sample = PkCircuitInput::generate_sample(preset); + let sample = PkCircuitInput::generate_sample(preset).unwrap(); let prover = ZkProver::new(&backend); let circuit = PkCircuit; From 0f57efa775ecd5563597938667b219f8d55d36cb Mon Sep 17 00:00:00 2001 From: Cedoor Date: Tue, 10 Feb 2026 16:29:02 +0100 Subject: [PATCH 3/3] fix(zk-helpers): remove duplicate map_err in share_encryption sample - Remove redundant .map_err() on generate_secret_shares_from_poly result - Error now converted to CircuitsErrors::Sample only once Co-authored-by: Cursor --- crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs index 73d72343ae..6bc307074a 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/sample.rs @@ -84,9 +84,6 @@ impl ShareEncryptionCircuitInput { })?; let esi_sss_u64 = share_manager .generate_secret_shares_from_poly(esi_poly.clone(), &mut rng.clone()) - .map_err(|e| { - CircuitsErrors::Sample(format!("Failed to generate error shares: {:?}", e)) - }) .map_err(|e| { CircuitsErrors::Sample(format!("Failed to generate error shares: {:?}", e)) })?;