diff --git a/Cargo.lock b/Cargo.lock index 186650d78f..2aa6f62c55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3345,38 +3345,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "e3-pvss" -version = "0.1.8" -dependencies = [ - "anyhow", - "e3-fhe-params", - "e3-polynomial 0.1.8", - "e3-zk-helpers", - "fhe", - "fhe-math", - "itertools 0.14.0", - "num-bigint", - "num-traits", - "rand 0.8.5", - "rayon", - "serde", - "serde_json", - "tempfile", - "thiserror 1.0.69", - "toml", -] - -[[package]] -name = "e3-pvss-cli" -version = "0.1.8" -dependencies = [ - "anyhow", - "clap", - "e3-fhe-params", - "e3-pvss", -] - [[package]] name = "e3-request" version = "0.1.8" @@ -3608,14 +3576,25 @@ dependencies = [ name = "e3-zk-helpers" version = "0.1.8" dependencies = [ + "anyhow", "ark-bn254 0.5.0", "ark-ff 0.5.0", + "clap", + "e3-fhe-params", "e3-polynomial 0.1.8", "e3-safe 0.1.8", "fhe", + "fhe-math", + "itertools 0.14.0", "num-bigint", "num-traits", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", + "tempfile", "thiserror 1.0.69", + "toml", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b2c13fd745..b53155d9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,8 +38,6 @@ members = [ "crates/parity-matrix", "crates/polynomial", "crates/zk-helpers", - "crates/pvss", - "crates/pvss-cli" ] exclude = [ "examples/CRISP", @@ -108,7 +106,6 @@ e3-trbfv = { version = "0.1.8", path = "./crates/trbfv" } e3-utils = { version = "0.1.8", path = "./crates/utils" } e3-safe = { version = "0.1.8", path = "./crates/safe" } e3-zk-helpers = { version = "0.1.8", path = "./crates/zk-helpers" } -e3-pvss = { version = "0.1.8", path = "./crates/pvss" } actix = "=0.13.5" actix-web = "=4.11.0" diff --git a/circuits/lib/Nargo.toml b/circuits/lib/Nargo.toml index 5602a4b52b..aafc1d80ce 100644 --- a/circuits/lib/Nargo.toml +++ b/circuits/lib/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "pvss_lib" +name = "lib" type = "lib" authors = ["Gnosis Guild / Enclave"] version = "1.0.0-beta.15" diff --git a/crates/Dockerfile b/crates/Dockerfile index 10a6c49ad5..5c4155b3ea 100644 --- a/crates/Dockerfile +++ b/crates/Dockerfile @@ -81,8 +81,6 @@ COPY crates/trbfv/Cargo.toml ./trbfv/Cargo.toml COPY crates/utils/Cargo.toml ./utils/Cargo.toml COPY crates/wasm/Cargo.toml ./wasm/Cargo.toml COPY crates/zk-helpers/Cargo.toml ./zk-helpers/Cargo.toml -COPY crates/pvss/Cargo.toml ./pvss/Cargo.toml -COPY crates/pvss-cli/Cargo.toml ./pvss-cli/Cargo.toml RUN echo 'fn main() { println!("cargo:warning=dependency cache build"); }' > ./entrypoint/build.rs RUN echo 'fn main() { println!("cargo:warning=dependency cache build"); }' > ./cli/build.rs @@ -98,7 +96,8 @@ RUN for d in ./*/ ; do \ fi \ done -# Stub binary so first build (dependency cache) succeeds +# Crates with [[bin]] in Cargo.toml need stub binaries so the first build succeeds +RUN mkdir -p ./zk-helpers/src/bin && echo 'fn main() {}' > ./zk-helpers/src/bin/zk_cli.rs RUN mkdir -p ./fhe-params/src/bin && echo 'fn main() {}' > ./fhe-params/src/bin/search_params.rs RUN cargo build --locked --release diff --git a/crates/pvss-cli/Cargo.toml b/crates/pvss-cli/Cargo.toml deleted file mode 100644 index 46b892b552..0000000000 --- a/crates/pvss-cli/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "e3-pvss-cli" -version.workspace = true -edition.workspace = true -license.workspace = true -description = "PVSS CLI for artifact generation" -repository = "https://github.com/gnosisguild/enclave/crates/pvss-cli" - -[dependencies] -e3-pvss = { workspace = true } -e3-fhe-params = { workspace = true } -clap = { workspace = true } -anyhow = { workspace = true } diff --git a/crates/pvss/Cargo.toml b/crates/pvss/Cargo.toml deleted file mode 100644 index 1246c23752..0000000000 --- a/crates/pvss/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "e3-pvss" -version.workspace = true -edition.workspace = true -license.workspace = true -description = "PVSS core codegen and computation crate" -repository = "https://github.com/gnosisguild/enclave/crates/pvss" - -[dependencies] -e3-polynomial = { workspace = true } -fhe = { workspace = true } -fhe-math = { workspace = true } -e3-fhe-params = { workspace = true } -e3-zk-helpers = { workspace = true } -num-bigint = { workspace = true } -num-traits = { workspace = true } -rand = { workspace = true } -rayon = { workspace = true } -itertools = "0.14.0" -serde = { workspace = true } -serde_json = { workspace = true } -toml = "0.8.23" -anyhow = { workspace = true } -tempfile = { workspace = true } -thiserror = { workspace = true } diff --git a/crates/pvss/src/circuits/mod.rs b/crates/pvss/src/circuits/mod.rs deleted file mode 100644 index 78aa5fbb5d..0000000000 --- a/crates/pvss/src/circuits/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -pub mod pk_bfv; diff --git a/crates/pvss/src/circuits/pk_bfv/circuit.rs b/crates/pvss/src/circuits/pk_bfv/circuit.rs deleted file mode 100644 index 8b6d7a4bac..0000000000 --- a/crates/pvss/src/circuits/pk_bfv/circuit.rs +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use crate::circuits::pk_bfv::codegen; -use crate::circuits::pk_bfv::computation::{Bits, Bounds, Witness}; -use crate::errors::CodegenError; -use crate::traits::{Circuit, CircuitCodegen, CircuitComputation, Computation}; -use crate::types::{Artifacts, DkgInputType}; -use e3_fhe_params::{BfvPreset, ParameterType}; -use fhe::bfv::{BfvParameters, PublicKey}; - -#[derive(Debug)] -pub struct PkBfvCircuit; - -#[derive(Debug)] -pub struct PkBfvComputationOutput { - pub bounds: Bounds, - pub bits: Bits, - pub witness: Witness, -} - -#[derive(Debug, Clone)] -pub struct PkBfvCodegenInput { - pub preset: BfvPreset, - pub public_key: PublicKey, -} - -impl Circuit for PkBfvCircuit { - const NAME: &'static str = "pk-bfv"; - const PREFIX: &'static str = "PK_BFV"; - const SUPPORTED_PARAMETER: ParameterType = ParameterType::DKG; - const DKG_INPUT_TYPE: Option = None; - const N_PROOFS: usize = 1; - const N_PUBLIC_INPUTS: usize = 1; -} - -impl CircuitCodegen for PkBfvCircuit { - type Input = PkBfvCodegenInput; - type Error = CodegenError; - - fn codegen(&self, input: Self::Input) -> Result { - codegen::codegen(input.preset, input.public_key) - } -} - -impl CircuitComputation for PkBfvCircuit { - type Params = BfvParameters; - type Input = PublicKey; - type Output = PkBfvComputationOutput; - type Error = CodegenError; - - fn compute( - &self, - params: &Self::Params, - input: &Self::Input, - ) -> Result { - let bounds = Bounds::compute(params, &())?; - let bits = Bits::compute(params, &bounds)?; - let witness = Witness::compute(params, input)?; - - Ok(PkBfvComputationOutput { - bounds, - bits, - witness, - }) - } -} diff --git a/crates/pvss/src/circuits/pk_bfv/mod.rs b/crates/pvss/src/circuits/pk_bfv/mod.rs deleted file mode 100644 index 63504b9145..0000000000 --- a/crates/pvss/src/circuits/pk_bfv/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -pub mod circuit; -pub mod codegen; -pub mod computation; diff --git a/crates/pvss/src/lib.rs b/crates/pvss/src/lib.rs deleted file mode 100644 index 9be7fb848e..0000000000 --- a/crates/pvss/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -pub mod circuits; -pub mod errors; -pub mod registry; -pub mod sample; -pub mod traits; -pub mod types; -pub mod utils; diff --git a/crates/pvss/src/sample.rs b/crates/pvss/src/sample.rs deleted file mode 100644 index 15a1992f1a..0000000000 --- a/crates/pvss/src/sample.rs +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use crate::types::Sample; -use fhe::bfv::{BfvParameters, PublicKey, SecretKey}; -use rand::thread_rng; -use std::sync::Arc; - -pub fn generate_sample(params: &Arc) -> Sample { - let mut rng = thread_rng(); - - let secret_key = SecretKey::random(¶ms, &mut rng); - let public_key = PublicKey::new(&secret_key, &mut rng); - - Sample { public_key } -} - -#[cfg(test)] -mod tests { - use super::*; - use e3_fhe_params::BfvParamSet; - use e3_fhe_params::DEFAULT_BFV_PRESET; - - #[test] - fn test_generate_sample() { - let params = BfvParamSet::from(DEFAULT_BFV_PRESET).build_arc(); - let sample = generate_sample(¶ms); - - assert_eq!(sample.public_key.c.c.len(), 2); - } -} diff --git a/crates/pvss/src/traits.rs b/crates/pvss/src/traits.rs deleted file mode 100644 index 78fa9dfaf1..0000000000 --- a/crates/pvss/src/traits.rs +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -//! Common computation behavior across circuits. - -use crate::types::DkgInputType; -use e3_fhe_params::ParameterType; -use serde_json::Value; - -pub trait Computation: Sized { - type Params; - type Input; - type Error; - - fn compute(params: &Self::Params, input: &Self::Input) -> Result; -} - -pub trait ConvertToJson { - fn convert_to_json(&self) -> serde_json::Result; -} - -pub trait ReduceToZkpModulus: Sized { - fn reduce_to_zkp_modulus(&self) -> Self; -} - -pub trait Circuit: Send + Sync { - const NAME: &'static str; - const PREFIX: &'static str; - const SUPPORTED_PARAMETER: ParameterType; - const DKG_INPUT_TYPE: Option; - const N_PROOFS: usize; - const N_PUBLIC_INPUTS: usize; - - fn name(&self) -> &'static str { - Self::NAME - } - - fn prefix(&self) -> &'static str { - Self::PREFIX - } - - fn supported_parameter(&self) -> ParameterType { - Self::SUPPORTED_PARAMETER - } - - fn dkg_input_type(&self) -> Option { - Self::DKG_INPUT_TYPE - } - - fn n_recursive_proofs(&self) -> usize { - Self::N_PROOFS - } - - fn n_public_inputs(&self) -> usize { - Self::N_PUBLIC_INPUTS - } -} - -pub trait CircuitMetadata: Send + Sync { - fn name(&self) -> &'static str; - fn supported_parameter(&self) -> ParameterType; - fn dkg_input_type(&self) -> Option; - fn n_recursive_proofs(&self) -> usize; - fn n_public_inputs(&self) -> usize; -} - -impl CircuitMetadata for T { - fn name(&self) -> &'static str { - T::NAME - } - - fn supported_parameter(&self) -> ParameterType { - T::SUPPORTED_PARAMETER - } - - fn dkg_input_type(&self) -> Option { - T::DKG_INPUT_TYPE - } - - fn n_recursive_proofs(&self) -> usize { - T::N_PROOFS - } - - fn n_public_inputs(&self) -> usize { - T::N_PUBLIC_INPUTS - } -} - -pub trait CircuitCodegen: Circuit { - type Input; - type Error; - - /// Generate artifacts for a circuit. - fn codegen(&self, input: Self::Input) -> Result; -} - -pub trait CircuitComputation: Circuit { - type Params; - type Input; - type Output; - type Error; - - /// Compute circuit-specific data. - fn compute( - &self, - params: &Self::Params, - input: &Self::Input, - ) -> Result; -} diff --git a/crates/pvss/src/utils.rs b/crates/pvss/src/utils.rs deleted file mode 100644 index 652855e5d3..0000000000 --- a/crates/pvss/src/utils.rs +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. - -use crate::errors::CodegenError; -use crate::types::{Configs, SecurityLevel, Template, Toml, Wrapper}; -use e3_zk_helpers::utils::to_string_1d_vec; -use num_bigint::BigInt; -use serde_json; -use std::path::Path; - -pub fn map_witness_2d_vector_to_json(values: &Vec>) -> Vec { - values - .iter() - .map(|value| { - serde_json::json!({ - "coefficients": to_string_1d_vec(value) - }) - }) - .collect() -} - -pub fn get_security_level(lambda: usize) -> SecurityLevel { - if lambda < 80 { - SecurityLevel::INSECURE - } else { - SecurityLevel::PRODUCTION - } -} - -pub fn generate_wrapper(n_recursive_proofs: usize, n_public_inputs: usize) -> Wrapper { - format!( - r#"use bb_proof_verification::{{UltraHonkProof, UltraHonkVerificationKey, verify_ultrahonk_proof}}; -use lib::math::commitments::compute_recursive_aggregation_commitment; - -// Number of proofs. -pub global N_PROOFS: u32 = {}; -/// Number of public inputs/outputs per proof. -pub global N_PUBLIC_INPUTS: u32 = {}; - -fn main( - verification_key: UltraHonkVerificationKey, - proofs: [UltraHonkProof; N_PROOFS], - public_inputs: pub [[Field; N_PUBLIC_INPUTS]; N_PROOFS], - key_hash: Field, -) -> pub Field {{ - for i in 0..N_PROOFS {{ - verify_ultrahonk_proof(verification_key, proofs[i], public_inputs[i], key_hash); - }} - - let mut aggregated_public_inputs = Vec::new(); - - for i in 0..N_PROOFS {{ - for j in 0..N_PUBLIC_INPUTS {{ - aggregated_public_inputs.push(public_inputs[i][j]); - }} - }} - - compute_recursive_aggregation_commitment(aggregated_public_inputs) -}} -"#, - n_recursive_proofs, n_public_inputs - ) -} - -pub fn write_toml(toml: &Toml, path: Option<&Path>) -> Result<(), CodegenError> { - let toml_path = path.unwrap_or_else(|| Path::new(".")); - let toml_path = toml_path.join("Prover.toml"); - Ok(std::fs::write(toml_path, toml)?) -} - -pub fn write_template(template: &Template, path: Option<&Path>) -> Result<(), CodegenError> { - let template_path = path.unwrap_or_else(|| Path::new(".")); - let template_path = template_path.join("main.nr"); - Ok(std::fs::write(template_path, template)?) -} - -pub fn write_configs(configs: &Configs, path: Option<&Path>) -> Result<(), CodegenError> { - let configs_path = path.unwrap_or_else(|| Path::new(".")); - let configs_path = configs_path.join("configs.nr"); - Ok(std::fs::write(configs_path, configs)?) -} - -pub fn write_wrapper(wrapper: &Wrapper, path: Option<&Path>) -> Result<(), CodegenError> { - let wrapper_path = path.unwrap_or_else(|| Path::new(".")); - let wrapper_path = wrapper_path.join("wrapper.nr"); - Ok(std::fs::write(wrapper_path, wrapper)?) -} diff --git a/crates/zk-helpers/Cargo.toml b/crates/zk-helpers/Cargo.toml index da4bd1a440..bedddc62c6 100644 --- a/crates/zk-helpers/Cargo.toml +++ b/crates/zk-helpers/Cargo.toml @@ -7,11 +7,28 @@ description = "ZK circuit helpers" repository = "https://github.com/gnosisguild/enclave/crates/zk-helpers" [dependencies] +anyhow = { workspace = true } ark-bn254 = { workspace = true } +clap = { workspace = true } ark-ff = { workspace = true } e3-polynomial = { workspace = true } e3-safe = { workspace = true } +e3-fhe-params = { workspace = true } fhe = { workspace = true } +fhe-math = { workspace = true } num-bigint = { workspace = true } num-traits = { workspace = true } thiserror = { workspace = true } +rand = { workspace = true } +rayon = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +toml = "0.8.23" +itertools = "0.14.0" + +[dev-dependencies] +tempfile = { workspace = true } + +[[bin]] +name = "zk_cli" +path = "src/bin/zk_cli.rs" \ No newline at end of file diff --git a/crates/zk-helpers/README.md b/crates/zk-helpers/README.md new file mode 100644 index 0000000000..b34559a6ac --- /dev/null +++ b/crates/zk-helpers/README.md @@ -0,0 +1,26 @@ +# zk-helpers + +ZK circuit artifact generation for the Noir prover. + +## zk-cli + +Generate `Prover.toml` and `configs.nr` for a circuit. + +```bash +# List circuits +cargo run -p e3-zk-helpers --bin zk_cli -- --list_circuits + +# Generate artifacts (default: output/) +cargo run -p e3-zk-helpers --bin zk_cli -- --circuit pk-bfv --preset default + +# Custom output dir; skip Prover.toml (only configs.nr) +cargo run -p e3-zk-helpers --bin zk_cli -- --circuit pk-bfv --preset default --output ./artifacts --toml +``` + +| Flag | Description | +| ------------------ | -------------------------------------------------- | +| `--list_circuits` | List circuits and exit | +| `--circuit ` | Circuit (e.g. `pk-bfv`) | +| `--preset ` | BFV preset (must match circuit) | +| `--output ` | Output dir (default: `output`) | +| `--toml` | Skip writing Prover.toml; always writes configs.nr | diff --git a/crates/pvss-cli/src/main.rs b/crates/zk-helpers/src/bin/zk_cli.rs similarity index 70% rename from crates/pvss-cli/src/main.rs rename to crates/zk-helpers/src/bin/zk_cli.rs index 6f1204e70d..b22e8473a9 100644 --- a/crates/pvss-cli/src/main.rs +++ b/crates/zk-helpers/src/bin/zk_cli.rs @@ -4,21 +4,25 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +//! ZK CLI — command-line tool for zero-knowledge circuit artifact generation. +//! +//! This binary lists available circuits and generates Prover.toml and configs.nr +//! for use with the Noir prover. Use `--list_circuits` to see circuits and +//! `--circuit --preset ` to generate artifacts. + use anyhow::{anyhow, Context, Result}; -use clap::Parser; +use clap::{arg, command, Parser}; use e3_fhe_params::{BfvParamSet, BfvPreset}; -use e3_pvss::circuits::pk_bfv::circuit::{PkBfvCircuit, PkBfvCodegenInput}; -use e3_pvss::circuits::pk_bfv::codegen::write_artifacts; -use e3_pvss::registry::CircuitRegistry; -use e3_pvss::sample; -use e3_pvss::traits::Circuit; -use e3_pvss::traits::CircuitCodegen; +use e3_zk_helpers::circuits::pk_bfv::circuit::{PkBfvCircuit, PkBfvCircuitInput}; +use e3_zk_helpers::codegen::{write_artifacts, CircuitCodegen}; +use e3_zk_helpers::registry::{Circuit, CircuitRegistry}; +use e3_zk_helpers::sample::Sample; use std::path::PathBuf; use std::sync::Arc; -/// Minimal PVSS CLI for generating circuit artifacts. +/// Minimal ZK CLI for generating circuit artifacts. #[derive(Debug, Parser)] -#[command(name = "pvss-cli")] +#[command(name = "zk-cli")] struct Cli { /// List all available circuits and exit. #[arg(long)] @@ -32,9 +36,13 @@ struct Cli { /// Output directory for generated artifacts. #[arg(long, default_value = "output")] output: PathBuf, + /// Skip generating Prover.toml (configs.nr is always generated). + #[arg(long)] + toml: bool, } -/// Parse a preset name into a BFV preset. +/// Parses a preset name (e.g. `"default"`) into a [`BfvPreset`]. +/// Returns an error listing available presets if the name is unknown. fn parse_preset(name: &str) -> Result { BfvPreset::from_name(name).map_err(|_| { let available = BfvPreset::list().join(", "); @@ -56,11 +64,9 @@ fn main() -> Result<()> { for circuit_name in circuits { if let Ok(circuit_meta) = registry.get(&circuit_name) { println!( - " {} - params_type: {:?}, n_recursive_proofs: {}, pub_inputs: {}", + " {} - params_type: {:?}", circuit_name, circuit_meta.supported_parameter(), - circuit_meta.n_recursive_proofs(), - circuit_meta.n_public_inputs() ); } } @@ -94,26 +100,27 @@ fn main() -> Result<()> { // Generate artifacts based on circuit name from registry. let params = BfvParamSet::from(preset).build_arc(); - let sample = sample::generate_sample(¶ms); + let sample = Sample::generate(¶ms); let circuit_name = circuit_meta.name(); let artifacts = match circuit_name { name if name == ::NAME => { let circuit = PkBfvCircuit; - circuit.codegen(PkBfvCodegenInput { - preset, - public_key: sample.public_key, - })? + circuit.codegen( + ¶ms, + &PkBfvCircuitInput { + public_key: sample.public_key, + }, + )? } name => return Err(anyhow!("circuit {} not yet implemented", name)), }; - write_artifacts( - &artifacts.toml, - &artifacts.template, - &artifacts.configs, - &artifacts.wrapper, - Some(args.output.as_path()), - )?; + let toml = if !args.toml { + None + } else { + Some(&artifacts.toml) + }; + write_artifacts(toml, &artifacts.configs, Some(args.output.as_path()))?; println!("Artifacts written to {}", args.output.display()); Ok(()) diff --git a/crates/pvss/src/types.rs b/crates/zk-helpers/src/ciphernodes_committee.rs similarity index 61% rename from crates/pvss/src/types.rs rename to crates/zk-helpers/src/ciphernodes_committee.rs index d244d8445a..c397e51721 100644 --- a/crates/pvss/src/types.rs +++ b/crates/zk-helpers/src/ciphernodes_committee.rs @@ -3,24 +3,6 @@ // This file is provided WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use fhe::bfv::PublicKey; - -pub type Toml = String; -pub type Template = String; -pub type Configs = String; -pub type Wrapper = String; - -/// Variant for input types for DKG. -/// -/// This variant is used to determine the type of input that is used for the DKG -/// circuits (C2, C3, C4) -#[derive(Clone)] -pub enum DkgInputType { - /// The input type that generates shares of a secret key using secret sharing. - SecretKey, - /// The input type that generates shares of smudging noise instead of secret key shares. - SmudgingNoise, -} /// @todo this must be integrated inside Ciphernodes & Smart Contract /// instead of being a separate type in here. The pvss crate should import this and @@ -69,31 +51,3 @@ impl CiphernodesCommitteeSize { // }, } } - -#[derive(Debug, Clone, Copy)] -pub enum SecurityLevel { - INSECURE, - PRODUCTION, -} - -impl SecurityLevel { - pub fn as_str(self) -> &'static str { - match self { - SecurityLevel::INSECURE => "insecure", - SecurityLevel::PRODUCTION => "production", - } - } -} - -#[derive(Debug, Clone)] -pub struct Sample { - pub public_key: PublicKey, -} - -#[derive(Debug, Clone)] -pub struct Artifacts { - pub toml: Toml, - pub configs: Configs, - pub template: Template, - pub wrapper: Wrapper, -} diff --git a/crates/zk-helpers/src/circuits/codegen.rs b/crates/zk-helpers/src/circuits/codegen.rs new file mode 100644 index 0000000000..fb3844e44d --- /dev/null +++ b/crates/zk-helpers/src/circuits/codegen.rs @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Circuit artifact types and I/O. +//! +//! [`CircuitCodegen`] is implemented by circuits that can produce [`Artifacts`] +//! (Prover.toml and configs.nr). Use [`write_artifacts`] to write them to disk. + +use crate::computation::Configs; +use crate::computation::Toml; +use crate::errors::CircuitsErrors; +use std::path::Path; + +/// Generated files for a circuit: Prover TOML and Noir configs. +#[derive(Debug, Clone)] +pub struct Artifacts { + /// Prover.toml content (witness and circuit inputs). + pub toml: Toml, + /// configs.nr content (constants for the Noir prover). + pub configs: Configs, +} + +/// Trait for circuits that can generate Prover.toml and configs.nr from circuit-specific input. +pub trait CircuitCodegen: crate::registry::Circuit { + /// Circuit-specific parameters (e.g. BFV parameters). + type Params; + /// Circuit-specific codegen input (e.g. preset + public key). + type Input; + /// Error type for codegen failures. + type Error; + + /// Produces [`Artifacts`] for this circuit from the given input. + fn codegen(&self, params: &Self::Params, input: &Self::Input) + -> Result; +} + +/// Writes the Prover TOML string to `path/Prover.toml`, or `./Prover.toml` if `path` is `None`. +pub fn write_toml(toml: &Toml, path: Option<&Path>) -> Result<(), CircuitsErrors> { + let toml_path = path.unwrap_or_else(|| Path::new(".")); + let toml_path = toml_path.join("Prover.toml"); + Ok(std::fs::write(toml_path, toml)?) +} + +/// Writes the Noir configs string to `path/configs.nr`, or `./configs.nr` if `path` is `None`. +pub fn write_configs(configs: &Configs, path: Option<&Path>) -> Result<(), CircuitsErrors> { + let configs_path = path.unwrap_or_else(|| Path::new(".")); + let configs_path = configs_path.join("configs.nr"); + Ok(std::fs::write(configs_path, configs)?) +} + +/// Writes Prover.toml (if `toml` is `Some`) and always configs.nr into the given directory +/// (or current directory if `path` is `None`). +pub fn write_artifacts( + toml: Option<&Toml>, + configs: &Configs, + path: Option<&Path>, +) -> Result<(), CircuitsErrors> { + if let Some(t) = toml { + write_toml(t, path)?; + } + write_configs(configs, path)?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::TempDir; + + #[test] + fn write_toml_creates_prover_toml_in_path() { + let toml_content = r#"[section] +key = "value" +"#; + let temp = TempDir::new().unwrap(); + write_toml(&toml_content.to_string(), Some(temp.path())).unwrap(); + let path = temp.path().join("Prover.toml"); + assert!(path.exists()); + let read = std::fs::read_to_string(&path).unwrap(); + assert_eq!(read, toml_content); + } + + #[test] + fn write_configs_creates_configs_nr_in_path() { + let configs_content = "pub global N: u32 = 1024;\n"; + let temp = TempDir::new().unwrap(); + write_configs(&configs_content.to_string(), Some(temp.path())).unwrap(); + let path = temp.path().join("configs.nr"); + assert!(path.exists()); + let read = std::fs::read_to_string(&path).unwrap(); + assert_eq!(read, configs_content); + } + + #[test] + fn write_artifacts_creates_both_files() { + let toml_content = "[section]\nkey = \"value\"\n"; + let configs_content = "pub global N: u32 = 1024;\n"; + let temp = TempDir::new().unwrap(); + write_artifacts( + Some(&toml_content.to_string()), + &configs_content.to_string(), + Some(temp.path()), + ) + .unwrap(); + let toml_path = temp.path().join("Prover.toml"); + let configs_path = temp.path().join("configs.nr"); + assert!(toml_path.exists()); + assert!(configs_path.exists()); + assert_eq!(std::fs::read_to_string(&toml_path).unwrap(), toml_content); + assert_eq!( + std::fs::read_to_string(&configs_path).unwrap(), + configs_content + ); + } +} diff --git a/crates/zk-helpers/src/commitments.rs b/crates/zk-helpers/src/circuits/commitments.rs similarity index 100% rename from crates/zk-helpers/src/commitments.rs rename to crates/zk-helpers/src/circuits/commitments.rs diff --git a/crates/zk-helpers/src/circuits/computation.rs b/crates/zk-helpers/src/circuits/computation.rs new file mode 100644 index 0000000000..524892bac8 --- /dev/null +++ b/crates/zk-helpers/src/circuits/computation.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Computation traits and artifact types. +//! +//! [`Computation`] is a generic trait for computing values from parameters and input. +//! [`CircuitComputation`] extends it for circuits that produce witness/bounds/bits. +//! [`Toml`] and [`Configs`] are the string types used for Prover.toml and configs.nr. + +/// Variant for input types for DKG. +#[derive(Clone)] +pub enum DkgInputType { + /// The input type that generates shares of a secret key using secret sharing. + SecretKey, + /// The input type that generates shares of smudging noise instead of secret key shares. + SmudgingNoise, +} + +/// Prover TOML file content (witness and circuit inputs). +pub type Toml = String; +/// Noir configs file content (global constants for the prover). +pub type Configs = String; + +/// Generic computation from parameters and input to a result. +pub trait Computation: Sized { + type Params; + type Input; + type Error; + + /// Computes the result from parameters and input. + fn compute(params: &Self::Params, input: &Self::Input) -> Result; +} + +/// Circuit-specific computation: parameters and input produce bounds, bits, witness, etc. +pub trait CircuitComputation: crate::registry::Circuit { + type Params; + type Input; + type Output; + type Error; + + /// Computes circuit-specific data (bounds, bits, witness) from parameters and input. + fn compute( + &self, + params: &Self::Params, + input: &Self::Input, + ) -> Result; +} + +/// Converts a value to a JSON [`serde_json::Value`] for serialization. +pub trait ConvertToJson { + fn convert_to_json(&self) -> serde_json::Result; +} + +/// Reduces coefficients (or similar) to the ZKP field modulus for use in the prover. +pub trait ReduceToZkpModulus: Sized { + fn reduce_to_zkp_modulus(&self) -> Self; +} diff --git a/crates/pvss/src/errors.rs b/crates/zk-helpers/src/circuits/errors.rs similarity index 70% rename from crates/pvss/src/errors.rs rename to crates/zk-helpers/src/circuits/errors.rs index 04ed43423a..da45852c57 100644 --- a/crates/pvss/src/errors.rs +++ b/crates/zk-helpers/src/circuits/errors.rs @@ -4,10 +4,14 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +//! Error types for circuit and codegen operations. + +use crate::utils::ZkHelpersUtilsError; use thiserror::Error; +/// Errors that can occur during circuit codegen or artifact I/O. #[derive(Error, Debug)] -pub enum CodegenError { +pub enum CircuitsErrors { #[error("I/O error: {0}")] Io(#[from] std::io::Error), #[error("TOML serialization error: {0}")] @@ -15,7 +19,7 @@ pub enum CodegenError { #[error("BFV error: {0}")] Fhe(#[from] fhe::Error), #[error("ZK helper error: {0}")] - ZkHelpers(#[from] e3_zk_helpers::utils::ZkHelpersUtilsError), + ZkHelpers(#[from] ZkHelpersUtilsError), #[error("Unexpected error: {0}")] Other(String), } diff --git a/crates/zk-helpers/src/circuits/mod.rs b/crates/zk-helpers/src/circuits/mod.rs new file mode 100644 index 0000000000..1c7e123a1b --- /dev/null +++ b/crates/zk-helpers/src/circuits/mod.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Zero-knowledge circuit types and code generation. +//! +//! This module provides circuit metadata ([`Circuit`](crate::registry::Circuit)), artifact +//! codegen ([`CircuitCodegen`], [`Artifacts`]), commitment helpers ([`commitments`]), +//! and sample data generation ([`Sample`]). The `pk_bfv` submodule implements the +//! public-key BFV commitment circuit. + +pub mod codegen; +pub mod commitments; +pub mod computation; +pub mod errors; +pub mod sample; + +pub use codegen::{write_artifacts, Artifacts, CircuitCodegen}; +pub use commitments::*; +pub use computation::{ + CircuitComputation, Computation, Configs, ConvertToJson, ReduceToZkpModulus, Toml, +}; +pub use errors::CircuitsErrors; +pub use sample::Sample; + +pub mod pk_bfv; +pub use pk_bfv::codegen::{generate_configs, generate_toml, TomlJson}; +pub use pk_bfv::computation::{Bits, Bounds, PkBfvComputationOutput, Witness}; +pub use pk_bfv::PkBfvCircuit; diff --git a/crates/zk-helpers/src/circuits/pk_bfv/circuit.rs b/crates/zk-helpers/src/circuits/pk_bfv/circuit.rs new file mode 100644 index 0000000000..7cd37300fa --- /dev/null +++ b/crates/zk-helpers/src/circuits/pk_bfv/circuit.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +use crate::computation::DkgInputType; +use crate::registry::Circuit; +use e3_fhe_params::ParameterType; +use fhe::bfv::PublicKey; + +#[derive(Debug)] +pub struct PkBfvCircuit; + +impl Circuit for PkBfvCircuit { + const NAME: &'static str = "pk-bfv"; + const PREFIX: &'static str = "PK_BFV"; + const SUPPORTED_PARAMETER: ParameterType = ParameterType::DKG; + const DKG_INPUT_TYPE: Option = None; +} + +pub struct PkBfvCircuitInput { + /// BFV public key. + pub public_key: PublicKey, +} diff --git a/crates/pvss/src/circuits/pk_bfv/codegen.rs b/crates/zk-helpers/src/circuits/pk_bfv/codegen.rs similarity index 50% rename from crates/pvss/src/circuits/pk_bfv/codegen.rs rename to crates/zk-helpers/src/circuits/pk_bfv/codegen.rs index da8d920d0c..d6402e3501 100644 --- a/crates/pvss/src/circuits/pk_bfv/codegen.rs +++ b/crates/zk-helpers/src/circuits/pk_bfv/codegen.rs @@ -4,37 +4,60 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::circuits::pk_bfv::circuit::PkBfvCircuit; +//! Code generation for the public-key BFV circuit: Prover.toml and configs.nr. + +use crate::circuits::pk_bfv::circuit::PkBfvCircuitInput; use crate::circuits::pk_bfv::computation::{Bits, Bounds, Witness}; -use crate::errors::CodegenError; -use crate::traits::Circuit; -use crate::traits::Computation; -use crate::traits::ReduceToZkpModulus; -use crate::types::Artifacts; -use crate::types::{Configs, Template, Toml, Wrapper}; -use crate::utils::generate_wrapper; -use crate::utils::get_security_level; +use crate::circuits::PkBfvCircuit; +use crate::codegen::Artifacts; +use crate::codegen::CircuitCodegen; +use crate::computation::Computation; +use crate::computation::Configs; +use crate::computation::ReduceToZkpModulus; +use crate::computation::Toml; +use crate::errors::CircuitsErrors; +use crate::registry::Circuit; use crate::utils::map_witness_2d_vector_to_json; -use crate::utils::write_configs; -use crate::utils::write_template; -use crate::utils::write_toml; -use crate::utils::write_wrapper; -use e3_fhe_params::BfvParamSet; -use e3_fhe_params::BfvPreset; use fhe::bfv::BfvParameters; -use fhe::bfv::PublicKey; use serde::{Deserialize, Serialize}; use serde_json; -use std::path::Path; use std::sync::Arc; +/// Implementation of [`CircuitCodegen`] for [`PkBfvCircuit`]. +impl CircuitCodegen for PkBfvCircuit { + type Params = Arc; + type Input = PkBfvCircuitInput; + type Error = CircuitsErrors; + + fn codegen( + &self, + params: &Self::Params, + input: &Self::Input, + ) -> Result { + // Compute. + let bounds = Bounds::compute(¶ms, &())?; + let bits = Bits::compute(¶ms, &bounds)?; + let witness = Witness::compute(¶ms, input)?; + let zkp_witness = witness.reduce_to_zkp_modulus(); + + let toml = generate_toml(zkp_witness)?; + let configs = generate_configs(¶ms, &bits); + + Ok(Artifacts { toml, configs }) + } +} + +/// JSON-serializable structure for Prover.toml (pk0is, pk1is arrays). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TomlJson { + /// First component of the public key per modulus (for the prover). pub pk0is: Vec, + /// Second component of the public key per modulus (for the prover). pub pk1is: Vec, } -pub fn generate_toml(witness: Witness) -> Result { +/// Builds the Prover TOML string from the pk-bfv witness (pk0is, pk1is). +pub fn generate_toml(witness: Witness) -> Result { let pk0is = map_witness_2d_vector_to_json(&witness.pk0is); let pk1is = map_witness_2d_vector_to_json(&witness.pk1is); @@ -42,47 +65,7 @@ pub fn generate_toml(witness: Witness) -> Result { Ok(toml::to_string(&toml_json)?) } -pub fn codegen(preset: BfvPreset, public_key: PublicKey) -> Result { - let params = BfvParamSet::from(preset).build_arc(); - // Compute. - let bounds = Bounds::compute(¶ms, &())?; - let bits = Bits::compute(¶ms, &bounds)?; - let witness = Witness::compute(¶ms, &public_key)?; - let zkp_witness = witness.reduce_to_zkp_modulus(); - - let toml = generate_toml(zkp_witness)?; - let configs = generate_configs(¶ms, &bits); - let template = generate_template(preset.metadata().lambda); - let wrapper = generate_wrapper( - ::N_PROOFS, - ::N_PUBLIC_INPUTS, - ); - - Ok(Artifacts { - toml, - configs, - template, - wrapper, - }) -} - -pub fn generate_template(lambda: usize) -> Template { - format!( - r#"use lib::configs::{}::bfv::{{L, N, {}_BIT_PK}}; -use lib::core::bfv_pk::BfvPkCommit; -use lib::math::polynomial::Polynomial; - -fn main(pk0is: [Polynomial; L], pk1is: [Polynomial; L]) -> pub Field {{ - let pk_bfv: BfvPkCommit = BfvPkCommit::new(pk0is, pk1is); - pk_bfv.verify() -}} -"#, - get_security_level(lambda).as_str(), - ::PREFIX, - ::PREFIX, - ) -} - +/// Builds the configs.nr string (N, L, bit parameters) for the Noir prover. pub fn generate_configs(params: &Arc, bits: &Bits) -> Configs { format!( r#"// Global configs for Public Key BFV circuit @@ -105,34 +88,27 @@ pub global {}_BIT_PK: u32 = {}; ) } -pub fn write_artifacts( - toml: &Toml, - template: &Template, - configs: &Configs, - wrapper: &Wrapper, - path: Option<&Path>, -) -> Result<(), CodegenError> { - write_toml(&toml, path)?; - write_template(&template, path)?; - write_configs(&configs, path)?; - write_wrapper(&wrapper, path)?; - Ok(()) -} - #[cfg(test)] mod tests { use super::*; - use crate::sample; + use crate::codegen::write_artifacts; + use crate::sample::Sample; use e3_fhe_params::BfvParamSet; use e3_fhe_params::DEFAULT_BFV_PRESET; use tempfile::TempDir; #[test] fn test_toml_generation_and_structure() { - let preset = DEFAULT_BFV_PRESET; - let params = BfvParamSet::from(preset).build_arc(); - let sample = sample::generate_sample(¶ms); - let artifacts = codegen(preset, sample.public_key).unwrap(); + let params = BfvParamSet::from(DEFAULT_BFV_PRESET).build_arc(); + let sample = Sample::generate(¶ms); + let artifacts = PkBfvCircuit + .codegen( + ¶ms, + &PkBfvCircuitInput { + public_key: sample.public_key, + }, + ) + .unwrap(); let parsed: toml::Value = artifacts.toml.parse().unwrap(); let pk0is = parsed @@ -148,10 +124,8 @@ mod tests { let temp_dir = TempDir::new().unwrap(); write_artifacts( - &artifacts.toml, - &artifacts.template, + Some(&artifacts.toml), &artifacts.configs, - &artifacts.wrapper, Some(temp_dir.path()), ) .unwrap(); @@ -166,16 +140,6 @@ mod tests { assert!(artifacts.toml.contains("[[pk0is]]")); assert!(artifacts.toml.contains("[[pk1is]]")); - let template_path = temp_dir.path().join("main.nr"); - assert!(template_path.exists()); - - let template_content = std::fs::read_to_string(&template_path).unwrap(); - assert!(template_content.contains("pk0is: [Polynomial; L],")); - assert!(template_content.contains("pk1is: [Polynomial; L]")); - - let wrapper_path = temp_dir.path().join("wrapper.nr"); - assert!(wrapper_path.exists()); - let configs_path = temp_dir.path().join("configs.nr"); assert!(configs_path.exists()); diff --git a/crates/pvss/src/circuits/pk_bfv/computation.rs b/crates/zk-helpers/src/circuits/pk_bfv/computation.rs similarity index 58% rename from crates/pvss/src/circuits/pk_bfv/computation.rs rename to crates/zk-helpers/src/circuits/pk_bfv/computation.rs index 93870e5fbd..5dffbfec2c 100644 --- a/crates/pvss/src/circuits/pk_bfv/computation.rs +++ b/crates/zk-helpers/src/circuits/pk_bfv/computation.rs @@ -4,15 +4,23 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::traits::Computation; -use crate::traits::ConvertToJson; -use crate::traits::ReduceToZkpModulus; +//! Computation types for the pk-bfv circuit: constants, bounds, bit widths, and witness. +//! +//! [`Constants`], [`Bounds`], [`Bits`], and [`Witness`] are produced from BFV parameters +//! and (for witness) a public key. They implement [`Computation`] and are used by codegen. + +use crate::circuits::pk_bfv::circuit::PkBfvCircuitInput; +use crate::computation::CircuitComputation; +use crate::computation::Computation; +use crate::computation::ConvertToJson; +use crate::computation::ReduceToZkpModulus; +use crate::errors::CircuitsErrors; +use crate::utils::calculate_bit_width; +use crate::utils::get_zkp_modulus; +use crate::PkBfvCircuit; use e3_polynomial::center_coefficients_mut; use e3_polynomial::reduce_coefficients_2d; -use e3_zk_helpers::utils::calculate_bit_width; -use e3_zk_helpers::utils::get_zkp_modulus; use fhe::bfv::BfvParameters; -use fhe::bfv::PublicKey; use fhe_math::rq::Representation; use itertools::izip; use num_bigint::BigInt; @@ -20,43 +28,95 @@ use num_bigint::BigUint; use rayon::prelude::*; use serde::{Deserialize, Serialize}; +/// Output of [`CircuitComputation::compute`] for [`PkBfvCircuit`]: bounds, bit widths, and witness. +#[derive(Debug)] +pub struct PkBfvComputationOutput { + /// Coefficient bounds for public key polynomials. + pub bounds: Bounds, + /// Bit widths for the prover (e.g. pk_bit). + pub bits: Bits, + /// Witness data (pk0is, pk1is) for the Noir prover. + pub witness: Witness, +} + +/// Implementation of [`CircuitComputation`] for [`PkBfvCircuit`]. +impl CircuitComputation for PkBfvCircuit { + type Params = BfvParameters; + type Input = PkBfvCircuitInput; + type Output = PkBfvComputationOutput; + type Error = CircuitsErrors; + + fn compute( + &self, + params: &Self::Params, + input: &Self::Input, + ) -> Result { + let bounds = Bounds::compute(params, &())?; + let bits = Bits::compute(params, &bounds)?; + let witness = Witness::compute(params, input)?; + + Ok(PkBfvComputationOutput { + bounds, + bits, + witness, + }) + } +} + +/// BFV parameters extracted for the circuit: degree, number of moduli, and modulus values. #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Constants { +pub struct Configs { + /// Polynomial degree (N). pub n: usize, + /// Number of CRT moduli (L). pub l: usize, + /// CRT moduli q_i. pub moduli: Vec, + /// Bits. + pub bits: Bits, + /// Bounds. + pub bounds: Bounds, } -#[derive(Debug, Clone)] +/// Bit widths used by the Noir prover (e.g. for packing coefficients). +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Bits { + /// Bit width for public key coefficients. pub pk_bit: u32, } -#[derive(Debug, Clone, Serialize, Deserialize)] +/// Coefficient bounds for public key polynomials (used to derive bit widths). +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Bounds { - /// Bound for public key polynomials (pk0, pk1) + /// Bound for public key polynomials (pk0, pk1). pub pk_bound: BigUint, } +/// Witness data for the pk-bfv circuit: public key polynomials in CRT form for the prover. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Witness { - /// Public key polynomials (pk0, pk1) for each CRT basis. + /// First component of the public key per modulus (coefficients as BigInt). pub pk0is: Vec>, + /// Second component of the public key per modulus (coefficients as BigInt). pub pk1is: Vec>, } -impl Computation for Constants { +impl Computation for Configs { type Params = BfvParameters; type Input = (); - type Error = std::convert::Infallible; + type Error = CircuitsErrors; - fn compute(params: &Self::Params, _: &Self::Input) -> Result { + fn compute(params: &Self::Params, _: &Self::Input) -> Result { let moduli = params.moduli().to_vec(); + let bounds = Bounds::compute(¶ms, &())?; + let bits = Bits::compute(¶ms, &bounds)?; - Ok(Constants { + Ok(Configs { n: params.degree(), l: moduli.len(), moduli, + bits, + bounds, }) } } @@ -64,7 +124,7 @@ impl Computation for Constants { impl Computation for Bits { type Params = BfvParameters; type Input = Bounds; - type Error = e3_zk_helpers::utils::ZkHelpersUtilsError; + type Error = crate::utils::ZkHelpersUtilsError; fn compute(_: &Self::Params, input: &Self::Input) -> Result { Ok(Bits { @@ -76,7 +136,7 @@ impl Computation for Bits { impl Computation for Bounds { type Params = BfvParameters; type Input = (); - type Error = crate::errors::CodegenError; + type Error = fhe::Error; fn compute(params: &Self::Params, _: &Self::Input) -> Result { let mut pk_bound_max = BigUint::from(0u32); @@ -97,16 +157,16 @@ impl Computation for Bounds { impl Computation for Witness { type Params = BfvParameters; - type Input = PublicKey; + type Input = PkBfvCircuitInput; type Error = fhe::Error; - fn compute(params: &Self::Params, public_key: &Self::Input) -> Result { + fn compute(params: &Self::Params, input: &Self::Input) -> Result { let moduli = params.moduli(); // Extract public key components (pk0, pk1) from the ciphertext structure // and change representation to Power Basis. - let mut pk0 = public_key.c.c[0].clone(); - let mut pk1 = public_key.c.c[1].clone(); + let mut pk0 = input.public_key.c.c[0].clone(); + let mut pk1 = input.public_key.c.c[1].clone(); pk0.change_representation(Representation::PowerBasis); pk1.change_representation(Representation::PowerBasis); @@ -139,7 +199,7 @@ impl Computation for Witness { } } -impl ConvertToJson for Constants { +impl ConvertToJson for Configs { fn convert_to_json(&self) -> serde_json::Result { serde_json::to_value(self) } @@ -169,9 +229,9 @@ impl ReduceToZkpModulus for Witness { #[cfg(test)] mod tests { use super::*; - use crate::sample::generate_sample; - use crate::traits::ConvertToJson; - use crate::traits::ReduceToZkpModulus; + use crate::computation::ConvertToJson; + use crate::computation::ReduceToZkpModulus; + use crate::sample::Sample; use e3_fhe_params::BfvParamSet; use e3_fhe_params::DEFAULT_BFV_PRESET; @@ -189,8 +249,14 @@ mod tests { #[test] fn test_witness_reduction_and_json_roundtrip() { let params = BfvParamSet::from(DEFAULT_BFV_PRESET).build_arc(); - let encryption_data = generate_sample(¶ms); - let witness = Witness::compute(¶ms, &encryption_data.public_key).unwrap(); + let encryption_data = Sample::generate(¶ms); + let witness = Witness::compute( + ¶ms, + &PkBfvCircuitInput { + public_key: encryption_data.public_key, + }, + ) + .unwrap(); let zkp_reduced = witness.reduce_to_zkp_modulus(); let json = zkp_reduced.convert_to_json().unwrap(); let decoded: Witness = serde_json::from_value(json.clone()).unwrap(); @@ -202,13 +268,15 @@ mod tests { #[test] fn test_constants_json_roundtrip() { let params = BfvParamSet::from(DEFAULT_BFV_PRESET).build_arc(); - let constants = Constants::compute(¶ms, &()).unwrap(); + let constants = Configs::compute(¶ms, &()).unwrap(); let json = constants.convert_to_json().unwrap(); - let decoded: Constants = serde_json::from_value(json).unwrap(); + let decoded: Configs = serde_json::from_value(json).unwrap(); assert_eq!(decoded.n, constants.n); assert_eq!(decoded.l, constants.l); assert_eq!(decoded.moduli, constants.moduli); + assert_eq!(decoded.bits, constants.bits); + assert_eq!(decoded.bounds, constants.bounds); } } diff --git a/crates/zk-helpers/src/circuits/pk_bfv/mod.rs b/crates/zk-helpers/src/circuits/pk_bfv/mod.rs new file mode 100644 index 0000000000..adfe084f06 --- /dev/null +++ b/crates/zk-helpers/src/circuits/pk_bfv/mod.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Public-key BFV commitment circuit. +//! +//! This circuit proves knowledge of a BFV public key (pk0, pk1) and produces +//! Prover.toml and configs.nr for the Noir prover. See [`PkBfvCircuit`] and +//! [`PkBfvCodegenInput`]. + +pub mod circuit; +pub mod codegen; +pub mod computation; +pub use circuit::*; +pub use codegen::*; +pub use computation::*; diff --git a/crates/zk-helpers/src/circuits/sample.rs b/crates/zk-helpers/src/circuits/sample.rs new file mode 100644 index 0000000000..be3bfc4ff1 --- /dev/null +++ b/crates/zk-helpers/src/circuits/sample.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Sample data generation for circuits. +//! +//! [`Sample`] produces a random BFV key pair; the public key is used as input +//! for codegen and tests (e.g. pk-bfv circuit). + +use fhe::bfv::{BfvParameters, PublicKey, SecretKey}; +use rand::thread_rng; +use std::sync::Arc; + +/// A sample BFV public key (and optionally related data) for circuit codegen or tests. +#[derive(Debug, Clone)] +pub struct Sample { + /// Randomly generated BFV public key. + pub public_key: PublicKey, +} + +impl Sample { + /// Generates a random secret key and public key for the given BFV parameters. + pub fn generate(params: &Arc) -> Self { + let mut rng = thread_rng(); + + let secret_key = SecretKey::random(¶ms, &mut rng); + let public_key = PublicKey::new(&secret_key, &mut rng); + + Self { public_key } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use e3_fhe_params::BfvParamSet; + use e3_fhe_params::DEFAULT_BFV_PRESET; + + #[test] + fn test_generate_sample() { + let params = BfvParamSet::from(DEFAULT_BFV_PRESET).build_arc(); + let sample = Sample::generate(¶ms); + + assert_eq!(sample.public_key.c.c.len(), 2); + } +} diff --git a/crates/zk-helpers/src/lib.rs b/crates/zk-helpers/src/lib.rs index 1d258c43af..979ae79c99 100644 --- a/crates/zk-helpers/src/lib.rs +++ b/crates/zk-helpers/src/lib.rs @@ -4,10 +4,14 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -pub mod commitments; +pub mod ciphernodes_committee; +pub mod circuits; pub mod packing; +pub mod registry; pub mod utils; -pub use commitments::*; +pub use ciphernodes_committee::*; +pub use circuits::*; pub use packing::*; +pub use registry::*; pub use utils::*; diff --git a/crates/zk-helpers/src/registry/mod.rs b/crates/zk-helpers/src/registry/mod.rs new file mode 100644 index 0000000000..02e9b54a4c --- /dev/null +++ b/crates/zk-helpers/src/registry/mod.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +//! Circuit registry and metadata. +//! +//! The registry maps circuit names (e.g. `pk-bfv`) to [`CircuitMetadata`]. Use +//! [`CircuitRegistry`] to register and look up circuits by name. + +pub mod registry; + +pub use registry::*; diff --git a/crates/pvss/src/registry.rs b/crates/zk-helpers/src/registry/registry.rs similarity index 59% rename from crates/pvss/src/registry.rs rename to crates/zk-helpers/src/registry/registry.rs index 4229475fd1..265ee3005d 100644 --- a/crates/pvss/src/registry.rs +++ b/crates/zk-helpers/src/registry/registry.rs @@ -4,8 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::traits::CircuitMetadata; -use crate::types::DkgInputType; +use crate::computation::DkgInputType; use e3_fhe_params::ParameterType; use std::collections::HashMap; use std::sync::Arc; @@ -18,7 +17,51 @@ pub enum RegistryError { UnknownCircuit { name: String }, } -/// Registry for PVSS circuits. +/// Trait for circuit metadata. +pub trait Circuit: Send + Sync { + const NAME: &'static str; + const PREFIX: &'static str; + const SUPPORTED_PARAMETER: ParameterType; + const DKG_INPUT_TYPE: Option; + + fn name(&self) -> &'static str { + Self::NAME + } + + fn prefix(&self) -> &'static str { + Self::PREFIX + } + + fn supported_parameter(&self) -> ParameterType { + Self::SUPPORTED_PARAMETER + } + + fn dkg_input_type(&self) -> Option { + Self::DKG_INPUT_TYPE + } +} + +pub trait CircuitMetadata: Send + Sync { + fn name(&self) -> &'static str; + fn supported_parameter(&self) -> ParameterType; + fn dkg_input_type(&self) -> Option; +} + +impl CircuitMetadata for T { + fn name(&self) -> &'static str { + T::NAME + } + + fn supported_parameter(&self) -> ParameterType { + T::SUPPORTED_PARAMETER + } + + fn dkg_input_type(&self) -> Option { + T::DKG_INPUT_TYPE + } +} + +/// Registry for ZK circuits. pub struct CircuitRegistry { circuits: HashMap>, } @@ -56,18 +99,6 @@ impl CircuitRegistry { Ok(self.get(name)?.dkg_input_type()) } - /// Get number of recursive proofs for a circuit. - /// This is used for determine the number of proofs required for aggregation. - pub fn n_recursive_proofs(&self, name: &str) -> Result { - Ok(self.get(name)?.n_recursive_proofs()) - } - - /// Get number of public inputs for a circuit. - /// This is used for determine the number of public inputs required for aggregation. - pub fn n_public_inputs(&self, name: &str) -> Result { - Ok(self.get(name)?.n_public_inputs()) - } - /// List all registered circuit names. pub fn list_circuits(&self) -> Vec { self.circuits.keys().cloned().collect() @@ -77,8 +108,15 @@ impl CircuitRegistry { #[cfg(test)] mod tests { use super::*; - use crate::circuits::pk_bfv::circuit::PkBfvCircuit; - use crate::traits::Circuit; + + pub struct TestCircuit; + + impl Circuit for TestCircuit { + const NAME: &'static str = "test"; + const PREFIX: &'static str = "TEST"; + const SUPPORTED_PARAMETER: ParameterType = ParameterType::DKG; + const DKG_INPUT_TYPE: Option = Some(DkgInputType::SecretKey); + } #[test] /// Unknown circuits should return an error. @@ -94,19 +132,11 @@ mod tests { /// Registry should expose metadata for registered circuits. fn registry_reports_expected_metadata() { let mut registry = CircuitRegistry::new(); - registry.register(Arc::new(PkBfvCircuit)); - let circuit = registry.get(::NAME).unwrap(); + registry.register(Arc::new(TestCircuit)); + let circuit = registry.get(::NAME).unwrap(); - assert_eq!(circuit.name(), ::NAME); + assert_eq!(circuit.name(), ::NAME); assert_eq!(circuit.supported_parameter(), ParameterType::DKG); - assert!(circuit.dkg_input_type().is_none()); - assert_eq!( - circuit.n_recursive_proofs(), - ::N_PROOFS - ); - assert_eq!( - circuit.n_public_inputs(), - ::N_PUBLIC_INPUTS - ); + assert!(circuit.dkg_input_type().is_some()); } } diff --git a/crates/zk-helpers/src/utils.rs b/crates/zk-helpers/src/utils.rs index 18237880c3..84eca15131 100644 --- a/crates/zk-helpers/src/utils.rs +++ b/crates/zk-helpers/src/utils.rs @@ -150,6 +150,24 @@ pub fn get_zkp_modulus() -> BigInt { .expect("Invalid ZKP modulus") } +/// Map a 2D vector of BigInt to a vector of JSON values. +/// +/// # Arguments +/// * `values` - 2D vector of BigInt values +/// +/// # Returns +/// A vector of JSON values +pub fn map_witness_2d_vector_to_json(values: &Vec>) -> Vec { + values + .iter() + .map(|value| { + serde_json::json!({ + "coefficients": to_string_1d_vec(value) + }) + }) + .collect() +} + #[cfg(test)] mod tests { use super::*; diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index bafa64ef45..306cbc5d04 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -2568,14 +2568,24 @@ dependencies = [ name = "e3-zk-helpers" version = "0.1.8" dependencies = [ + "anyhow", "ark-bn254 0.5.0", "ark-ff 0.5.0", + "clap", + "e3-fhe-params", "e3-polynomial 0.1.8", "e3-safe 0.1.8", "fhe", + "fhe-math", + "itertools 0.14.0", "num-bigint", "num-traits", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", "thiserror 1.0.69", + "toml", ] [[package]] @@ -5624,15 +5634,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/templates/default/Cargo.lock b/templates/default/Cargo.lock index 3b421f369d..b444b796f6 100644 --- a/templates/default/Cargo.lock +++ b/templates/default/Cargo.lock @@ -1266,7 +1266,7 @@ dependencies = [ "anyhow", "e3-fhe-params", "e3-greco-helpers", - "e3-polynomial", + "e3-polynomial 0.1.8", "e3-zk-helpers", "fhe", "fhe-traits", @@ -1405,14 +1405,24 @@ dependencies = [ name = "e3-zk-helpers" version = "0.1.8" dependencies = [ + "anyhow", "ark-bn254 0.5.0", "ark-ff 0.5.0", + "clap", + "e3-fhe-params", "e3-polynomial 0.1.8", "e3-safe 0.1.8", "fhe", + "fhe-math", + "itertools 0.14.0", "num-bigint", "num-traits", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", "thiserror", + "toml", ] [[package]] @@ -3703,15 +3713,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]]