diff --git a/Cargo.lock b/Cargo.lock index dd300ca26d..95f47768f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2828,12 +2828,12 @@ dependencies = [ "anyhow", "e3-fhe-params", "e3-greco-helpers", + "e3-zk-helpers", "fhe", "fhe-traits", "rand 0.8.5", "thiserror 1.0.69", "zkfhe-greco", - "zkfhe-shared", ] [[package]] @@ -3124,7 +3124,6 @@ dependencies = [ "fhe", "num-bigint", "thiserror 1.0.69", - "zkfhe-shared", ] [[package]] @@ -3145,13 +3144,13 @@ name = "e3-greco-helpers" version = "0.1.7" dependencies = [ "e3-fhe-params", + "e3-zk-helpers", "fhe", "fhe-math", "fhe-traits", "num-bigint", "rand 0.8.5", "zkfhe-greco", - "zkfhe-shared", ] [[package]] @@ -3286,6 +3285,18 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "e3-polynomial" +version = "0.1.7" +dependencies = [ + "bincode", + "criterion", + "num-bigint", + "num-traits", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "e3-program-server" version = "0.1.7" @@ -3317,6 +3328,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "e3-safe" +version = "0.1.7" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "hex", + "sha3", + "taceo-poseidon2", +] + [[package]] name = "e3-sdk" version = "0.1.7" @@ -3493,6 +3515,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "e3-zk-helpers" +version = "0.1.7" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "e3-polynomial", + "e3-safe", + "fhe", + "num-bigint", + "num-traits", + "thiserror 1.0.69", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -6503,18 +6539,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "polynomial" -version = "0.1.7" -dependencies = [ - "bincode", - "criterion", - "num-bigint", - "num-traits", - "serde", - "thiserror 1.0.69", -] - [[package]] name = "polynomial" version = "0.1.7" @@ -7398,17 +7422,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" -[[package]] -name = "safe" -version = "0.1.7" -dependencies = [ - "ark-bn254 0.5.0", - "ark-ff 0.5.0", - "hex", - "sha3", - "taceo-poseidon2", -] - [[package]] name = "safe" version = "0.1.7" @@ -9579,7 +9592,7 @@ dependencies = [ "itertools 0.14.0", "num-bigint", "num-traits", - "polynomial 0.1.7 (git+https://github.com/gnosisguild/enclave?branch=main)", + "polynomial", "rand 0.8.5", "rayon", "serde", @@ -9603,9 +9616,9 @@ dependencies = [ "fhe-traits", "num-bigint", "num-traits", - "polynomial 0.1.7 (git+https://github.com/gnosisguild/enclave?branch=main)", + "polynomial", "rand 0.8.5", - "safe 0.1.7 (git+https://github.com/gnosisguild/enclave)", + "safe", "serde", "serde_json", "thiserror 1.0.69", diff --git a/Cargo.toml b/Cargo.toml index 834246d797..d96e9db888 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,8 @@ members = [ "crates/utils", "crates/wasm", "crates/parity-matrix", - "crates/polynomial" + "crates/polynomial", + "crates/zk-helpers" ] exclude = [ "examples/CRISP", @@ -93,11 +94,14 @@ e3-net = { version = "0.1.7", path = "./crates/net" } e3-compute-provider = { version = "0.1.7", path = "./crates/compute-provider" } e3-sortition = { version = "0.1.7", path = "./crates/sortition" } e3-program-server = { version = "0.1.7", path = "./crates/program-server" } +e3-polynomial = { version = "0.1.7", path = "./crates/polynomial" } e3-support-scripts = { version = "0.1.7", path = "./crates/support-scripts" } e3-test-helpers = { version = "0.1.7", path = "./crates/test-helpers" } e3-tests = { version = "0.1.7", path = "./crates/tests" } e3-trbfv = { version = "0.1.7", path = "./crates/trbfv" } e3-utils = { version = "0.1.7", path = "./crates/utils" } +e3-safe = { version = "0.1.7", path = "./crates/safe" } +e3-zk-helpers = { version = "0.1.7", path = "./crates/zk-helpers" } actix = "=0.13.5" actix-web = "=4.11.0" @@ -111,6 +115,8 @@ alloy-primitives = { version = "=1.3.0", default-features = false, features = [ alloy-sol-types = "=1.3.0" alloy-dyn-abi = "=1.3.0" argon2 = "=0.5.3" +ark-ff = "0.5.0" +ark-bn254 = "0.5.0" anyhow = "=1.0.98" async-std = { version = "=1.13.1", features = ["attributes"] } async-trait = "=0.1.88" diff --git a/circuits/lib/src/math/commitments.nr b/circuits/lib/src/math/commitments.nr index 6789c4674f..b75219c113 100644 --- a/circuits/lib/src/math/commitments.nr +++ b/circuits/lib/src/math/commitments.nr @@ -53,7 +53,14 @@ pub global DS_PK_AGG: [u8; 64] = [ ]; // Domain separator - "AGGREGATION" pub global DS_AGGREGATION: [u8; 64] = [ - 0x41, 0x47, 0x47, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; +// Domain separator - "CIPHERTEXT" +pub global DS_CIPHERTEXT: [u8; 64] = [ + 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -74,14 +81,14 @@ pub global DS_CLG_ENC_BFV: [u8; 64] = [ ]; // Domain separator - "CLG_GRECO" pub global DS_CLG_GRECO: [u8; 64] = [ - 0x43, 0x4c, 0x47, 0x5f, 0x47, 0x72, 0x65, 0x63, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x4c, 0x47, 0x5f, 0x47, 0x52, 0x45, 0x43, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; // Domain separator - "CLG_DEC_SHARE" pub global DS_CLG_DEC_SHARE: [u8; 64] = [ - 0x43, 0x4c, 0x47, 0x5f, 0x44, 0x65, 0x63, 0x53, 0x68, 0x61, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x4c, 0x47, 0x5f, 0x44, 0x45, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -191,6 +198,16 @@ pub fn compute_aggregation_commitment(payload: Vec) -> Field { compute_safe(DS_AGGREGATION, payload, [0x80000000 | payload.len(), 1]).get(0) } +pub fn compute_ciphertext_commitment( + ct0: [Polynomial; L], + ct1: [Polynomial; L], +) -> Field { + let mut payload = multiple_polynomial_payload::(Vec::new(), ct0); + payload = multiple_polynomial_payload::(payload, ct1); + + compute_commitments(payload, DS_CIPHERTEXT, [0x80000000 | payload.len(), 1]).get(0) +} + /// COMMITMENTS FOR CHALLENGES pub fn compute_pk_trbfv_challenge(payload: Vec) -> Vec { diff --git a/crates/Dockerfile b/crates/Dockerfile index 0cb580c1ef..f620dba64a 100644 --- a/crates/Dockerfile +++ b/crates/Dockerfile @@ -79,6 +79,7 @@ COPY crates/tests/Cargo.toml ./tests/Cargo.toml 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 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 diff --git a/crates/bfv-client/Cargo.toml b/crates/bfv-client/Cargo.toml index 4042e44773..82491bc0c2 100644 --- a/crates/bfv-client/Cargo.toml +++ b/crates/bfv-client/Cargo.toml @@ -14,5 +14,5 @@ fhe.workspace = true fhe-traits.workspace = true greco = { package = "zkfhe-greco", git = "https://github.com/gnosisguild/zkfhe-generator" } rand.workspace = true -shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator" } thiserror = { workspace = true } +e3-zk-helpers = { workspace = true } diff --git a/crates/bfv-client/src/client.rs b/crates/bfv-client/src/client.rs index e2ea23dd4a..080733e02f 100644 --- a/crates/bfv-client/src/client.rs +++ b/crates/bfv-client/src/client.rs @@ -7,14 +7,14 @@ use anyhow::{anyhow, Result}; use e3_fhe_params::build_bfv_params_arc; use e3_greco_helpers::{bfv_ciphertext_to_greco, bfv_public_key_to_greco}; +use e3_zk_helpers::commitments::{compute_ciphertext_commitment, compute_pk_agg_commitment}; +use e3_zk_helpers::utils::calculate_bit_width; use fhe::bfv::{Ciphertext, Encoding, Plaintext, PublicKey}; use fhe::Error as FheError; use fhe_traits::{DeserializeParametrized, FheEncoder, FheEncrypter, Serialize}; use greco::bounds::GrecoBounds; use greco::vectors::GrecoVectors; use rand::thread_rng; -use shared::commitments::compute_poly_commitment; -use shared::template::calculate_bit_width; /// Encrypt some data using BFV homomorphic encryption /// @@ -109,7 +109,7 @@ where let (_, bounds) = GrecoBounds::compute(¶ms, 0)?; - let bit_pk = shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + let bit_pk = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; // Create Greco input validation ZK proof let input_val_vectors = GrecoVectors::compute( @@ -147,7 +147,7 @@ pub fn compute_pk_commitment( let bit_pk = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; let (pk0is, pk1is) = bfv_public_key_to_greco(&public_key, ¶ms); - let commitment_bigint = compute_poly_commitment(&pk0is, &pk1is, bit_pk); + let commitment_bigint = compute_pk_agg_commitment(&pk0is, &pk1is, bit_pk); let bytes = commitment_bigint.to_bytes_be().1; @@ -185,7 +185,7 @@ pub fn compute_ct_commitment( let (_, bounds) = GrecoBounds::compute(¶ms, 0)?; let bit_ct = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; - let commitment_bigint = compute_poly_commitment(&ct0is, &ct1is, bit_ct); + let commitment_bigint = compute_ciphertext_commitment(&ct0is, &ct1is, bit_ct); let bytes = commitment_bigint.to_bytes_be().1; diff --git a/crates/fhe-params/Cargo.toml b/crates/fhe-params/Cargo.toml index e36478a590..935dce4aab 100644 --- a/crates/fhe-params/Cargo.toml +++ b/crates/fhe-params/Cargo.toml @@ -10,7 +10,6 @@ repository = "https://github.com/gnosisguild/enclave/crates/fhe-params" fhe = { workspace = true } num-bigint = { workspace = true } thiserror = { workspace = true } -shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator" } alloy-dyn-abi = { workspace = true, optional = true } alloy-primitives = { workspace = true, optional = true } diff --git a/crates/fhe-params/src/constants.rs b/crates/fhe-params/src/constants.rs index 4ccff52349..d2e4f5ed3f 100644 --- a/crates/fhe-params/src/constants.rs +++ b/crates/fhe-params/src/constants.rs @@ -84,4 +84,9 @@ pub mod defaults { /// when variance is not specified. Both variance() and error1_variance default to this value. pub const VARIANCE: usize = 10; pub const ERROR1_VARIANCE: u32 = 10; + + /// Default insecure security parameter (λ). + pub const DEFAULT_INSECURE_LAMBDA: usize = 2; + /// Default secure security parameter (λ). + pub const DEFAULT_SECURE_LAMBDA: usize = 80; } diff --git a/crates/fhe-params/src/presets.rs b/crates/fhe-params/src/presets.rs index 9912cb2b81..dfaed3e3a8 100644 --- a/crates/fhe-params/src/presets.rs +++ b/crates/fhe-params/src/presets.rs @@ -7,11 +7,12 @@ use crate::builder::build_pair_for_preset; use crate::builder::{build_bfv_params_from_set, build_bfv_params_from_set_arc}; use crate::constants::{ + defaults::DEFAULT_INSECURE_LAMBDA, + defaults::DEFAULT_SECURE_LAMBDA, insecure_512, search_defaults::{B, B_CHI}, secure_8192, }; -use shared::{SecurityLevel, DEFAULT_INSECURE_LAMBDA, DEFAULT_SECURE_LAMBDA}; use std::sync::Arc; use thiserror::Error as ThisError; @@ -64,8 +65,6 @@ pub enum BfvPreset { pub struct PresetMetadata { /// The canonical name of the preset (e.g., "INSECURE_THRESHOLD_BFV_512") pub name: &'static str, - /// Security level classification (Secure if λ ≥ 80, Insecure otherwise) - pub security_level: SecurityLevel, /// LWE dimension (d) - the degree of the polynomial ring, must be a power of 2 /// /// This determines the size of the polynomial ring R_q = Z_q[X]/(X^d + 1). @@ -221,14 +220,12 @@ impl BfvPreset { match self { BfvPreset::InsecureThresholdBfv512 | BfvPreset::InsecureDkg512 => PresetMetadata { name: self.name(), - security_level: SecurityLevel::from_lambda(DEFAULT_INSECURE_LAMBDA), degree: insecure_512::DEGREE, num_parties: insecure_512::NUM_PARTIES, lambda: DEFAULT_INSECURE_LAMBDA, }, BfvPreset::SecureThresholdBfv8192 | BfvPreset::SecureDkg8192 => PresetMetadata { name: self.name(), - security_level: SecurityLevel::from_lambda(DEFAULT_SECURE_LAMBDA), degree: secure_8192::DEGREE, num_parties: secure_8192::NUM_PARTIES, lambda: DEFAULT_SECURE_LAMBDA, @@ -364,13 +361,13 @@ mod tests { let metadata = insecure.metadata(); assert_eq!(metadata.degree, insecure_512::DEGREE); assert_eq!(metadata.num_parties, insecure_512::NUM_PARTIES); - assert_eq!(metadata.lambda, shared::DEFAULT_INSECURE_LAMBDA); + assert_eq!(metadata.lambda, DEFAULT_INSECURE_LAMBDA); let secure = BfvPreset::SecureThresholdBfv8192; let metadata = secure.metadata(); assert_eq!(metadata.degree, secure_8192::DEGREE); assert_eq!(metadata.num_parties, secure_8192::NUM_PARTIES); - assert_eq!(metadata.lambda, shared::DEFAULT_SECURE_LAMBDA); + assert_eq!(metadata.lambda, DEFAULT_SECURE_LAMBDA); } #[test] @@ -380,14 +377,14 @@ mod tests { assert_eq!(defaults.n, insecure_512::threshold::SEARCH_N); assert_eq!(defaults.k, insecure_512::threshold::SEARCH_K); assert_eq!(defaults.z, insecure_512::threshold::SEARCH_Z); - assert_eq!(defaults.lambda, shared::DEFAULT_INSECURE_LAMBDA as u32); + assert_eq!(defaults.lambda, DEFAULT_INSECURE_LAMBDA as u32); let preset = BfvPreset::SecureThresholdBfv8192; let defaults = preset.search_defaults().unwrap(); assert_eq!(defaults.n, secure_8192::threshold::SEARCH_N); assert_eq!(defaults.k, secure_8192::threshold::SEARCH_K); assert_eq!(defaults.z, secure_8192::threshold::SEARCH_Z); - assert_eq!(defaults.lambda, shared::DEFAULT_SECURE_LAMBDA as u32); + assert_eq!(defaults.lambda, DEFAULT_SECURE_LAMBDA as u32); // DKG presets don't have search defaults assert!(BfvPreset::InsecureDkg512.search_defaults().is_none()); diff --git a/crates/greco-helpers/Cargo.toml b/crates/greco-helpers/Cargo.toml index b69428eb6d..cec4b15967 100644 --- a/crates/greco-helpers/Cargo.toml +++ b/crates/greco-helpers/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/gnosisguild/enclave/crates/greco-helpers" fhe = { workspace = true } fhe-math = { git = "https://github.com/gnosisguild/fhe.rs" } num-bigint = { workspace = true } -shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator" } +e3-zk-helpers = { workspace = true } [dev-dependencies] e3-fhe-params = { workspace = true } diff --git a/crates/greco-helpers/src/lib.rs b/crates/greco-helpers/src/lib.rs index 0ac3618642..92277ab372 100644 --- a/crates/greco-helpers/src/lib.rs +++ b/crates/greco-helpers/src/lib.rs @@ -4,10 +4,10 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. +use e3_zk_helpers::utils::get_zkp_modulus; use fhe::bfv::{BfvParameters, Ciphertext, PublicKey}; use fhe_math::rq::Representation; use num_bigint::BigInt; -use shared::constants::get_zkp_modulus; use std::sync::Arc; /// Converts a BFV coefficient (in [0, qi)) to centered format [-(qi-1)/2, (qi-1)/2]. @@ -171,12 +171,12 @@ pub fn bfv_public_key_to_greco( mod tests { use super::*; use e3_fhe_params::{BfvParamSet, BfvPreset}; + use e3_zk_helpers::utils::calculate_bit_width; use fhe::bfv::{Encoding, Plaintext, PublicKey, SecretKey}; use fhe_traits::FheEncoder; use greco::bounds::GrecoBounds; use greco::vectors::GrecoVectors; use rand::thread_rng; - use shared::template::calculate_bit_width; #[test] fn test_bfv_public_key_to_greco() { diff --git a/crates/polynomial/Cargo.toml b/crates/polynomial/Cargo.toml index 60440b4e62..47739f9672 100644 --- a/crates/polynomial/Cargo.toml +++ b/crates/polynomial/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "polynomial" +name = "e3-polynomial" version.workspace = true edition.workspace = true license.workspace = true diff --git a/crates/polynomial/benches/polynomial.rs b/crates/polynomial/benches/polynomial.rs index e4ac19c2c8..ce738a188c 100644 --- a/crates/polynomial/benches/polynomial.rs +++ b/crates/polynomial/benches/polynomial.rs @@ -5,9 +5,9 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use e3_polynomial::Polynomial; use num_bigint::BigInt; use num_traits::{One, Zero}; -use polynomial::Polynomial; fn create_test_polynomials(degree: usize) -> (Polynomial, Polynomial) { let mut coeffs1 = Vec::new(); @@ -123,7 +123,7 @@ fn benchmark_utility_functions(c: &mut Criterion) { group.bench_function("reduce_and_center", |b| { b.iter(|| { - black_box(polynomial::utils::reduce_and_center( + black_box(e3_polynomial::utils::reduce_and_center( &x, &modulus, &half_modulus, @@ -137,7 +137,7 @@ fn benchmark_utility_functions(c: &mut Criterion) { group.bench_function("range_check_standard", |b| { b.iter(|| { - black_box(polynomial::utils::range_check_standard( + black_box(e3_polynomial::utils::range_check_standard( &coeffs, &bound, &modulus, )) }) diff --git a/crates/polynomial/src/polynomial.rs b/crates/polynomial/src/polynomial.rs index 86fad50b29..b148193586 100644 --- a/crates/polynomial/src/polynomial.rs +++ b/crates/polynomial/src/polynomial.rs @@ -209,7 +209,7 @@ impl Polynomial { /// # Examples /// /// ``` - /// use polynomial::Polynomial; + /// use e3_polynomial::Polynomial; /// use num_bigint::BigInt; /// /// let poly = Polynomial::new(vec![ @@ -422,7 +422,7 @@ impl Polynomial { /// # Examples /// /// ``` - /// use polynomial::Polynomial; + /// use e3_polynomial::Polynomial; /// use num_bigint::BigInt; /// /// // Reduce modulo x^4 + 1 (cyclotomic polynomial for N=4) diff --git a/crates/safe/Cargo.toml b/crates/safe/Cargo.toml index 499c153e57..82bc0bdce8 100644 --- a/crates/safe/Cargo.toml +++ b/crates/safe/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "safe" +name = "e3-safe" version.workspace = true edition.workspace = true license.workspace = true @@ -8,7 +8,7 @@ repository = "https://github.com/gnosisguild/enclave/crates/safe" [dependencies] sha3 = "0.10.8" -ark-ff = "0.5" -ark-bn254 = "0.5" +ark-ff = { workspace = true } +ark-bn254 = { workspace = true } taceo-poseidon2 = { version = "0.2", features = ["bn254", "t4"] } hex = { workspace = true } diff --git a/crates/support/Cargo.lock b/crates/support/Cargo.lock index 076c40346e..efe34d2c28 100644 --- a/crates/support/Cargo.lock +++ b/crates/support/Cargo.lock @@ -2987,53 +2987,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] -name = "e3-bfv-helpers" -version = "0.1.5" +name = "e3-bfv-client" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" dependencies = [ - "alloy-dyn-abi", - "alloy-primitives", "anyhow", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80)", + "e3-greco-helpers", "fhe", - "fhe-math", "fhe-traits", - "fhe-util", - "itertools 0.14.0", - "ndarray", - "num-bigint", - "num-traits", "rand 0.8.5", - "strum", "thiserror 1.0.69", "zkfhe-greco", "zkfhe-shared", ] [[package]] -name = "e3-bfv-helpers" +name = "e3-compute-provider" version = "0.1.5" source = "git+https://github.com/gnosisguild/enclave?rev=632766e4ed1ceeccdeb023a56f16413a33be6f46#632766e4ed1ceeccdeb023a56f16413a33be6f46" dependencies = [ - "alloy-dyn-abi", - "alloy-primitives", - "anyhow", - "fhe", - "fhe-math", - "fhe-traits", - "fhe-util", + "ark-bn254 0.4.0", + "ark-ff 0.4.2", + "hex", + "lean-imt", + "light-poseidon", "num-bigint", - "rand 0.8.5", - "strum", - "thiserror 1.0.69", - "zkfhe-greco", + "num-traits", + "rayon", + "serde", + "sha3", + "zk-kit-imt", ] [[package]] name = "e3-compute-provider" -version = "0.1.5" -source = "git+https://github.com/gnosisguild/enclave?rev=632766e4ed1ceeccdeb023a56f16413a33be6f46#632766e4ed1ceeccdeb023a56f16413a33be6f46" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", + "e3-bfv-client", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80)", "hex", "lean-imt", "light-poseidon", @@ -3045,13 +3040,47 @@ dependencies = [ "zk-kit-imt", ] +[[package]] +name = "e3-fhe-params" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" +dependencies = [ + "alloy-dyn-abi", + "alloy-primitives", + "fhe", + "num-bigint", + "thiserror 1.0.69", + "zkfhe-shared", +] + +[[package]] +name = "e3-fhe-params" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=dbdaaee1db9d868adacc03f75bb2ac85024359ae#dbdaaee1db9d868adacc03f75bb2ac85024359ae" +dependencies = [ + "fhe", + "num-bigint", + "thiserror 1.0.69", +] + +[[package]] +name = "e3-greco-helpers" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" +dependencies = [ + "fhe", + "fhe-math", + "num-bigint", + "zkfhe-shared", +] + [[package]] name = "e3-support-app" version = "0.1.0" dependencies = [ "actix-web", "anyhow", - "e3-compute-provider", + "e3-compute-provider 0.1.5", "e3-support-host", "e3-support-types", "env_logger", @@ -3074,8 +3103,8 @@ dependencies = [ "boundless-market", "bytemuck", "dotenvy", - "e3-bfv-helpers 0.1.5", - "e3-compute-provider", + "e3-compute-provider 0.1.5", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=dbdaaee1db9d868adacc03f75bb2ac85024359ae)", "e3-user-program", "fhe", "fhe-traits", @@ -3105,8 +3134,8 @@ dependencies = [ name = "e3-user-program" version = "0.1.0" dependencies = [ - "e3-bfv-helpers 0.1.5 (git+https://github.com/gnosisguild/enclave?rev=632766e4ed1ceeccdeb023a56f16413a33be6f46)", - "e3-compute-provider", + "e3-compute-provider 0.1.7", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=dbdaaee1db9d868adacc03f75bb2ac85024359ae)", "fhe", "fhe-traits", ] diff --git a/crates/support/Cargo.toml b/crates/support/Cargo.toml index 0b7d63df80..49ba45a1f5 100644 --- a/crates/support/Cargo.toml +++ b/crates/support/Cargo.toml @@ -13,6 +13,8 @@ e3-support-app = { path = "./app" } e3-support-host = { path = "./host" } e3-user-program = { path = "./program" } e3-support-types = { path = "./types" } +e3-fhe-params = { git = "https://github.com/gnosisguild/enclave", rev = "dbdaaee1db9d868adacc03f75bb2ac85024359ae" } + methods = { path = "./methods" } alloy-primitives = { version = "1.3", default-features = false, features = [ "rlp", diff --git a/crates/support/methods/guest/Cargo.lock b/crates/support/methods/guest/Cargo.lock index e62e604de3..86d53cdc06 100644 --- a/crates/support/methods/guest/Cargo.lock +++ b/crates/support/methods/guest/Cargo.lock @@ -1037,22 +1037,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "e3-bfv-helpers" -version = "0.1.5" -source = "git+https://github.com/gnosisguild/enclave?rev=632766e4ed1ceeccdeb023a56f16413a33be6f46#632766e4ed1ceeccdeb023a56f16413a33be6f46" +name = "e3-bfv-client" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" dependencies = [ - "alloy-dyn-abi", - "alloy-primitives", "anyhow", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80)", + "e3-greco-helpers", "fhe", - "fhe-math", "fhe-traits", - "fhe-util", - "num-bigint", "rand 0.8.5", - "strum", "thiserror 1.0.69", "zkfhe-greco", + "zkfhe-shared", ] [[package]] @@ -1073,12 +1070,66 @@ dependencies = [ "zk-kit-imt", ] +[[package]] +name = "e3-compute-provider" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ff 0.4.2", + "e3-bfv-client", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80)", + "hex", + "lean-imt", + "light-poseidon", + "num-bigint", + "num-traits", + "rayon", + "serde", + "sha3", + "zk-kit-imt", +] + +[[package]] +name = "e3-fhe-params" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" +dependencies = [ + "alloy-dyn-abi", + "alloy-primitives", + "fhe", + "num-bigint", + "thiserror 1.0.69", + "zkfhe-shared", +] + +[[package]] +name = "e3-fhe-params" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=dbdaaee1db9d868adacc03f75bb2ac85024359ae#dbdaaee1db9d868adacc03f75bb2ac85024359ae" +dependencies = [ + "fhe", + "num-bigint", + "thiserror 1.0.69", +] + +[[package]] +name = "e3-greco-helpers" +version = "0.1.7" +source = "git+https://github.com/gnosisguild/enclave?rev=7a361de36d7ccd8f421b874008895cc07f246b80#7a361de36d7ccd8f421b874008895cc07f246b80" +dependencies = [ + "fhe", + "fhe-math", + "num-bigint", + "zkfhe-shared", +] + [[package]] name = "e3-user-program" version = "0.1.0" dependencies = [ - "e3-bfv-helpers", - "e3-compute-provider", + "e3-compute-provider 0.1.7", + "e3-fhe-params 0.1.7 (git+https://github.com/gnosisguild/enclave?rev=dbdaaee1db9d868adacc03f75bb2ac85024359ae)", "fhe", "fhe-traits", ] @@ -1421,7 +1472,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bincode", - "e3-compute-provider", + "e3-compute-provider 0.1.5", "e3-user-program", "risc0-zkvm", ] @@ -2786,27 +2837,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "subtle" version = "2.6.1" diff --git a/crates/support/program/Cargo.toml b/crates/support/program/Cargo.toml index 9bde077c7f..e281d133bf 100644 --- a/crates/support/program/Cargo.toml +++ b/crates/support/program/Cargo.toml @@ -7,4 +7,4 @@ edition = "2024" fhe = { workspace = true } fhe-traits = { workspace = true } e3-compute-provider = { git = "https://github.com/gnosisguild/enclave", rev = "7a361de36d7ccd8f421b874008895cc07f246b80" } -e3-fhe-params = { git = "https://github.com/gnosisguild/enclave", rev = "7a361de36d7ccd8f421b874008895cc07f246b80" } +e3-fhe-params = { git = "https://github.com/gnosisguild/enclave", rev = "dbdaaee1db9d868adacc03f75bb2ac85024359ae" } diff --git a/crates/zk-helpers/Cargo.toml b/crates/zk-helpers/Cargo.toml new file mode 100644 index 0000000000..da4bd1a440 --- /dev/null +++ b/crates/zk-helpers/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "e3-zk-helpers" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "ZK circuit helpers" +repository = "https://github.com/gnosisguild/enclave/crates/zk-helpers" + +[dependencies] +ark-bn254 = { workspace = true } +ark-ff = { workspace = true } +e3-polynomial = { workspace = true } +e3-safe = { workspace = true } +fhe = { workspace = true } +num-bigint = { workspace = true } +num-traits = { workspace = true } +thiserror = { workspace = true } diff --git a/crates/zk-helpers/src/commitments.rs b/crates/zk-helpers/src/commitments.rs new file mode 100644 index 0000000000..4e4e20f557 --- /dev/null +++ b/crates/zk-helpers/src/commitments.rs @@ -0,0 +1,596 @@ +// 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. + +//! Commitment computation functions for zero-knowledge circuits. +//! +//! This module provides functions to compute commitments to various cryptographic objects +//! (polynomials, public keys, secret keys, shares, etc.) using the SAFE sponge hash function. +//! All functions match the corresponding Noir circuit implementations exactly. + +use crate::packing::flatten; +use crate::utils::compute_safe; +use ark_bn254::Fr as Field; +use ark_ff::BigInteger; +use ark_ff::PrimeField; +use num_bigint::BigInt; + +// ============================================================================ +// DOMAIN SEPARATORS +// ============================================================================ + +/// Domain separator for BFV public key commitments. +/// String: "PK_BFV" +const DS_PK_BFV: [u8; 64] = [ + 0x50, 0x4b, 0x5f, 0x42, 0x46, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for TRBFV public key commitments. +/// String: "PK_TRBFV" +const DS_PK_TRBFV: [u8; 64] = [ + 0x50, 0x4b, 0x5f, 0x54, 0x52, 0x42, 0x46, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for secret commitments (sk_trbfv or e_sm). +/// String: "SECRET" +const DS_SECRET: [u8; 64] = [ + 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for shares party-modulus commitments. +/// String: "SPM" +const DS_SPM: [u8; 64] = [ + 0x53, 0x50, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for aggregated shares commitments. +/// String: "AGG_SHARES" +const DS_AGG_SHARES: [u8; 64] = [ + 0x41, 0x47, 0x47, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for public key aggregation commitments. +/// String: "PK_AGG" +const DS_PK_AGG: [u8; 64] = [ + 0x50, 0x4b, 0x5f, 0x41, 0x47, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for aggregation commitments. +/// String: "AGGREGATION" +const DS_AGGREGATION: [u8; 64] = [ + 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for general-purpose ciphertext commitments. +/// String: "CIPHERTEXT" +const DS_CIPHERTEXT: [u8; 64] = [ + 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for public key TRBFV challenge. +/// String: "CLG_PK_TRBFV" +const DS_CLG_PK_TRBFV: [u8; 64] = [ + 0x43, 0x4c, 0x47, 0x5f, 0x50, 0x4b, 0x5f, 0x54, 0x52, 0x42, 0x46, 0x56, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for encryption BFV challenge. +/// String: "CLG_ENC_BFV" +const DS_CLG_ENC_BFV: [u8; 64] = [ + 0x43, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x43, 0x5f, 0x42, 0x46, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for Greco challenge. +/// String: "CLG_GRECO" +const DS_CLG_GRECO: [u8; 64] = [ + 0x43, 0x4c, 0x47, 0x5f, 0x47, 0x52, 0x45, 0x43, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +/// Domain separator for decryption share challenge. +/// String: "CLG_DEC_SHARE" +const DS_CLG_DEC_SHARE: [u8; 64] = [ + 0x43, 0x4c, 0x47, 0x5f, 0x44, 0x45, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]; + +// ============================================================================ +// WRAPPERS +// ============================================================================ + +/// Compute commitments using SAFE sponge with the given domain separator and payload. +/// +/// This matches the Noir `compute_commitments` function exactly. +/// +/// # Arguments +/// * `payload` - Vector of field elements to hash +/// * `domain_separator` - Domain separator for cross-protocol security +/// * `io_pattern` - IO pattern `[ABSORB(input_size), SQUEEZE(output_size)]` +/// +/// # Returns +/// A vector of field elements from the sponge +pub fn compute_commitments( + payload: Vec, + domain_separator: [u8; 64], + io_pattern: [u32; 2], +) -> Vec { + compute_safe(domain_separator, payload, io_pattern) +} + +/// Compute a commitment to the BFV public key polynomials by flattening them and hashing. +/// +/// This matches the Noir `compute_pk_bfv_commitment` function exactly. +/// +/// # Arguments +/// * `pk0` - First component of the BFV public key (one vector per modulus) +/// * `pk1` - Second component of the BFV public key (one vector per modulus) +/// * `bit_pk` - The bit width for public key coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_pk_bfv_commitment(pk0: &[Vec], pk1: &[Vec], bit_pk: u32) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, pk0, bit_pk); + payload = flatten(payload, pk1, bit_pk); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_PK_BFV, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute a commitment to the TRBFV public key polynomials by flattening them and hashing. +/// +/// This matches the Noir `compute_pk_trbfv_commitment` function exactly. +/// +/// # Arguments +/// * `pk0` - First component of the TRBFV public key (one vector per modulus) +/// * `pk1` - Second component of the TRBFV public key (one vector per modulus) +/// * `bit_pk` - The bit width for public key coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_pk_trbfv_commitment( + pk0: &[Vec], + pk1: &[Vec], + bit_pk: u32, +) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, pk0, bit_pk); + payload = flatten(payload, pk1, bit_pk); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_PK_TRBFV, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute a commitment to the secret key polynomial by flattening it and hashing. +/// +/// This matches the Noir `compute_secret_sk_commitment` function exactly. +/// +/// # Arguments +/// * `sk` - Secret key polynomial coefficients +/// * `bit_sk` - The bit width for secret key coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_secret_sk_commitment(sk: &[BigInt], bit_sk: u32) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, &[sk.to_vec()], bit_sk); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_SECRET, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute a commitment to the smudging noise (e_sm). +/// +/// This matches the Noir `compute_secret_e_sm_commitment` function exactly. +/// +/// # Arguments +/// * `e_sm` - Smudging noise polynomial coefficients (one vector per modulus) +/// * `bit_e_sm` - The bit width for smudging noise coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_secret_e_sm_commitment(e_sm: &[Vec], bit_e_sm: u32) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, e_sm, bit_e_sm); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_SECRET, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +// ============================================================================ +// COMMITMENTS +// ============================================================================ + +/// Compute SPM commitment from message polynomial. +/// +/// This matches the Noir `compute_spm_commitment_from_message` function exactly. +/// +/// # Arguments +/// * `message` - Message polynomial coefficients +/// * `bit_msg` - The bit width for message coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_spm_commitment_from_message(message: &[BigInt], bit_msg: u32) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, &[message.to_vec()], bit_msg); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_SPM, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute SPM commitment from shares. +/// +/// This matches the Noir `compute_spm_commitment_from_shares` function exactly. +/// Used in C2 (verify shares circuit). +/// +/// # Arguments +/// * `y` - 3D array of share values: `y[coeff_idx][mod_idx][party_idx]` +/// * `party_idx` - Index of the party (0-based) +/// * `mod_idx` - Index of the modulus +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_spm_commitment_from_shares( + y: &[Vec>], + party_idx: usize, + mod_idx: usize, +) -> BigInt { + let mut payload = Vec::new(); + + // Add shares y[coeff_idx][mod_idx][party_idx + 1] for each coefficient + for coeff_y in y { + let share_value = coeff_y.get(mod_idx).expect("Modulus index out of bounds"); + let share_value = share_value + .get(party_idx + 1) + .expect("Party index out of bounds"); + payload.push(crate::utils::bigint_to_field(share_value)); + } + + // Include party_idx and mod_idx in the hash + payload.push(Field::from(party_idx as u64)); + payload.push(Field::from(mod_idx as u64)); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_SPM, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute public key aggregation commitment. +/// +/// This matches the Noir `compute_pk_agg_commitment` function exactly. +/// +/// # Arguments +/// * `pk0` - First component of the public key (one vector per modulus) +/// * `pk1` - Second component of the public key (one vector per modulus) +/// * `bit_pk` - The bit width for public key coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_pk_agg_commitment(pk0: &[Vec], pk1: &[Vec], bit_pk: u32) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, pk0, bit_pk); + payload = flatten(payload, pk1, bit_pk); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_PK_AGG, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute aggregation commitment. +/// +/// This matches the Noir `compute_aggregation_commitment` function exactly. +/// +/// # Arguments +/// * `payload` - Prepared payload as a vector of field elements +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_aggregation_commitment(payload: Vec) -> BigInt { + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_AGGREGATION, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute CRISP ciphertext commitment. +/// +/// # Arguments +/// * `ct0` - First component of the ciphertext (one vector per modulus) +/// * `ct1` - Second component of the ciphertext (one vector per modulus) +/// * `bit_ct` - The bit width for ciphertext coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_ciphertext_commitment( + ct0: &[Vec], + ct1: &[Vec], + bit_ct: u32, +) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, ct0, bit_ct); + payload = flatten(payload, ct1, bit_ct); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_CIPHERTEXT, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +/// Compute aggregated shares commitment (either sk_shares or e_sm_shares). +/// +/// This matches the Noir `compute_aggregated_shares_commitment` function exactly. +/// +/// # Arguments +/// * `agg_shares` - Array of aggregated share polynomials (one per modulus) +/// * `bit_msg` - The bit width for message coefficient bounds +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_aggregated_shares_commitment(agg_shares: &[Vec], bit_msg: u32) -> BigInt { + let mut payload = Vec::new(); + payload = flatten(payload, agg_shares, bit_msg); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_AGG_SHARES, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +// ============================================================================ +// COMMITMENTS FOR CHALLENGES +// ============================================================================ + +/// Compute public key TRBFV challenge. +/// +/// This matches the Noir `compute_pk_trbfv_challenge` function exactly. +/// +/// # Arguments +/// * `payload` - Prepared payload as a vector of field elements +/// * `l` - Number of moduli +/// +/// # Returns +/// A vector of `BigInt` challenges (2*L elements) +pub fn compute_pk_trbfv_challenge(payload: Vec, l: usize) -> Vec { + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, (2 * l as u32)]; + + compute_commitments(payload, DS_CLG_PK_TRBFV, io_pattern) + .into_iter() + .map(|challenge_field| { + let challenge_bytes = challenge_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &challenge_bytes) + }) + .collect() +} + +/// Compute BFV encryption challenge. +/// +/// This matches the Noir `compute_bfv_enc_challenge` function exactly. +/// +/// # Arguments +/// * `payload` - Prepared payload as a vector of field elements +/// * `l` - Number of moduli +/// +/// # Returns +/// A vector of `BigInt` challenges (2*L elements) +pub fn compute_bfv_enc_challenge(payload: Vec, l: usize) -> Vec { + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, (2 * l as u32)]; + + compute_commitments(payload, DS_CLG_ENC_BFV, io_pattern) + .into_iter() + .map(|challenge_field| { + let challenge_bytes = challenge_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &challenge_bytes) + }) + .collect() +} + +/// Compute Greco challenge commitment. +/// +/// This matches the Noir `compute_greco_challenge_commitment` function exactly. +/// Verifies pk_commitment using pk0is and pk1is, then generates challenges from gammas_payload. +/// +/// # Arguments +/// * `pk0is` - First component of public keys (one vector per modulus) +/// * `pk1is` - Second component of public keys (one vector per modulus) +/// * `gammas_payload` - Payload for generating challenges +/// * `pk_commitment` - Expected public key commitment value +/// * `bit_pk` - The bit width for public key coefficient bounds +/// * `l` - Number of moduli +/// +/// # Returns +/// A vector of `BigInt` challenges (2*L elements) +/// +/// # Panics +/// Panics if the computed public key commitment doesn't match `pk_commitment` +pub fn compute_greco_challenge_commitment( + pk0is: &[Vec], + pk1is: &[Vec], + gammas_payload: Vec, + pk_commitment: &BigInt, + bit_pk: u32, + l: usize, +) -> Vec { + // Verify pk_commitment matches the commitment from pk0is and pk1is + let computed_pk_commitment = compute_pk_agg_commitment(pk0is, pk1is, bit_pk); + if computed_pk_commitment != *pk_commitment { + panic!( + "PK commitment mismatch in Greco circuit: expected {}, got {}", + pk_commitment, computed_pk_commitment + ); + } + + let input_size = gammas_payload.len() as u32; + let io_pattern = [0x80000000 | input_size, (2 * l as u32)]; + + compute_commitments(gammas_payload, DS_CLG_GRECO, io_pattern) + .into_iter() + .map(|challenge_field| { + let challenge_bytes = challenge_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &challenge_bytes) + }) + .collect() +} + +/// Compute decryption share challenge. +/// +/// This matches the Noir `compute_dec_share_challenge` function exactly. +/// +/// # Arguments +/// * `payload` - Prepared payload as a vector of field elements +/// +/// # Returns +/// A `BigInt` representing the commitment hash value +pub fn compute_dec_share_challenge(payload: Vec) -> BigInt { + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + + let commitment_field = compute_commitments(payload, DS_CLG_DEC_SHARE, io_pattern)[0]; + let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &commitment_bytes) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::utils::bigint_to_field; + + fn field_to_bigint(value: Field) -> BigInt { + let bytes = value.into_bigint().to_bytes_le(); + BigInt::from_bytes_le(num_bigint::Sign::Plus, &bytes) + } + + #[test] + fn compute_ciphertext_commitment_matches_manual_payload() { + let ct0 = vec![vec![BigInt::from(1), BigInt::from(2)]]; + let ct1 = vec![vec![BigInt::from(3), BigInt::from(4)]]; + let bit_ct = 4; + + let mut payload = Vec::new(); + payload = flatten(payload, &ct0, bit_ct); + payload = flatten(payload, &ct1, bit_ct); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + let expected = field_to_bigint(compute_commitments(payload, DS_CIPHERTEXT, io_pattern)[0]); + + let actual = compute_ciphertext_commitment(&ct0, &ct1, bit_ct); + assert_eq!(actual, expected); + } + + #[test] + fn compute_spm_commitment_from_shares_matches_manual_payload() { + let y = vec![ + vec![ + vec![BigInt::from(0), BigInt::from(11), BigInt::from(12)], + vec![BigInt::from(0), BigInt::from(21), BigInt::from(22)], + ], + vec![ + vec![BigInt::from(0), BigInt::from(13), BigInt::from(14)], + vec![BigInt::from(0), BigInt::from(23), BigInt::from(24)], + ], + vec![ + vec![BigInt::from(0), BigInt::from(15), BigInt::from(16)], + vec![BigInt::from(0), BigInt::from(25), BigInt::from(26)], + ], + ]; + let party_idx = 0; + let mod_idx = 1; + + let mut payload = Vec::new(); + for coeff_y in &y { + let share_value = &coeff_y[mod_idx][party_idx + 1]; + payload.push(bigint_to_field(share_value)); + } + payload.push(Field::from(party_idx as u64)); + payload.push(Field::from(mod_idx as u64)); + + let input_size = payload.len() as u32; + let io_pattern = [0x80000000 | input_size, 1]; + let expected = field_to_bigint(compute_commitments(payload, DS_SPM, io_pattern)[0]); + + let actual = compute_spm_commitment_from_shares(&y, party_idx, mod_idx); + assert_eq!(actual, expected); + } + + #[test] + fn challenge_lengths_match_expected_output() { + let payload = vec![Field::from(1u64), Field::from(2u64)]; + let l = 3; + + let pk_trbfv = compute_pk_trbfv_challenge(payload.clone(), l); + let bfv_enc = compute_bfv_enc_challenge(payload, l); + + assert_eq!(pk_trbfv.len(), 2 * l); + assert_eq!(bfv_enc.len(), 2 * l); + } +} diff --git a/crates/zk-helpers/src/lib.rs b/crates/zk-helpers/src/lib.rs new file mode 100644 index 0000000000..1d258c43af --- /dev/null +++ b/crates/zk-helpers/src/lib.rs @@ -0,0 +1,13 @@ +// 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 commitments; +pub mod packing; +pub mod utils; + +pub use commitments::*; +pub use packing::*; +pub use utils::*; diff --git a/crates/zk-helpers/src/packing.rs b/crates/zk-helpers/src/packing.rs new file mode 100644 index 0000000000..8105d77d79 --- /dev/null +++ b/crates/zk-helpers/src/packing.rs @@ -0,0 +1,166 @@ +// 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. + +//! Polynomial packing utilities for zero-knowledge circuits +//! +//! This module provides functions to pack polynomial coefficients into field elements +//! using a nibble-aligned layout, matching the Noir implementation exactly. + +use ark_bn254::Fr as Field; +use ark_ff::PrimeField; +use num_bigint::BigInt; +use num_traits::Zero; + +/// Compute hex-aligned packing parameters for a given `BIT`. +/// Matches the Noir `packing_layout` function exactly. +/// +/// # Arguments +/// * `bit` - The bit width for coefficient bounds +/// +/// # Returns +/// A tuple of (nibble_bits, group) where: +/// - `nibble_bits`: The bit width rounded up to the next multiple of 4 +/// - `group`: Maximum number of limbs that fit in one BN254 field element +fn packing_layout(bit: u32) -> (u32, u32) { + // Ceil BIT up to the next multiple of 4 (nibble alignment). + let nibble_bits = bit.div_ceil(4) * 4; + + // Each stored limb uses an extra nibble because negative coefficients + // will be shifted to positive, so radix = 2^(nibble_bits+4). + assert!(nibble_bits + 4 <= 254); + + // Maximum limbs that fit in one BN254 element without wrap. + let group = 254 / (nibble_bits + 4); + assert!(group >= 1); + (nibble_bits, group) +} + +/// Pack values into a Vec of carriers using the shared hex-aligned layout. +/// +/// Matches the Noir `packer` function exactly. +/// Packs multiple coefficients into each field element using nibble-aligned layout. +/// +/// # Arguments +/// * `values` - Slice of BigInt coefficients to pack +/// * `bit` - The bit width for coefficient bounds +/// +/// # Returns +/// A vector of field elements containing the packed coefficients. +/// The number of field elements is `ceil(values.len() / group)` where `group` is +/// determined by the packing layout. +fn packer(values: &[BigInt], bit: u32) -> Vec { + // Layout parameters: nibble-aligned width and limbs-per-carrier group size. + let (nibble_bits, group) = packing_layout(bit); + + let base = BigInt::from(2).pow(nibble_bits); + let radix = BigInt::from(2).pow(nibble_bits + 4); + + // Number of chunks to emit: ceil(A / group). + let a = values.len() as u32; + let num_chunks = a.div_ceil(group); + let mut out = Vec::new(); + + // Process in fixed-size chunks of `group` limbs. + for chunk in 0..num_chunks { + // How many real values go into this chunk. + let remain = a - (chunk * group); + let take = if remain < group { remain } else { group }; + + // Build field element accumulator (big-endian concatenation in `radix`). + let mut acc = BigInt::zero(); + for i in 0..take { + let v = &values[(chunk * group + i) as usize]; + acc = acc * &radix + (v + &base); + } + + // Pad remaining limb slots with the canonical zero-limb `digit = base`. + for _ in 0..(group - take) { + acc = acc * &radix + &base; + } + + // Convert BigInt to Field element + let acc_biguint = if acc < BigInt::zero() { + // Should not happen with our packing scheme, but handle it + panic!("Negative accumulator in packer"); + } else { + acc.to_biguint().unwrap() + }; + + // Convert to Field via bytes + let bytes = acc_biguint.to_bytes_le(); + let field_elem = Field::from_le_bytes_mod_order(&bytes); + out.push(field_elem); + } + out +} + +/// Flatten `L` polynomials into a single linear stream of packed `Field` carriers. +/// +/// Matches the Noir `flatten` function exactly. +/// Packs each polynomial using the same bit width and appends them sequentially. +/// +/// # Arguments +/// * `inputs` - Initial vector of field elements to append to +/// * `polys` - Slice of polynomials (each represented as Vec) +/// * `bit` - The bit width for coefficient bounds +/// +/// # Returns +/// Extended vector with packed polynomial coefficients appended in order. +/// The polynomials are packed sequentially, maintaining a stable transcript layout. +pub fn flatten(mut inputs: Vec, polys: &[Vec], bit: u32) -> Vec { + for poly in polys { + // Pack coefficients into carriers using the same BIT layout. + let packed = packer(poly, bit); + + // Append carriers in-order to `inputs` to keep a stable transcript layout. + inputs.extend(packed); + } + + // Return the extended input stream. + inputs +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_packing_layout() { + // Test nibble alignment + // For bit=1 or 4: nibble_bits=4, radix uses 4+4=8 bits, so group=254/8=31 + assert_eq!(packing_layout(1), (4, 31)); + assert_eq!(packing_layout(4), (4, 31)); + // For bit=5 or 8: nibble_bits=8, radix uses 8+4=12 bits, so group=254/12=21 + assert_eq!(packing_layout(5), (8, 21)); + assert_eq!(packing_layout(8), (8, 21)); + // For bit=51: nibble_bits=52, radix uses 52+4=56 bits, so group=254/56=4 + assert_eq!(packing_layout(51), (52, 4)); + } + + #[test] + fn test_packer_single_value() { + let values = vec![BigInt::from(42)]; + let packed = packer(&values, 8); + assert!(!packed.is_empty()); + } + + #[test] + fn test_flatten_empty() { + let inputs = Vec::new(); + let polys: Vec> = vec![]; + let result = flatten(inputs, &polys, 8); + assert_eq!(result.len(), 0); + } + + #[test] + fn test_flatten_single_poly() { + let inputs = Vec::new(); + let poly = vec![BigInt::from(1), BigInt::from(2), BigInt::from(3)]; + let polys = vec![poly]; + let result = flatten(inputs, &polys, 8); + assert!(!result.is_empty()); + } +} diff --git a/crates/zk-helpers/src/utils.rs b/crates/zk-helpers/src/utils.rs new file mode 100644 index 0000000000..18237880c3 --- /dev/null +++ b/crates/zk-helpers/src/utils.rs @@ -0,0 +1,210 @@ +// 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. + +//! Utility functions for zkFHE circuit generation +//! +//! This module contains helper functions for: +//! - String conversion of BigInt vectors +//! - SAFE sponge hash computation +//! - BigInt to Field element conversion +//! - Bit width calculation from bounds +//! - ZKP modulus constants + +use ark_bn254::Fr as Field; +use ark_bn254::Fr as FieldElement; +use ark_ff::PrimeField; +use e3_safe::SafeSponge; +use num_bigint::BigInt; +use num_traits::Zero; +use std::str::FromStr; +use thiserror::Error as ThisError; + +#[derive(ThisError, Debug)] +pub enum ZkHelpersUtilsError { + #[error("Failed to parse bound: {0}")] + ParseBound(String), +} + +pub type Result = std::result::Result; + +/// Convert a 1D vector of BigInt to a vector of strings. +/// +/// # Arguments +/// * `vec` - Slice of BigInt values +/// +/// # Returns +/// A vector of strings, one per BigInt value +pub fn to_string_1d_vec(vec: &[BigInt]) -> Vec { + vec.iter().map(|x| x.to_string()).collect() +} + +/// Convert a 2D vector of BigInt to a vector of vectors of strings. +/// +/// # Arguments +/// * `poly` - Slice of BigInt vectors (e.g., polynomial coefficients per modulus) +/// +/// # Returns +/// A 2D vector of strings +pub fn to_string_2d_vec(poly: &[Vec]) -> Vec> { + poly.iter().map(|row| to_string_1d_vec(row)).collect() +} + +/// Convert a 3D vector of BigInt to a vector of vectors of vectors of strings. +/// +/// # Arguments +/// * `vec` - 3D slice of BigInt values +/// +/// # Returns +/// A 3D vector of strings +pub fn to_string_3d_vec(vec: &[Vec>]) -> Vec>> { + vec.iter().map(|d1| to_string_2d_vec(d1)).collect() +} + +/// Compute SAFE sponge hash with the given domain separator and inputs. +/// +/// This is a convenience wrapper around the SAFE sponge API that performs +/// START, ABSORB, SQUEEZE, and FINISH operations in sequence. +/// +/// # Arguments +/// * `domain_separator` - 64-byte domain separator for cross-protocol security +/// * `inputs` - Vector of field elements to absorb +/// * `io_pattern` - IO pattern array `[ABSORB(input_size), SQUEEZE(output_size)]` +/// +/// # Returns +/// A vector of field elements squeezed from the sponge +pub fn compute_safe( + domain_separator: [u8; 64], + inputs: Vec, + io_pattern: [u32; 2], +) -> Vec { + let mut sponge = SafeSponge::start(io_pattern, domain_separator); + sponge.absorb(inputs); + let digests = sponge.squeeze(); + sponge.finish(); + + digests +} + +/// Convert BigInt to Field by reducing modulo ZKP modulus. +/// +/// This is a helper to simplify BigInt to Field conversion. +/// Handles negative values by reducing them to the positive range [0, ZKP_MODULUS). +/// +/// # Arguments +/// * `value` - BigInt value to convert +/// +/// # Returns +/// A field element representing the value modulo ZKP modulus +pub fn bigint_to_field(value: &BigInt) -> FieldElement { + let zkp_modulus = get_zkp_modulus(); + let reduced = if value < &BigInt::zero() { + (value % &zkp_modulus) + &zkp_modulus + } else { + value % &zkp_modulus + }; + let biguint = reduced + .to_biguint() + .unwrap_or_else(|| (&zkp_modulus + reduced).to_biguint().unwrap()); + let bytes = biguint.to_bytes_le(); + FieldElement::from_le_bytes_mod_order(&bytes) +} + +/// Calculate bit width from a bound string. +/// +/// # Arguments +/// * `bound_str` - String representation of the bound value +/// +/// # Returns +/// The calculated bit width, or an error if the bound cannot be parsed +/// +/// # Errors +/// Returns `ZkHelpersUtilsError::ParseBound` if the bound string cannot be parsed as a BigInt +pub fn calculate_bit_width(bound_str: &str) -> Result { + let bound = BigInt::from_str(bound_str) + .map_err(|e| ZkHelpersUtilsError::ParseBound(format!("{bound_str}: {e}")))?; + + if bound <= BigInt::from(0) { + return Ok(1); // Minimum 1 bit + } + + Ok(bound.bits() as u32) +} + +/// Get the ZKP modulus as a BigInt. +/// +/// The ZKP modulus is the BN254 scalar field modulus: +/// 21888242871839275222246405745257275088548364400416034343698204186575808495617 +/// +/// # Returns +/// The ZKP modulus as a BigInt +/// +/// # Panics +/// Panics if the modulus constant is invalid (should never happen) +pub fn get_zkp_modulus() -> BigInt { + BigInt::from_str( + "21888242871839275222246405745257275088548364400416034343698204186575808495617", + ) + .expect("Invalid ZKP modulus") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn calculate_bit_width_handles_zero_and_positive_bounds() { + assert_eq!(calculate_bit_width("0").unwrap(), 1); + assert_eq!(calculate_bit_width("1").unwrap(), 1); + assert_eq!(calculate_bit_width("2").unwrap(), 2); + assert_eq!(calculate_bit_width("3").unwrap(), 2); + assert_eq!(calculate_bit_width("4").unwrap(), 3); + assert_eq!(calculate_bit_width("7").unwrap(), 3); + assert_eq!(calculate_bit_width("8").unwrap(), 4); + } + + #[test] + fn calculate_bit_width_rejects_invalid_input() { + let err = calculate_bit_width("nope").unwrap_err(); + let msg = format!("{err}"); + assert!(msg.contains("Failed to parse bound")); + } + + #[test] + fn bigint_to_field_reduces_modulus() { + let modulus = get_zkp_modulus(); + let value = modulus.clone() + BigInt::from(5); + let reduced = bigint_to_field(&value); + assert_eq!(reduced, bigint_to_field(&BigInt::from(5))); + } + + #[test] + fn bigint_to_field_handles_negative() { + let modulus = get_zkp_modulus(); + let value = BigInt::from(-1); + let expected = bigint_to_field(&(modulus - BigInt::from(1))); + assert_eq!(bigint_to_field(&value), expected); + } + + #[test] + fn to_string_helpers_round_trip() { + let one = BigInt::from(1); + let two = BigInt::from(2); + let three = BigInt::from(3); + + assert_eq!( + to_string_1d_vec(&[one.clone(), two.clone()]), + vec!["1", "2"] + ); + assert_eq!( + to_string_2d_vec(&[vec![one.clone(), two.clone()], vec![three.clone()]]), + vec![vec!["1", "2"], vec!["3"]] + ); + assert_eq!( + to_string_3d_vec(&[vec![vec![one, two, three]]]), + vec![vec![vec!["1", "2", "3"]]] + ); + } +} diff --git a/examples/CRISP/Cargo.lock b/examples/CRISP/Cargo.lock index 48f6cb8f20..708b6b44c6 100644 --- a/examples/CRISP/Cargo.lock +++ b/examples/CRISP/Cargo.lock @@ -2381,12 +2381,12 @@ dependencies = [ "anyhow", "e3-fhe-params", "e3-greco-helpers", + "e3-zk-helpers", "fhe", "fhe-traits", "rand 0.8.5", "thiserror 1.0.69", "zkfhe-greco", - "zkfhe-shared", ] [[package]] @@ -2433,17 +2433,16 @@ dependencies = [ "fhe", "num-bigint", "thiserror 1.0.69", - "zkfhe-shared", ] [[package]] name = "e3-greco-helpers" version = "0.1.7" dependencies = [ + "e3-zk-helpers", "fhe", "fhe-math", "num-bigint", - "zkfhe-shared", ] [[package]] @@ -2461,6 +2460,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "e3-polynomial" +version = "0.1.7" +dependencies = [ + "num-bigint", + "num-traits", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "e3-program-server" version = "0.1.7" @@ -2476,6 +2485,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "e3-safe" +version = "0.1.7" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "hex", + "sha3", + "taceo-poseidon2", +] + [[package]] name = "e3-sdk" version = "0.1.7" @@ -2522,6 +2542,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "e3-zk-helpers" +version = "0.1.7" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "e3-polynomial", + "e3-safe", + "fhe", + "num-bigint", + "num-traits", + "thiserror 1.0.69", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -4330,16 +4364,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "polynomial" -version = "0.1.7" -dependencies = [ - "num-bigint", - "num-traits", - "serde", - "thiserror 1.0.69", -] - [[package]] name = "polynomial" version = "0.1.7" @@ -6746,7 +6770,9 @@ dependencies = [ "crisp-constants", "e3-bfv-client", "e3-fhe-params", + "e3-polynomial", "e3-sdk", + "e3-zk-helpers", "eyre", "fhe", "fhe-math", @@ -6756,13 +6782,11 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "polynomial 0.1.7", "rand 0.8.5", "rayon", "serde", "serde_json", "zkfhe-greco", - "zkfhe-shared", ] [[package]] @@ -6790,7 +6814,7 @@ dependencies = [ "itertools 0.14.0", "num-bigint", "num-traits", - "polynomial 0.1.7 (git+https://github.com/gnosisguild/enclave?branch=main)", + "polynomial", "rand 0.8.5", "rayon", "serde", @@ -6814,7 +6838,7 @@ dependencies = [ "fhe-traits", "num-bigint", "num-traits", - "polynomial 0.1.7 (git+https://github.com/gnosisguild/enclave?branch=main)", + "polynomial", "rand 0.8.5", "safe", "serde", diff --git a/examples/CRISP/Cargo.toml b/examples/CRISP/Cargo.toml index d865596241..217c6c5ca9 100644 --- a/examples/CRISP/Cargo.toml +++ b/examples/CRISP/Cargo.toml @@ -38,9 +38,10 @@ derivative = "=2.2.0" e3-compute-provider = { path = "../../crates/compute-provider" } e3-program-server = { path = "../../crates/program-server" } e3-bfv-client = { path = "../../crates/bfv-client" } +e3-zk-helpers = { path = "../../crates/zk-helpers" } e3-fhe-params = { path = "../../crates/fhe-params" } e3-sdk = { path = "../../crates/sdk", default-features = false } -polynomial = { path = "../../crates/polynomial" } +e3-polynomial = { path = "../../crates/polynomial" } eyre = "=0.6.12" env_logger = "=0.11.8" hex = { version = "=0.4.3" } diff --git a/examples/CRISP/circuits/src/commitments.nr b/examples/CRISP/circuits/src/commitments.nr deleted file mode 100644 index 063043897b..0000000000 --- a/examples/CRISP/circuits/src/commitments.nr +++ /dev/null @@ -1,27 +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 lib::math::commitments::compute_commitments; -use lib::math::commitments::multiple_polynomial_payload; -use lib::math::polynomial::Polynomial; - -// Domain separator - "CRISP_CT" -pub global DS_CRISP_CT: [u8; 64] = [ - 0x43, 0x52, 0x49, 0x53, 0x50, 0x5f, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; - -pub fn compute_ct_commitment( - ct0: [Polynomial; L], - ct1: [Polynomial; L], -) -> Field { - let mut payload = multiple_polynomial_payload::(Vec::new(), ct0); - payload = multiple_polynomial_payload::(payload, ct1); - - compute_commitments(payload, DS_CRISP_CT, [0x80000000 | payload.len(), 1]).get(0) -} diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index d33a8a19fc..87865aa74d 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -4,15 +4,14 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use commitments::compute_ct_commitment; use lib::configs::insecure::trbfv::{ GRECO_BIT_CT, GRECO_BIT_E0, GRECO_BIT_E1, GRECO_BIT_K, GRECO_BIT_P1, GRECO_BIT_P2, GRECO_BIT_PK, GRECO_BIT_R1, GRECO_BIT_R2, GRECO_BIT_U, GRECO_CONFIGS, L, N, Q_MOD_T_MOD_P, }; use lib::core::greco::Greco; +use lib::math::commitments::compute_ciphertext_commitment; use lib::math::polynomial::Polynomial; -mod commitments; mod constants; mod ciphertext_addition; use ciphertext_addition::CiphertextAddition; @@ -172,7 +171,7 @@ fn main( // * Return sum ciphertext commitment (sum_ct_commitment) // Generate the vote ciphertext commitment. - let ct_commitment = compute_ct_commitment::<512, 2, 36>(ct0is, ct1is); + let ct_commitment = compute_ciphertext_commitment::<512, 2, 36>(ct0is, ct1is); if is_mask_vote == false { check_coefficient_values_with_balance(k1, Q_MOD_T_MOD_P, balance); @@ -188,8 +187,10 @@ fn main( if is_first_vote { ct_commitment } else { - let _prev_ct_commitment = compute_ct_commitment::<512, 2, 36>(prev_ct0is, prev_ct1is); - let sum_ct_commitment = compute_ct_commitment::<512, 2, 36>(sum_ct0is, sum_ct1is); + let _prev_ct_commitment = + compute_ciphertext_commitment::<512, 2, 36>(prev_ct0is, prev_ct1is); + let sum_ct_commitment = + compute_ciphertext_commitment::<512, 2, 36>(sum_ct0is, sum_ct1is); let ct_add: CiphertextAddition<512, 2, 36, 36, 36> = CiphertextAddition::new( GRECO_CONFIGS, diff --git a/examples/CRISP/crates/zk-inputs/Cargo.toml b/examples/CRISP/crates/zk-inputs/Cargo.toml index c730ecc5af..bf6e8cd988 100644 --- a/examples/CRISP/crates/zk-inputs/Cargo.toml +++ b/examples/CRISP/crates/zk-inputs/Cargo.toml @@ -15,8 +15,8 @@ fhe-traits.workspace = true e3-bfv-client = { workspace = true } e3-fhe-params = { workspace = true } greco = { package = "zkfhe-greco", git = "https://github.com/gnosisguild/zkfhe-generator" } -shared = { package = "zkfhe-shared", git = "https://github.com/gnosisguild/zkfhe-generator" } -polynomial = { workspace = true, features = ["serde"] } +e3-zk-helpers = { workspace = true } +e3-polynomial = { workspace = true, features = ["serde"] } serde.workspace = true serde_json.workspace = true num-bigint = "0.4.6" diff --git a/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs b/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs index debaf02c7f..033b96e448 100644 --- a/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs +++ b/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs @@ -4,19 +4,20 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use crate::commitments::compute_commitment; +use e3_polynomial::{reduce_and_center_coefficients_mut, reduce_coefficients_2d}; +use e3_zk_helpers::commitments::compute_ciphertext_commitment; +use e3_zk_helpers::utils::{calculate_bit_width, get_zkp_modulus}; use eyre::{Context, Result}; use fhe::bfv::BfvParameters; use fhe::bfv::Ciphertext; use fhe::bfv::Plaintext; use fhe_math::rq::Representation; +use greco::bounds::GrecoBounds; use itertools::izip; use num_bigint::BigInt; use num_integer::Integer; use num_traits::Zero; -use polynomial::{reduce_and_center_coefficients_mut, reduce_coefficients_2d}; use rayon::iter::{ParallelBridge, ParallelIterator}; -use shared::constants::get_zkp_modulus; use std::sync::Arc; /// Set of inputs for validation of a ciphertext addition. @@ -230,7 +231,10 @@ impl CiphertextAdditionInputs { res.r1is[i] = r1i; } - res.prev_ct_commitment = compute_commitment(params, &res.prev_ct0is, &res.prev_ct1is)?; + let (_, bounds) = GrecoBounds::compute(¶ms, 0)?; + let bit = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + res.prev_ct_commitment = + compute_ciphertext_commitment(&res.prev_ct0is, &res.prev_ct1is, bit); Ok(res) } diff --git a/examples/CRISP/crates/zk-inputs/src/commitments.rs b/examples/CRISP/crates/zk-inputs/src/commitments.rs deleted file mode 100644 index b64f3e1508..0000000000 --- a/examples/CRISP/crates/zk-inputs/src/commitments.rs +++ /dev/null @@ -1,63 +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 ark_bn254::Fr as Field; -use ark_ff::BigInteger; -use ark_ff::PrimeField; -use eyre::Result; -use fhe::bfv::BfvParameters; -use greco::bounds::GrecoBounds; -use num_bigint::BigInt; -use shared::packing::flatten; -use shared::utils::compute_safe; -use std::sync::Arc; - -/// Computes the commitment to a set of ciphertext polynomials. -/// -/// # Arguments -/// * `ct0is` - The first component of the ciphertext polynomials. -/// * `ct1is` - The second component of the ciphertext polynomials. -/// -/// # Returns -/// The commitment as a BigInt. -pub fn compute_commitment( - bfv_params: Arc, - ct0is: &[Vec], - ct1is: &[Vec], -) -> Result { - let (_, bounds) = GrecoBounds::compute(&bfv_params, 0)?; - let bit = shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string())?; - - // Step 1: Flatten both polynomial components (matches commitment_payload in Noir) - let mut inputs: Vec = Vec::new(); - inputs = flatten(inputs, ct0is, bit); - inputs = flatten(inputs, ct1is, bit); - - // Step 2: Hash using SafeSponge (matches generate_challenge in Noir) - // Domain separator - "CRISP_CT" - let domain_separator: [u8; 64] = [ - 0x43, 0x52, 0x49, 0x53, 0x50, 0x5f, 0x43, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - ]; - - // IO Pattern: ABSORB(input_size), SQUEEZE(1) - let input_size = inputs.len() as u32; - let io_pattern = [0x80000000 | input_size, 1]; - - let commitment = compute_safe(domain_separator, inputs, io_pattern); - - // Convert Field to BigInt - let commitment_field = commitment[0]; - let commitment_bytes = commitment_field.into_bigint().to_bytes_le(); - - Ok(BigInt::from_bytes_le( - num_bigint::Sign::Plus, - &commitment_bytes, - )) -} diff --git a/examples/CRISP/crates/zk-inputs/src/lib.rs b/examples/CRISP/crates/zk-inputs/src/lib.rs index b6dc3df8c8..1404157dcd 100644 --- a/examples/CRISP/crates/zk-inputs/src/lib.rs +++ b/examples/CRISP/crates/zk-inputs/src/lib.rs @@ -8,10 +8,11 @@ //! //! This crate contains the main logic for generating CRISP inputs for zero-knowledge proofs. -use crate::commitments::compute_commitment; use crisp_constants::get_default_paramset; use e3_fhe_params::build_bfv_params_arc; -use e3_fhe_params::{constants::insecure_512, BfvParamSet, BfvPreset}; +use e3_fhe_params::BfvParamSet; +use e3_zk_helpers::commitments::compute_ciphertext_commitment; +use e3_zk_helpers::utils::calculate_bit_width; use eyre::{Context, Result}; use fhe::bfv::BfvParameters; use fhe::bfv::Ciphertext; @@ -29,7 +30,6 @@ mod ciphertext_addition; use crate::ciphertext_addition::CiphertextAdditionInputs; mod serialization; use serialization::{construct_inputs, serialize_inputs_to_json}; -mod commitments; pub struct ZKInputsGenerator { bfv_params: Arc, @@ -96,7 +96,7 @@ impl ZKInputsGenerator { let (_, bounds) = GrecoBounds::compute(&self.bfv_params, 0)?; - let bit_pk = shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + let bit_pk = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; // Compute the vectors of the GRECO inputs. let greco_vectors = GrecoVectors::compute( @@ -172,7 +172,7 @@ impl ZKInputsGenerator { let (_, bounds) = GrecoBounds::compute(&self.bfv_params, 0)?; - let bit_pk = shared::template::calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + let bit_pk = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; // Compute the vectors of the GRECO inputs. let greco_vectors = GrecoVectors::compute( @@ -273,19 +273,22 @@ impl ZKInputsGenerator { ct0is: &[Vec], ct1is: &[Vec], ) -> Result { - compute_commitment(self.bfv_params.clone(), ct0is, ct1is) + let (_, bounds) = GrecoBounds::compute(&self.bfv_params, 0)?; + let bit = calculate_bit_width(&bounds.pk_bounds[0].to_string())?; + + Ok(compute_ciphertext_commitment(ct0is, ct1is, bit)) } } #[cfg(test)] mod tests { use super::*; - - const DEFAULT_DEGREE: usize = 512; + use e3_fhe_params::constants::insecure_512; + use e3_fhe_params::BfvPreset; /// Helper function to create a vote vector with alternating 0s and 1s (deterministic) fn create_vote_vector() -> Vec { - (0..DEFAULT_DEGREE).map(|i| (i % 2) as u64).collect() + (0..insecure_512::DEGREE).map(|i| (i % 2) as u64).collect() } #[test] diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol index 0a1e45bce1..f21107efac 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol @@ -1,11 +1,14 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2022 Aztec +// 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. pragma solidity >=0.8.21; uint256 constant N = 524288; uint256 constant LOG_N = 19; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 22; -uint256 constant VK_HASH = 0x2db23f945f10f8251c7fae6d42923485512fb4ae365cae7afb2088bf1f8fcba4; +uint256 constant VK_HASH = 0x084c1dbd3a478acf6065432b799017fb92cc2e06de6373862a1a7578db01fbfb; library HonkVerificationKey { function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { Honk.VerificationKey memory vk = Honk.VerificationKey({ @@ -33,8 +36,8 @@ library HonkVerificationKey { y: uint256(0x0a9c42dc2ec320da7f3be004c85731dfc8de9f97d78e6cdd25e3e0c5aa26e366) }), qc: Honk.G1Point({ - x: uint256(0x2cea75256a79391fa13e40b434288ce68ab5c8707c2fff771405c11682141696), - y: uint256(0x0a32621fc716daa418f975b8cb711449937aec57aff134bca7f8b13bd4bc14d1) + x: uint256(0x253d6e099522a46c571c16f013f89a847e18e937b2515fb26a115b21d44b3dd7), + y: uint256(0x2e92543d0491394566f08541590dd3d4bf7a6918bd846fbf7886809266c83174) }), qLookup: Honk.G1Point({ x: uint256(0x111ada27d4243c5df982e1cd77f2d9aff394ba4f2ba2faf8ec1a8e5b6d78d1e7), diff --git a/examples/CRISP/server/Dockerfile b/examples/CRISP/server/Dockerfile index 0cb6f8241b..056691e0a0 100644 --- a/examples/CRISP/server/Dockerfile +++ b/examples/CRISP/server/Dockerfile @@ -90,6 +90,7 @@ COPY crates/tests/Cargo.toml crates/tests/Cargo.toml COPY crates/trbfv/Cargo.toml crates/trbfv/Cargo.toml COPY crates/utils/Cargo.toml crates/utils/Cargo.toml COPY crates/wasm/Cargo.toml crates/wasm/Cargo.toml +COPY crates/zk-helpers/Cargo.toml crates/zk-helpers/Cargo.toml RUN set -eux; \ find \ diff --git a/templates/default/Cargo.lock b/templates/default/Cargo.lock index 96072886cc..9cdc5ddfe0 100644 --- a/templates/default/Cargo.lock +++ b/templates/default/Cargo.lock @@ -1169,12 +1169,12 @@ dependencies = [ "anyhow", "e3-fhe-params", "e3-greco-helpers", + "e3-zk-helpers", "fhe", "fhe-traits", "rand 0.8.5", "thiserror", "zkfhe-greco", - "zkfhe-shared", ] [[package]] @@ -1205,17 +1205,25 @@ dependencies = [ "fhe", "num-bigint", "thiserror", - "zkfhe-shared", ] [[package]] name = "e3-greco-helpers" version = "0.1.7" dependencies = [ + "e3-zk-helpers", "fhe", "fhe-math", "num-bigint", - "zkfhe-shared", +] + +[[package]] +name = "e3-polynomial" +version = "0.1.7" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", ] [[package]] @@ -1233,6 +1241,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "e3-safe" +version = "0.1.7" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "hex", + "sha3", + "taceo-poseidon2", +] + [[package]] name = "e3-support-scripts-dev" version = "0.1.0" @@ -1256,6 +1275,20 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "e3-zk-helpers" +version = "0.1.7" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "e3-polynomial", + "e3-safe", + "fhe", + "num-bigint", + "num-traits", + "thiserror", +] + [[package]] name = "ecdsa" version = "0.16.9" diff --git a/templates/default/program/src/lib.rs b/templates/default/program/src/lib.rs index cf36581c1f..b29231ae3a 100644 --- a/templates/default/program/src/lib.rs +++ b/templates/default/program/src/lib.rs @@ -26,7 +26,7 @@ pub fn fhe_processor(fhe_inputs: &FHEInputs) -> Vec { mod tests { use super::*; use anyhow::Result; - use e3_fhe_params::{BfvParamSet, BfvPreset, build_bfv_params_arc, encode_bfv_params}; + use e3_fhe_params::{build_bfv_params_arc, encode_bfv_params, BfvParamSet, BfvPreset}; use fhe::bfv::{Encoding, Plaintext, PublicKey, SecretKey}; use fhe_traits::FheEncoder; use fhe_traits::FheEncrypter; @@ -48,9 +48,9 @@ mod tests { let secret_key = SecretKey::random(¶ms, &mut OsRng); let public_key = PublicKey::new(&secret_key, &mut rng); - // 10 - let ten = public_key.try_encrypt( - &Plaintext::try_encode(&[10u64], Encoding::poly(), ¶ms)?, + // 3 + let three = public_key.try_encrypt( + &Plaintext::try_encode(&[3u64], Encoding::poly(), ¶ms)?, &mut rng, )?; @@ -63,7 +63,7 @@ mod tests { // Prepare inputs let fhe_inputs = FHEInputs { params: encode_bfv_params(¶ms), - ciphertexts: vec![(ten.to_bytes(), 0), (two.to_bytes(), 1)], + ciphertexts: vec![(three.to_bytes(), 0), (two.to_bytes(), 1)], }; // Run the processor @@ -72,7 +72,7 @@ mod tests { // Decrypt result let decrypted = secret_key.try_decrypt(&Ciphertext::from_bytes(&result, ¶ms)?)?; - assert_eq!(decrypted.value[0], 12); + assert_eq!(decrypted.value[0], 5); Ok(()) } }