diff --git a/Cargo.lock b/Cargo.lock index c7e628c25c..93a9a02ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3839,6 +3839,7 @@ dependencies = [ "nargo", "noirc_abi", "num-bigint", + "paste", "reqwest", "serde", "serde_json", diff --git a/crates/multithread/src/multithread.rs b/crates/multithread/src/multithread.rs index 74f15c7de4..97ef97a2e7 100644 --- a/crates/multithread/src/multithread.rs +++ b/crates/multithread/src/multithread.rs @@ -31,7 +31,7 @@ use e3_trbfv::gen_esi_sss::gen_esi_sss; use e3_trbfv::gen_pk_share_and_sk_sss::gen_pk_share_and_sk_sss; use e3_trbfv::{TrBFVError, TrBFVRequest, TrBFVResponse}; use e3_utils::SharedRng; -use e3_zk_helpers::circuits::dkg::pk::circuit::PkCircuit; +use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitInput}; use e3_zk_prover::{Provable, ZkBackend, ZkProver}; use fhe::bfv::PublicKey; use fhe_traits::DeserializeParametrized; @@ -357,6 +357,7 @@ fn handle_pk_bfv_proof( })?; let circuit = PkCircuit; + let circuit_input = PkCircuitInput { public_key: pk_bfv }; let e3_id_str = request.e3_id.to_string(); let preset_counterpart = req .params_preset @@ -365,7 +366,7 @@ fn handle_pk_bfv_proof( // But here we have to pass the InsecureThreshold512 preset because the underlaying witness generator // builds both params, but will only use the DKG one let proof = circuit - .prove(prover, &preset_counterpart, &pk_bfv, &e3_id_str) + .prove(prover, &preset_counterpart, &circuit_input, &e3_id_str) .map_err(|e| { ComputeRequestError::new( ComputeRequestErrorKind::Zk(ZkEventError::ProofGenerationFailed(e.to_string())), diff --git a/crates/zk-prover/Cargo.toml b/crates/zk-prover/Cargo.toml index 31f2967f31..3628916fd5 100644 --- a/crates/zk-prover/Cargo.toml +++ b/crates/zk-prover/Cargo.toml @@ -47,6 +47,7 @@ e3-data.workspace = true e3-utils.workspace = true [dev-dependencies] +paste = "1" tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } [features] diff --git a/crates/zk-prover/src/circuits/dkg/pk.rs b/crates/zk-prover/src/circuits/dkg/pk.rs index 417549840f..cf5ba53e02 100644 --- a/crates/zk-prover/src/circuits/dkg/pk.rs +++ b/crates/zk-prover/src/circuits/dkg/pk.rs @@ -12,12 +12,11 @@ use e3_fhe_params::BfvPreset; use e3_zk_helpers::circuits::dkg::pk::circuit::{PkCircuit, PkCircuitInput}; use e3_zk_helpers::circuits::dkg::pk::computation::Witness; use e3_zk_helpers::Computation; -use fhe::bfv::PublicKey; use noirc_abi::InputMap; impl Provable for PkCircuit { type Params = BfvPreset; - type Input = PublicKey; + type Input = PkCircuitInput; fn circuit(&self) -> CircuitName { CircuitName::PkBfv @@ -28,11 +27,7 @@ impl Provable for PkCircuit { preset: &Self::Params, input: &Self::Input, ) -> Result { - let circuit_input = PkCircuitInput { - public_key: input.clone(), - }; - - let witness = Witness::compute(preset.clone(), &circuit_input) + let witness = Witness::compute(preset.clone(), input) .map_err(|e| ZkError::WitnessGenerationFailed(e.to_string()))?; let mut inputs = InputMap::new(); diff --git a/crates/zk-prover/tests/local_e2e_tests.rs b/crates/zk-prover/tests/local_e2e_tests.rs index 686e49c0a6..d914c7f48e 100644 --- a/crates/zk-prover/tests/local_e2e_tests.rs +++ b/crates/zk-prover/tests/local_e2e_tests.rs @@ -6,6 +6,9 @@ //! Local end-to-end tests that require a local bb binary. //! These tests will be skipped if bb is not found on the system. +//! +//! To add a new circuit: add setup_*_test() and one line in `e2e_proof_tests!`. +//! Commitment consistency tests are defined separately. mod common; @@ -78,148 +81,126 @@ async fn setup_test_prover(bb: &PathBuf) -> (ZkBackend, tempfile::TempDir) { (backend, temp) } -#[tokio::test] -async fn test_pk_generation_proof_generation() { - let bb = match find_bb().await { - Some(p) => p, - None => { - println!("skipping: bb not found"); - return; - } - }; - - let (backend, _temp) = setup_test_prover(&bb).await; +async fn setup_circuit_fixtures(backend: &ZkBackend, circuit_path: &[&str], fixture_name: &str) { + let circuit_dir = circuit_path + .iter() + .fold(backend.circuits_dir.clone(), |p, seg| p.join(seg)); let fixtures = fixtures_dir(); - - let circuit_dir = backend.circuits_dir.join("threshold").join("pk_generation"); fs::create_dir_all(&circuit_dir).await.unwrap(); fs::copy( - fixtures.join("pk_generation.json"), - circuit_dir.join("pk_generation.json"), + fixtures.join(format!("{fixture_name}.json")), + circuit_dir.join(format!("{fixture_name}.json")), ) .await .unwrap(); fs::copy( - fixtures.join("pk_generation.vk"), - circuit_dir.join("pk_generation.vk"), + fixtures.join(format!("{fixture_name}.vk")), + circuit_dir.join(format!("{fixture_name}.vk")), ) .await .unwrap(); +} +async fn setup_pk_generation_test() -> Option<( + ZkBackend, + tempfile::TempDir, + ZkProver, + PkGenerationCircuit, + PkGenerationCircuitInput, + BfvPreset, + &'static str, +)> { + let committee = CiphernodesCommitteeSize::Small.values(); let preset = BfvPreset::InsecureThreshold512; + let bb = find_bb().await?; + let (backend, temp) = setup_test_prover(&bb).await; - let sample = - PkGenerationCircuitInput::generate_sample(preset, CiphernodesCommitteeSize::Small.values()) - .unwrap(); + setup_circuit_fixtures(&backend, &["threshold", "pk_generation"], "pk_generation").await; + let sample = PkGenerationCircuitInput::generate_sample(preset, committee).ok()?; let prover = ZkProver::new(&backend); - let circuit = PkGenerationCircuit; - let e3_id = "0"; - - let proof = circuit - .prove(&prover, &preset, &sample, e3_id) - .expect("proof generation should succeed"); - - assert!(!proof.data.is_empty(), "proof data should not be empty"); - assert!( - !proof.public_signals.is_empty(), - "public signals should not be empty" - ); - prover.cleanup(e3_id).unwrap(); + Some(( + backend, + temp, + prover, + PkGenerationCircuit, + sample, + preset, + "0", + )) } -#[tokio::test] -async fn test_pk_generation_proof_verification() { - let bb = match find_bb().await { - Some(p) => p, - None => { - println!("skipping: bb not found"); - return; - } - }; - - let (backend, _temp) = setup_test_prover(&bb).await; - let fixtures = fixtures_dir(); - - let circuit_dir = backend.circuits_dir.join("threshold").join("pk_generation"); - fs::create_dir_all(&circuit_dir).await.unwrap(); - fs::copy( - fixtures.join("pk_generation.json"), - circuit_dir.join("pk_generation.json"), - ) - .await - .unwrap(); - fs::copy( - fixtures.join("pk_generation.vk"), - circuit_dir.join("pk_generation.vk"), - ) - .await - .unwrap(); - +async fn setup_pk_bfv_test() -> Option<( + ZkBackend, + tempfile::TempDir, + ZkProver, + PkCircuit, + PkCircuitInput, + BfvPreset, + &'static str, +)> { let preset = BfvPreset::InsecureThreshold512; + let bb = find_bb().await?; + let (backend, temp) = setup_test_prover(&bb).await; - let sample = - PkGenerationCircuitInput::generate_sample(preset, CiphernodesCommitteeSize::Small.values()) - .unwrap(); + setup_circuit_fixtures(&backend, &["dkg", "pk"], "pk").await; + let sample = PkCircuitInput::generate_sample(preset).ok()?; let prover = ZkProver::new(&backend); - let circuit = PkGenerationCircuit; - let e3_id = "0"; - let proof = circuit - .prove(&prover, &preset, &sample, e3_id) - .expect("proof generation should succeed"); + Some((backend, temp, prover, PkCircuit, sample, preset, "0")) +} - let party_id = 1; - let verification_result = circuit.verify(&prover, &proof, e3_id, party_id); - assert!( - verification_result.as_ref().is_ok_and(|&v| v), - "Proof verification failed: {:?}", - verification_result - ); +macro_rules! e2e_proof_tests { + ($(($name:ident, $setup:expr)),* $(,)?) => { + $( + paste::paste! { + #[tokio::test] + async fn []() { + let Some((_backend, _temp, prover, circuit, sample, preset, e3_id)) = + $setup.await + else { + println!("skipping: bb not found"); + return; + }; + + let proof = circuit + .prove(&prover, &preset, &sample, e3_id) + .expect("proof generation should succeed"); + + assert!(!proof.data.is_empty(), "proof data should not be empty"); + assert!(!proof.public_signals.is_empty(), "public signals should not be empty"); + + let party_id = 1; + let verification_result = circuit.verify(&prover, &proof, e3_id, party_id); + assert!( + verification_result.as_ref().is_ok_and(|&v| v), + "Proof verification failed: {:?}", + verification_result + ); + + prover.cleanup(e3_id).unwrap(); + } + } + )* + }; +} - prover.cleanup(e3_id).unwrap(); +e2e_proof_tests! { + (pk_generation, setup_pk_generation_test()), + (pk_bfv, setup_pk_bfv_test()), } #[tokio::test] async fn test_pk_generation_commitment_consistency() { - let bb = match find_bb().await { - Some(p) => p, - None => { - println!("skipping: bb not found"); - return; - } + let Some((_backend, _temp, prover, circuit, sample, preset, e3_id)) = + setup_pk_generation_test().await + else { + println!("skipping: bb not found"); + return; }; - let (backend, _temp) = setup_test_prover(&bb).await; - let fixtures = fixtures_dir(); - - let circuit_dir = backend.circuits_dir.join("threshold").join("pk_generation"); - fs::create_dir_all(&circuit_dir).await.unwrap(); - fs::copy( - fixtures.join("pk_generation.json"), - circuit_dir.join("pk_generation.json"), - ) - .await - .unwrap(); - fs::copy( - fixtures.join("pk_generation.vk"), - circuit_dir.join("pk_generation.vk"), - ) - .await - .unwrap(); - - let preset = BfvPreset::InsecureThreshold512; - - let sample = - PkGenerationCircuitInput::generate_sample(preset, CiphernodesCommitteeSize::Small.values()) - .unwrap(); - - let prover = ZkProver::new(&backend); - let circuit = PkGenerationCircuit; - let e3_id = "0"; - let proof = circuit .prove(&prover, &preset, &sample, e3_id) .expect("proof generation should succeed"); @@ -233,7 +214,7 @@ async fn test_pk_generation_commitment_consistency() { assert_eq!(total_fields, 1027); // The 3 commitments are the last 3 field elements - let offset = (total_fields - 3) * field_size; // 1024 * 64 = 65536 + let offset = (total_fields - 3) * field_size; let sk_commitment_from_proof = BigInt::from_bytes_be(Sign::Plus, &signals[offset..offset + field_size]); @@ -277,123 +258,16 @@ async fn test_pk_generation_commitment_consistency() { prover.cleanup(e3_id).unwrap(); } -#[tokio::test] -async fn test_pk_bfv_proof_generation() { - let bb = match find_bb().await { - Some(p) => p, - None => { - println!("skipping: bb not found"); - return; - } - }; - - let (backend, _temp) = setup_test_prover(&bb).await; - let fixtures = fixtures_dir(); - - let circuit_dir = backend.circuits_dir.join("dkg").join("pk"); - fs::create_dir_all(&circuit_dir).await.unwrap(); - fs::copy(fixtures.join("pk.json"), circuit_dir.join("pk.json")) - .await - .unwrap(); - fs::copy(fixtures.join("pk.vk"), circuit_dir.join("pk.vk")) - .await - .unwrap(); - - let preset = BfvPreset::InsecureThreshold512; - let sample = PkCircuitInput::generate_sample(preset).unwrap(); - - let prover = ZkProver::new(&backend); - let circuit = PkCircuit; - let e3_id = "test-pk-bfv-001"; - - let proof = circuit - .prove(&prover, &preset, &sample.public_key, e3_id) - .expect("proof generation should succeed"); - - assert!(!proof.data.is_empty(), "proof data should not be empty"); - assert!( - !proof.public_signals.is_empty(), - "public signals should not be empty" - ); - - prover.cleanup(e3_id).unwrap(); -} - -#[tokio::test] -async fn test_pk_bfv_proof_verification() { - let bb = match find_bb().await { - Some(p) => p, - None => { - println!("skipping: bb not found"); - return; - } - }; - - let (backend, _temp) = setup_test_prover(&bb).await; - let fixtures = fixtures_dir(); - - let circuit_dir = backend.circuits_dir.join("dkg").join("pk"); - fs::create_dir_all(&circuit_dir).await.unwrap(); - fs::copy(fixtures.join("pk.json"), circuit_dir.join("pk.json")) - .await - .unwrap(); - fs::copy(fixtures.join("pk.vk"), circuit_dir.join("pk.vk")) - .await - .unwrap(); - - let preset = BfvPreset::InsecureThreshold512; - let sample = PkCircuitInput::generate_sample(preset).unwrap(); - - let prover = ZkProver::new(&backend); - let circuit = PkCircuit; - let e3_id = "test-verify-001"; - - let proof = circuit - .prove(&prover, &preset, &sample.public_key, e3_id) - .expect("proof generation should succeed"); - - let party_id = 1; - let verification_result = circuit.verify(&prover, &proof, e3_id, party_id); - assert!( - verification_result.as_ref().is_ok_and(|&v| v), - "Proof verification failed: {:?}", - verification_result - ); - - prover.cleanup(e3_id).unwrap(); -} - #[tokio::test] async fn test_pk_bfv_commitment_consistency() { - let bb = match find_bb().await { - Some(p) => p, - None => { - println!("skipping: bb not found"); - return; - } + let Some((_backend, _temp, prover, circuit, sample, preset, e3_id)) = setup_pk_bfv_test().await + else { + println!("skipping: bb not found"); + return; }; - let (backend, _temp) = setup_test_prover(&bb).await; - let fixtures = fixtures_dir(); - - let circuit_dir = backend.circuits_dir.join("dkg").join("pk"); - fs::create_dir_all(&circuit_dir).await.unwrap(); - fs::copy(fixtures.join("pk.json"), circuit_dir.join("pk.json")) - .await - .unwrap(); - fs::copy(fixtures.join("pk.vk"), circuit_dir.join("pk.vk")) - .await - .unwrap(); - - let preset = BfvPreset::InsecureThreshold512; - let sample = PkCircuitInput::generate_sample(preset).unwrap(); - - let prover = ZkProver::new(&backend); - let circuit = PkCircuit; - let e3_id = "test-commitment-001"; - let proof = circuit - .prove(&prover, &preset, &sample.public_key, e3_id) + .prove(&prover, &preset, &sample, e3_id) .expect("proof generation should succeed"); // Verify the commitment from the proof is a valid field element @@ -405,11 +279,8 @@ async fn test_pk_bfv_commitment_consistency() { ); // Compute the commitment independently to ensure consistency - let circuit_input = PkCircuitInput { - public_key: sample.public_key.clone(), - }; let computation_output = - PkCircuit::compute(preset, &circuit_input).expect("computation should succeed"); + PkCircuit::compute(preset, &sample).expect("computation should succeed"); let commitment_calculated = compute_dkg_pk_commitment( &computation_output.witness.pk0is, &computation_output.witness.pk1is,