From 1979e399c476591cbc46d0cbe3fc511710746977 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 13 Feb 2026 11:14:45 +0100 Subject: [PATCH 1/3] improve c7 circuit computation --- .../computation.rs | 183 +++++++++--------- .../decrypted_shares_aggregation/sample.rs | 7 +- 2 files changed, 96 insertions(+), 94 deletions(-) diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs index 2210549538..82d8481f35 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs @@ -7,8 +7,9 @@ //! Bounds, configs, bits, and input computation for the Decryption Share Aggregation TRBFV circuit. //! //! Uses [`crate::threshold::decrypted_shares_aggregation::utils`] for Q/delta, modular inverses, -//! Lagrange-at-zero recovery, and scalar CRT reconstruction. Input coefficients are normalized -//! with [`e3_polynomial::reduce`] to [0, zkp_modulus) inside [`Inputs::compute`]. +//! Lagrange-at-zero recovery, and scalar CRT reconstruction. Decryption shares are normalized +//! with [`e3_polynomial::CrtPolynomial::reduce`]; all input coefficients are reduced to +//! [0, zkp_modulus) with [`e3_polynomial::reduce`] inside [`Inputs::compute`]. use crate::calculate_bit_width; use crate::get_zkp_modulus; @@ -20,7 +21,8 @@ use crate::{CircuitComputation, Computation}; use e3_fhe_params::build_pair_for_preset; use e3_fhe_params::BfvPreset; use e3_polynomial::reduce; -use fhe_math::rq::Representation; +use e3_polynomial::{CrtPolynomial, Polynomial}; +use fhe_math::rq::{Poly, Representation}; use num_bigint::{BigInt, BigUint}; use num_traits::Zero; use serde::{Deserialize, Serialize}; @@ -80,19 +82,20 @@ pub struct Configs { } /// Input for decrypted shares aggregation (same shape as old DecSharesAggTrBfvVectors). -/// All coefficients reduced to [0, zkp_modulus) by compute. +/// All polynomial-shaped data uses [`Polynomial`] / [`CrtPolynomial`] to match the Noir circuit; +/// coefficients are reduced to [0, zkp_modulus) by compute. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Inputs { - /// [party][modulus][coeff] - pub decryption_shares: Vec>>, + /// One CrtPolynomial per party (public witnesses); circuit: `[[Polynomial; L]; T+1]` + pub decryption_shares: Vec, /// Party IDs (1-based: 1, 2, ..., T+1) pub party_ids: Vec, - /// Message polynomial coefficients - pub message: Vec, - /// u_global polynomial (CRT reconstruction) - pub u_global: Vec, - /// [modulus][coeff] - pub crt_quotients: Vec>, + /// Message polynomial (public witness) + pub message: Polynomial, + /// u_global polynomial (CRT reconstruction, secret witness) + pub u_global: Polynomial, + /// CRT quotient polynomials per modulus (secret witnesses) + pub crt_quotients: CrtPolynomial, } impl Computation for Bounds { @@ -152,6 +155,21 @@ impl Computation for Configs { } } +/// Truncate to first max_len coefficients (index 0 = constant term, ascending order). +fn truncate_to_max_coeffs(v: &[BigInt], max_len: usize) -> Vec { + v.iter().take(max_len).cloned().collect() +} + +/// Truncate each limb of a [`CrtPolynomial`] to max_len coefficients. +fn truncate_crt_to_max_coeffs(crt: CrtPolynomial, max_len: usize) -> CrtPolynomial { + let limbs = crt + .limbs + .iter() + .map(|limb| Polynomial::new(truncate_to_max_coeffs(limb.coefficients(), max_len))) + .collect(); + CrtPolynomial::new(limbs) +} + impl Computation for Inputs { type Preset = BfvPreset; type Data = DecryptedSharesAggregationCircuitData; @@ -168,9 +186,10 @@ impl Computation for Inputs { let degree = ctx.degree; let threshold = data.committee.threshold; let max_msg_non_zero_coeffs = configs.max_msg_non_zero_coeffs; + let moduli = ctx.moduli(); // Copy to PowerBasis for coefficient extraction - let d_share_polys: Vec<_> = data + let d_share_polys: Vec = data .d_share_polys .iter() .map(|p| { @@ -189,46 +208,28 @@ impl Computation for Inputs { ))); } - // 1. Extract decryption shares per modulus per party [party][modulus][coeff] - let mut decryption_shares = Vec::with_capacity(d_share_polys.len()); + // Decryption shares: one CrtPolynomial per party (from_fhe + reduce) + let mut decryption_shares: Vec = Vec::with_capacity(d_share_polys.len()); for d_share in &d_share_polys { - let coeffs = d_share.coefficients(); - let mut party_shares = Vec::with_capacity(num_moduli); - for m in 0..num_moduli { - let modulus_row = coeffs.row(m); - let qi_bigint = BigInt::from(ctx.moduli()[m]); - let coeff_vec: Vec = modulus_row - .iter() - .map(|&x| { - let mut coeff = BigInt::from(x); - coeff %= &qi_bigint; - if coeff < BigInt::zero() { - coeff += &qi_bigint; - } - coeff - }) - .collect(); - party_shares.push(coeff_vec); - } - decryption_shares.push(party_shares); + let mut crt = CrtPolynomial::from_fhe_polynomial(d_share); + crt.reduce(moduli) + .map_err(|e| CircuitsErrors::Other(e.to_string()))?; + decryption_shares.push(crt); } - // 2. Party IDs (1-based) let party_ids: Vec = data .reconstructing_parties .iter() .map(|&x| BigInt::from(x)) .collect(); - - // 3. Message (pad to degree for computation, then truncate to MAX_MSG_NON_ZERO_COEFFS for input) let mut message: Vec = data.message_vec.iter().map(|&x| BigInt::from(x)).collect(); message.resize(degree, BigInt::zero()); - // 4. u^{(l)} via Lagrange per modulus + // u^{(l)} per modulus via Lagrange at zero let reconstructing_parties = &data.reconstructing_parties; let mut u_per_modulus: Vec> = Vec::new(); for m in 0..num_moduli { - let modulus = ctx.moduli()[m]; + let modulus = moduli[m]; let mut u_modulus_coeffs = Vec::with_capacity(degree); for coeff_idx in 0..degree { let shares: Vec = (0..=threshold) @@ -245,23 +246,23 @@ impl Computation for Inputs { u_per_modulus.push(u_modulus_coeffs); } - // 5. u_global via CRT reconstruction - let mut u_global: Vec = Vec::with_capacity(degree); + // u_global per coefficient via CRT reconstruction + let mut u_global_vec: Vec = Vec::with_capacity(degree); for coeff_idx in 0..degree { let rests: Vec = (0..num_moduli) .map(|m| u_per_modulus[m][coeff_idx]) .collect(); - let u_global_coeff = utils::crt_reconstruct(&rests, ctx.moduli())?; - u_global.push(BigInt::from(u_global_coeff)); + let u_global_coeff = utils::crt_reconstruct(&rests, moduli)?; + u_global_vec.push(BigInt::from(u_global_coeff)); } - // 6. CRT quotients: r^{(m)} = (u_global - u^{(m)}) / q_m - let mut crt_quotients: Vec> = Vec::new(); + // CRT quotients: r^{(m)} = (u_global - u^{(m)}) / q_m + let mut crt_quotients_limbs: Vec = Vec::with_capacity(num_moduli); for (m, u_modulus) in u_per_modulus.iter().enumerate().take(num_moduli) { - let q_m = ctx.moduli()[m]; + let q_m = moduli[m]; let q_m_bigint = BigInt::from(q_m); let mut r_m_coeffs = Vec::with_capacity(degree); - for (coeff_idx, u_global_val) in u_global.iter().enumerate().take(degree) { + for (coeff_idx, u_global_val) in u_global_vec.iter().enumerate().take(degree) { let u_m = BigInt::from(u_modulus[coeff_idx]); let diff = u_global_val - &u_m; let remainder = &diff % &q_m_bigint; @@ -273,43 +274,38 @@ impl Computation for Inputs { } r_m_coeffs.push(&diff / &q_m_bigint); } - crt_quotients.push(r_m_coeffs); + crt_quotients_limbs.push(Polynomial::new(r_m_coeffs)); } + let mut crt_quotients = CrtPolynomial::new(crt_quotients_limbs); - // Truncate to max_msg_non_zero_coeffs. Do NOT reverse: match old impl (dec_shares_agg_trbfv - // vectors.rs) and circuit—index 0 = constant term (ascending order). - let truncate = |v: &[BigInt]| -> Vec { - v.iter().take(max_msg_non_zero_coeffs).cloned().collect() - }; - let decryption_shares: Vec>> = decryption_shares - .into_iter() - .map(|party| party.into_iter().map(|row| truncate(&row)).collect()) - .collect(); - let message = truncate(&message); - let u_global = truncate(&u_global); - let crt_quotients: Vec> = crt_quotients + // Truncate to max_msg_non_zero_coeffs (index 0 = constant term, ascending order) + decryption_shares = decryption_shares .into_iter() - .map(|row| truncate(&row)) + .map(|crt| truncate_crt_to_max_coeffs(crt, max_msg_non_zero_coeffs)) .collect(); + let message_trunc = truncate_to_max_coeffs(&message, max_msg_non_zero_coeffs); + let u_global_trunc = truncate_to_max_coeffs(&u_global_vec, max_msg_non_zero_coeffs); + crt_quotients = truncate_crt_to_max_coeffs(crt_quotients, max_msg_non_zero_coeffs); let zkp_modulus = get_zkp_modulus(); - let decryption_shares: Vec>> = decryption_shares - .iter() - .map(|party| { - party - .iter() - .map(|row| row.iter().map(|c| reduce(c, &zkp_modulus)).collect()) - .collect() - }) - .collect(); + for crt in &mut decryption_shares { + crt.reduce_uniform(&zkp_modulus); + } let party_ids: Vec = party_ids.iter().map(|c| reduce(c, &zkp_modulus)).collect(); - let message: Vec = message.iter().map(|c| reduce(c, &zkp_modulus)).collect(); - let u_global: Vec = u_global.iter().map(|c| reduce(c, &zkp_modulus)).collect(); - let crt_quotients: Vec> = crt_quotients - .iter() - .map(|row| row.iter().map(|c| reduce(c, &zkp_modulus)).collect()) - .collect(); + let message = Polynomial::new( + message_trunc + .iter() + .map(|c| reduce(c, &zkp_modulus)) + .collect(), + ); + let u_global = Polynomial::new( + u_global_trunc + .iter() + .map(|c| reduce(c, &zkp_modulus)) + .collect(), + ); + crt_quotients.reduce_uniform(&zkp_modulus); Ok(Inputs { decryption_shares, @@ -322,26 +318,18 @@ impl Computation for Inputs { fn to_json(&self) -> serde_json::Result { use crate::bigint_1d_to_json_values; - use crate::poly_coefficients_to_toml_json; + use crate::crt_polynomial_to_toml_json; + use crate::polynomial_to_toml_json; let decryption_shares_json: Vec> = self .decryption_shares .iter() - .map(|party| { - party - .iter() - .map(|modulus_row| poly_coefficients_to_toml_json(modulus_row)) - .collect() - }) + .map(crt_polynomial_to_toml_json) .collect(); let party_ids_json = bigint_1d_to_json_values(&self.party_ids); - let message_json = poly_coefficients_to_toml_json(&self.message); - let u_global_json = poly_coefficients_to_toml_json(&self.u_global); - let crt_quotients_json: Vec = self - .crt_quotients - .iter() - .map(|row| poly_coefficients_to_toml_json(row)) - .collect(); + let message_json = polynomial_to_toml_json(&self.message); + let u_global_json = polynomial_to_toml_json(&self.u_global); + let crt_quotients_json = crt_polynomial_to_toml_json(&self.crt_quotients); let json = serde_json::json!({ "decryption_shares": decryption_shares_json, @@ -395,8 +383,19 @@ mod tests { let configs = Configs::compute(preset, &()).unwrap(); assert_eq!(out.inputs.decryption_shares.len(), committee.threshold + 1); assert_eq!(out.inputs.party_ids.len(), committee.threshold + 1); - assert_eq!(out.inputs.message.len(), configs.max_msg_non_zero_coeffs); - assert_eq!(out.inputs.u_global.len(), configs.max_msg_non_zero_coeffs); + assert_eq!( + out.inputs.message.coefficients().len(), + configs.max_msg_non_zero_coeffs + ); + assert_eq!( + out.inputs.u_global.coefficients().len(), + configs.max_msg_non_zero_coeffs + ); + assert_eq!(out.inputs.crt_quotients.limbs.len(), configs.l); + assert_eq!( + out.inputs.crt_quotients.limb(0).coefficients().len(), + configs.max_msg_non_zero_coeffs + ); assert!(out.bits.noise_bit > 0); } } diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs index e44b273978..af642cfc73 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/sample.rs @@ -295,7 +295,10 @@ mod tests { &(), ) .unwrap(); - assert_eq!(inputs.message.len(), configs.max_msg_non_zero_coeffs); + assert_eq!( + inputs.message.coefficients().len(), + configs.max_msg_non_zero_coeffs + ); } /// Input message matches sample (ascending order: index 0 = constant term). @@ -311,7 +314,7 @@ mod tests { let n = configs.max_msg_non_zero_coeffs; for i in 0..n { let expected = sample.message_vec.get(i).copied().unwrap_or(0); - let w = &inputs.message[i]; + let w = &inputs.message.coefficients()[i]; let exp = BigInt::from(expected); assert_eq!(w, &exp, "message coeff {} mismatch", i); } From 2403461592f5c628cb22b493586995f79bb698e1 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 13 Feb 2026 13:51:29 +0100 Subject: [PATCH 2/3] remove useless zkp_mod reduction from inputs computation --- circuits/bin/dkg/share_encryption/src/main.nr | 12 +- circuits/lib/src/configs/insecure/dkg.nr | 78 +- .../lib/src/configs/insecure/threshold.nr | 5 +- circuits/lib/src/configs/secure/dkg.nr | 78 +- circuits/lib/src/configs/secure/threshold.nr | 5 +- .../core/threshold/user_data_encryption.nr | 8 +- .../src/circuits/dkg/pk/computation.rs | 10 +- .../dkg/share_computation/computation.rs | 16 +- .../dkg/share_decryption/computation.rs | 2 +- .../circuits/dkg/share_encryption/codegen.rs | 16 +- .../dkg/share_encryption/computation.rs | 41 +- .../computation.rs | 8 +- .../threshold/pk_aggregation/computation.rs | 9 - .../threshold/pk_generation/computation.rs | 12 - .../threshold/share_decryption/computation.rs | 16 - .../threshold/user_data_encryption/codegen.rs | 40 +- .../user_data_encryption/computation.rs | 56 +- .../threshold/user_data_encryption/utils.rs | 18 +- crates/zk-helpers/src/utils.rs | 70 +- examples/CRISP/circuits/src/main.nr | 10 +- .../CRISP/crates/zk-inputs-wasm/src/lib.rs | 32 +- .../zk-inputs/src/ciphertext_addition.rs | 16 +- .../contracts/CRISPVerifier.sol | 4299 +++++++++-------- 23 files changed, 2365 insertions(+), 2492 deletions(-) diff --git a/circuits/bin/dkg/share_encryption/src/main.nr b/circuits/bin/dkg/share_encryption/src/main.nr index 8885a32668..ad96bbb1f2 100644 --- a/circuits/bin/dkg/share_encryption/src/main.nr +++ b/circuits/bin/dkg/share_encryption/src/main.nr @@ -5,10 +5,10 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use lib::configs::default::dkg::{ - DKG_SHARE_ENCRYPTION_BIT_CT, DKG_SHARE_ENCRYPTION_BIT_E0, DKG_SHARE_ENCRYPTION_BIT_E1, - DKG_SHARE_ENCRYPTION_BIT_MSG, DKG_SHARE_ENCRYPTION_BIT_P1, DKG_SHARE_ENCRYPTION_BIT_P2, - DKG_SHARE_ENCRYPTION_BIT_PK, DKG_SHARE_ENCRYPTION_BIT_R1, DKG_SHARE_ENCRYPTION_BIT_R2, - DKG_SHARE_ENCRYPTION_BIT_U, DKG_SHARE_ENCRYPTION_CONFIGS, L, N, + L, N, SHARE_ENCRYPTION_BIT_CT, SHARE_ENCRYPTION_BIT_E0, SHARE_ENCRYPTION_BIT_E1, + SHARE_ENCRYPTION_BIT_MSG, SHARE_ENCRYPTION_BIT_P1, SHARE_ENCRYPTION_BIT_P2, + SHARE_ENCRYPTION_BIT_PK, SHARE_ENCRYPTION_BIT_R1, SHARE_ENCRYPTION_BIT_R2, + SHARE_ENCRYPTION_BIT_U, SHARE_ENCRYPTION_CONFIGS, }; use lib::core::dkg::share_encryption::ShareEncryption; use lib::math::polynomial::Polynomial; @@ -31,8 +31,8 @@ fn main( p1is: [Polynomial<(2 * N) - 1>; L], p2is: [Polynomial; L], ) { - let share_encryption: ShareEncryption = ShareEncryption::new( - DKG_SHARE_ENCRYPTION_CONFIGS, + let share_encryption: ShareEncryption = ShareEncryption::new( + SHARE_ENCRYPTION_CONFIGS, expected_pk_commitment, expected_message_commitment, pk0is, diff --git a/circuits/lib/src/configs/insecure/dkg.nr b/circuits/lib/src/configs/insecure/dkg.nr index a3aec22e08..774ff59618 100644 --- a/circuits/lib/src/configs/insecure/dkg.nr +++ b/circuits/lib/src/configs/insecure/dkg.nr @@ -13,6 +13,8 @@ use crate::core::dkg::share_encryption::Configs as ShareEncryptionConfigs; pub global N: u32 = 512; pub global L: u32 = 1; pub global QIS: [Field; L] = [2251799813554177]; +pub global PLAINTEXT_MODULUS: Field = 68719403009; +pub global Q_MOD_T: Field = 2415755265; /************************************ ------------------------------------- @@ -70,46 +72,44 @@ share_encryption_e_sm (CIRCUIT 3b) ------------------------------------- ************************************/ -pub global DKG_SHARE_ENCRYPTION_BIT_PK: u32 = 50; -pub global DKG_SHARE_ENCRYPTION_BIT_CT: u32 = 50; -pub global DKG_SHARE_ENCRYPTION_BIT_U: u32 = 1; -pub global DKG_SHARE_ENCRYPTION_BIT_E0: u32 = 3; -pub global DKG_SHARE_ENCRYPTION_BIT_E1: u32 = 3; -pub global DKG_SHARE_ENCRYPTION_BIT_MSG: u32 = 36; -pub global DKG_SHARE_ENCRYPTION_BIT_R1: u32 = 35; -pub global DKG_SHARE_ENCRYPTION_BIT_R2: u32 = 50; -pub global DKG_SHARE_ENCRYPTION_BIT_P1: u32 = 9; -pub global DKG_SHARE_ENCRYPTION_BIT_P2: u32 = 50; - -pub global DKG_SHARE_ENCRYPTION_T: Field = 68719403009; -pub global DKG_SHARE_ENCRYPTION_Q_MOD_T: Field = 2415755265; -pub global DKG_SHARE_ENCRYPTION_K0IS: [Field; L] = [1284838520228573]; -pub global DKG_SHARE_ENCRYPTION_PK_BOUNDS: [Field; L] = [1125899906777088]; -pub global DKG_SHARE_ENCRYPTION_E0_BOUND: Field = 6; -pub global DKG_SHARE_ENCRYPTION_E1_BOUND: Field = 6; -pub global DKG_SHARE_ENCRYPTION_U_BOUND: Field = 1; -pub global DKG_SHARE_ENCRYPTION_R1_LOW_BOUNDS: [Field; L] = [19605059183]; -pub global DKG_SHARE_ENCRYPTION_R1_UP_BOUNDS: [Field; L] = [19605059183]; -pub global DKG_SHARE_ENCRYPTION_R2_BOUNDS: [Field; L] = [1125899906777088]; -pub global DKG_SHARE_ENCRYPTION_P1_BOUNDS: [Field; L] = [256]; -pub global DKG_SHARE_ENCRYPTION_P2_BOUNDS: [Field; L] = [1125899906777088]; -pub global DKG_SHARE_ENCRYPTION_MSG_BOUND: Field = 68719403008; - -pub global DKG_SHARE_ENCRYPTION_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( - DKG_SHARE_ENCRYPTION_T, - DKG_SHARE_ENCRYPTION_Q_MOD_T, +pub global SHARE_ENCRYPTION_BIT_PK: u32 = 50; +pub global SHARE_ENCRYPTION_BIT_CT: u32 = 50; +pub global SHARE_ENCRYPTION_BIT_U: u32 = 1; +pub global SHARE_ENCRYPTION_BIT_E0: u32 = 3; +pub global SHARE_ENCRYPTION_BIT_E1: u32 = 3; +pub global SHARE_ENCRYPTION_BIT_MSG: u32 = 36; +pub global SHARE_ENCRYPTION_BIT_R1: u32 = 35; +pub global SHARE_ENCRYPTION_BIT_R2: u32 = 50; +pub global SHARE_ENCRYPTION_BIT_P1: u32 = 9; +pub global SHARE_ENCRYPTION_BIT_P2: u32 = 50; + +pub global SHARE_ENCRYPTION_K0IS: [Field; L] = [1284838520228573]; +pub global SHARE_ENCRYPTION_PK_BOUNDS: [Field; L] = [1125899906777088]; +pub global SHARE_ENCRYPTION_E0_BOUND: Field = 6; +pub global SHARE_ENCRYPTION_E1_BOUND: Field = 6; +pub global SHARE_ENCRYPTION_U_BOUND: Field = 1; +pub global SHARE_ENCRYPTION_R1_LOW_BOUNDS: [Field; L] = [19605059183]; +pub global SHARE_ENCRYPTION_R1_UP_BOUNDS: [Field; L] = [19605059183]; +pub global SHARE_ENCRYPTION_R2_BOUNDS: [Field; L] = [1125899906777088]; +pub global SHARE_ENCRYPTION_P1_BOUNDS: [Field; L] = [256]; +pub global SHARE_ENCRYPTION_P2_BOUNDS: [Field; L] = [1125899906777088]; +pub global SHARE_ENCRYPTION_MSG_BOUND: Field = 68719403008; + +pub global SHARE_ENCRYPTION_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( + PLAINTEXT_MODULUS, + Q_MOD_T, QIS, - DKG_SHARE_ENCRYPTION_K0IS, - DKG_SHARE_ENCRYPTION_PK_BOUNDS, - DKG_SHARE_ENCRYPTION_E0_BOUND, - DKG_SHARE_ENCRYPTION_E1_BOUND, - DKG_SHARE_ENCRYPTION_U_BOUND, - DKG_SHARE_ENCRYPTION_R1_LOW_BOUNDS, - DKG_SHARE_ENCRYPTION_R1_UP_BOUNDS, - DKG_SHARE_ENCRYPTION_R2_BOUNDS, - DKG_SHARE_ENCRYPTION_P1_BOUNDS, - DKG_SHARE_ENCRYPTION_P2_BOUNDS, - DKG_SHARE_ENCRYPTION_MSG_BOUND, + SHARE_ENCRYPTION_K0IS, + SHARE_ENCRYPTION_PK_BOUNDS, + SHARE_ENCRYPTION_E0_BOUND, + SHARE_ENCRYPTION_E1_BOUND, + SHARE_ENCRYPTION_U_BOUND, + SHARE_ENCRYPTION_R1_LOW_BOUNDS, + SHARE_ENCRYPTION_R1_UP_BOUNDS, + SHARE_ENCRYPTION_R2_BOUNDS, + SHARE_ENCRYPTION_P1_BOUNDS, + SHARE_ENCRYPTION_P2_BOUNDS, + SHARE_ENCRYPTION_MSG_BOUND, ); /************************************ diff --git a/circuits/lib/src/configs/insecure/threshold.nr b/circuits/lib/src/configs/insecure/threshold.nr index 5f77691d70..5c3260b4ea 100644 --- a/circuits/lib/src/configs/insecure/threshold.nr +++ b/circuits/lib/src/configs/insecure/threshold.nr @@ -14,6 +14,7 @@ use crate::core::threshold::user_data_encryption::Configs as UserDataEncryptionC pub global N: u32 = 512; pub global L: u32 = 2; pub global QIS: [Field; L] = [68719403009, 68719230977]; +pub global Q_MOD_T: Field = -7; pub global PLAINTEXT_MODULUS: Field = 100; pub global Q_INVERSE_MOD_T: Field = 57; @@ -72,8 +73,6 @@ pub global USER_DATA_ENCRYPTION_BIT_R2: u32 = 35; pub global USER_DATA_ENCRYPTION_BIT_P1: u32 = 9; pub global USER_DATA_ENCRYPTION_BIT_P2: u32 = 35; -pub global USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P: Field = - 21888242871839275222246405745257275088548364400416034343698204186575808495610; pub global USER_DATA_ENCRYPTION_K0IS: [Field; L] = [61160268678, 8933500027]; pub global USER_DATA_ENCRYPTION_PK_BOUNDS: [Field; L] = [34359701504, 34359615488]; pub global USER_DATA_ENCRYPTION_E0_BOUND: Field = 20; @@ -88,7 +87,7 @@ pub global USER_DATA_ENCRYPTION_P1_BOUNDS: [Field; L] = [256, 256]; pub global USER_DATA_ENCRYPTION_P2_BOUNDS: [Field; L] = [34359701504, 34359615488]; pub global USER_DATA_ENCRYPTION_CONFIGS: UserDataEncryptionConfigs = UserDataEncryptionConfigs::new( - USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P, + Q_MOD_T, QIS, USER_DATA_ENCRYPTION_K0IS, USER_DATA_ENCRYPTION_PK_BOUNDS, diff --git a/circuits/lib/src/configs/secure/dkg.nr b/circuits/lib/src/configs/secure/dkg.nr index e2edcd8421..ac7bb418e6 100644 --- a/circuits/lib/src/configs/secure/dkg.nr +++ b/circuits/lib/src/configs/secure/dkg.nr @@ -13,6 +13,8 @@ use crate::core::dkg::share_encryption::Configs as ShareEncryptionConfigs; pub global N: u32 = 8192; pub global L: u32 = 2; pub global QIS: [Field; L] = [72057594082099201, 72057594062438401]; +pub global PLAINTEXT_MODULUS: Field = 18014398509481984; +pub global Q_MOD_T: Field = 1082658244788225; /************************************ ------------------------------------- @@ -80,46 +82,44 @@ share_encryption_e_sm (CIRCUIT 3b) ------------------------------------- ************************************/ -pub global DKG_SHARE_ENCRYPTION_BIT_PK: u32 = 56; -pub global DKG_SHARE_ENCRYPTION_BIT_CT: u32 = 56; -pub global DKG_SHARE_ENCRYPTION_BIT_U: u32 = 1; -pub global DKG_SHARE_ENCRYPTION_BIT_E0: u32 = 5; -pub global DKG_SHARE_ENCRYPTION_BIT_E1: u32 = 5; -pub global DKG_SHARE_ENCRYPTION_BIT_MSG: u32 = 54; -pub global DKG_SHARE_ENCRYPTION_BIT_R1: u32 = 53; -pub global DKG_SHARE_ENCRYPTION_BIT_R2: u32 = 56; -pub global DKG_SHARE_ENCRYPTION_BIT_P1: u32 = 13; -pub global DKG_SHARE_ENCRYPTION_BIT_P2: u32 = 56; - -pub global DKG_SHARE_ENCRYPTION_T: Field = 18014398509481984; -pub global DKG_SHARE_ENCRYPTION_Q_MOD_T: Field = 1082658244788225; -pub global DKG_SHARE_ENCRYPTION_K0IS: [Field; L] = [70854796903366627, 47439047573780733]; -pub global DKG_SHARE_ENCRYPTION_PK_BOUNDS: [Field; L] = [36028797041049600, 36028797031219200]; -pub global DKG_SHARE_ENCRYPTION_E0_BOUND: Field = 20; -pub global DKG_SHARE_ENCRYPTION_E1_BOUND: Field = 20; -pub global DKG_SHARE_ENCRYPTION_U_BOUND: Field = 1; -pub global DKG_SHARE_ENCRYPTION_R1_LOW_BOUNDS: [Field; L] = [8856849607495681, 5929880944709633]; -pub global DKG_SHARE_ENCRYPTION_R1_UP_BOUNDS: [Field; L] = [8856849607495680, 5929880944709632]; -pub global DKG_SHARE_ENCRYPTION_R2_BOUNDS: [Field; L] = [36028797041049600, 36028797031219200]; -pub global DKG_SHARE_ENCRYPTION_P1_BOUNDS: [Field; L] = [4096, 4096]; -pub global DKG_SHARE_ENCRYPTION_P2_BOUNDS: [Field; L] = [36028797041049600, 36028797031219200]; -pub global DKG_SHARE_ENCRYPTION_MSG_BOUND: Field = 18014398509481983; - -pub global DKG_SHARE_ENCRYPTION_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( - DKG_SHARE_ENCRYPTION_T, - DKG_SHARE_ENCRYPTION_Q_MOD_T, +pub global SHARE_ENCRYPTION_BIT_PK: u32 = 56; +pub global SHARE_ENCRYPTION_BIT_CT: u32 = 56; +pub global SHARE_ENCRYPTION_BIT_U: u32 = 1; +pub global SHARE_ENCRYPTION_BIT_E0: u32 = 5; +pub global SHARE_ENCRYPTION_BIT_E1: u32 = 5; +pub global SHARE_ENCRYPTION_BIT_MSG: u32 = 54; +pub global SHARE_ENCRYPTION_BIT_R1: u32 = 53; +pub global SHARE_ENCRYPTION_BIT_R2: u32 = 56; +pub global SHARE_ENCRYPTION_BIT_P1: u32 = 13; +pub global SHARE_ENCRYPTION_BIT_P2: u32 = 56; + +pub global SHARE_ENCRYPTION_K0IS: [Field; L] = [70854796903366627, 47439047573780733]; +pub global SHARE_ENCRYPTION_PK_BOUNDS: [Field; L] = [36028797041049600, 36028797031219200]; +pub global SHARE_ENCRYPTION_E0_BOUND: Field = 20; +pub global SHARE_ENCRYPTION_E1_BOUND: Field = 20; +pub global SHARE_ENCRYPTION_U_BOUND: Field = 1; +pub global SHARE_ENCRYPTION_R1_LOW_BOUNDS: [Field; L] = [8856849607495681, 5929880944709633]; +pub global SHARE_ENCRYPTION_R1_UP_BOUNDS: [Field; L] = [8856849607495680, 5929880944709632]; +pub global SHARE_ENCRYPTION_R2_BOUNDS: [Field; L] = [36028797041049600, 36028797031219200]; +pub global SHARE_ENCRYPTION_P1_BOUNDS: [Field; L] = [4096, 4096]; +pub global SHARE_ENCRYPTION_P2_BOUNDS: [Field; L] = [36028797041049600, 36028797031219200]; +pub global SHARE_ENCRYPTION_MSG_BOUND: Field = 18014398509481983; + +pub global SHARE_ENCRYPTION_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( + PLAINTEXT_MODULUS, + Q_MOD_T, QIS, - DKG_SHARE_ENCRYPTION_K0IS, - DKG_SHARE_ENCRYPTION_PK_BOUNDS, - DKG_SHARE_ENCRYPTION_E0_BOUND, - DKG_SHARE_ENCRYPTION_E1_BOUND, - DKG_SHARE_ENCRYPTION_U_BOUND, - DKG_SHARE_ENCRYPTION_R1_LOW_BOUNDS, - DKG_SHARE_ENCRYPTION_R1_UP_BOUNDS, - DKG_SHARE_ENCRYPTION_R2_BOUNDS, - DKG_SHARE_ENCRYPTION_P1_BOUNDS, - DKG_SHARE_ENCRYPTION_P2_BOUNDS, - DKG_SHARE_ENCRYPTION_MSG_BOUND, + SHARE_ENCRYPTION_K0IS, + SHARE_ENCRYPTION_PK_BOUNDS, + SHARE_ENCRYPTION_E0_BOUND, + SHARE_ENCRYPTION_E1_BOUND, + SHARE_ENCRYPTION_U_BOUND, + SHARE_ENCRYPTION_R1_LOW_BOUNDS, + SHARE_ENCRYPTION_R1_UP_BOUNDS, + SHARE_ENCRYPTION_R2_BOUNDS, + SHARE_ENCRYPTION_P1_BOUNDS, + SHARE_ENCRYPTION_P2_BOUNDS, + SHARE_ENCRYPTION_MSG_BOUND, ); /************************************ diff --git a/circuits/lib/src/configs/secure/threshold.nr b/circuits/lib/src/configs/secure/threshold.nr index dc02e7728d..c08510d2a1 100644 --- a/circuits/lib/src/configs/secure/threshold.nr +++ b/circuits/lib/src/configs/secure/threshold.nr @@ -15,6 +15,7 @@ pub global N: u32 = 8192; pub global L: u32 = 4; pub global QIS: [Field; L] = [2251799822204929, 4503599627763713, 4503599631433729, 4503599634579457]; +pub global Q_MOD_T: Field = -19; pub global PLAINTEXT_MODULUS: Field = 100; pub global Q_INVERSE_MOD_T: Field = 21; @@ -75,8 +76,6 @@ pub global USER_DATA_ENCRYPTION_BIT_R2: u32 = 52; pub global USER_DATA_ENCRYPTION_BIT_P1: u32 = 13; pub global USER_DATA_ENCRYPTION_BIT_P2: u32 = 52; -pub global USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P: Field = - 21888242871839275222246405745257275088548364400416034343698204186575808495598; pub global USER_DATA_ENCRYPTION_K0IS: [Field; L] = [1553741877321401, 3467771713378059, 3107483745689273, 4188347660158895]; pub global USER_DATA_ENCRYPTION_PK_BOUNDS: [Field; L] = @@ -95,7 +94,7 @@ pub global USER_DATA_ENCRYPTION_P2_BOUNDS: [Field; L] = [1125899911102464, 2251799813881856, 2251799815716864, 2251799817289728]; pub global USER_DATA_ENCRYPTION_CONFIGS: UserDataEncryptionConfigs = UserDataEncryptionConfigs::new( - USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P, + Q_MOD_T, QIS, USER_DATA_ENCRYPTION_K0IS, USER_DATA_ENCRYPTION_PK_BOUNDS, diff --git a/circuits/lib/src/core/threshold/user_data_encryption.nr b/circuits/lib/src/core/threshold/user_data_encryption.nr index fcad6705d1..94f18494d7 100644 --- a/circuits/lib/src/core/threshold/user_data_encryption.nr +++ b/circuits/lib/src/core/threshold/user_data_encryption.nr @@ -12,8 +12,8 @@ use crate::math::polynomial::Polynomial; pub struct Configs { /// CRT moduli: [q_0, q_1, ..., q_{L-1}] pub qis: [Field; L], - /// Plaintext modulus: q mod t mod p - pub q_mod_t_mod_p: Field, + /// Plaintext modulus: q mod t + pub q_mod_t: Field, /// Scaling factors for each basis: [k0_0, k0_1, ..., k0_{L-1}] pub k0is: [Field; L], /// Bounds for public key polynomials for each CRT basis @@ -42,7 +42,7 @@ pub struct Configs { impl Configs { pub fn new( - q_mod_t_mod_p: Field, + q_mod_t: Field, qis: [Field; L], k0is: [Field; L], pk_bounds: [Field; L], @@ -59,7 +59,7 @@ impl Configs { ) -> Self { Configs { qis, - q_mod_t_mod_p, + q_mod_t, k0is, pk_bounds, e0_bound, diff --git a/crates/zk-helpers/src/circuits/dkg/pk/computation.rs b/crates/zk-helpers/src/circuits/dkg/pk/computation.rs index eb1dc392c7..75bdcffbd5 100644 --- a/crates/zk-helpers/src/circuits/dkg/pk/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/pk/computation.rs @@ -13,7 +13,6 @@ use crate::circuits::dkg::pk::circuit::PkCircuit; use crate::circuits::dkg::pk::circuit::PkCircuitData; use crate::compute_max_modulus; use crate::crt_polynomial_to_toml_json; -use crate::get_zkp_modulus; use crate::utils::compute_modulus_bit; use crate::CircuitsErrors; use crate::{CircuitComputation, Computation}; @@ -147,13 +146,8 @@ impl Computation for Inputs { build_pair_for_preset(preset).map_err(|e| CircuitsErrors::Sample(e.to_string()))?; let moduli = dkg_params.moduli(); - let mut pk0is = crate::math::fhe_poly_to_crt_centered(&data.public_key.c.c[0], moduli)?; - let mut pk1is = crate::math::fhe_poly_to_crt_centered(&data.public_key.c.c[1], moduli)?; - - let zkp_modulus = &get_zkp_modulus(); - - pk0is.reduce_uniform(zkp_modulus); - pk1is.reduce_uniform(zkp_modulus); + let pk0is = crate::math::fhe_poly_to_crt_centered(&data.public_key.c.c[0], moduli)?; + let pk1is = crate::math::fhe_poly_to_crt_centered(&data.public_key.c.c[1], moduli)?; Ok(Inputs { pk0is, pk1is }) } diff --git a/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs index a231411701..feb90ac85d 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_computation/computation.rs @@ -10,16 +10,15 @@ //! and (for input) secret plus shares. Input values are normalized to [0, q_j) per modulus //! and then to the ZKP field modulus so the Noir circuit's range check and parity check succeed. +use crate::bigint_3d_to_json_values; use crate::circuits::commitments::{ compute_share_computation_e_sm_commitment, compute_share_computation_sk_commitment, }; use crate::computation::DkgInputType; use crate::dkg::share_computation::ShareComputationCircuit; use crate::dkg::share_computation::ShareComputationCircuitData; -use crate::poly_coefficients_to_toml_json; use crate::CircuitsErrors; -use crate::{bigint_3d_to_json_values, get_zkp_modulus}; -use crate::{calculate_bit_width, crt_polynomial_to_toml_json}; +use crate::{calculate_bit_width, crt_polynomial_to_toml_json, poly_coefficients_to_toml_json}; use crate::{CircuitComputation, Computation}; use e3_fhe_params::build_pair_for_preset; use e3_fhe_params::BfvPreset; @@ -229,17 +228,6 @@ impl Computation for Inputs { } }; - let zkp_modulus = &get_zkp_modulus(); - - secret_crt.reduce_uniform(zkp_modulus); - for coeff in &mut y { - for mod_row in coeff.iter_mut() { - for value in mod_row.iter_mut() { - *value = reduce(value, zkp_modulus); - } - } - } - Ok(Inputs { secret_crt, y, diff --git a/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs index 3d504b983a..5fa464abe8 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_decryption/computation.rs @@ -196,7 +196,7 @@ impl Computation for Inputs { // Used as input for Nargo execution. /// Serializes input so that `decrypted_shares` matches Noir's `[[Polynomial; L]; H]`: - /// each polynomial is `{ "coefficients": [string, ...] }`. + /// each polynomial is `{ "coefficients": [number|string, ...] }` (numbers when fit in i64). fn to_json(&self) -> serde_json::Result { let expected_commitments = bigint_2d_to_json_values(&self.expected_commitments); let decrypted_shares: Vec> = self diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs index 5837d1fdf1..fab0083097 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/codegen.rs @@ -65,6 +65,8 @@ pub fn generate_configs(preset: BfvPreset, configs: &Configs) -> CodegenConfigs pub global N: u32 = {}; pub global L: u32 = {}; pub global QIS: [Field; L] = [{}]; +pub global PLAINTEXT_MODULUS: Field = {}; +pub global Q_MOD_T: Field = {}; /************************************ ------------------------------------- @@ -84,8 +86,6 @@ pub global {}_BIT_R2: u32 = {}; pub global {}_BIT_P1: u32 = {}; pub global {}_BIT_P2: u32 = {}; -pub global {}_T: Field = {}; -pub global {}_Q_MOD_T: Field = {}; pub global {}_K0IS: [Field; L] = [{}]; pub global {}_PK_BOUNDS: [Field; L] = [{}]; pub global {}_E0_BOUND: Field = {}; @@ -99,8 +99,8 @@ pub global {}_P2_BOUNDS: [Field; L] = [{}]; pub global {}_MSG_BOUND: Field = {}; pub global {}_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( - {}_T, - {}_Q_MOD_T, + PLAINTEXT_MODULUS, + Q_MOD_T, QIS, {}_K0IS, {}_PK_BOUNDS, @@ -118,6 +118,8 @@ pub global {}_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( preset.dkg_counterpart().unwrap().metadata().degree, preset.dkg_counterpart().unwrap().metadata().num_moduli, qis_str, + configs.t, + configs.q_mod_t, prefix, configs.bits.pk_bit, prefix, @@ -139,10 +141,6 @@ pub global {}_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( prefix, configs.bits.p2_bit, prefix, - configs.t, - prefix, - configs.q_mod_t, - prefix, k0is_str, prefix, pk_bounds_str, @@ -176,8 +174,6 @@ pub global {}_CONFIGS: ShareEncryptionConfigs = ShareEncryptionConfigs::new( prefix, prefix, prefix, - prefix, - prefix, ) } diff --git a/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs b/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs index d8ee71dabb..503b8db454 100644 --- a/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs +++ b/crates/zk-helpers/src/circuits/dkg/share_encryption/computation.rs @@ -406,11 +406,6 @@ impl Computation for Inputs { pk1.reverse(); e0_crt.reverse(); - ct0.reduce(moduli)?; - ct1.reduce(moduli)?; - pk0.reduce(moduli)?; - pk1.reduce(moduli)?; - ct0.center(moduli)?; ct1.center(moduli)?; pk0.center(moduli)?; @@ -515,32 +510,16 @@ impl Computation for Inputs { e0_quotients.push(e0_quotient); } - let mut pk0is = CrtPolynomial::new(pk0is); - let mut pk1is = CrtPolynomial::new(pk1is); - let mut ct0is = CrtPolynomial::new(ct0is); - let mut ct1is = CrtPolynomial::new(ct1is); - let mut r1is = CrtPolynomial::new(r1is); - let mut r2is = CrtPolynomial::new(r2is); - let mut p1is = CrtPolynomial::new(p1is); - let mut p2is = CrtPolynomial::new(p2is); - let mut e0is = CrtPolynomial::new(e0is); - let mut e0_quotients = CrtPolynomial::new(e0_quotients); - - let zkp_modulus = get_zkp_modulus(); - - pk0is.reduce_uniform(&zkp_modulus); - pk1is.reduce_uniform(&zkp_modulus); - ct0is.reduce_uniform(&zkp_modulus); - ct1is.reduce_uniform(&zkp_modulus); - r1is.reduce_uniform(&zkp_modulus); - r2is.reduce_uniform(&zkp_modulus); - p1is.reduce_uniform(&zkp_modulus); - p2is.reduce_uniform(&zkp_modulus); - e0is.reduce_uniform(&zkp_modulus); - e0_quotients.reduce_uniform(&zkp_modulus); - e1.reduce(&zkp_modulus); - u.reduce(&zkp_modulus); - e0_mod_q.reduce(&zkp_modulus); + let pk0is = CrtPolynomial::new(pk0is); + let pk1is = CrtPolynomial::new(pk1is); + let ct0is = CrtPolynomial::new(ct0is); + let ct1is = CrtPolynomial::new(ct1is); + let r1is = CrtPolynomial::new(r1is); + let r2is = CrtPolynomial::new(r2is); + let p1is = CrtPolynomial::new(p1is); + let p2is = CrtPolynomial::new(p2is); + let e0is = CrtPolynomial::new(e0is); + let e0_quotients = CrtPolynomial::new(e0_quotients); let pk_bit = compute_modulus_bit(&dkg_params); let msg_bit = compute_msg_bit(&dkg_params); diff --git a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs index 82d8481f35..22130f3802 100644 --- a/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/decrypted_shares_aggregation/computation.rs @@ -211,9 +211,7 @@ impl Computation for Inputs { // Decryption shares: one CrtPolynomial per party (from_fhe + reduce) let mut decryption_shares: Vec = Vec::with_capacity(d_share_polys.len()); for d_share in &d_share_polys { - let mut crt = CrtPolynomial::from_fhe_polynomial(d_share); - crt.reduce(moduli) - .map_err(|e| CircuitsErrors::Other(e.to_string()))?; + let crt = CrtPolynomial::from_fhe_polynomial(d_share); decryption_shares.push(crt); } @@ -289,9 +287,6 @@ impl Computation for Inputs { let zkp_modulus = get_zkp_modulus(); - for crt in &mut decryption_shares { - crt.reduce_uniform(&zkp_modulus); - } let party_ids: Vec = party_ids.iter().map(|c| reduce(c, &zkp_modulus)).collect(); let message = Polynomial::new( message_trunc @@ -305,7 +300,6 @@ impl Computation for Inputs { .map(|c| reduce(c, &zkp_modulus)) .collect(), ); - crt_quotients.reduce_uniform(&zkp_modulus); Ok(Inputs { decryption_shares, diff --git a/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs b/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs index 0234aee8fb..85d8b1efa2 100644 --- a/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/pk_aggregation/computation.rs @@ -13,7 +13,6 @@ use crate::bigint_1d_to_json_values; use crate::compute_modulus_bit; use crate::compute_pk_aggregation_commitment; use crate::crt_polynomial_to_toml_json; -use crate::get_zkp_modulus; use crate::threshold::pk_aggregation::circuit::PkAggregationCircuit; use crate::threshold::pk_aggregation::circuit::PkAggregationCircuitData; use crate::CircuitsErrors; @@ -158,7 +157,6 @@ impl Computation for Inputs { let bit_pk = compute_modulus_bit(&threshold_params); let moduli = threshold_params.moduli(); - let zkp_modulus = &get_zkp_modulus(); // Coefficients must be in [0, q_i), not centered to (-q_i/2, q_i/2]. The circuit sums // party coefficients then applies reduce_mod to get a value in [0, q_l); the aggregated @@ -177,22 +175,15 @@ impl Computation for Inputs { let mut expected_threshold_pk_commitments = Vec::new(); pk0_agg.reverse(); - pk0_agg.reduce(moduli)?; - pk0_agg.reduce_uniform(zkp_modulus); pk1_agg.reverse(); pk1_agg.scalar_mul(&BigInt::from(data.committee.h)); pk1_agg.reduce(moduli)?; - pk1_agg.reduce_uniform(zkp_modulus); for party_index in 0..data.committee.h { pk0[party_index].reverse(); - pk0[party_index].reduce(moduli)?; - pk0[party_index].reduce_uniform(zkp_modulus); pk1[party_index].reverse(); - pk1[party_index].reduce(moduli)?; - pk1[party_index].reduce_uniform(zkp_modulus); let commitment = compute_pk_aggregation_commitment(&pk0[party_index], &pk1[party_index], bit_pk); diff --git a/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs b/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs index 2d5bef1b17..f9b4ccac3b 100644 --- a/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/pk_generation/computation.rs @@ -11,7 +11,6 @@ use crate::calculate_bit_width; use crate::crt_polynomial_to_toml_json; -use crate::get_zkp_modulus; use crate::math::{cyclotomic_polynomial, decompose_residue}; use crate::polynomial_to_toml_json; use crate::threshold::pk_generation::circuit::PkGenerationCircuit; @@ -278,7 +277,6 @@ impl Computation for Inputs { .map( |(i, (qi, mut pk0_share, mut a, mut eek, mut e_sm, mut sk))| { pk0_share.reverse(); - pk0_share.reduce(&qi); pk0_share.center(&qi); a.reverse(); @@ -336,16 +334,6 @@ impl Computation for Inputs { e_sm.add_limb(e_smi); } - let zkp_modulus = &get_zkp_modulus(); - - pk0_share.reduce_uniform(zkp_modulus); - a.reduce_uniform(zkp_modulus); - r1.reduce_uniform(zkp_modulus); - r2.reduce_uniform(zkp_modulus); - e_sm.reduce_uniform(zkp_modulus); - eek.reduce(zkp_modulus); - sk.reduce(zkp_modulus); - Ok(Inputs { a: a.clone(), eek, diff --git a/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs b/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs index 995b67aa05..1a999fe702 100644 --- a/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/share_decryption/computation.rs @@ -15,7 +15,6 @@ use crate::circuits::commitments::compute_aggregated_shares_commitment; use crate::compute_modulus_bit; use crate::crt_polynomial_to_toml_json; use crate::decompose_residue; -use crate::get_zkp_modulus; use crate::threshold::share_decryption::circuit::ShareDecryptionCircuit; use crate::threshold::share_decryption::circuit::ShareDecryptionCircuitData; use crate::CircuitsErrors; @@ -250,23 +249,18 @@ impl Computation for Inputs { .par_bridge() .map(|(i, (qi, mut ct0, mut ct1, mut s, mut e, mut d_share))| { ct0.reverse(); - ct0.reduce(&qi); ct0.center(&qi); ct1.reverse(); - ct1.reduce(&qi); ct1.center(&qi); s.reverse(); - s.reduce(&qi); s.center(&qi); e.reverse(); - e.reduce(&qi); e.center(&qi); d_share.reverse(); - d_share.reduce(&qi); d_share.center(&qi); // Compute d_share_hat = ct0 + ct1 * s + e @@ -307,16 +301,6 @@ impl Computation for Inputs { d.add_limb(d_sharei); } - let zkp_modulus = &get_zkp_modulus(); - - ct0.reduce_uniform(zkp_modulus); - ct1.reduce_uniform(zkp_modulus); - sk.reduce_uniform(zkp_modulus); - e_sm.reduce_uniform(zkp_modulus); - r1.reduce_uniform(zkp_modulus); - r2.reduce_uniform(zkp_modulus); - d.reduce_uniform(zkp_modulus); - // Compute commitments to s and e (matches circuit's commitment functions) let modulus_bit = compute_modulus_bit(&threshold_params); let expected_sk_commitment = compute_aggregated_shares_commitment(&sk, modulus_bit); diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs index dc8879490e..6e5ad0dd2d 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/codegen.rs @@ -81,6 +81,7 @@ pub fn generate_configs(_preset: BfvPreset, configs: &Configs) -> CodegenConfigs pub global N: u32 = {}; pub global L: u32 = {}; pub global QIS: [Field; L] = [{}]; +pub global Q_MOD_T: Field = {}; /************************************ ------------------------------------- @@ -99,7 +100,6 @@ pub global {}_BIT_R2: u32 = {}; pub global {}_BIT_P1: u32 = {}; pub global {}_BIT_P2: u32 = {}; -pub global {}_Q_MOD_T_MOD_P: Field = {}; pub global {}_K0IS: [Field; L] = [{}]; pub global {}_PK_BOUNDS: [Field; L] = [{}]; pub global {}_E0_BOUND: Field = {}; @@ -114,25 +114,26 @@ pub global {}_P1_BOUNDS: [Field; L] = [{}]; pub global {}_P2_BOUNDS: [Field; L] = [{}]; pub global {}_CONFIGS: UserDataEncryptionConfigs = UserDataEncryptionConfigs::new( -{}_Q_MOD_T_MOD_P, -QIS, -{}_K0IS, -{}_PK_BOUNDS, -{}_E0_BOUND, -{}_E1_BOUND, -{}_U_BOUND, -{}_R1_LOW_BOUNDS, -{}_R1_UP_BOUNDS, -{}_R2_BOUNDS, -{}_P1_BOUNDS, -{}_P2_BOUNDS, -{}_K1_LOW_BOUND, -{}_K1_UP_BOUND + Q_MOD_T, + QIS, + {}_K0IS, + {}_PK_BOUNDS, + {}_E0_BOUND, + {}_E1_BOUND, + {}_U_BOUND, + {}_R1_LOW_BOUNDS, + {}_R1_UP_BOUNDS, + {}_R2_BOUNDS, + {}_P1_BOUNDS, + {}_P2_BOUNDS, + {}_K1_LOW_BOUND, + {}_K1_UP_BOUND ); "#, - configs.n, // N - configs.l, // L - qis_str, // QIS array + configs.n, // N + configs.l, // L + qis_str, // QIS array + configs.q_mod_t, // Q_MOD_T prefix, configs.bits.pk_bit, // BIT_PK prefix, @@ -154,8 +155,6 @@ QIS, prefix, configs.bits.p2_bit, // BIT_P2 prefix, - configs.q_mod_t_mod_p, // Q_MOD_T_MOD_P - prefix, k0is_str, // K0IS array prefix, pk_bounds_str, // PK_BOUNDS array @@ -192,7 +191,6 @@ QIS, prefix, prefix, prefix, - prefix, ) } diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs index d8dca6a969..733cb66c2a 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/computation.rs @@ -12,11 +12,9 @@ use crate::calculate_bit_width; use crate::commitments::compute_pk_aggregation_commitment; use crate::compute_ciphertext_commitment; -use crate::crt_polynomial_to_toml_json; use crate::get_zkp_modulus; use crate::math::{compute_k0is, compute_q_mod_t, compute_q_product}; use crate::math::{cyclotomic_polynomial, decompose_residue}; -use crate::polynomial_to_toml_json; use crate::threshold::user_data_encryption::circuit::UserDataEncryptionCircuit; use crate::threshold::user_data_encryption::circuit::UserDataEncryptionCircuitData; use crate::utils::compute_modulus_bit; @@ -25,7 +23,6 @@ use crate::{CircuitComputation, Computation}; use e3_fhe_params::build_pair_for_preset; use e3_fhe_params::BfvPreset; use e3_polynomial::center; -use e3_polynomial::reduce; use e3_polynomial::CrtPolynomial; use e3_polynomial::Polynomial; use fhe::bfv::SecretKey; @@ -76,7 +73,7 @@ pub struct Configs { pub n: usize, pub l: usize, pub moduli: Vec, - pub q_mod_t_mod_p: BigInt, + pub q_mod_t: BigInt, pub k0is: Vec, pub bits: Bits, pub bounds: Bounds, @@ -146,10 +143,8 @@ impl Computation for Configs { let q = compute_q_product(&moduli); let q_mod_t_uint = compute_q_mod_t(&q, plaintext); let t = BigInt::from(plaintext); - let p = get_zkp_modulus(); let q_mod_t = center(&BigInt::from(q_mod_t_uint), &t); - let q_mod_t_mod_p = reduce(&q_mod_t, &p); let k0is = compute_k0is(threshold_params.moduli(), threshold_params.plaintext())?; @@ -159,7 +154,7 @@ impl Computation for Configs { Ok(Configs { n: threshold_params.degree(), l: moduli.len(), - q_mod_t_mod_p, + q_mod_t, k0is, moduli, bits, @@ -421,11 +416,6 @@ impl Computation for Inputs { pk1.reverse(); e0.reverse(); - ct0.reduce(&moduli)?; - ct1.reduce(&moduli)?; - pk0.reduce(&moduli)?; - pk1.reduce(&moduli)?; - ct0.center(&moduli)?; ct1.center(&moduli)?; pk0.center(&moduli)?; @@ -540,33 +530,20 @@ impl Computation for Inputs { e0_quotients.push(e0_quotient); } - let mut pk0is = CrtPolynomial::new(pk0is); - let mut pk1is = CrtPolynomial::new(pk1is); - let mut ct0is = CrtPolynomial::new(ct0is); - let mut ct1is = CrtPolynomial::new(ct1is); - let mut r1is = CrtPolynomial::new(r1is); - let mut r2is = CrtPolynomial::new(r2is); - let mut p1is = CrtPolynomial::new(p1is); - let mut p2is = CrtPolynomial::new(p2is); - let mut e0is = CrtPolynomial::new(e0is); - let mut e0_quotients = CrtPolynomial::new(e0_quotients); - + let pk0is = CrtPolynomial::new(pk0is); + let pk1is = CrtPolynomial::new(pk1is); + let ct0is = CrtPolynomial::new(ct0is); + let ct1is = CrtPolynomial::new(ct1is); + let r1is = CrtPolynomial::new(r1is); + let r2is = CrtPolynomial::new(r2is); + let p1is = CrtPolynomial::new(p1is); + let p2is = CrtPolynomial::new(p2is); + let e0is = CrtPolynomial::new(e0is); + let e0_quotients = CrtPolynomial::new(e0_quotients); + + // e0 is mod Q (huge); reduce to zkp_modulus so it fits in the proof system field. let zkp_modulus = get_zkp_modulus(); - - pk0is.reduce_uniform(&zkp_modulus); - pk1is.reduce_uniform(&zkp_modulus); - ct0is.reduce_uniform(&zkp_modulus); - ct1is.reduce_uniform(&zkp_modulus); - r1is.reduce_uniform(&zkp_modulus); - r2is.reduce_uniform(&zkp_modulus); - p1is.reduce_uniform(&zkp_modulus); - p2is.reduce_uniform(&zkp_modulus); - e0is.reduce_uniform(&zkp_modulus); - e0_quotients.reduce_uniform(&zkp_modulus); - e1.reduce(&zkp_modulus); - u.reduce(&zkp_modulus); e0_mod_q.reduce(&zkp_modulus); - k1.reduce(&zkp_modulus); let pk_bit = compute_modulus_bit(&threshold_params); let pk_commitment = compute_pk_aggregation_commitment(&pk0is, &pk1is, pk_bit); @@ -593,8 +570,11 @@ impl Computation for Inputs { }) } - // Used as input for Nargo execution. + // Used as input for Nargo execution. Coefficients are JSON numbers when they fit in i64, else strings. fn to_json(&self) -> serde_json::Result { + use crate::crt_polynomial_to_toml_json; + use crate::polynomial_to_toml_json; + let pk0is = crt_polynomial_to_toml_json(&self.pk0is); let pk1is = crt_polynomial_to_toml_json(&self.pk1is); let ct0is = crt_polynomial_to_toml_json(&self.ct0is); diff --git a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs index ee5010d5b3..087ad161a7 100644 --- a/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs +++ b/crates/zk-helpers/src/circuits/threshold/user_data_encryption/utils.rs @@ -5,7 +5,7 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use crate::math::fhe_poly_to_crt_centered; -use crate::utils::{compute_modulus_bit, get_zkp_modulus, ZkHelpersUtilsError}; +use crate::utils::{compute_modulus_bit, ZkHelpersUtilsError}; use e3_polynomial::{CrtPolynomial, CrtPolynomialError}; use fhe::bfv::{BfvParameters, Ciphertext, PublicKey}; @@ -28,13 +28,9 @@ pub fn bfv_ciphertext_to_greco( ciphertext: &Ciphertext, ) -> Result<(CrtPolynomial, CrtPolynomial), CrtPolynomialError> { let moduli = params.moduli(); - let zkp_modulus = get_zkp_modulus(); - let mut ct0is = fhe_poly_to_crt_centered(&ciphertext.c[0], moduli)?; - let mut ct1is = fhe_poly_to_crt_centered(&ciphertext.c[1], moduli)?; - - ct0is.reduce_uniform(&zkp_modulus); - ct1is.reduce_uniform(&zkp_modulus); + let ct0is = fhe_poly_to_crt_centered(&ciphertext.c[0], moduli)?; + let ct1is = fhe_poly_to_crt_centered(&ciphertext.c[1], moduli)?; Ok((ct0is, ct1is)) } @@ -58,13 +54,9 @@ pub fn bfv_public_key_to_greco( public_key: &PublicKey, ) -> Result<(CrtPolynomial, CrtPolynomial), CrtPolynomialError> { let moduli = params.moduli(); - let zkp_modulus = get_zkp_modulus(); - - let mut pk0is = fhe_poly_to_crt_centered(&public_key.c.c[0], moduli)?; - let mut pk1is = fhe_poly_to_crt_centered(&public_key.c.c[1], moduli)?; - pk0is.reduce_uniform(&zkp_modulus); - pk1is.reduce_uniform(&zkp_modulus); + let pk0is = fhe_poly_to_crt_centered(&public_key.c.c[0], moduli)?; + let pk1is = fhe_poly_to_crt_centered(&public_key.c.c[1], moduli)?; Ok((pk0is, pk1is)) } diff --git a/crates/zk-helpers/src/utils.rs b/crates/zk-helpers/src/utils.rs index e7d6df7b56..36711f866b 100644 --- a/crates/zk-helpers/src/utils.rs +++ b/crates/zk-helpers/src/utils.rs @@ -20,7 +20,7 @@ use e3_polynomial::{CrtPolynomial, Polynomial}; use e3_safe::SafeSponge; use fhe::bfv::BfvParameters; use num_bigint::BigInt; -use num_traits::Zero; +use num_traits::{ToPrimitive, Zero}; use std::fmt::Display; use std::str::FromStr; use thiserror::Error as ThisError; @@ -207,16 +207,24 @@ pub fn get_zkp_modulus() -> BigInt { .expect("Invalid ZKP modulus") } -/// Poly-with-coefficients shape for TOML JSON: `{"coefficients": [string, ...]}`. -/// -/// Use for a single limb (e.g. sk_secret) where the circuit expects one "coefficients" array. +/// Converts a BigInt to a JSON value: number when it fits in i64 (preserves sign), else string. +pub fn bigint_to_json_value(n: &BigInt) -> serde_json::Value { + n.to_i64() + .map(serde_json::Number::from) + .map(serde_json::Value::Number) + .unwrap_or_else(|| serde_json::Value::String(n.to_string())) +} + +/// Poly-with-coefficients shape for TOML JSON: `{"coefficients": [number|string, ...]}`. +/// Coefficients use numbers when they fit in i64, else strings. pub fn poly_coefficients_to_toml_json(coefficients: &[BigInt]) -> serde_json::Value { serde_json::json!({ - "coefficients": to_string_1d_vec(coefficients) + "coefficients": coefficients.iter().map(bigint_to_json_value).collect::>() }) } -/// Map a CRT polynomial to a vector of JSON values (one `{"coefficients": [...]}` per limb). +/// Map a CRT polynomial to a vector of JSON values (one `{"coefficients": [number|string, ...]}` per limb). +/// Coefficients use numbers when they fit in i64, else strings. pub fn crt_polynomial_to_toml_json(crt_polynomial: &CrtPolynomial) -> Vec { crt_polynomial .limbs @@ -225,67 +233,31 @@ pub fn crt_polynomial_to_toml_json(crt_polynomial: &CrtPolynomial) -> Vec Vec { - bigint_1d - .iter() - .map(|v| serde_json::Value::String(v.to_string())) - .collect() + bigint_1d.iter().map(bigint_to_json_value).collect() } -/// Convert a 2D vector of BigInt to a vector of vectors of JSON values. -/// -/// # Arguments -/// * `bigint_2d` - 2D vector of BigInt values -/// -/// # Returns -/// A vector of vectors of JSON values +/// Convert a 2D vector of BigInt to a vector of vectors of JSON values (numbers when fit in i64, else strings). pub fn bigint_2d_to_json_values(y: &[Vec]) -> Vec> { y.iter() - .map(|coeff| { - coeff - .iter() - .map(|v| serde_json::Value::String(v.to_string())) - .collect() - }) + .map(|coeff| coeff.iter().map(bigint_to_json_value).collect()) .collect() } -/// Nested BigInt structure to JSON: map each value to `Value::String(s)`. -/// -/// # Arguments -/// * `y` - 3D vector of BigInt values -/// -/// # Returns -/// A vector of vectors of vectors of JSON values +/// Nested BigInt structure to JSON (numbers when fit in i64, else strings). pub fn bigint_3d_to_json_values(y: &[Vec>]) -> Vec>> { y.iter() .map(|coeff| { coeff .iter() - .map(|v| { - v.iter() - .map(|x| serde_json::Value::String(x.to_string())) - .collect() - }) + .map(|v| v.iter().map(bigint_to_json_value).collect()) .collect() }) .collect() } -/// Map a polynomial to a vector of JSON values. -/// -/// # Arguments -/// * `polynomial` - Polynomial to convert to TOML JSON -/// -/// # Returns -/// A vector of JSON values +/// Map a polynomial to TOML JSON: `{"coefficients": [number|string, ...]}`. pub fn polynomial_to_toml_json(polynomial: &Polynomial) -> serde_json::Value { poly_coefficients_to_toml_json(polynomial.coefficients()) } diff --git a/examples/CRISP/circuits/src/main.nr b/examples/CRISP/circuits/src/main.nr index 031e01f3c6..8d33893006 100644 --- a/examples/CRISP/circuits/src/main.nr +++ b/examples/CRISP/circuits/src/main.nr @@ -5,10 +5,10 @@ // or FITNESS FOR A PARTICULAR PURPOSE. use lib::configs::default::threshold::{ - L, N, USER_DATA_ENCRYPTION_BIT_CT, USER_DATA_ENCRYPTION_BIT_E0, USER_DATA_ENCRYPTION_BIT_E1, - USER_DATA_ENCRYPTION_BIT_K, USER_DATA_ENCRYPTION_BIT_P1, USER_DATA_ENCRYPTION_BIT_P2, - USER_DATA_ENCRYPTION_BIT_PK, USER_DATA_ENCRYPTION_BIT_R1, USER_DATA_ENCRYPTION_BIT_R2, - USER_DATA_ENCRYPTION_BIT_U, USER_DATA_ENCRYPTION_CONFIGS, USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P, + L, N, Q_MOD_T, USER_DATA_ENCRYPTION_BIT_CT, USER_DATA_ENCRYPTION_BIT_E0, + USER_DATA_ENCRYPTION_BIT_E1, USER_DATA_ENCRYPTION_BIT_K, USER_DATA_ENCRYPTION_BIT_P1, + USER_DATA_ENCRYPTION_BIT_P2, USER_DATA_ENCRYPTION_BIT_PK, USER_DATA_ENCRYPTION_BIT_R1, + USER_DATA_ENCRYPTION_BIT_R2, USER_DATA_ENCRYPTION_BIT_U, USER_DATA_ENCRYPTION_CONFIGS, }; use lib::core::threshold::user_data_encryption::UserDataEncryption; use lib::math::commitments::compute_ciphertext_commitment; @@ -177,7 +177,7 @@ fn main( let ct_commitment = compute_ciphertext_commitment::<512, 2, 36>(ct0is, ct1is); if is_mask_vote == false { - check_coefficient_values_with_balance(k1, USER_DATA_ENCRYPTION_Q_MOD_T_MOD_P, balance, num_options); + check_coefficient_values_with_balance(k1, Q_MOD_T, balance, num_options); validate_signature(hashed_message, public_key_x, public_key_y, signature); let voter_address = address_to_field(derive_address(public_key_x, public_key_y)); diff --git a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs index d3c7ce5c8b..34fd3daea7 100644 --- a/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs +++ b/examples/CRISP/crates/zk-inputs-wasm/src/lib.rs @@ -14,6 +14,23 @@ use num_bigint::BigInt; use wasm_bindgen::prelude::*; use zk_inputs::ZKInputsGenerator as CoreZKInputsGenerator; +/// Converts a JsValue (string, number, or JS BigInt) to a string suitable for BigInt parsing. +/// Circuit inputs may serialize coefficients as JSON numbers when they fit in i64, +/// or pass them as JavaScript BigInt values. +fn js_value_to_bigint_string(val: JsValue) -> Option { + if let Some(s) = val.as_string() { + return Some(s); + } + if let Some(n) = val.as_f64() { + return Some(format!("{:.0}", n)); + } + // JS BigInt: convert via js_sys::BigInt::toString(10) + val.dyn_into::() + .ok() + .and_then(|b| b.to_string(10).ok()) + .and_then(|js| JsValue::from(js).as_string()) +} + /// JavaScript-compatible CRISP ZK inputs generator. #[wasm_bindgen] pub struct ZKInputsGenerator { @@ -135,7 +152,8 @@ impl ZKInputsGenerator { ct0is: JsValue, ct1is: JsValue, ) -> Result { - // Parse nested arrays: ct0is and ct1is are arrays of arrays (one array per CRT limb) + // Parse nested arrays: ct0is and ct1is are arrays of arrays (one array per CRT limb). + // Coefficients may be strings or numbers (JSON can emit numbers when values fit in i64). let ct0is_array: js_sys::Array = js_sys::Array::from(&ct0is); let ct1is_array: js_sys::Array = js_sys::Array::from(&ct1is); @@ -148,10 +166,8 @@ impl ZKInputsGenerator { let mut coefficients: Vec = Vec::new(); for j in 0..inner_array.length() { - let s = inner_array - .get(j) - .as_string() - .ok_or_else(|| JsValue::from_str("Expected string in inner array"))?; + let s = js_value_to_bigint_string(inner_array.get(j)) + .ok_or_else(|| JsValue::from_str("Expected string, number, or BigInt in inner array"))?; let bigint = s .parse::() .map_err(|e| JsValue::from_str(&format!("Failed to parse BigInt: {}", e)))?; @@ -169,10 +185,8 @@ impl ZKInputsGenerator { let mut coefficients: Vec = Vec::new(); for j in 0..inner_array.length() { - let s = inner_array - .get(j) - .as_string() - .ok_or_else(|| JsValue::from_str("Expected string in inner array"))?; + let s = js_value_to_bigint_string(inner_array.get(j)) + .ok_or_else(|| JsValue::from_str("Expected string, number, or BigInt in inner array"))?; let bigint = s .parse::() .map_err(|e| JsValue::from_str(&format!("Failed to parse BigInt: {}", e)))?; diff --git a/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs b/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs index 27b6cd1f72..fcd1a73632 100644 --- a/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs +++ b/examples/CRISP/crates/zk-inputs/src/ciphertext_addition.rs @@ -8,7 +8,6 @@ use e3_polynomial::{CrtPolynomial, Polynomial}; use e3_zk_helpers::commitments::compute_ciphertext_commitment; use e3_zk_helpers::crt_polynomial_to_toml_json; use e3_zk_helpers::utils::compute_modulus_bit; -use e3_zk_helpers::utils::get_zkp_modulus; use eyre::{Context, Result}; use fhe::bfv::BfvParameters; use fhe::bfv::Ciphertext; @@ -83,20 +82,7 @@ impl CiphertextAdditionWitness { let mut r1 = Self::compute_quotient(&sum_ct1, &ct1, &prev_ct1, &moduli) .with_context(|| "Failed to compute r1 quotient")?; - let zkp_modulus = &get_zkp_modulus(); - - // Reduce all coefficients modulo the ZKP modulus so they lie in the proof system's - // native field. The circuit expects witnesses in [0, zkp_modulus); unreduced values - // would break constraint satisfaction or overflow the field representation. - prev_ct0.reduce_uniform(zkp_modulus); - prev_ct1.reduce_uniform(zkp_modulus); - ct0.reduce_uniform(zkp_modulus); - ct1.reduce_uniform(zkp_modulus); - sum_ct0.reduce_uniform(zkp_modulus); - sum_ct1.reduce_uniform(zkp_modulus); - r0.reduce_uniform(zkp_modulus); - r1.reduce_uniform(zkp_modulus); - + // Coefficients are centered per modulus; no zkp reduce. The circuit reduces mod r when needed. let pk_bit = compute_modulus_bit(params); let prev_ct_commitment = compute_ciphertext_commitment(&prev_ct0, &prev_ct1, pk_bit); diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol index 04a372f081..51a8dac58d 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPVerifier.sol @@ -10,143 +10,143 @@ uint256 constant LOG_N = 19; uint256 constant NUMBER_OF_PUBLIC_INPUTS = 23; uint256 constant VK_HASH = 0x2349c3d182cb0307ecfa17d2cb91dcf9e7742a8daa67aa106831c761dd22030d; library HonkVerificationKey { - function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { - Honk.VerificationKey memory vk = Honk.VerificationKey({ - circuitSize: uint256(524288), - logCircuitSize: uint256(19), - publicInputsSize: uint256(23), - ql: Honk.G1Point({ - x: uint256(0x253c5186ca1b682f7d302cd73b85ea3447a5a2c64fc82b70652f7fc44b203128), - y: uint256(0x14f602413e183c57d3d3124290ab48b6c8b0cbace773766a573ead30bf5c2f4f) - }), - qr: Honk.G1Point({ - x: uint256(0x2d20633d112c7c7efd2821f2860e9d1287f8f68ddd4b0216bb179078497a571c), - y: uint256(0x11928235f13ba529b1ee2f75fe26dda5ed5c79ec36028c09ad2bd8b957546344) - }), - qo: Honk.G1Point({ - x: uint256(0x2b570f8e2eccc265f39b9e73778cf6e282554452adb19d2a2406952447d4aa6e), - y: uint256(0x14afd678c64e0dacd64724504e36bca6eccb5a477a5e2987b6543246b8135a90) - }), - q4: Honk.G1Point({ - x: uint256(0x2d7f53a245e9c855325eeb73c8fea788f5b457293cacfd7e3e332210a27a7d2f), - y: uint256(0x182d5ef8840426caa829856d7e800ee98509383d9ee637d1facbc11f7a8fba0d) - }), - qm: Honk.G1Point({ - x: uint256(0x19d90118c106191d2f2f0350a56547c943c1ed5910d47dd067761099b54d11f3), - y: uint256(0x0f8f03c2def6108448f72f5177b6a2eed01250670efb63ea6f5ab65abc2a6885) - }), - qc: Honk.G1Point({ - x: uint256(0x2ead500c79e717f5cdbba88ffef46477c4f30926bf59547a9c141c4158f7c843), - y: uint256(0x027d6b571b395cf61aae9626f172ce4e41e598456f7b8e535c027e9129b2c98e) - }), - qLookup: Honk.G1Point({ - x: uint256(0x136236e1bfc2af648ac078e134c1b4b9114b11937ebafcdd87f8ca7660715ebb), - y: uint256(0x02293c705250462935a653b7b993e13e2e8bc6480c45c84976d526cbdbd071df) - }), - qArith: Honk.G1Point({ - x: uint256(0x074e7ead679c76d2c2d6d3f158cb522e71d14a169b2b1d0792cdfdad726f17a4), - y: uint256(0x21f9acd78d4150858f29c259c07e4552f6ec4bffac6c1e174cfb90e2e26bd68a) - }), - qDeltaRange: Honk.G1Point({ - x: uint256(0x0cebfba75751a4eff3d7855bf0f314e523e2c709a75992eb5674a546627248e3), - y: uint256(0x16b715150f4de399940c535932174f5c634c9fd9137a94008bb38f89f1350b4c) - }), - qElliptic: Honk.G1Point({ - x: uint256(0x174dba7fa4e38a55a78e43dda6c3fb81a1293285cf33eeb333ee186bb4331563), - y: uint256(0x0a2ff722359e35596e5abeec834c6cd73f4b06f5f2e837b31364f199f449ca77) - }), - qMemory: Honk.G1Point({ - x: uint256(0x29f116092db8eb3773b017110ca5d3a7b35fa1b064cde91c9e280e20399d7ec2), - y: uint256(0x1f0e39e18ded75167a270d6e31b9a6ad9a9d1b0d67c365eeb172a3fea4d1371e) - }), - qNnf: Honk.G1Point({ - x: uint256(0x19da50fca27dd36a006892b7cb6b36d7cfdac9d65fec6ae7a1a2e092e0988de5), - y: uint256(0x08f96f1d8e37be6dc83c9fbb3b912493841e4e81ab28a60c0fc85337cc4ed977) - }), - qPoseidon2External: Honk.G1Point({ - x: uint256(0x2d20446a333063e66c1a552fecdfe12d0fdddeef0034569ac350f3299b09955a), - y: uint256(0x175e6e8776625134217ce9516a11cbbeab8c4c1c8b41cfb75f43de1f500b07d2) - }), - qPoseidon2Internal: Honk.G1Point({ - x: uint256(0x090a76cca3c754a2ba40abdc2572fff64645060cfa5c48239a1df4dbb8360828), - y: uint256(0x0499d4c6aed1f78b33a6701459cd069e9bf7262754d2f15074a6c2401950761f) - }), - s1: Honk.G1Point({ - x: uint256(0x012609fcd0fa67214d78f2d5f5e3e2b459a3c4b21531646de7d51d9cd5383aa9), - y: uint256(0x21394cebbe5f66f3aecf43d318b3cb9fc7640825baba0c5a5c190a20ceeb5edb) - }), - s2: Honk.G1Point({ - x: uint256(0x080c7f024d9c813c60c84fc3e9bcad553c51f276d55e8d7a03c044b41cedef36), - y: uint256(0x2109db10b2da84ec2c4a1b62689d0952ababe94915293dfee09f1083010e5cfb) - }), - s3: Honk.G1Point({ - x: uint256(0x2f7d6b77cd5c3ee56c255be61d0e90fd7da5898a7ce4850aaf1060513cb7cd9d), - y: uint256(0x2ce5fdacbf91fc3358f4eba637d60f109f70fa929a6a1bc1f9d86e7856b87dcd) - }), - s4: Honk.G1Point({ - x: uint256(0x1db1540beb4dc13e8519b82205e9b4cb48833040c8b322d626df57e078afecc4), - y: uint256(0x28eb12f9f02ac092326277365f9eeaff536b8dbf771f9ace22d0e122f922196a) - }), - t1: Honk.G1Point({ - x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f), - y: uint256(0x00c7781bda34afc32dedfb0d0f6b16c987c83375da000e180b44ad25685fc2ae) - }), - t2: Honk.G1Point({ - x: uint256(0x29345f914a28707887bee191c3a928191de584827a6d1a78ccce1d7629ca9dc0), - y: uint256(0x1920cebd0b33ac9713424e3bc03d53d79dc72f6afc24c90e56593094a213444c) - }), - t3: Honk.G1Point({ - x: uint256(0x261c990958bc2ef77a45467d9639ab2c68cf787ff7bce55ce3074dfdaedc8f8f), - y: uint256(0x23c1c05424a40360a61e46f4deab04988a6f5b71dda351e0da608cff1f332ee0) - }), - t4: Honk.G1Point({ - x: uint256(0x2b651d2fd644b2972d72ec439dc69d3339d0b052a296bfc48c6a08396aaca078), - y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71) - }), - id1: Honk.G1Point({ - x: uint256(0x0a10b2e79989b15e3a69bd491cdae007b0bf9c82d9be3d7867b33e2287b91dac), - y: uint256(0x257794eaba7a0e7aed16e03d4d8c4cf7d878b029f4ea45c559bbc19b5ec4d1de) - }), - id2: Honk.G1Point({ - x: uint256(0x25791b725ea7c712316ac4ffe10fdfcf37bd2b8f9d730ba2e26fa709bc7c3ae0), - y: uint256(0x15fba3e7928d36dc4dd6d6ff198b928c7246310974d4f74161cfd2781b9d3686) - }), - id3: Honk.G1Point({ - x: uint256(0x270be32427e6404801b9b014f983d80acf18b828c6cf049f430d98fbe34e85e0), - y: uint256(0x2e7d27a99c4b574557ea6117c390c65072cdfa51a08621141ae26d7e143d0669) - }), - id4: Honk.G1Point({ - x: uint256(0x18e2902febdb3e45358fe65981f338a7ffd1b16edd917de670fabe5d15307e20), - y: uint256(0x2f6f36f307a0f7012dc3918795312e71a1d4752966c2bc3151e5f5b3151fc236) - }), - lagrangeFirst: Honk.G1Point({ - x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), - y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) - }), - lagrangeLast: Honk.G1Point({ - x: uint256(0x12b14f226e24a52e0bc85bc5478c806023107f257430aae49136064dd3315c60), - y: uint256(0x0dfe490435cf839caf81df5c8a164afc5e3c8646f36bf27c19850b3f913edce4) - }) - }); - return vk; - } + function loadVerificationKey() internal pure returns (Honk.VerificationKey memory) { + Honk.VerificationKey memory vk = Honk.VerificationKey({ + circuitSize: uint256(524288), + logCircuitSize: uint256(19), + publicInputsSize: uint256(23), + ql: Honk.G1Point({ + x: uint256(0x253c5186ca1b682f7d302cd73b85ea3447a5a2c64fc82b70652f7fc44b203128), + y: uint256(0x14f602413e183c57d3d3124290ab48b6c8b0cbace773766a573ead30bf5c2f4f) + }), + qr: Honk.G1Point({ + x: uint256(0x2d20633d112c7c7efd2821f2860e9d1287f8f68ddd4b0216bb179078497a571c), + y: uint256(0x11928235f13ba529b1ee2f75fe26dda5ed5c79ec36028c09ad2bd8b957546344) + }), + qo: Honk.G1Point({ + x: uint256(0x2b570f8e2eccc265f39b9e73778cf6e282554452adb19d2a2406952447d4aa6e), + y: uint256(0x14afd678c64e0dacd64724504e36bca6eccb5a477a5e2987b6543246b8135a90) + }), + q4: Honk.G1Point({ + x: uint256(0x2d7f53a245e9c855325eeb73c8fea788f5b457293cacfd7e3e332210a27a7d2f), + y: uint256(0x182d5ef8840426caa829856d7e800ee98509383d9ee637d1facbc11f7a8fba0d) + }), + qm: Honk.G1Point({ + x: uint256(0x19d90118c106191d2f2f0350a56547c943c1ed5910d47dd067761099b54d11f3), + y: uint256(0x0f8f03c2def6108448f72f5177b6a2eed01250670efb63ea6f5ab65abc2a6885) + }), + qc: Honk.G1Point({ + x: uint256(0x2ead500c79e717f5cdbba88ffef46477c4f30926bf59547a9c141c4158f7c843), + y: uint256(0x027d6b571b395cf61aae9626f172ce4e41e598456f7b8e535c027e9129b2c98e) + }), + qLookup: Honk.G1Point({ + x: uint256(0x136236e1bfc2af648ac078e134c1b4b9114b11937ebafcdd87f8ca7660715ebb), + y: uint256(0x02293c705250462935a653b7b993e13e2e8bc6480c45c84976d526cbdbd071df) + }), + qArith: Honk.G1Point({ + x: uint256(0x074e7ead679c76d2c2d6d3f158cb522e71d14a169b2b1d0792cdfdad726f17a4), + y: uint256(0x21f9acd78d4150858f29c259c07e4552f6ec4bffac6c1e174cfb90e2e26bd68a) + }), + qDeltaRange: Honk.G1Point({ + x: uint256(0x0cebfba75751a4eff3d7855bf0f314e523e2c709a75992eb5674a546627248e3), + y: uint256(0x16b715150f4de399940c535932174f5c634c9fd9137a94008bb38f89f1350b4c) + }), + qElliptic: Honk.G1Point({ + x: uint256(0x174dba7fa4e38a55a78e43dda6c3fb81a1293285cf33eeb333ee186bb4331563), + y: uint256(0x0a2ff722359e35596e5abeec834c6cd73f4b06f5f2e837b31364f199f449ca77) + }), + qMemory: Honk.G1Point({ + x: uint256(0x29f116092db8eb3773b017110ca5d3a7b35fa1b064cde91c9e280e20399d7ec2), + y: uint256(0x1f0e39e18ded75167a270d6e31b9a6ad9a9d1b0d67c365eeb172a3fea4d1371e) + }), + qNnf: Honk.G1Point({ + x: uint256(0x19da50fca27dd36a006892b7cb6b36d7cfdac9d65fec6ae7a1a2e092e0988de5), + y: uint256(0x08f96f1d8e37be6dc83c9fbb3b912493841e4e81ab28a60c0fc85337cc4ed977) + }), + qPoseidon2External: Honk.G1Point({ + x: uint256(0x2d20446a333063e66c1a552fecdfe12d0fdddeef0034569ac350f3299b09955a), + y: uint256(0x175e6e8776625134217ce9516a11cbbeab8c4c1c8b41cfb75f43de1f500b07d2) + }), + qPoseidon2Internal: Honk.G1Point({ + x: uint256(0x090a76cca3c754a2ba40abdc2572fff64645060cfa5c48239a1df4dbb8360828), + y: uint256(0x0499d4c6aed1f78b33a6701459cd069e9bf7262754d2f15074a6c2401950761f) + }), + s1: Honk.G1Point({ + x: uint256(0x012609fcd0fa67214d78f2d5f5e3e2b459a3c4b21531646de7d51d9cd5383aa9), + y: uint256(0x21394cebbe5f66f3aecf43d318b3cb9fc7640825baba0c5a5c190a20ceeb5edb) + }), + s2: Honk.G1Point({ + x: uint256(0x080c7f024d9c813c60c84fc3e9bcad553c51f276d55e8d7a03c044b41cedef36), + y: uint256(0x2109db10b2da84ec2c4a1b62689d0952ababe94915293dfee09f1083010e5cfb) + }), + s3: Honk.G1Point({ + x: uint256(0x2f7d6b77cd5c3ee56c255be61d0e90fd7da5898a7ce4850aaf1060513cb7cd9d), + y: uint256(0x2ce5fdacbf91fc3358f4eba637d60f109f70fa929a6a1bc1f9d86e7856b87dcd) + }), + s4: Honk.G1Point({ + x: uint256(0x1db1540beb4dc13e8519b82205e9b4cb48833040c8b322d626df57e078afecc4), + y: uint256(0x28eb12f9f02ac092326277365f9eeaff536b8dbf771f9ace22d0e122f922196a) + }), + t1: Honk.G1Point({ + x: uint256(0x1f16b037f0b4c96ea2a30a118a44e139881c0db8a4d6c9fde7db5c1c1738e61f), + y: uint256(0x00c7781bda34afc32dedfb0d0f6b16c987c83375da000e180b44ad25685fc2ae) + }), + t2: Honk.G1Point({ + x: uint256(0x29345f914a28707887bee191c3a928191de584827a6d1a78ccce1d7629ca9dc0), + y: uint256(0x1920cebd0b33ac9713424e3bc03d53d79dc72f6afc24c90e56593094a213444c) + }), + t3: Honk.G1Point({ + x: uint256(0x261c990958bc2ef77a45467d9639ab2c68cf787ff7bce55ce3074dfdaedc8f8f), + y: uint256(0x23c1c05424a40360a61e46f4deab04988a6f5b71dda351e0da608cff1f332ee0) + }), + t4: Honk.G1Point({ + x: uint256(0x2b651d2fd644b2972d72ec439dc69d3339d0b052a296bfc48c6a08396aaca078), + y: uint256(0x2d7e8c1ecb92e2490049b50efc811df63f1ca97e58d5e82852dbec0c29715d71) + }), + id1: Honk.G1Point({ + x: uint256(0x0a10b2e79989b15e3a69bd491cdae007b0bf9c82d9be3d7867b33e2287b91dac), + y: uint256(0x257794eaba7a0e7aed16e03d4d8c4cf7d878b029f4ea45c559bbc19b5ec4d1de) + }), + id2: Honk.G1Point({ + x: uint256(0x25791b725ea7c712316ac4ffe10fdfcf37bd2b8f9d730ba2e26fa709bc7c3ae0), + y: uint256(0x15fba3e7928d36dc4dd6d6ff198b928c7246310974d4f74161cfd2781b9d3686) + }), + id3: Honk.G1Point({ + x: uint256(0x270be32427e6404801b9b014f983d80acf18b828c6cf049f430d98fbe34e85e0), + y: uint256(0x2e7d27a99c4b574557ea6117c390c65072cdfa51a08621141ae26d7e143d0669) + }), + id4: Honk.G1Point({ + x: uint256(0x18e2902febdb3e45358fe65981f338a7ffd1b16edd917de670fabe5d15307e20), + y: uint256(0x2f6f36f307a0f7012dc3918795312e71a1d4752966c2bc3151e5f5b3151fc236) + }), + lagrangeFirst: Honk.G1Point({ + x: uint256(0x0000000000000000000000000000000000000000000000000000000000000001), + y: uint256(0x0000000000000000000000000000000000000000000000000000000000000002) + }), + lagrangeLast: Honk.G1Point({ + x: uint256(0x12b14f226e24a52e0bc85bc5478c806023107f257430aae49136064dd3315c60), + y: uint256(0x0dfe490435cf839caf81df5c8a164afc5e3c8646f36bf27c19850b3f913edce4) + }) + }); + return vk; + } } pragma solidity ^0.8.27; interface IVerifier { - function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); + function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external returns (bool); } type Fr is uint256; -using { add as + } for Fr global; -using { sub as - } for Fr global; -using { mul as * } for Fr global; +using {add as +} for Fr global; +using {sub as -} for Fr global; +using {mul as *} for Fr global; -using { exp as ^ } for Fr global; -using { notEqual as != } for Fr global; -using { equal as == } for Fr global; +using {exp as ^} for Fr global; +using {notEqual as !=} for Fr global; +using {equal as ==} for Fr global; uint256 constant SUBGROUP_SIZE = 256; uint256 constant MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Prime field order @@ -159,135 +159,135 @@ Fr constant ZERO = Fr.wrap(0); // Instantiation library FrLib { - function from(uint256 value) internal pure returns (Fr) { - unchecked { - return Fr.wrap(value % MODULUS); - } - } - - function fromBytes32(bytes32 value) internal pure returns (Fr) { - unchecked { - return Fr.wrap(uint256(value) % MODULUS); - } - } - - function toBytes32(Fr value) internal pure returns (bytes32) { - unchecked { - return bytes32(Fr.unwrap(value)); - } - } - - function invert(Fr value) internal view returns (Fr) { - uint256 v = Fr.unwrap(value); - uint256 result; - - // Call the modexp precompile to invert in the field - assembly { - let free := mload(0x40) - mstore(free, 0x20) - mstore(add(free, 0x20), 0x20) - mstore(add(free, 0x40), 0x20) - mstore(add(free, 0x60), v) - mstore(add(free, 0x80), sub(MODULUS, 2)) - mstore(add(free, 0xa0), MODULUS) - let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) - if iszero(success) { - revert(0, 0) - } - result := mload(0x00) - mstore(0x40, add(free, 0x80)) - } - - return Fr.wrap(result); - } - - function pow(Fr base, uint256 v) internal view returns (Fr) { - uint256 b = Fr.unwrap(base); - uint256 result; - - // Call the modexp precompile to invert in the field - assembly { - let free := mload(0x40) - mstore(free, 0x20) - mstore(add(free, 0x20), 0x20) - mstore(add(free, 0x40), 0x20) - mstore(add(free, 0x60), b) - mstore(add(free, 0x80), v) - mstore(add(free, 0xa0), MODULUS) - let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) - if iszero(success) { - revert(0, 0) - } - result := mload(0x00) - mstore(0x40, add(free, 0x80)) - } - - return Fr.wrap(result); - } - - function div(Fr numerator, Fr denominator) internal view returns (Fr) { - unchecked { - return numerator * invert(denominator); + function from(uint256 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(value % MODULUS); + } + } + + function fromBytes32(bytes32 value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(uint256(value) % MODULUS); + } + } + + function toBytes32(Fr value) internal pure returns (bytes32) { + unchecked { + return bytes32(Fr.unwrap(value)); + } + } + + function invert(Fr value) internal view returns (Fr) { + uint256 v = Fr.unwrap(value); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), v) + mstore(add(free, 0x80), sub(MODULUS, 2)) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function pow(Fr base, uint256 v) internal view returns (Fr) { + uint256 b = Fr.unwrap(base); + uint256 result; + + // Call the modexp precompile to invert in the field + assembly { + let free := mload(0x40) + mstore(free, 0x20) + mstore(add(free, 0x20), 0x20) + mstore(add(free, 0x40), 0x20) + mstore(add(free, 0x60), b) + mstore(add(free, 0x80), v) + mstore(add(free, 0xa0), MODULUS) + let success := staticcall(gas(), 0x05, free, 0xc0, 0x00, 0x20) + if iszero(success) { + revert(0, 0) + } + result := mload(0x00) + mstore(0x40, add(free, 0x80)) + } + + return Fr.wrap(result); + } + + function div(Fr numerator, Fr denominator) internal view returns (Fr) { + unchecked { + return numerator * invert(denominator); + } + } + + function sqr(Fr value) internal pure returns (Fr) { + unchecked { + return value * value; + } + } + + function unwrap(Fr value) internal pure returns (uint256) { + unchecked { + return Fr.unwrap(value); + } + } + + function neg(Fr value) internal pure returns (Fr) { + unchecked { + return Fr.wrap(MODULUS - Fr.unwrap(value)); + } } - } - - function sqr(Fr value) internal pure returns (Fr) { - unchecked { - return value * value; - } - } - - function unwrap(Fr value) internal pure returns (uint256) { - unchecked { - return Fr.unwrap(value); - } - } - - function neg(Fr value) internal pure returns (Fr) { - unchecked { - return Fr.wrap(MODULUS - Fr.unwrap(value)); - } - } } // Free functions function add(Fr a, Fr b) pure returns (Fr) { - unchecked { - return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); - } + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } } function mul(Fr a, Fr b) pure returns (Fr) { - unchecked { - return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); - } + unchecked { + return Fr.wrap(mulmod(Fr.unwrap(a), Fr.unwrap(b), MODULUS)); + } } function sub(Fr a, Fr b) pure returns (Fr) { - unchecked { - return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); - } + unchecked { + return Fr.wrap(addmod(Fr.unwrap(a), MODULUS - Fr.unwrap(b), MODULUS)); + } } function exp(Fr base, Fr exponent) pure returns (Fr) { - if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); - // Implement exponent with a loop as we will overflow otherwise - for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { - base = base * base; - } - return base; + if (Fr.unwrap(exponent) == 0) return Fr.wrap(1); + // Implement exponent with a loop as we will overflow otherwise + for (uint256 i = 1; i < Fr.unwrap(exponent); i += i) { + base = base * base; + } + return base; } function notEqual(Fr a, Fr b) pure returns (bool) { - unchecked { - return Fr.unwrap(a) != Fr.unwrap(b); - } + unchecked { + return Fr.unwrap(a) != Fr.unwrap(b); + } } function equal(Fr a, Fr b) pure returns (bool) { - unchecked { - return Fr.unwrap(a) == Fr.unwrap(b); - } + unchecked { + return Fr.unwrap(a) == Fr.unwrap(b); + } } uint256 constant CONST_PROOF_SIZE_LOG_N = 28; @@ -308,1325 +308,1332 @@ uint256 constant NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1; // ENUM FOR WIRES enum WIRE { - Q_M, - Q_C, - Q_L, - Q_R, - Q_O, - Q_4, - Q_LOOKUP, - Q_ARITH, - Q_RANGE, - Q_ELLIPTIC, - Q_MEMORY, - Q_NNF, - Q_POSEIDON2_EXTERNAL, - Q_POSEIDON2_INTERNAL, - SIGMA_1, - SIGMA_2, - SIGMA_3, - SIGMA_4, - ID_1, - ID_2, - ID_3, - ID_4, - TABLE_1, - TABLE_2, - TABLE_3, - TABLE_4, - LAGRANGE_FIRST, - LAGRANGE_LAST, - W_L, - W_R, - W_O, - W_4, - Z_PERM, - LOOKUP_INVERSES, - LOOKUP_READ_COUNTS, - LOOKUP_READ_TAGS, - W_L_SHIFT, - W_R_SHIFT, - W_O_SHIFT, - W_4_SHIFT, - Z_PERM_SHIFT + Q_M, + Q_C, + Q_L, + Q_R, + Q_O, + Q_4, + Q_LOOKUP, + Q_ARITH, + Q_RANGE, + Q_ELLIPTIC, + Q_MEMORY, + Q_NNF, + Q_POSEIDON2_EXTERNAL, + Q_POSEIDON2_INTERNAL, + SIGMA_1, + SIGMA_2, + SIGMA_3, + SIGMA_4, + ID_1, + ID_2, + ID_3, + ID_4, + TABLE_1, + TABLE_2, + TABLE_3, + TABLE_4, + LAGRANGE_FIRST, + LAGRANGE_LAST, + W_L, + W_R, + W_O, + W_4, + Z_PERM, + LOOKUP_INVERSES, + LOOKUP_READ_COUNTS, + LOOKUP_READ_TAGS, + W_L_SHIFT, + W_R_SHIFT, + W_O_SHIFT, + W_4_SHIFT, + Z_PERM_SHIFT } library Honk { - struct G1Point { - uint256 x; - uint256 y; - } - - struct VerificationKey { - // Misc Params - uint256 circuitSize; - uint256 logCircuitSize; - uint256 publicInputsSize; - // Selectors - G1Point qm; - G1Point qc; - G1Point ql; - G1Point qr; - G1Point qo; - G1Point q4; - G1Point qLookup; // Lookup - G1Point qArith; // Arithmetic widget - G1Point qDeltaRange; // Delta Range sort - G1Point qMemory; // Memory - G1Point qNnf; // Non-native Field - G1Point qElliptic; // Auxillary - G1Point qPoseidon2External; - G1Point qPoseidon2Internal; - // Copy cnstraints - G1Point s1; - G1Point s2; - G1Point s3; - G1Point s4; - // Copy identity - G1Point id1; - G1Point id2; - G1Point id3; - G1Point id4; - // Precomputed lookup table - G1Point t1; - G1Point t2; - G1Point t3; - G1Point t4; - // Fixed first and last - G1Point lagrangeFirst; - G1Point lagrangeLast; - } - - struct RelationParameters { - // challenges - Fr eta; - Fr etaTwo; - Fr etaThree; - Fr beta; - Fr gamma; - // derived - Fr publicInputsDelta; - } - - struct Proof { - // Pairing point object - Fr[PAIRING_POINTS_SIZE] pairingPointObject; - // Free wires - G1Point w1; - G1Point w2; - G1Point w3; - G1Point w4; - // Lookup helpers - Permutations - G1Point zPerm; - // Lookup helpers - logup - G1Point lookupReadCounts; - G1Point lookupReadTags; - G1Point lookupInverses; - // Sumcheck - Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; - Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; - // Shplemini - G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; - Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; - G1Point shplonkQ; - G1Point kzgQuotient; - } - - struct ZKProof { - // Pairing point object - Fr[PAIRING_POINTS_SIZE] pairingPointObject; - // Commitments to wire polynomials - G1Point w1; - G1Point w2; - G1Point w3; - G1Point w4; - // Commitments to logup witness polynomials - G1Point lookupReadCounts; - G1Point lookupReadTags; - G1Point lookupInverses; - // Commitment to grand permutation polynomial - G1Point zPerm; - G1Point[3] libraCommitments; - // Sumcheck - Fr libraSum; - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; - Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; - Fr libraEvaluation; - // ZK - G1Point geminiMaskingPoly; - Fr geminiMaskingEval; - // Shplemini - G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; - Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; - Fr[4] libraPolyEvals; - G1Point shplonkQ; - G1Point kzgQuotient; - } + struct G1Point { + uint256 x; + uint256 y; + } + + struct VerificationKey { + // Misc Params + uint256 circuitSize; + uint256 logCircuitSize; + uint256 publicInputsSize; + // Selectors + G1Point qm; + G1Point qc; + G1Point ql; + G1Point qr; + G1Point qo; + G1Point q4; + G1Point qLookup; // Lookup + G1Point qArith; // Arithmetic widget + G1Point qDeltaRange; // Delta Range sort + G1Point qMemory; // Memory + G1Point qNnf; // Non-native Field + G1Point qElliptic; // Auxillary + G1Point qPoseidon2External; + G1Point qPoseidon2Internal; + // Copy cnstraints + G1Point s1; + G1Point s2; + G1Point s3; + G1Point s4; + // Copy identity + G1Point id1; + G1Point id2; + G1Point id3; + G1Point id4; + // Precomputed lookup table + G1Point t1; + G1Point t2; + G1Point t3; + G1Point t4; + // Fixed first and last + G1Point lagrangeFirst; + G1Point lagrangeLast; + } + + struct RelationParameters { + // challenges + Fr eta; + Fr etaTwo; + Fr etaThree; + Fr beta; + Fr gamma; + // derived + Fr publicInputsDelta; + } + + struct Proof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // Free wires + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Lookup helpers - Permutations + G1Point zPerm; + // Lookup helpers - logup + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Sumcheck + Fr[BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + G1Point shplonkQ; + G1Point kzgQuotient; + } + + struct ZKProof { + // Pairing point object + Fr[PAIRING_POINTS_SIZE] pairingPointObject; + // Commitments to wire polynomials + G1Point w1; + G1Point w2; + G1Point w3; + G1Point w4; + // Commitments to logup witness polynomials + G1Point lookupReadCounts; + G1Point lookupReadTags; + G1Point lookupInverses; + // Commitment to grand permutation polynomial + G1Point zPerm; + G1Point[3] libraCommitments; + // Sumcheck + Fr libraSum; + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH][CONST_PROOF_SIZE_LOG_N] sumcheckUnivariates; + Fr[NUMBER_OF_ENTITIES] sumcheckEvaluations; + Fr libraEvaluation; + // ZK + G1Point geminiMaskingPoly; + Fr geminiMaskingEval; + // Shplemini + G1Point[CONST_PROOF_SIZE_LOG_N - 1] geminiFoldComms; + Fr[CONST_PROOF_SIZE_LOG_N] geminiAEvaluations; + Fr[4] libraPolyEvals; + G1Point shplonkQ; + G1Point kzgQuotient; + } } // ZKTranscript library to generate fiat shamir challenges, the ZK transcript only differest struct ZKTranscript { - // Oink - Honk.RelationParameters relationParameters; - Fr[NUMBER_OF_ALPHAS] alphas; - Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; - // Sumcheck - Fr libraChallenge; - Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; - // Shplemini - Fr rho; - Fr geminiR; - Fr shplonkNu; - Fr shplonkZ; - // Derived - Fr publicInputsDelta; + // Oink + Honk.RelationParameters relationParameters; + Fr[NUMBER_OF_ALPHAS] alphas; + Fr[CONST_PROOF_SIZE_LOG_N] gateChallenges; + // Sumcheck + Fr libraChallenge; + Fr[CONST_PROOF_SIZE_LOG_N] sumCheckUChallenges; + // Shplemini + Fr rho; + Fr geminiR; + Fr shplonkNu; + Fr shplonkZ; + // Derived + Fr publicInputsDelta; } library ZKTranscriptLib { - function generateTranscript( - Honk.ZKProof memory proof, - bytes32[] calldata publicInputs, - uint256 vkHash, - uint256 publicInputsSize, - uint256 logN - ) external pure returns (ZKTranscript memory t) { - Fr previousChallenge; - (t.relationParameters, previousChallenge) = generateRelationParametersChallenges( - proof, - publicInputs, - vkHash, - publicInputsSize, - previousChallenge - ); - - (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); - - (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); - (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); - (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); - - (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); - - (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); - - (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); - - (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); - return t; - } - - function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { - uint256 challengeU256 = uint256(Fr.unwrap(challenge)); - uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - uint256 hi = challengeU256 >> 128; - first = FrLib.fromBytes32(bytes32(lo)); - second = FrLib.fromBytes32(bytes32(hi)); - } - - function generateRelationParametersChallenges( - Honk.ZKProof memory proof, - bytes32[] calldata publicInputs, - uint256 vkHash, - uint256 publicInputsSize, - Fr previousChallenge - ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { - (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); - - (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); - } - - function generateEtaChallenge( - Honk.ZKProof memory proof, - bytes32[] calldata publicInputs, - uint256 vkHash, - uint256 publicInputsSize - ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { - bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6); - round0[0] = bytes32(vkHash); - - for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) { - round0[1 + i] = bytes32(publicInputs[i]); - } - for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); - } - - // Create the first challenge - // Note: w4 is added to the challenge later on - round0[1 + publicInputsSize] = bytes32(proof.w1.x); - round0[1 + publicInputsSize + 1] = bytes32(proof.w1.y); - round0[1 + publicInputsSize + 2] = bytes32(proof.w2.x); - round0[1 + publicInputsSize + 3] = bytes32(proof.w2.y); - round0[1 + publicInputsSize + 4] = bytes32(proof.w3.x); - round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y); - - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); - (eta, etaTwo) = splitChallenge(previousChallenge); - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - - (etaThree, ) = splitChallenge(previousChallenge); - } - - function generateBetaAndGammaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { - bytes32[7] memory round1; - round1[0] = FrLib.toBytes32(previousChallenge); - round1[1] = bytes32(proof.lookupReadCounts.x); - round1[2] = bytes32(proof.lookupReadCounts.y); - round1[3] = bytes32(proof.lookupReadTags.x); - round1[4] = bytes32(proof.lookupReadTags.y); - round1[5] = bytes32(proof.w4.x); - round1[6] = bytes32(proof.w4.y); - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); - (beta, gamma) = splitChallenge(nextPreviousChallenge); - } - - // Alpha challenges non-linearise the gate contributions - function generateAlphaChallenges( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) { - // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup - uint256[5] memory alpha0; - alpha0[0] = Fr.unwrap(previousChallenge); - alpha0[1] = proof.lookupInverses.x; - alpha0[2] = proof.lookupInverses.y; - alpha0[3] = proof.zPerm.x; - alpha0[4] = proof.zPerm.y; - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); - Fr alpha; - (alpha, ) = splitChallenge(nextPreviousChallenge); - - // Compute powers of alpha for batching subrelations - alphas[0] = alpha; - for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { - alphas[i] = alphas[i - 1] * alpha; - } - } - - function generateGateChallenges( - Fr previousChallenge, - uint256 logN - ) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); - (gateChallenges[0], ) = splitChallenge(previousChallenge); - for (uint256 i = 1; i < logN; i++) { - gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; - } - nextPreviousChallenge = previousChallenge; - } - - function generateLibraChallenge( - Fr previousChallenge, - Honk.ZKProof memory proof - ) internal pure returns (Fr libraChallenge, Fr nextPreviousChallenge) { - // 2 comm, 1 sum, 1 challenge - uint256[4] memory challengeData; - challengeData[0] = Fr.unwrap(previousChallenge); - challengeData[1] = proof.libraCommitments[0].x; - challengeData[2] = proof.libraCommitments[0].y; - challengeData[3] = Fr.unwrap(proof.libraSum); - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); - (libraChallenge, ) = splitChallenge(nextPreviousChallenge); - } - - function generateSumcheckChallenges( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { - for (uint256 i = 0; i < logN; i++) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; - univariateChal[0] = prevChallenge; - - for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; - } - prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - - (sumcheckChallenges[i], ) = splitChallenge(prevChallenge); - } - nextPreviousChallenge = prevChallenge; - } - - // We add Libra claimed eval + 3 comm + 1 more eval - function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) internal pure returns (Fr rho, Fr nextPreviousChallenge) { - uint256[NUMBER_OF_ENTITIES + 9] memory rhoChallengeElements; - rhoChallengeElements[0] = Fr.unwrap(prevChallenge); - uint256 i; - for (i = 1; i <= NUMBER_OF_ENTITIES; i++) { - rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); - } - rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); - - i += 1; - rhoChallengeElements[i] = proof.libraCommitments[1].x; - rhoChallengeElements[i + 1] = proof.libraCommitments[1].y; - i += 2; - rhoChallengeElements[i] = proof.libraCommitments[2].x; - rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; - i += 2; - rhoChallengeElements[i] = proof.geminiMaskingPoly.x; - rhoChallengeElements[i + 1] = proof.geminiMaskingPoly.y; - - i += 2; - rhoChallengeElements[i] = Fr.unwrap(proof.geminiMaskingEval); - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); - (rho, ) = splitChallenge(nextPreviousChallenge); - } - - function generateGeminiRChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr geminiR, Fr nextPreviousChallenge) { - uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); - gR[0] = Fr.unwrap(prevChallenge); - - for (uint256 i = 0; i < logN - 1; i++) { - gR[1 + i * 2] = proof.geminiFoldComms[i].x; - gR[2 + i * 2] = proof.geminiFoldComms[i].y; - } - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - - (geminiR, ) = splitChallenge(nextPreviousChallenge); - } - - function generateShplonkNuChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge, - uint256 logN - ) internal pure returns (Fr shplonkNu, Fr nextPreviousChallenge) { - uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); - shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); - - for (uint256 i = 1; i <= logN; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); - } - - uint256 libraIdx = 0; - for (uint256 i = logN + 1; i <= logN + 4; i++) { - shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); - libraIdx++; - } - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); - (shplonkNu, ) = splitChallenge(nextPreviousChallenge); - } - - function generateShplonkZChallenge( - Honk.ZKProof memory proof, - Fr prevChallenge - ) internal pure returns (Fr shplonkZ, Fr nextPreviousChallenge) { - uint256[3] memory shplonkZChallengeElements; - shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); - - shplonkZChallengeElements[1] = proof.shplonkQ.x; - shplonkZChallengeElements[2] = proof.shplonkQ.y; - - nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); - (shplonkZ, ) = splitChallenge(nextPreviousChallenge); - } - - function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { - uint256 boundary = 0x0; - - // Pairing point object - for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - } - // Commitments - p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - - // Lookup / Permutation Helper Commitments - p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - - p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - // Sumcheck univariates - for (uint256 i = 0; i < logN; i++) { - for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { - p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - } - } - - // Sumcheck evaluations - for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { - p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - } - - p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - - p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - p.geminiMaskingEval = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - - // Gemini - // Read gemini fold univariates - for (uint256 i = 0; i < logN - 1; i++) { - p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - } - - // Read gemini a evaluations - for (uint256 i = 0; i < logN; i++) { - p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - } - - for (uint256 i = 0; i < 4; i++) { - p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); - boundary += FIELD_ELEMENT_SIZE; - } - - // Shplonk - p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - boundary += GROUP_ELEMENT_SIZE; - // KZG - p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); - } -} - -// Field arithmetic libraries - -library RelationsLib { - Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) - - function accumulateRelationEvaluations( - Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_ALPHAS] memory alphas, - Fr powPartialEval - ) internal pure returns (Fr accumulator) { - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; - - // Accumulate all relations in Ultra Honk - each with varying number of subrelations - accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); - accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); - accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); - accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); - - // batch the subrelations with the alpha challenges to obtain the full honk relation - accumulator = scaleAndBatchSubrelations(evaluations, alphas); - } - - /** - * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids - * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code - * editors, and thus is noisy. - */ - function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { - return p[uint256(_wire)]; - } - - uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; - /** - * Ultra Arithmetic Relation - * - */ - - function accumulateArithmeticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - // Relation 0 - Fr q_arith = wire(p, WIRE.Q_ARITH); + function generateTranscript( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + uint256 logN + ) external pure returns (ZKTranscript memory t) { + Fr previousChallenge; + (t.relationParameters, previousChallenge) = + generateRelationParametersChallenges(proof, publicInputs, vkHash, publicInputsSize, previousChallenge); + + (t.alphas, previousChallenge) = generateAlphaChallenges(previousChallenge, proof); + + (t.gateChallenges, previousChallenge) = generateGateChallenges(previousChallenge, logN); + (t.libraChallenge, previousChallenge) = generateLibraChallenge(previousChallenge, proof); + (t.sumCheckUChallenges, previousChallenge) = generateSumcheckChallenges(proof, previousChallenge, logN); + + (t.rho, previousChallenge) = generateRhoChallenge(proof, previousChallenge); + + (t.geminiR, previousChallenge) = generateGeminiRChallenge(proof, previousChallenge, logN); + + (t.shplonkNu, previousChallenge) = generateShplonkNuChallenge(proof, previousChallenge, logN); + + (t.shplonkZ, previousChallenge) = generateShplonkZChallenge(proof, previousChallenge); + return t; + } + + function splitChallenge(Fr challenge) internal pure returns (Fr first, Fr second) { + uint256 challengeU256 = uint256(Fr.unwrap(challenge)); + uint256 lo = challengeU256 & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 hi = challengeU256 >> 128; + first = FrLib.fromBytes32(bytes32(lo)); + second = FrLib.fromBytes32(bytes32(hi)); + } + + function generateRelationParametersChallenges( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize, + Fr previousChallenge + ) internal pure returns (Honk.RelationParameters memory rp, Fr nextPreviousChallenge) { + (rp.eta, rp.etaTwo, rp.etaThree, previousChallenge) = + generateEtaChallenge(proof, publicInputs, vkHash, publicInputsSize); + + (rp.beta, rp.gamma, nextPreviousChallenge) = generateBetaAndGammaChallenges(previousChallenge, proof); + } + + function generateEtaChallenge( + Honk.ZKProof memory proof, + bytes32[] calldata publicInputs, + uint256 vkHash, + uint256 publicInputsSize + ) internal pure returns (Fr eta, Fr etaTwo, Fr etaThree, Fr previousChallenge) { + bytes32[] memory round0 = new bytes32[](1 + publicInputsSize + 6); + round0[0] = bytes32(vkHash); + + for (uint256 i = 0; i < publicInputsSize - PAIRING_POINTS_SIZE; i++) { + round0[1 + i] = bytes32(publicInputs[i]); + } + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + round0[1 + publicInputsSize - PAIRING_POINTS_SIZE + i] = FrLib.toBytes32(proof.pairingPointObject[i]); + } + + // Create the first challenge + // Note: w4 is added to the challenge later on + round0[1 + publicInputsSize] = bytes32(proof.w1.x); + round0[1 + publicInputsSize + 1] = bytes32(proof.w1.y); + round0[1 + publicInputsSize + 2] = bytes32(proof.w2.x); + round0[1 + publicInputsSize + 3] = bytes32(proof.w2.y); + round0[1 + publicInputsSize + 4] = bytes32(proof.w3.x); + round0[1 + publicInputsSize + 5] = bytes32(proof.w3.y); + + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round0))); + (eta, etaTwo) = splitChallenge(previousChallenge); + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + + (etaThree,) = splitChallenge(previousChallenge); + } + + function generateBetaAndGammaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr beta, Fr gamma, Fr nextPreviousChallenge) { - Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); - - Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; - accum = - accum + - (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + - (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + - (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + - (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + - wire(p, WIRE.Q_C); - accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); - accum = accum * q_arith; - accum = accum * domainSep; - evals[0] = accum; - } - - // Relation 1 + bytes32[7] memory round1; + round1[0] = FrLib.toBytes32(previousChallenge); + round1[1] = bytes32(proof.lookupReadCounts.x); + round1[2] = bytes32(proof.lookupReadCounts.y); + round1[3] = bytes32(proof.lookupReadTags.x); + round1[4] = bytes32(proof.lookupReadTags.y); + round1[5] = bytes32(proof.w4.x); + round1[6] = bytes32(proof.w4.y); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(round1))); + (beta, gamma) = splitChallenge(nextPreviousChallenge); + } + + // Alpha challenges non-linearise the gate contributions + function generateAlphaChallenges(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr[NUMBER_OF_ALPHAS] memory alphas, Fr nextPreviousChallenge) { - Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); - accum = accum * (q_arith - Fr.wrap(2)); - accum = accum * (q_arith - ONE); - accum = accum * q_arith; - accum = accum * domainSep; - evals[1] = accum; - } - } - - function accumulatePermutationRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - Fr grand_product_numerator; - Fr grand_product_denominator; - + // Generate the original sumcheck alpha 0 by hashing zPerm and zLookup + uint256[5] memory alpha0; + alpha0[0] = Fr.unwrap(previousChallenge); + alpha0[1] = proof.lookupInverses.x; + alpha0[2] = proof.lookupInverses.y; + alpha0[3] = proof.zPerm.x; + alpha0[4] = proof.zPerm.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(alpha0))); + Fr alpha; + (alpha,) = splitChallenge(nextPreviousChallenge); + + // Compute powers of alpha for batching subrelations + alphas[0] = alpha; + for (uint256 i = 1; i < NUMBER_OF_ALPHAS; i++) { + alphas[i] = alphas[i - 1] * alpha; + } + } + + function generateGateChallenges(Fr previousChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory gateChallenges, Fr nextPreviousChallenge) { - Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; - num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); - num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); - - grand_product_numerator = num; - } + previousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(Fr.unwrap(previousChallenge)))); + (gateChallenges[0],) = splitChallenge(previousChallenge); + for (uint256 i = 1; i < logN; i++) { + gateChallenges[i] = gateChallenges[i - 1] * gateChallenges[i - 1]; + } + nextPreviousChallenge = previousChallenge; + } + + function generateLibraChallenge(Fr previousChallenge, Honk.ZKProof memory proof) + internal + pure + returns (Fr libraChallenge, Fr nextPreviousChallenge) { - Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; - den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); - den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); - - grand_product_denominator = den; - } - - // Contribution 2 + // 2 comm, 1 sum, 1 challenge + uint256[4] memory challengeData; + challengeData[0] = Fr.unwrap(previousChallenge); + challengeData[1] = proof.libraCommitments[0].x; + challengeData[2] = proof.libraCommitments[0].y; + challengeData[3] = Fr.unwrap(proof.libraSum); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(challengeData))); + (libraChallenge,) = splitChallenge(nextPreviousChallenge); + } + + function generateSumcheckChallenges(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckChallenges, Fr nextPreviousChallenge) { - Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + for (uint256 i = 0; i < logN; i++) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH + 1] memory univariateChal; + univariateChal[0] = prevChallenge; - acc = acc - ((wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) * grand_product_denominator); - acc = acc * domainSep; - evals[2] = acc; - } + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + univariateChal[j + 1] = proof.sumcheckUnivariates[i][j]; + } + prevChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(univariateChal))); - // Contribution 3 - { - Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; - evals[3] = acc; + (sumcheckChallenges[i],) = splitChallenge(prevChallenge); + } + nextPreviousChallenge = prevChallenge; } - } - - function accumulateLogDerivativeLookupRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - Fr write_term; - Fr read_term; - // Calculate the write term (the table accumulation) + // We add Libra claimed eval + 3 comm + 1 more eval + function generateRhoChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr rho, Fr nextPreviousChallenge) { - write_term = - wire(p, WIRE.TABLE_1) + - rp.gamma + - (wire(p, WIRE.TABLE_2) * rp.eta) + - (wire(p, WIRE.TABLE_3) * rp.etaTwo) + - (wire(p, WIRE.TABLE_4) * rp.etaThree); - } - - // Calculate the write term + uint256[NUMBER_OF_ENTITIES + 9] memory rhoChallengeElements; + rhoChallengeElements[0] = Fr.unwrap(prevChallenge); + uint256 i; + for (i = 1; i <= NUMBER_OF_ENTITIES; i++) { + rhoChallengeElements[i] = Fr.unwrap(proof.sumcheckEvaluations[i - 1]); + } + rhoChallengeElements[i] = Fr.unwrap(proof.libraEvaluation); + + i += 1; + rhoChallengeElements[i] = proof.libraCommitments[1].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[1].y; + i += 2; + rhoChallengeElements[i] = proof.libraCommitments[2].x; + rhoChallengeElements[i + 1] = proof.libraCommitments[2].y; + i += 2; + rhoChallengeElements[i] = proof.geminiMaskingPoly.x; + rhoChallengeElements[i + 1] = proof.geminiMaskingPoly.y; + + i += 2; + rhoChallengeElements[i] = Fr.unwrap(proof.geminiMaskingEval); + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(rhoChallengeElements))); + (rho,) = splitChallenge(nextPreviousChallenge); + } + + function generateGeminiRChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr geminiR, Fr nextPreviousChallenge) { - Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); - Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); - Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); - - read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + (wire(p, WIRE.Q_O) * rp.etaThree); - } - - Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; - Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; - - Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + - wire(p, WIRE.Q_LOOKUP) - - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); - - // Inverse calculated correctly relation - Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; - accumulatorNone = accumulatorNone * domainSep; - - // Inverse - Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; - - Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); - - Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; + uint256[] memory gR = new uint256[]((logN - 1) * 2 + 1); + gR[0] = Fr.unwrap(prevChallenge); - evals[4] = accumulatorNone; - evals[5] = accumulatorOne; - evals[6] = read_tag_boolean_relation * domainSep; - } + for (uint256 i = 0; i < logN - 1; i++) { + gR[1 + i * 2] = proof.geminiFoldComms[i].x; + gR[2 + i * 2] = proof.geminiFoldComms[i].y; + } - function accumulateDeltaRangeRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - Fr minus_one = ZERO - ONE; - Fr minus_two = ZERO - Fr.wrap(2); - Fr minus_three = ZERO - Fr.wrap(3); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(gR))); - // Compute wire differences - Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); - Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); - Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); - Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); - - // Contribution 6 - { - Fr acc = delta_1; - acc = acc * (delta_1 + minus_one); - acc = acc * (delta_1 + minus_two); - acc = acc * (delta_1 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[7] = acc; + (geminiR,) = splitChallenge(nextPreviousChallenge); } - // Contribution 7 + function generateShplonkNuChallenge(Honk.ZKProof memory proof, Fr prevChallenge, uint256 logN) + internal + pure + returns (Fr shplonkNu, Fr nextPreviousChallenge) { - Fr acc = delta_2; - acc = acc * (delta_2 + minus_one); - acc = acc * (delta_2 + minus_two); - acc = acc * (delta_2 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[8] = acc; - } + uint256[] memory shplonkNuChallengeElements = new uint256[](logN + 1 + 4); + shplonkNuChallengeElements[0] = Fr.unwrap(prevChallenge); - // Contribution 8 - { - Fr acc = delta_3; - acc = acc * (delta_3 + minus_one); - acc = acc * (delta_3 + minus_two); - acc = acc * (delta_3 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[9] = acc; - } + for (uint256 i = 1; i <= logN; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.geminiAEvaluations[i - 1]); + } - // Contribution 9 - { - Fr acc = delta_4; - acc = acc * (delta_4 + minus_one); - acc = acc * (delta_4 + minus_two); - acc = acc * (delta_4 + minus_three); - acc = acc * wire(p, WIRE.Q_RANGE); - acc = acc * domainSep; - evals[10] = acc; - } - } - - struct EllipticParams { - // Points - Fr x_1; - Fr y_1; - Fr x_2; - Fr y_2; - Fr y_3; - Fr x_3; - // push accumulators into memory - Fr x_double_identity; - } - - function accumulateEllipticRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - EllipticParams memory ep; - ep.x_1 = wire(p, WIRE.W_R); - ep.y_1 = wire(p, WIRE.W_O); - - ep.x_2 = wire(p, WIRE.W_L_SHIFT); - ep.y_2 = wire(p, WIRE.W_4_SHIFT); - ep.y_3 = wire(p, WIRE.W_O_SHIFT); - ep.x_3 = wire(p, WIRE.W_R_SHIFT); - - Fr q_sign = wire(p, WIRE.Q_L); - Fr q_is_double = wire(p, WIRE.Q_M); - - // Contribution 10 point addition, x-coordinate check - // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 - Fr x_diff = (ep.x_2 - ep.x_1); - Fr y1_sqr = (ep.y_1 * ep.y_1); - { - // Move to top - Fr partialEval = domainSep; - - Fr y2_sqr = (ep.y_2 * ep.y_2); - Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; - Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); - x_add_identity = x_add_identity * x_diff * x_diff; - x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; - - evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); - } + uint256 libraIdx = 0; + for (uint256 i = logN + 1; i <= logN + 4; i++) { + shplonkNuChallengeElements[i] = Fr.unwrap(proof.libraPolyEvals[libraIdx]); + libraIdx++; + } - // Contribution 11 point addition, x-coordinate check - // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 - { - Fr y1_plus_y3 = ep.y_1 + ep.y_3; - Fr y_diff = ep.y_2 * q_sign - ep.y_1; - Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; - evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkNuChallengeElements))); + (shplonkNu,) = splitChallenge(nextPreviousChallenge); } - // Contribution 10 point doubling, x-coordinate check - // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 - // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + function generateShplonkZChallenge(Honk.ZKProof memory proof, Fr prevChallenge) + internal + pure + returns (Fr shplonkZ, Fr nextPreviousChallenge) { - Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; - Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; - y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; - Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + uint256[3] memory shplonkZChallengeElements; + shplonkZChallengeElements[0] = Fr.unwrap(prevChallenge); + + shplonkZChallengeElements[1] = proof.shplonkQ.x; + shplonkZChallengeElements[2] = proof.shplonkQ.y; + + nextPreviousChallenge = FrLib.fromBytes32(keccak256(abi.encodePacked(shplonkZChallengeElements))); + (shplonkZ,) = splitChallenge(nextPreviousChallenge); + } + + function loadProof(bytes calldata proof, uint256 logN) internal pure returns (Honk.ZKProof memory p) { + uint256 boundary = 0x0; + + // Pairing point object + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + p.pairingPointObject[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + // Commitments + p.w1 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w2 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w3 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + // Lookup / Permutation Helper Commitments + p.lookupReadCounts = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupReadTags = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.w4 = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.lookupInverses = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.zPerm = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[0] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + + p.libraSum = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + // Sumcheck univariates + for (uint256 i = 0; i < logN; i++) { + for (uint256 j = 0; j < ZK_BATCHED_RELATION_PARTIAL_LENGTH; j++) { + p.sumcheckUnivariates[i][j] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + } + + // Sumcheck evaluations + for (uint256 i = 0; i < NUMBER_OF_ENTITIES; i++) { + p.sumcheckEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + p.libraEvaluation = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; - // NOTE: pushed into memory (stack >:'( ) - ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + p.libraCommitments[1] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.libraCommitments[2] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.geminiMaskingPoly = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + p.geminiMaskingEval = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; - Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - evals[11] = evals[11] + acc; + // Gemini + // Read gemini fold univariates + for (uint256 i = 0; i < logN - 1; i++) { + p.geminiFoldComms[i] = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + } + + // Read gemini a evaluations + for (uint256 i = 0; i < logN; i++) { + p.geminiAEvaluations[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + for (uint256 i = 0; i < 4; i++) { + p.libraPolyEvals[i] = bytesToFr(proof[boundary:boundary + FIELD_ELEMENT_SIZE]); + boundary += FIELD_ELEMENT_SIZE; + } + + // Shplonk + p.shplonkQ = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); + boundary += GROUP_ELEMENT_SIZE; + // KZG + p.kzgQuotient = bytesToG1Point(proof[boundary:boundary + GROUP_ELEMENT_SIZE]); } +} - // Contribution 11 point doubling, y-coordinate check - // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 - { - Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; - Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); - evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; - } - } - - // Parameters used within the Memory Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct MemParams { - Fr memory_record_check; - Fr partial_record_check; - Fr next_gate_access_type; - Fr record_delta; - Fr index_delta; - Fr adjacent_values_match_if_adjacent_indices_match; - Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; - Fr access_check; - Fr next_gate_access_type_is_boolean; - Fr ROM_consistency_check_identity; - Fr RAM_consistency_check_identity; - Fr timestamp_delta; - Fr RAM_timestamp_check_identity; - Fr memory_identity; - Fr index_is_monotonically_increasing; - } - - function accumulateMemoryRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Honk.RelationParameters memory rp, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - MemParams memory ap; - - /** - * MEMORY - * - * A RAM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) - * * v: `value` of memory cell being accessed - * * a: `access` type of record. read: 0 = read, 1 = write - * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three - * - * A ROM memory record contains a tuple of the following fields: - * * i: `index` of memory cell being accessed - * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) - * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three - * - * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + - * selectors, depending on whether the gate is a RAM read/write or a ROM read - * - * | gate type | i | v2/t | v | a | r | - * | --------- | -- | ----- | -- | -- | -- | - * | ROM | w1 | w2 | w3 | -- | w4 | - * | RAM | w1 | w2 | w3 | qc | w4 | - * - * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on - * `w2` to fix its value) - * - * - */ - - /** - * Memory Record Check - * Partial degree: 1 - * Total degree: 4 - * - * A ROM/ROM access gate can be evaluated with the identity: - * - * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 - * - * For ROM gates, qc = 0 - */ - ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); - ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); - ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); - ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 - ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); - - /** - * Contribution 13 & 14 - * ROM Consistency Check - * Partial degree: 1 - * Total degree: 4 - * - * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of - * records that are sorted. - * - * We apply the following checks for the sorted records: - * - * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 - * 2. index values for adjacent records are monotonically increasing - * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} - * - */ - ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); - ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); - - ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 - - ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 - - evals[14] = - ap.adjacent_values_match_if_adjacent_indices_match * - (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 - evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 +// Field arithmetic libraries - ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 +library RelationsLib { + Fr internal constant GRUMPKIN_CURVE_B_PARAMETER_NEGATED = Fr.wrap(17); // -(-17) + + function accumulateRelationEvaluations( + Fr[NUMBER_OF_ENTITIES] memory purportedEvaluations, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_ALPHAS] memory alphas, + Fr powPartialEval + ) internal pure returns (Fr accumulator) { + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations; + + // Accumulate all relations in Ultra Honk - each with varying number of subrelations + accumulateArithmeticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePermutationRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateLogDerivativeLookupRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateDeltaRangeRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateEllipticRelation(purportedEvaluations, evaluations, powPartialEval); + accumulateMemoryRelation(purportedEvaluations, rp, evaluations, powPartialEval); + accumulateNnfRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonExternalRelation(purportedEvaluations, evaluations, powPartialEval); + accumulatePoseidonInternalRelation(purportedEvaluations, evaluations, powPartialEval); + + // batch the subrelations with the alpha challenges to obtain the full honk relation + accumulator = scaleAndBatchSubrelations(evaluations, alphas); + } /** - * Contributions 15,16,17 - * RAM Consistency Check - * - * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` - * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. - * This is validated by requiring `access` to be boolean - * - * For two adjacent entries in the sorted list if _both_ - * A) index values match - * B) adjacent access value is 0 (i.e. next gate is a READ) - * then - * C) both values must match. - * The gate boolean check is - * (A && B) => C === !(A && B) || C === !A || !B || C - * - * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized - * with a WRITE operation. + * Aesthetic helper function that is used to index by enum into proof.sumcheckEvaluations, it avoids + * the relation checking code being cluttered with uint256 type casting, which is often a different colour in code + * editors, and thus is noisy. */ - Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 - ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8 - - // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta - // deg 1 or 4 - ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); - ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); - ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; - - Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = - (ap.index_delta * MINUS_ONE + ONE) * - value_delta * - (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 - - // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the - // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't - // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access - // type is correct, to cover this edge case - // deg 2 or 4 - ap.next_gate_access_type_is_boolean = ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; - - // Putting it all together... - evals[16] = - ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation * - (wire(p, WIRE.Q_O)) * - (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 - evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 - evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 - - ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + function wire(Fr[NUMBER_OF_ENTITIES] memory p, WIRE _wire) internal pure returns (Fr) { + return p[uint256(_wire)]; + } + uint256 internal constant NEG_HALF_MODULO_P = 0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000; /** - * RAM Timestamp Consistency Check - * - * | w1 | w2 | w3 | w4 | - * | index | timestamp | timestamp_check | -- | - * - * Let delta_index = index_{i + 1} - index_{i} + * Ultra Arithmetic Relation * - * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i - * Else timestamp_check = 0 */ - ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); - ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 - /** - * Complete Contribution 12 - * The complete RAM/ROM memory identity - * Partial degree: - */ - ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 - ap.memory_identity = ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 - ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 - ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 - - // (deg 3 or 9) + (deg 4) + (deg 3) - ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 - evals[13] = ap.memory_identity; - } - - // Constants for the Non-native Field relation - Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); - Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); - - // Parameters used within the Non-Native Field Relation - // A struct is used to work around stack too deep. This relation has alot of variables - struct NnfParams { - Fr limb_subproduct; - Fr non_native_field_gate_1; - Fr non_native_field_gate_2; - Fr non_native_field_gate_3; - Fr limb_accumulator_1; - Fr limb_accumulator_2; - Fr nnf_identity; - } - - function accumulateNnfRelation(Fr[NUMBER_OF_ENTITIES] memory p, Fr[NUMBER_OF_SUBRELATIONS] memory evals, Fr domainSep) internal pure { - NnfParams memory ap; - - /** - * Contribution 12 - * Non native field arithmetic gate 2 - * deg 4 - * - * _ _ - * / _ _ _ 14 \ - * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | - * \_ _/ - * - * - */ - ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); - ap.non_native_field_gate_2 = (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; - ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); - - ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; - ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); - ap.non_native_field_gate_1 = ap.limb_subproduct; - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); - ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); - - ap.non_native_field_gate_3 = ap.limb_subproduct; - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); - ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); - - Fr non_native_field_identity = ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; - non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); - - // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm - // deg 2 - ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; - ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); - ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); - ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); - - // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm - // deg 2 - ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; - ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); - ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); - ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); - - Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; - limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 - - ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; - ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); - evals[19] = ap.nnf_identity; - } - - struct PoseidonExternalParams { - Fr s1; - Fr s2; - Fr s3; - Fr s4; - Fr u1; - Fr u2; - Fr u3; - Fr u4; - Fr t0; - Fr t1; - Fr t2; - Fr t3; - Fr v1; - Fr v2; - Fr v3; - Fr v4; - Fr q_pos_by_scaling; - } - - function accumulatePoseidonExternalRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - PoseidonExternalParams memory ep; - - ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); - ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); - ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); - ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); - - ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; - ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; - ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; - ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; - // matrix mul v = M_E * u with 14 additions - ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 - ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 - ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 - // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 - ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 - // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 - ep.v4 = ep.t1 + ep.t1; - ep.v4 = ep.v4 + ep.v4 + ep.t3; - // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 - ep.v2 = ep.t0 + ep.t0; - ep.v2 = ep.v2 + ep.v2 + ep.t2; - // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 - ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 - ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 - - ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; - evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); - - evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); - - evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); - - evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); - } - - struct PoseidonInternalParams { - Fr u1; - Fr u2; - Fr u3; - Fr u4; - Fr u_sum; - Fr v1; - Fr v2; - Fr v3; - Fr v4; - Fr s1; - Fr q_pos_by_scaling; - } - - function accumulatePoseidonInternalRelation( - Fr[NUMBER_OF_ENTITIES] memory p, - Fr[NUMBER_OF_SUBRELATIONS] memory evals, - Fr domainSep - ) internal pure { - PoseidonInternalParams memory ip; - - Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ - FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), - FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), - FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), - FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) - ]; - - // add round constants - ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); - - // apply s-box round - ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; - ip.u2 = wire(p, WIRE.W_R); - ip.u3 = wire(p, WIRE.W_O); - ip.u4 = wire(p, WIRE.W_4); - - // matrix mul with v = M_I * u 4 muls and 7 additions - ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; - - ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; - - ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; - evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); - - ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; - evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); - - ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; - evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); - - ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; - evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); - } - - function scaleAndBatchSubrelations( - Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, - Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges - ) internal pure returns (Fr accumulator) { - accumulator = evaluations[0]; - - for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { - accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; - } - } + function accumulateArithmeticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + // Relation 0 + Fr q_arith = wire(p, WIRE.Q_ARITH); + { + Fr neg_half = Fr.wrap(NEG_HALF_MODULO_P); + + Fr accum = (q_arith - Fr.wrap(3)) * (wire(p, WIRE.Q_M) * wire(p, WIRE.W_R) * wire(p, WIRE.W_L)) * neg_half; + accum = accum + (wire(p, WIRE.Q_L) * wire(p, WIRE.W_L)) + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_R)) + + (wire(p, WIRE.Q_O) * wire(p, WIRE.W_O)) + (wire(p, WIRE.Q_4) * wire(p, WIRE.W_4)) + wire(p, WIRE.Q_C); + accum = accum + (q_arith - ONE) * wire(p, WIRE.W_4_SHIFT); + accum = accum * q_arith; + accum = accum * domainSep; + evals[0] = accum; + } + + // Relation 1 + { + Fr accum = wire(p, WIRE.W_L) + wire(p, WIRE.W_4) - wire(p, WIRE.W_L_SHIFT) + wire(p, WIRE.Q_M); + accum = accum * (q_arith - Fr.wrap(2)); + accum = accum * (q_arith - ONE); + accum = accum * q_arith; + accum = accum * domainSep; + evals[1] = accum; + } + } + + function accumulatePermutationRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr grand_product_numerator; + Fr grand_product_denominator; + + { + Fr num = wire(p, WIRE.W_L) + wire(p, WIRE.ID_1) * rp.beta + rp.gamma; + num = num * (wire(p, WIRE.W_R) + wire(p, WIRE.ID_2) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_O) + wire(p, WIRE.ID_3) * rp.beta + rp.gamma); + num = num * (wire(p, WIRE.W_4) + wire(p, WIRE.ID_4) * rp.beta + rp.gamma); + + grand_product_numerator = num; + } + { + Fr den = wire(p, WIRE.W_L) + wire(p, WIRE.SIGMA_1) * rp.beta + rp.gamma; + den = den * (wire(p, WIRE.W_R) + wire(p, WIRE.SIGMA_2) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_O) + wire(p, WIRE.SIGMA_3) * rp.beta + rp.gamma); + den = den * (wire(p, WIRE.W_4) + wire(p, WIRE.SIGMA_4) * rp.beta + rp.gamma); + + grand_product_denominator = den; + } + + // Contribution 2 + { + Fr acc = (wire(p, WIRE.Z_PERM) + wire(p, WIRE.LAGRANGE_FIRST)) * grand_product_numerator; + + acc = acc + - ( + (wire(p, WIRE.Z_PERM_SHIFT) + (wire(p, WIRE.LAGRANGE_LAST) * rp.publicInputsDelta)) + * grand_product_denominator + ); + acc = acc * domainSep; + evals[2] = acc; + } + + // Contribution 3 + { + Fr acc = (wire(p, WIRE.LAGRANGE_LAST) * wire(p, WIRE.Z_PERM_SHIFT)) * domainSep; + evals[3] = acc; + } + } + + function accumulateLogDerivativeLookupRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr write_term; + Fr read_term; + + // Calculate the write term (the table accumulation) + { + write_term = wire(p, WIRE.TABLE_1) + rp.gamma + (wire(p, WIRE.TABLE_2) * rp.eta) + + (wire(p, WIRE.TABLE_3) * rp.etaTwo) + (wire(p, WIRE.TABLE_4) * rp.etaThree); + } + + // Calculate the write term + { + Fr derived_entry_1 = wire(p, WIRE.W_L) + rp.gamma + (wire(p, WIRE.Q_R) * wire(p, WIRE.W_L_SHIFT)); + Fr derived_entry_2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_M) * wire(p, WIRE.W_R_SHIFT); + Fr derived_entry_3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_C) * wire(p, WIRE.W_O_SHIFT); + + read_term = derived_entry_1 + (derived_entry_2 * rp.eta) + (derived_entry_3 * rp.etaTwo) + + (wire(p, WIRE.Q_O) * rp.etaThree); + } + + Fr read_inverse = wire(p, WIRE.LOOKUP_INVERSES) * write_term; + Fr write_inverse = wire(p, WIRE.LOOKUP_INVERSES) * read_term; + + Fr inverse_exists_xor = wire(p, WIRE.LOOKUP_READ_TAGS) + wire(p, WIRE.Q_LOOKUP) + - (wire(p, WIRE.LOOKUP_READ_TAGS) * wire(p, WIRE.Q_LOOKUP)); + + // Inverse calculated correctly relation + Fr accumulatorNone = read_term * write_term * wire(p, WIRE.LOOKUP_INVERSES) - inverse_exists_xor; + accumulatorNone = accumulatorNone * domainSep; + + // Inverse + Fr accumulatorOne = wire(p, WIRE.Q_LOOKUP) * read_inverse - wire(p, WIRE.LOOKUP_READ_COUNTS) * write_inverse; + + Fr read_tag = wire(p, WIRE.LOOKUP_READ_TAGS); + + Fr read_tag_boolean_relation = read_tag * read_tag - read_tag; + + evals[4] = accumulatorNone; + evals[5] = accumulatorOne; + evals[6] = read_tag_boolean_relation * domainSep; + } + + function accumulateDeltaRangeRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + Fr minus_one = ZERO - ONE; + Fr minus_two = ZERO - Fr.wrap(2); + Fr minus_three = ZERO - Fr.wrap(3); + + // Compute wire differences + Fr delta_1 = wire(p, WIRE.W_R) - wire(p, WIRE.W_L); + Fr delta_2 = wire(p, WIRE.W_O) - wire(p, WIRE.W_R); + Fr delta_3 = wire(p, WIRE.W_4) - wire(p, WIRE.W_O); + Fr delta_4 = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_4); + + // Contribution 6 + { + Fr acc = delta_1; + acc = acc * (delta_1 + minus_one); + acc = acc * (delta_1 + minus_two); + acc = acc * (delta_1 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[7] = acc; + } + + // Contribution 7 + { + Fr acc = delta_2; + acc = acc * (delta_2 + minus_one); + acc = acc * (delta_2 + minus_two); + acc = acc * (delta_2 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[8] = acc; + } + + // Contribution 8 + { + Fr acc = delta_3; + acc = acc * (delta_3 + minus_one); + acc = acc * (delta_3 + minus_two); + acc = acc * (delta_3 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[9] = acc; + } + + // Contribution 9 + { + Fr acc = delta_4; + acc = acc * (delta_4 + minus_one); + acc = acc * (delta_4 + minus_two); + acc = acc * (delta_4 + minus_three); + acc = acc * wire(p, WIRE.Q_RANGE); + acc = acc * domainSep; + evals[10] = acc; + } + } + + struct EllipticParams { + // Points + Fr x_1; + Fr y_1; + Fr x_2; + Fr y_2; + Fr y_3; + Fr x_3; + // push accumulators into memory + Fr x_double_identity; + } + + function accumulateEllipticRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + EllipticParams memory ep; + ep.x_1 = wire(p, WIRE.W_R); + ep.y_1 = wire(p, WIRE.W_O); + + ep.x_2 = wire(p, WIRE.W_L_SHIFT); + ep.y_2 = wire(p, WIRE.W_4_SHIFT); + ep.y_3 = wire(p, WIRE.W_O_SHIFT); + ep.x_3 = wire(p, WIRE.W_R_SHIFT); + + Fr q_sign = wire(p, WIRE.Q_L); + Fr q_is_double = wire(p, WIRE.Q_M); + + // Contribution 10 point addition, x-coordinate check + // q_elliptic * (x3 + x2 + x1)(x2 - x1)(x2 - x1) - y2^2 - y1^2 + 2(y2y1)*q_sign = 0 + Fr x_diff = (ep.x_2 - ep.x_1); + Fr y1_sqr = (ep.y_1 * ep.y_1); + { + // Move to top + Fr partialEval = domainSep; + + Fr y2_sqr = (ep.y_2 * ep.y_2); + Fr y1y2 = ep.y_1 * ep.y_2 * q_sign; + Fr x_add_identity = (ep.x_3 + ep.x_2 + ep.x_1); + x_add_identity = x_add_identity * x_diff * x_diff; + x_add_identity = x_add_identity - y2_sqr - y1_sqr + y1y2 + y1y2; + + evals[11] = x_add_identity * partialEval * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 11 point addition, x-coordinate check + // q_elliptic * (q_sign * y1 + y3)(x2 - x1) + (x3 - x1)(y2 - q_sign * y1) = 0 + { + Fr y1_plus_y3 = ep.y_1 + ep.y_3; + Fr y_diff = ep.y_2 * q_sign - ep.y_1; + Fr y_add_identity = y1_plus_y3 * x_diff + (ep.x_3 - ep.x_1) * y_diff; + evals[12] = y_add_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * (ONE - q_is_double); + } + + // Contribution 10 point doubling, x-coordinate check + // (x3 + x1 + x1) (4y1*y1) - 9 * x1 * x1 * x1 * x1 = 0 + // N.B. we're using the equivalence x1*x1*x1 === y1*y1 - curve_b to reduce degree by 1 + { + Fr x_pow_4 = (y1_sqr + GRUMPKIN_CURVE_B_PARAMETER_NEGATED) * ep.x_1; + Fr y1_sqr_mul_4 = y1_sqr + y1_sqr; + y1_sqr_mul_4 = y1_sqr_mul_4 + y1_sqr_mul_4; + Fr x1_pow_4_mul_9 = x_pow_4 * Fr.wrap(9); + + // NOTE: pushed into memory (stack >:'( ) + ep.x_double_identity = (ep.x_3 + ep.x_1 + ep.x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9; + + Fr acc = ep.x_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + evals[11] = evals[11] + acc; + } + + // Contribution 11 point doubling, y-coordinate check + // (y1 + y1) (2y1) - (3 * x1 * x1)(x1 - x3) = 0 + { + Fr x1_sqr_mul_3 = (ep.x_1 + ep.x_1 + ep.x_1) * ep.x_1; + Fr y_double_identity = x1_sqr_mul_3 * (ep.x_1 - ep.x_3) - (ep.y_1 + ep.y_1) * (ep.y_1 + ep.y_3); + evals[12] = evals[12] + y_double_identity * domainSep * wire(p, WIRE.Q_ELLIPTIC) * q_is_double; + } + } + + // Parameters used within the Memory Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct MemParams { + Fr memory_record_check; + Fr partial_record_check; + Fr next_gate_access_type; + Fr record_delta; + Fr index_delta; + Fr adjacent_values_match_if_adjacent_indices_match; + Fr adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation; + Fr access_check; + Fr next_gate_access_type_is_boolean; + Fr ROM_consistency_check_identity; + Fr RAM_consistency_check_identity; + Fr timestamp_delta; + Fr RAM_timestamp_check_identity; + Fr memory_identity; + Fr index_is_monotonically_increasing; + } + + function accumulateMemoryRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Honk.RelationParameters memory rp, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + MemParams memory ap; + + /** + * MEMORY + * + * A RAM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * t: `timestamp` of memory cell being accessed (used for RAM, set to 0 for ROM) + * * v: `value` of memory cell being accessed + * * a: `access` type of record. read: 0 = read, 1 = write + * * r: `record` of memory cell. record = access + index * eta + timestamp * eta_two + value * eta_three + * + * A ROM memory record contains a tuple of the following fields: + * * i: `index` of memory cell being accessed + * * v: `value1` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * v2:`value2` of memory cell being accessed (ROM tables can store up to 2 values per index) + * * r: `record` of memory cell. record = index * eta + value2 * eta_two + value1 * eta_three + * + * When performing a read/write access, the values of i, t, v, v2, a, r are stored in the following wires + + * selectors, depending on whether the gate is a RAM read/write or a ROM read + * + * | gate type | i | v2/t | v | a | r | + * | --------- | -- | ----- | -- | -- | -- | + * | ROM | w1 | w2 | w3 | -- | w4 | + * | RAM | w1 | w2 | w3 | qc | w4 | + * + * (for accesses where `index` is a circuit constant, it is assumed the circuit will apply a copy constraint on + * `w2` to fix its value) + * + * + */ + + /** + * Memory Record Check + * Partial degree: 1 + * Total degree: 4 + * + * A ROM/ROM access gate can be evaluated with the identity: + * + * qc + w1 \eta + w2 \eta_two + w3 \eta_three - w4 = 0 + * + * For ROM gates, qc = 0 + */ + ap.memory_record_check = wire(p, WIRE.W_O) * rp.etaThree; + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_R) * rp.etaTwo); + ap.memory_record_check = ap.memory_record_check + (wire(p, WIRE.W_L) * rp.eta); + ap.memory_record_check = ap.memory_record_check + wire(p, WIRE.Q_C); + ap.partial_record_check = ap.memory_record_check; // used in RAM consistency check; deg 1 or 4 + ap.memory_record_check = ap.memory_record_check - wire(p, WIRE.W_4); + + /** + * Contribution 13 & 14 + * ROM Consistency Check + * Partial degree: 1 + * Total degree: 4 + * + * For every ROM read, a set equivalence check is applied between the record witnesses, and a second set of + * records that are sorted. + * + * We apply the following checks for the sorted records: + * + * 1. w1, w2, w3 correctly map to 'index', 'v1, 'v2' for a given record value at w4 + * 2. index values for adjacent records are monotonically increasing + * 3. if, at gate i, index_i == index_{i + 1}, then value1_i == value1_{i + 1} and value2_i == value2_{i + 1} + * + */ + ap.index_delta = wire(p, WIRE.W_L_SHIFT) - wire(p, WIRE.W_L); + ap.record_delta = wire(p, WIRE.W_4_SHIFT) - wire(p, WIRE.W_4); + + ap.index_is_monotonically_increasing = ap.index_delta * (ap.index_delta - Fr.wrap(1)); // deg 2 + + ap.adjacent_values_match_if_adjacent_indices_match = (ap.index_delta * MINUS_ONE + ONE) * ap.record_delta; // deg 2 + + evals[14] = ap.adjacent_values_match_if_adjacent_indices_match * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + evals[15] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)) + * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 + + ap.ROM_consistency_check_identity = ap.memory_record_check * (wire(p, WIRE.Q_L) * wire(p, WIRE.Q_R)); // deg 3 or 7 + + /** + * Contributions 15,16,17 + * RAM Consistency Check + * + * The 'access' type of the record is extracted with the expression `w_4 - ap.partial_record_check` + * (i.e. for an honest Prover `w1 * eta + w2 * eta^2 + w3 * eta^3 - w4 = access`. + * This is validated by requiring `access` to be boolean + * + * For two adjacent entries in the sorted list if _both_ + * A) index values match + * B) adjacent access value is 0 (i.e. next gate is a READ) + * then + * C) both values must match. + * The gate boolean check is + * (A && B) => C === !(A && B) || C === !A || !B || C + * + * N.B. it is the responsibility of the circuit writer to ensure that every RAM cell is initialized + * with a WRITE operation. + */ + Fr access_type = (wire(p, WIRE.W_4) - ap.partial_record_check); // will be 0 or 1 for honest Prover; deg 1 or 4 + ap.access_check = access_type * (access_type - Fr.wrap(1)); // check value is 0 or 1; deg 2 or 8 + + // reverse order we could re-use `ap.partial_record_check` 1 - ((w3' * eta + w2') * eta + w1') * eta + // deg 1 or 4 + ap.next_gate_access_type = wire(p, WIRE.W_O_SHIFT) * rp.etaThree; + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_R_SHIFT) * rp.etaTwo); + ap.next_gate_access_type = ap.next_gate_access_type + (wire(p, WIRE.W_L_SHIFT) * rp.eta); + ap.next_gate_access_type = wire(p, WIRE.W_4_SHIFT) - ap.next_gate_access_type; + + Fr value_delta = wire(p, WIRE.W_O_SHIFT) - wire(p, WIRE.W_O); + ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation = + (ap.index_delta * MINUS_ONE + ONE) * value_delta * (ap.next_gate_access_type * MINUS_ONE + ONE); // deg 3 or 6 + + // We can't apply the RAM consistency check identity on the final entry in the sorted list (the wires in the + // next gate would make the identity fail). We need to validate that its 'access type' bool is correct. Can't + // do with an arithmetic gate because of the `eta` factors. We need to check that the *next* gate's access + // type is correct, to cover this edge case + // deg 2 or 4 + ap.next_gate_access_type_is_boolean = + ap.next_gate_access_type * ap.next_gate_access_type - ap.next_gate_access_type; + + // Putting it all together... + evals[16] = ap.adjacent_values_match_if_adjacent_indices_match_and_next_access_is_a_read_operation + * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 5 or 8 + evals[17] = ap.index_is_monotonically_increasing * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 + evals[18] = ap.next_gate_access_type_is_boolean * (wire(p, WIRE.Q_O)) * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 6 + + ap.RAM_consistency_check_identity = ap.access_check * (wire(p, WIRE.Q_O)); // deg 3 or 9 + + /** + * RAM Timestamp Consistency Check + * + * | w1 | w2 | w3 | w4 | + * | index | timestamp | timestamp_check | -- | + * + * Let delta_index = index_{i + 1} - index_{i} + * + * Iff delta_index == 0, timestamp_check = timestamp_{i + 1} - timestamp_i + * Else timestamp_check = 0 + */ + ap.timestamp_delta = wire(p, WIRE.W_R_SHIFT) - wire(p, WIRE.W_R); + ap.RAM_timestamp_check_identity = (ap.index_delta * MINUS_ONE + ONE) * ap.timestamp_delta - wire(p, WIRE.W_O); // deg 3 + + /** + * Complete Contribution 12 + * The complete RAM/ROM memory identity + * Partial degree: + */ + ap.memory_identity = ap.ROM_consistency_check_identity; // deg 3 or 6 + ap.memory_identity = + ap.memory_identity + ap.RAM_timestamp_check_identity * (wire(p, WIRE.Q_4) * wire(p, WIRE.Q_L)); // deg 4 + ap.memory_identity = ap.memory_identity + ap.memory_record_check * (wire(p, WIRE.Q_M) * wire(p, WIRE.Q_L)); // deg 3 or 6 + ap.memory_identity = ap.memory_identity + ap.RAM_consistency_check_identity; // deg 3 or 9 + + // (deg 3 or 9) + (deg 4) + (deg 3) + ap.memory_identity = ap.memory_identity * (wire(p, WIRE.Q_MEMORY) * domainSep); // deg 4 or 10 + evals[13] = ap.memory_identity; + } + + // Constants for the Non-native Field relation + Fr constant LIMB_SIZE = Fr.wrap(uint256(1) << 68); + Fr constant SUBLIMB_SHIFT = Fr.wrap(uint256(1) << 14); + + // Parameters used within the Non-Native Field Relation + // A struct is used to work around stack too deep. This relation has alot of variables + struct NnfParams { + Fr limb_subproduct; + Fr non_native_field_gate_1; + Fr non_native_field_gate_2; + Fr non_native_field_gate_3; + Fr limb_accumulator_1; + Fr limb_accumulator_2; + Fr nnf_identity; + } + + function accumulateNnfRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + NnfParams memory ap; + + /** + * Contribution 12 + * Non native field arithmetic gate 2 + * deg 4 + * + * _ _ + * / _ _ _ 14 \ + * q_2 . q_4 | (w_1 . w_2) + (w_1 . w_2) + (w_1 . w_4 + w_2 . w_3 - w_3) . 2 - w_3 - w_4 | + * \_ _/ + * + * + */ + ap.limb_subproduct = wire(p, WIRE.W_L) * wire(p, WIRE.W_R_SHIFT) + wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R); + ap.non_native_field_gate_2 = + (wire(p, WIRE.W_L) * wire(p, WIRE.W_4) + wire(p, WIRE.W_R) * wire(p, WIRE.W_O) - wire(p, WIRE.W_O_SHIFT)); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * LIMB_SIZE; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 - wire(p, WIRE.W_4_SHIFT); + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 + ap.limb_subproduct; + ap.non_native_field_gate_2 = ap.non_native_field_gate_2 * wire(p, WIRE.Q_4); + + ap.limb_subproduct = ap.limb_subproduct * LIMB_SIZE; + ap.limb_subproduct = ap.limb_subproduct + (wire(p, WIRE.W_L_SHIFT) * wire(p, WIRE.W_R_SHIFT)); + ap.non_native_field_gate_1 = ap.limb_subproduct; + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 - (wire(p, WIRE.W_O) + wire(p, WIRE.W_4)); + ap.non_native_field_gate_1 = ap.non_native_field_gate_1 * wire(p, WIRE.Q_O); + + ap.non_native_field_gate_3 = ap.limb_subproduct; + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 + wire(p, WIRE.W_4); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 - (wire(p, WIRE.W_O_SHIFT) + wire(p, WIRE.W_4_SHIFT)); + ap.non_native_field_gate_3 = ap.non_native_field_gate_3 * wire(p, WIRE.Q_M); + + Fr non_native_field_identity = + ap.non_native_field_gate_1 + ap.non_native_field_gate_2 + ap.non_native_field_gate_3; + non_native_field_identity = non_native_field_identity * wire(p, WIRE.Q_R); + + // ((((w2' * 2^14 + w1') * 2^14 + w3) * 2^14 + w2) * 2^14 + w1 - w4) * qm + // deg 2 + ap.limb_accumulator_1 = wire(p, WIRE.W_R_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_O); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_R); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * SUBLIMB_SHIFT; + ap.limb_accumulator_1 = ap.limb_accumulator_1 + wire(p, WIRE.W_L); + ap.limb_accumulator_1 = ap.limb_accumulator_1 - wire(p, WIRE.W_4); + ap.limb_accumulator_1 = ap.limb_accumulator_1 * wire(p, WIRE.Q_4); + + // ((((w3' * 2^14 + w2') * 2^14 + w1') * 2^14 + w4) * 2^14 + w3 - w4') * qm + // deg 2 + ap.limb_accumulator_2 = wire(p, WIRE.W_O_SHIFT) * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_R_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_L_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_4); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * SUBLIMB_SHIFT; + ap.limb_accumulator_2 = ap.limb_accumulator_2 + wire(p, WIRE.W_O); + ap.limb_accumulator_2 = ap.limb_accumulator_2 - wire(p, WIRE.W_4_SHIFT); + ap.limb_accumulator_2 = ap.limb_accumulator_2 * wire(p, WIRE.Q_M); + + Fr limb_accumulator_identity = ap.limb_accumulator_1 + ap.limb_accumulator_2; + limb_accumulator_identity = limb_accumulator_identity * wire(p, WIRE.Q_O); // deg 3 + + ap.nnf_identity = non_native_field_identity + limb_accumulator_identity; + ap.nnf_identity = ap.nnf_identity * (wire(p, WIRE.Q_NNF) * domainSep); + evals[19] = ap.nnf_identity; + } + + struct PoseidonExternalParams { + Fr s1; + Fr s2; + Fr s3; + Fr s4; + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr t0; + Fr t1; + Fr t2; + Fr t3; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonExternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonExternalParams memory ep; + + ep.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + ep.s2 = wire(p, WIRE.W_R) + wire(p, WIRE.Q_R); + ep.s3 = wire(p, WIRE.W_O) + wire(p, WIRE.Q_O); + ep.s4 = wire(p, WIRE.W_4) + wire(p, WIRE.Q_4); + + ep.u1 = ep.s1 * ep.s1 * ep.s1 * ep.s1 * ep.s1; + ep.u2 = ep.s2 * ep.s2 * ep.s2 * ep.s2 * ep.s2; + ep.u3 = ep.s3 * ep.s3 * ep.s3 * ep.s3 * ep.s3; + ep.u4 = ep.s4 * ep.s4 * ep.s4 * ep.s4 * ep.s4; + // matrix mul v = M_E * u with 14 additions + ep.t0 = ep.u1 + ep.u2; // u_1 + u_2 + ep.t1 = ep.u3 + ep.u4; // u_3 + u_4 + ep.t2 = ep.u2 + ep.u2 + ep.t1; // 2u_2 + // ep.t2 += ep.t1; // 2u_2 + u_3 + u_4 + ep.t3 = ep.u4 + ep.u4 + ep.t0; // 2u_4 + // ep.t3 += ep.t0; // u_1 + u_2 + 2u_4 + ep.v4 = ep.t1 + ep.t1; + ep.v4 = ep.v4 + ep.v4 + ep.t3; + // ep.v4 += ep.t3; // u_1 + u_2 + 4u_3 + 6u_4 + ep.v2 = ep.t0 + ep.t0; + ep.v2 = ep.v2 + ep.v2 + ep.t2; + // ep.v2 += ep.t2; // 4u_1 + 6u_2 + u_3 + u_4 + ep.v1 = ep.t3 + ep.v2; // 5u_1 + 7u_2 + u_3 + 3u_4 + ep.v3 = ep.t2 + ep.v4; // u_1 + 3u_2 + 5u_3 + 7u_4 + + ep.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_EXTERNAL) * domainSep; + evals[20] = evals[20] + ep.q_pos_by_scaling * (ep.v1 - wire(p, WIRE.W_L_SHIFT)); + + evals[21] = evals[21] + ep.q_pos_by_scaling * (ep.v2 - wire(p, WIRE.W_R_SHIFT)); + + evals[22] = evals[22] + ep.q_pos_by_scaling * (ep.v3 - wire(p, WIRE.W_O_SHIFT)); + + evals[23] = evals[23] + ep.q_pos_by_scaling * (ep.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + struct PoseidonInternalParams { + Fr u1; + Fr u2; + Fr u3; + Fr u4; + Fr u_sum; + Fr v1; + Fr v2; + Fr v3; + Fr v4; + Fr s1; + Fr q_pos_by_scaling; + } + + function accumulatePoseidonInternalRelation( + Fr[NUMBER_OF_ENTITIES] memory p, + Fr[NUMBER_OF_SUBRELATIONS] memory evals, + Fr domainSep + ) internal pure { + PoseidonInternalParams memory ip; + + Fr[4] memory INTERNAL_MATRIX_DIAGONAL = [ + FrLib.from(0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7), + FrLib.from(0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b), + FrLib.from(0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15), + FrLib.from(0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b) + ]; + + // add round constants + ip.s1 = wire(p, WIRE.W_L) + wire(p, WIRE.Q_L); + + // apply s-box round + ip.u1 = ip.s1 * ip.s1 * ip.s1 * ip.s1 * ip.s1; + ip.u2 = wire(p, WIRE.W_R); + ip.u3 = wire(p, WIRE.W_O); + ip.u4 = wire(p, WIRE.W_4); + + // matrix mul with v = M_I * u 4 muls and 7 additions + ip.u_sum = ip.u1 + ip.u2 + ip.u3 + ip.u4; + + ip.q_pos_by_scaling = wire(p, WIRE.Q_POSEIDON2_INTERNAL) * domainSep; + + ip.v1 = ip.u1 * INTERNAL_MATRIX_DIAGONAL[0] + ip.u_sum; + evals[24] = evals[24] + ip.q_pos_by_scaling * (ip.v1 - wire(p, WIRE.W_L_SHIFT)); + + ip.v2 = ip.u2 * INTERNAL_MATRIX_DIAGONAL[1] + ip.u_sum; + evals[25] = evals[25] + ip.q_pos_by_scaling * (ip.v2 - wire(p, WIRE.W_R_SHIFT)); + + ip.v3 = ip.u3 * INTERNAL_MATRIX_DIAGONAL[2] + ip.u_sum; + evals[26] = evals[26] + ip.q_pos_by_scaling * (ip.v3 - wire(p, WIRE.W_O_SHIFT)); + + ip.v4 = ip.u4 * INTERNAL_MATRIX_DIAGONAL[3] + ip.u_sum; + evals[27] = evals[27] + ip.q_pos_by_scaling * (ip.v4 - wire(p, WIRE.W_4_SHIFT)); + } + + function scaleAndBatchSubrelations( + Fr[NUMBER_OF_SUBRELATIONS] memory evaluations, + Fr[NUMBER_OF_ALPHAS] memory subrelationChallenges + ) internal pure returns (Fr accumulator) { + accumulator = evaluations[0]; + + for (uint256 i = 1; i < NUMBER_OF_SUBRELATIONS; ++i) { + accumulator = accumulator + evaluations[i] * subrelationChallenges[i - 1]; + } + } } // Field arithmetic libraries - prevent littering the code with modmul / addmul library CommitmentSchemeLib { - using FrLib for Fr; - - // Avoid stack too deep - struct ShpleminiIntermediates { - Fr unshiftedScalar; - Fr shiftedScalar; - Fr unshiftedScalarNeg; - Fr shiftedScalarNeg; - // Scalar to be multiplied by [1]₁ - Fr constantTermAccumulator; - // Accumulator for powers of rho - Fr batchingChallenge; - // Linear combination of multilinear (sumcheck) evaluations and powers of rho - Fr batchedEvaluation; - Fr[4] denominators; - Fr[4] batchingScalars; - // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated - Fr posInvertedDenominator; - // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated - Fr negInvertedDenominator; - // ν^{2i} * 1/(z - r^{2^i}) - Fr scalingFactorPos; - // ν^{2i+1} * 1/(z + r^{2^i}) - Fr scalingFactorNeg; - // Fold_i(r^{2^i}) reconstructed by Verifier - Fr[] foldPosEvaluations; - } - - function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { - Fr[] memory squares = new Fr[](logN); - squares[0] = r; - for (uint256 i = 1; i < logN; ++i) { - squares[i] = squares[i - 1].sqr(); - } - return squares; - } - // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 - - function computeFoldPosEvaluations( - Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, - Fr batchedEvalAccumulator, - Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, - Fr[] memory geminiEvalChallengePowers, - uint256 logSize - ) internal view returns (Fr[] memory) { - Fr[] memory foldPosEvaluations = new Fr[](logSize); - for (uint256 i = logSize; i > 0; --i) { - Fr challengePower = geminiEvalChallengePowers[i - 1]; - Fr u = sumcheckUChallenges[i - 1]; - - Fr batchedEvalRoundAcc = ((challengePower * batchedEvalAccumulator * Fr.wrap(2)) - - geminiEvaluations[i - 1] * - (challengePower * (ONE - u) - u)); - // Divide by the denominator - batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); - - batchedEvalAccumulator = batchedEvalRoundAcc; - foldPosEvaluations[i - 1] = batchedEvalRoundAcc; - } - return foldPosEvaluations; - } + using FrLib for Fr; + + // Avoid stack too deep + struct ShpleminiIntermediates { + Fr unshiftedScalar; + Fr shiftedScalar; + Fr unshiftedScalarNeg; + Fr shiftedScalarNeg; + // Scalar to be multiplied by [1]₁ + Fr constantTermAccumulator; + // Accumulator for powers of rho + Fr batchingChallenge; + // Linear combination of multilinear (sumcheck) evaluations and powers of rho + Fr batchedEvaluation; + Fr[4] denominators; + Fr[4] batchingScalars; + // 1/(z - r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr posInvertedDenominator; + // 1/(z + r^{2^i}) for i = 0, ..., logSize, dynamically updated + Fr negInvertedDenominator; + // ν^{2i} * 1/(z - r^{2^i}) + Fr scalingFactorPos; + // ν^{2i+1} * 1/(z + r^{2^i}) + Fr scalingFactorNeg; + // Fold_i(r^{2^i}) reconstructed by Verifier + Fr[] foldPosEvaluations; + } + + function computeSquares(Fr r, uint256 logN) internal pure returns (Fr[] memory) { + Fr[] memory squares = new Fr[](logN); + squares[0] = r; + for (uint256 i = 1; i < logN; ++i) { + squares[i] = squares[i - 1].sqr(); + } + return squares; + } + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., m-1 + + function computeFoldPosEvaluations( + Fr[CONST_PROOF_SIZE_LOG_N] memory sumcheckUChallenges, + Fr batchedEvalAccumulator, + Fr[CONST_PROOF_SIZE_LOG_N] memory geminiEvaluations, + Fr[] memory geminiEvalChallengePowers, + uint256 logSize + ) internal view returns (Fr[] memory) { + Fr[] memory foldPosEvaluations = new Fr[](logSize); + for (uint256 i = logSize; i > 0; --i) { + Fr challengePower = geminiEvalChallengePowers[i - 1]; + Fr u = sumcheckUChallenges[i - 1]; + + Fr batchedEvalRoundAcc = ( + (challengePower * batchedEvalAccumulator * Fr.wrap(2)) + - geminiEvaluations[i - 1] * (challengePower * (ONE - u) - u) + ); + // Divide by the denominator + batchedEvalRoundAcc = batchedEvalRoundAcc * (challengePower * (ONE - u) + u).invert(); + + batchedEvalAccumulator = batchedEvalRoundAcc; + foldPosEvaluations[i - 1] = batchedEvalRoundAcc; + } + return foldPosEvaluations; + } } uint256 constant Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; // EC group order. F_q function bytes32ToString(bytes32 value) pure returns (string memory result) { - bytes memory alphabet = "0123456789abcdef"; - - bytes memory str = new bytes(66); - str[0] = "0"; - str[1] = "x"; - for (uint256 i = 0; i < 32; i++) { - str[2 + i * 2] = alphabet[uint8(value[i] >> 4)]; - str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)]; - } - result = string(str); + bytes memory alphabet = "0123456789abcdef"; + + bytes memory str = new bytes(66); + str[0] = "0"; + str[1] = "x"; + for (uint256 i = 0; i < 32; i++) { + str[2 + i * 2] = alphabet[uint8(value[i] >> 4)]; + str[3 + i * 2] = alphabet[uint8(value[i] & 0x0f)]; + } + result = string(str); } // Fr utility function bytesToFr(bytes calldata proofSection) pure returns (Fr scalar) { - scalar = FrLib.fromBytes32(bytes32(proofSection)); + scalar = FrLib.fromBytes32(bytes32(proofSection)); } // EC Point utilities function bytesToG1Point(bytes calldata proofSection) pure returns (Honk.G1Point memory point) { - point = Honk.G1Point({ x: uint256(bytes32(proofSection[0x00:0x20])) % Q, y: uint256(bytes32(proofSection[0x20:0x40])) % Q }); + point = Honk.G1Point({ + x: uint256(bytes32(proofSection[0x00:0x20])) % Q, + y: uint256(bytes32(proofSection[0x20:0x40])) % Q + }); } function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point memory) { - point.y = (Q - point.y) % Q; - return point; + point.y = (Q - point.y) % Q; + return point; } /** @@ -1641,32 +1648,33 @@ function negateInplace(Honk.G1Point memory point) pure returns (Honk.G1Point mem * @return lhs * @return rhs */ -function convertPairingPointsToG1( - Fr[PAIRING_POINTS_SIZE] memory pairingPoints -) pure returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) { - uint256 lhsX = Fr.unwrap(pairingPoints[0]); - lhsX |= Fr.unwrap(pairingPoints[1]) << 68; - lhsX |= Fr.unwrap(pairingPoints[2]) << 136; - lhsX |= Fr.unwrap(pairingPoints[3]) << 204; - lhs.x = lhsX; - - uint256 lhsY = Fr.unwrap(pairingPoints[4]); - lhsY |= Fr.unwrap(pairingPoints[5]) << 68; - lhsY |= Fr.unwrap(pairingPoints[6]) << 136; - lhsY |= Fr.unwrap(pairingPoints[7]) << 204; - lhs.y = lhsY; - - uint256 rhsX = Fr.unwrap(pairingPoints[8]); - rhsX |= Fr.unwrap(pairingPoints[9]) << 68; - rhsX |= Fr.unwrap(pairingPoints[10]) << 136; - rhsX |= Fr.unwrap(pairingPoints[11]) << 204; - rhs.x = rhsX; - - uint256 rhsY = Fr.unwrap(pairingPoints[12]); - rhsY |= Fr.unwrap(pairingPoints[13]) << 68; - rhsY |= Fr.unwrap(pairingPoints[14]) << 136; - rhsY |= Fr.unwrap(pairingPoints[15]) << 204; - rhs.y = rhsY; +function convertPairingPointsToG1(Fr[PAIRING_POINTS_SIZE] memory pairingPoints) + pure + returns (Honk.G1Point memory lhs, Honk.G1Point memory rhs) +{ + uint256 lhsX = Fr.unwrap(pairingPoints[0]); + lhsX |= Fr.unwrap(pairingPoints[1]) << 68; + lhsX |= Fr.unwrap(pairingPoints[2]) << 136; + lhsX |= Fr.unwrap(pairingPoints[3]) << 204; + lhs.x = lhsX; + + uint256 lhsY = Fr.unwrap(pairingPoints[4]); + lhsY |= Fr.unwrap(pairingPoints[5]) << 68; + lhsY |= Fr.unwrap(pairingPoints[6]) << 136; + lhsY |= Fr.unwrap(pairingPoints[7]) << 204; + lhs.y = lhsY; + + uint256 rhsX = Fr.unwrap(pairingPoints[8]); + rhsX |= Fr.unwrap(pairingPoints[9]) << 68; + rhsX |= Fr.unwrap(pairingPoints[10]) << 136; + rhsX |= Fr.unwrap(pairingPoints[11]) << 204; + rhs.x = rhsX; + + uint256 rhsY = Fr.unwrap(pairingPoints[12]); + rhsY |= Fr.unwrap(pairingPoints[13]) << 68; + rhsY |= Fr.unwrap(pairingPoints[14]) << 136; + rhsY |= Fr.unwrap(pairingPoints[15]) << 204; + rhs.y = rhsY; } /** @@ -1678,32 +1686,32 @@ function convertPairingPointsToG1( * @return recursionSeparator The recursion separator - generated from hashing the above. */ function generateRecursionSeparator( - Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints, - Honk.G1Point memory accLhs, - Honk.G1Point memory accRhs + Fr[PAIRING_POINTS_SIZE] memory proofPairingPoints, + Honk.G1Point memory accLhs, + Honk.G1Point memory accRhs ) pure returns (Fr recursionSeparator) { - // hash the proof aggregated X - // hash the proof aggregated Y - // hash the accum X - // hash the accum Y + // hash the proof aggregated X + // hash the proof aggregated Y + // hash the accum X + // hash the accum Y - (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); + (Honk.G1Point memory proofLhs, Honk.G1Point memory proofRhs) = convertPairingPointsToG1(proofPairingPoints); - uint256[8] memory recursionSeparatorElements; + uint256[8] memory recursionSeparatorElements; - // Proof points - recursionSeparatorElements[0] = proofLhs.x; - recursionSeparatorElements[1] = proofLhs.y; - recursionSeparatorElements[2] = proofRhs.x; - recursionSeparatorElements[3] = proofRhs.y; + // Proof points + recursionSeparatorElements[0] = proofLhs.x; + recursionSeparatorElements[1] = proofLhs.y; + recursionSeparatorElements[2] = proofRhs.x; + recursionSeparatorElements[3] = proofRhs.y; - // Accumulator points - recursionSeparatorElements[4] = accLhs.x; - recursionSeparatorElements[5] = accLhs.y; - recursionSeparatorElements[6] = accRhs.x; - recursionSeparatorElements[7] = accRhs.y; + // Accumulator points + recursionSeparatorElements[4] = accLhs.x; + recursionSeparatorElements[5] = accLhs.y; + recursionSeparatorElements[6] = accRhs.x; + recursionSeparatorElements[7] = accRhs.y; - recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); + recursionSeparator = FrLib.fromBytes32(keccak256(abi.encodePacked(recursionSeparatorElements))); } /** @@ -1715,17 +1723,16 @@ function generateRecursionSeparator( * @param recursionSeperator The separator to use for the multiplication. * @return `(recursionSeperator * basePoint) + other`. */ -function mulWithSeperator( - Honk.G1Point memory basePoint, - Honk.G1Point memory other, - Fr recursionSeperator -) view returns (Honk.G1Point memory) { - Honk.G1Point memory result; +function mulWithSeperator(Honk.G1Point memory basePoint, Honk.G1Point memory other, Fr recursionSeperator) + view + returns (Honk.G1Point memory) +{ + Honk.G1Point memory result; - result = ecMul(recursionSeperator, basePoint); - result = ecAdd(result, other); + result = ecMul(recursionSeperator, basePoint); + result = ecAdd(result, other); - return result; + return result; } /** @@ -1737,41 +1744,41 @@ function mulWithSeperator( * @return result The result of the multiplication. */ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point memory) { - Honk.G1Point memory result; - - assembly { - let free := mload(0x40) - // Write the point into memory (two 32 byte words) - // Memory layout: - // Address | value - // free | point.x - // free + 0x20| point.y - mstore(free, mload(point)) - mstore(add(free, 0x20), mload(add(point, 0x20))) - // Write the scalar into memory (one 32 byte word) - // Memory layout: - // Address | value - // free + 0x40| value - mstore(add(free, 0x40), value) - - // Call the ecMul precompile, it takes in the following - // [point.x, point.y, scalar], and returns the result back into the free memory location. - let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40) - if iszero(success) { - revert(0, 0) - } - // Copy the result of the multiplication back into the result memory location. - // Memory layout: - // Address | value - // result | result.x - // result + 0x20| result.y - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) - - mstore(0x40, add(free, 0x60)) - } - - return result; + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write the point into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | point.x + // free + 0x20| point.y + mstore(free, mload(point)) + mstore(add(free, 0x20), mload(add(point, 0x20))) + // Write the scalar into memory (one 32 byte word) + // Memory layout: + // Address | value + // free + 0x40| value + mstore(add(free, 0x40), value) + + // Call the ecMul precompile, it takes in the following + // [point.x, point.y, scalar], and returns the result back into the free memory location. + let success := staticcall(gas(), 0x07, free, 0x60, free, 0x40) + if iszero(success) { + revert(0, 0) + } + // Copy the result of the multiplication back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x60)) + } + + return result; } /** @@ -1783,637 +1790,649 @@ function ecMul(Fr value, Honk.G1Point memory point) view returns (Honk.G1Point m * @return result The result of the addition. */ function ecAdd(Honk.G1Point memory lhs, Honk.G1Point memory rhs) view returns (Honk.G1Point memory) { - Honk.G1Point memory result; - - assembly { - let free := mload(0x40) - // Write lhs into memory (two 32 byte words) - // Memory layout: - // Address | value - // free | lhs.x - // free + 0x20| lhs.y - mstore(free, mload(lhs)) - mstore(add(free, 0x20), mload(add(lhs, 0x20))) - - // Write rhs into memory (two 32 byte words) - // Memory layout: - // Address | value - // free + 0x40| rhs.x - // free + 0x60| rhs.y - mstore(add(free, 0x40), mload(rhs)) - mstore(add(free, 0x60), mload(add(rhs, 0x20))) - - // Call the ecAdd precompile, it takes in the following - // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. - let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) - if iszero(success) { - revert(0, 0) - } - - // Copy the result of the addition back into the result memory location. - // Memory layout: - // Address | value - // result | result.x - // result + 0x20| result.y - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) - - mstore(0x40, add(free, 0x80)) - } - - return result; + Honk.G1Point memory result; + + assembly { + let free := mload(0x40) + // Write lhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free | lhs.x + // free + 0x20| lhs.y + mstore(free, mload(lhs)) + mstore(add(free, 0x20), mload(add(lhs, 0x20))) + + // Write rhs into memory (two 32 byte words) + // Memory layout: + // Address | value + // free + 0x40| rhs.x + // free + 0x60| rhs.y + mstore(add(free, 0x40), mload(rhs)) + mstore(add(free, 0x60), mload(add(rhs, 0x20))) + + // Call the ecAdd precompile, it takes in the following + // [lhs.x, lhs.y, rhs.x, rhs.y], and returns their addition back into the free memory location. + let success := staticcall(gas(), 0x06, free, 0x80, free, 0x40) + if iszero(success) { revert(0, 0) } + + // Copy the result of the addition back into the result memory location. + // Memory layout: + // Address | value + // result | result.x + // result + 0x20| result.y + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + + mstore(0x40, add(free, 0x80)) + } + + return result; } function validateOnCurve(Honk.G1Point memory point) pure { - uint256 x = point.x; - uint256 y = point.y; + uint256 x = point.x; + uint256 y = point.y; - bool success = false; - assembly { - let xx := mulmod(x, x, Q) - success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q)) - } + bool success = false; + assembly { + let xx := mulmod(x, x, Q) + success := eq(mulmod(y, y, Q), addmod(mulmod(x, xx, Q), 3, Q)) + } - require(success, "point is not on the curve"); + require(success, "point is not on the curve"); } function pairing(Honk.G1Point memory rhs, Honk.G1Point memory lhs) view returns (bool decodedResult) { - bytes memory input = abi.encodePacked( - rhs.x, - rhs.y, - // Fixed G2 point - uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), - uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), - uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), - uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), - lhs.x, - lhs.y, - // G2 point from VK - uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), - uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), - uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), - uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) - ); - - (bool success, bytes memory result) = address(0x08).staticcall(input); - decodedResult = success && abi.decode(result, (bool)); + bytes memory input = abi.encodePacked( + rhs.x, + rhs.y, + // Fixed G2 point + uint256(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2), + uint256(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed), + uint256(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b), + uint256(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa), + lhs.x, + lhs.y, + // G2 point from VK + uint256(0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1), + uint256(0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0), + uint256(0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4), + uint256(0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55) + ); + + (bool success, bytes memory result) = address(0x08).staticcall(input); + decodedResult = success && abi.decode(result, (bool)); } // Field arithmetic libraries - prevent littering the code with modmul / addmul -abstract contract BaseZKHonkVerifier is IVerifier { - using FrLib for Fr; - - uint256 immutable $N; - uint256 immutable $LOG_N; - uint256 immutable $VK_HASH; - uint256 immutable $NUM_PUBLIC_INPUTS; - - constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { - $N = _N; - $LOG_N = _logN; - $VK_HASH = _vkHash; - $NUM_PUBLIC_INPUTS = _numPublicInputs; - } - - // Errors - error ProofLengthWrong(); - error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); - error PublicInputsLengthWrong(); - error SumcheckFailed(); - error ShpleminiFailed(); - error GeminiChallengeInSubgroup(); - error ConsistencyCheckFailed(); - - // Constants for proof length calculation (matching UltraKeccakZKFlavor) - uint256 constant NUM_WITNESS_ENTITIES = 8; - uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points - uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements - uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations - - // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula) - function calculateProofSize(uint256 logN) internal pure returns (uint256) { - // Witness and Libra commitments - uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments - proofLength += NUM_ELEMENTS_COMM * 4; // Libra concat, grand sum, quotient comms + Gemini masking - - // Sumcheck - proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates - proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations - - // Libra and Gemini - proofLength += NUM_ELEMENTS_FR * 3; // Libra sum, claimed eval, Gemini masking eval - proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations - proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations - // PCS commitments - proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments - proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments - // Pairing points - proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs - return proofLength; - } - - uint256 constant SHIFTED_COMMITMENTS_START = 30; - - function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); +abstract contract BaseZKHonkVerifier is IVerifier { + using FrLib for Fr; - function verify(bytes calldata proof, bytes32[] calldata publicInputs) public view override returns (bool verified) { - // Calculate expected proof size based on $LOG_N - uint256 expectedProofSize = calculateProofSize($LOG_N); + uint256 immutable $N; + uint256 immutable $LOG_N; + uint256 immutable $VK_HASH; + uint256 immutable $NUM_PUBLIC_INPUTS; - // Check the received proof is the expected size where each field element is 32 bytes - if (proof.length != expectedProofSize * 32) { - revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + constructor(uint256 _N, uint256 _logN, uint256 _vkHash, uint256 _numPublicInputs) { + $N = _N; + $LOG_N = _logN; + $VK_HASH = _vkHash; + $NUM_PUBLIC_INPUTS = _numPublicInputs; } - Honk.VerificationKey memory vk = loadVerificationKey(); - Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N); + // Errors + error ProofLengthWrong(); + error ProofLengthWrongWithLogN(uint256 logN, uint256 actualLength, uint256 expectedLength); + error PublicInputsLengthWrong(); + error SumcheckFailed(); + error ShpleminiFailed(); + error GeminiChallengeInSubgroup(); + error ConsistencyCheckFailed(); - if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) { - revert PublicInputsLengthWrong(); - } + // Constants for proof length calculation (matching UltraKeccakZKFlavor) + uint256 constant NUM_WITNESS_ENTITIES = 8; + uint256 constant NUM_ELEMENTS_COMM = 2; // uint256 elements for curve points + uint256 constant NUM_ELEMENTS_FR = 1; // uint256 elements for field elements + uint256 constant NUM_LIBRA_EVALUATIONS = 4; // libra evaluations - // Generate the fiat shamir challenges for the whole protocol - ZKTranscript memory t = ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); + // Calculate proof size based on log_n (matching UltraKeccakZKFlavor formula) + function calculateProofSize(uint256 logN) internal pure returns (uint256) { + // Witness and Libra commitments + uint256 proofLength = NUM_WITNESS_ENTITIES * NUM_ELEMENTS_COMM; // witness commitments + proofLength += NUM_ELEMENTS_COMM * 4; // Libra concat, grand sum, quotient comms + Gemini masking - // Derive public input delta - t.relationParameters.publicInputsDelta = computePublicInputDelta( - publicInputs, - p.pairingPointObject, - t.relationParameters.beta, - t.relationParameters.gamma /*pubInputsOffset=*/, - 1 - ); + // Sumcheck + proofLength += logN * ZK_BATCHED_RELATION_PARTIAL_LENGTH * NUM_ELEMENTS_FR; // sumcheck univariates + proofLength += NUMBER_OF_ENTITIES * NUM_ELEMENTS_FR; // sumcheck evaluations - // Sumcheck - if (!verifySumcheck(p, t)) revert SumcheckFailed(); + // Libra and Gemini + proofLength += NUM_ELEMENTS_FR * 3; // Libra sum, claimed eval, Gemini masking eval + proofLength += logN * NUM_ELEMENTS_FR; // Gemini a evaluations + proofLength += NUM_LIBRA_EVALUATIONS * NUM_ELEMENTS_FR; // libra evaluations - if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); + // PCS commitments + proofLength += (logN - 1) * NUM_ELEMENTS_COMM; // Gemini Fold commitments + proofLength += NUM_ELEMENTS_COMM * 2; // Shplonk Q and KZG W commitments - verified = true; - } + // Pairing points + proofLength += PAIRING_POINTS_SIZE; // pairing inputs carried on public inputs - uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28; + return proofLength; + } - function computePublicInputDelta( - bytes32[] memory publicInputs, - Fr[PAIRING_POINTS_SIZE] memory pairingPointObject, - Fr beta, - Fr gamma, - uint256 offset - ) internal view returns (Fr publicInputDelta) { - Fr numerator = Fr.wrap(1); - Fr denominator = Fr.wrap(1); + uint256 constant SHIFTED_COMMITMENTS_START = 30; - Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); - Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); + function loadVerificationKey() internal pure virtual returns (Honk.VerificationKey memory); + function verify(bytes calldata proof, bytes32[] calldata publicInputs) + public + view + override + returns (bool verified) { - for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { - Fr pubInput = FrLib.fromBytes32(publicInputs[i]); - - numerator = numerator * (numeratorAcc + pubInput); - denominator = denominator * (denominatorAcc + pubInput); - - numeratorAcc = numeratorAcc + beta; - denominatorAcc = denominatorAcc - beta; - } + // Calculate expected proof size based on $LOG_N + uint256 expectedProofSize = calculateProofSize($LOG_N); - for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { - Fr pubInput = pairingPointObject[i]; + // Check the received proof is the expected size where each field element is 32 bytes + if (proof.length != expectedProofSize * 32) { + revert ProofLengthWrongWithLogN($LOG_N, proof.length, expectedProofSize * 32); + } - numerator = numerator * (numeratorAcc + pubInput); - denominator = denominator * (denominatorAcc + pubInput); + Honk.VerificationKey memory vk = loadVerificationKey(); + Honk.ZKProof memory p = ZKTranscriptLib.loadProof(proof, $LOG_N); - numeratorAcc = numeratorAcc + beta; - denominatorAcc = denominatorAcc - beta; - } - } + if (publicInputs.length != vk.publicInputsSize - PAIRING_POINTS_SIZE) { + revert PublicInputsLengthWrong(); + } - // Fr delta = numerator / denominator; // TOOO: batch invert later? - publicInputDelta = FrLib.div(numerator, denominator); - } + // Generate the fiat shamir challenges for the whole protocol + ZKTranscript memory t = + ZKTranscriptLib.generateTranscript(p, publicInputs, $VK_HASH, $NUM_PUBLIC_INPUTS, $LOG_N); - function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { - Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 - Fr powPartialEvaluation = Fr.wrap(1); + // Derive public input delta + t.relationParameters.publicInputsDelta = computePublicInputDelta( + publicInputs, + p.pairingPointObject, + t.relationParameters.beta, + t.relationParameters.gamma, /*pubInputsOffset=*/ + 1 + ); - // We perform sumcheck reductions over log n rounds ( the multivariate degree ) - for (uint256 round; round < $LOG_N; ++round) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; - Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; - if (totalSum != roundTargetSum) revert SumcheckFailed(); + // Sumcheck + if (!verifySumcheck(p, t)) revert SumcheckFailed(); - Fr roundChallenge = tp.sumCheckUChallenges[round]; + if (!verifyShplemini(p, vk, t)) revert ShpleminiFailed(); - // Update the round target for the next rounf - roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); - powPartialEvaluation = powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + verified = true; } - // Last round - Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( - proof.sumcheckEvaluations, - tp.relationParameters, - tp.alphas, - powPartialEvaluation - ); - - Fr evaluation = Fr.wrap(1); - for (uint256 i = 2; i < $LOG_N; i++) { - evaluation = evaluation * tp.sumCheckUChallenges[i]; - } - - grandHonkRelationSum = grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; - verified = (grandHonkRelationSum == roundTargetSum); - } - - // Return the new target sum for the next sumcheck round - function computeNextTargetSum( - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, - Fr roundChallenge - ) internal view returns (Fr targetSum) { - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), - Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), - Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), - Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) - ]; - - // To compute the next target sum, we evaluate the given univariate at a point u (challenge). - - // Performing Barycentric evaluations - // Compute B(x) - Fr numeratorValue = Fr.wrap(1); - for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); - } - - Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; - for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); - } - - for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { - targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; - } - - // Scale the sum by the value of B(x) - targetSum = targetSum * numeratorValue; - } - - uint256 constant LIBRA_COMMITMENTS = 3; - uint256 constant LIBRA_EVALUATIONS = 4; - uint256 constant LIBRA_UNIVARIATES_LENGTH = 9; - - struct PairingInputs { - Honk.G1Point P_0; - Honk.G1Point P_1; - } - - function verifyShplemini( - Honk.ZKProof memory proof, - Honk.VerificationKey memory vk, - ZKTranscript memory tp - ) internal view returns (bool verified) { - CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack - - // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size - Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); - // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings - Fr[] memory scalars = new Fr[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3); - Honk.G1Point[] memory commitments = new Honk.G1Point[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3); - - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); - - mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); - mem.shiftedScalar = tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); - - scalars[0] = Fr.wrap(1); - commitments[0] = proof.shplonkQ; - - /* Batch multivariate opening claims, shifted and unshifted - * The vector of scalars is populated as follows: - * \f[ - * \left( - * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), - * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), - * \ldots, - * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) - * \right) - * \f] - * - * The following vector is concatenated to the vector of commitments: - * \f[ - * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} - * \f] - * - * Simultaneously, the evaluation of the multilinear polynomial - * \f[ - * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} - * \f] - * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. - * - * This approach minimizes the number of iterations over the commitments to multilinear polynomials - * and eliminates the need to store the powers of \f$ \rho \f$. - */ - mem.batchedEvaluation = proof.geminiMaskingEval; - mem.batchingChallenge = tp.rho; - mem.unshiftedScalarNeg = mem.unshiftedScalar.neg(); - mem.shiftedScalarNeg = mem.shiftedScalar.neg(); - - scalars[1] = mem.unshiftedScalarNeg; - for (uint256 i = 0; i < NUMBER_UNSHIFTED; ++i) { - scalars[i + 2] = mem.unshiftedScalarNeg * mem.batchingChallenge; - mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge); - mem.batchingChallenge = mem.batchingChallenge * tp.rho; - } - // g commitments are accumulated at r - // For each of the to be shifted commitments perform the shift in place by - // adding to the unshifted value. - // We do so, as the values are to be used in batchMul later, and as - // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute. - // Applied to w1, w2, w3, w4 and zPerm - for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) { - uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; - uint256 evaluationOff = i + NUMBER_UNSHIFTED; - - scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); - mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); - mem.batchingChallenge = mem.batchingChallenge * tp.rho; - } - - commitments[1] = proof.geminiMaskingPoly; - - commitments[2] = vk.qm; - commitments[3] = vk.qc; - commitments[4] = vk.ql; - commitments[5] = vk.qr; - commitments[6] = vk.qo; - commitments[7] = vk.q4; - commitments[8] = vk.qLookup; - commitments[9] = vk.qArith; - commitments[10] = vk.qDeltaRange; - commitments[11] = vk.qElliptic; - commitments[12] = vk.qMemory; - commitments[13] = vk.qNnf; - commitments[14] = vk.qPoseidon2External; - commitments[15] = vk.qPoseidon2Internal; - commitments[16] = vk.s1; - commitments[17] = vk.s2; - commitments[18] = vk.s3; - commitments[19] = vk.s4; - commitments[20] = vk.id1; - commitments[21] = vk.id2; - commitments[22] = vk.id3; - commitments[23] = vk.id4; - commitments[24] = vk.t1; - commitments[25] = vk.t2; - commitments[26] = vk.t3; - commitments[27] = vk.t4; - commitments[28] = vk.lagrangeFirst; - commitments[29] = vk.lagrangeLast; - - // Accumulate proof points - commitments[30] = proof.w1; - commitments[31] = proof.w2; - commitments[32] = proof.w3; - commitments[33] = proof.w4; - commitments[34] = proof.zPerm; - commitments[35] = proof.lookupInverses; - commitments[36] = proof.lookupReadCounts; - commitments[37] = proof.lookupReadTags; - - /* Batch gemini claims from the prover - * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from - * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars - * - * 1. Moves the vector - * \f[ - * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) - * \f] - * to the 'commitments' vector. - * - * 2. Computes the scalars: - * \f[ - * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} - * \f] - * and places them into the 'scalars' vector. - * - * 3. Accumulates the summands of the constant term: - * \f[ - * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} - * \f] - * and adds them to the 'constant_term_accumulator'. - */ - - // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: - // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 - Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( - tp.sumCheckUChallenges, - mem.batchedEvaluation, - proof.geminiAEvaluations, - powers_of_evaluation_challenge, - $LOG_N - ); + uint256 constant PERMUTATION_ARGUMENT_VALUE_SEPARATOR = 1 << 28; - mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; - mem.constantTermAccumulator = mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + function computePublicInputDelta( + bytes32[] memory publicInputs, + Fr[PAIRING_POINTS_SIZE] memory pairingPointObject, + Fr beta, + Fr gamma, + uint256 offset + ) internal view returns (Fr publicInputDelta) { + Fr numerator = Fr.wrap(1); + Fr denominator = Fr.wrap(1); - mem.batchingChallenge = tp.shplonkNu.sqr(); - uint256 boundary = NUMBER_UNSHIFTED + 2; + Fr numeratorAcc = gamma + (beta * FrLib.from(PERMUTATION_ARGUMENT_VALUE_SEPARATOR + offset)); + Fr denominatorAcc = gamma - (beta * FrLib.from(offset + 1)); - // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; - // Compute scalar multipliers for each fold commitment - for (uint256 i = 0; i < $LOG_N - 1; ++i) { - bool dummy_round = i >= ($LOG_N - 1); + { + for (uint256 i = 0; i < $NUM_PUBLIC_INPUTS - PAIRING_POINTS_SIZE; i++) { + Fr pubInput = FrLib.fromBytes32(publicInputs[i]); - if (!dummy_round) { - // Update inverted denominators - mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); - mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); - - // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] - mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; - mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; - scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); - // Accumulate the const term contribution given by - // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) - Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; - accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; - mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; - } - // Update the running power of v - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } - commitments[boundary + i] = proof.geminiFoldComms[i]; - } + for (uint256 i = 0; i < PAIRING_POINTS_SIZE; i++) { + Fr pubInput = pairingPointObject[i]; - boundary += $LOG_N - 1; + numerator = numerator * (numeratorAcc + pubInput); + denominator = denominator * (denominatorAcc + pubInput); - // Finalize the batch opening claim - mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); - mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); - mem.denominators[2] = mem.denominators[0]; - mem.denominators[3] = mem.denominators[0]; + numeratorAcc = numeratorAcc + beta; + denominatorAcc = denominatorAcc - beta; + } + } - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; - for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { - Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; - mem.batchingScalars[i] = scalingFactor.neg(); - mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; - mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + // Fr delta = numerator / denominator; // TOOO: batch invert later? + publicInputDelta = FrLib.div(numerator, denominator); } - scalars[boundary] = mem.batchingScalars[0]; - scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; - scalars[boundary + 2] = mem.batchingScalars[3]; - for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) { - commitments[boundary++] = proof.libraCommitments[i]; - } - - commitments[boundary] = Honk.G1Point({ x: 1, y: 2 }); - scalars[boundary++] = mem.constantTermAccumulator; + function verifySumcheck(Honk.ZKProof memory proof, ZKTranscript memory tp) internal view returns (bool verified) { + Fr roundTargetSum = tp.libraChallenge * proof.libraSum; // default 0 + Fr powPartialEvaluation = Fr.wrap(1); - if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { - revert ConsistencyCheckFailed(); - } + // We perform sumcheck reductions over log n rounds ( the multivariate degree ) + for (uint256 round; round < $LOG_N; ++round) { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariate = proof.sumcheckUnivariates[round]; + Fr totalSum = roundUnivariate[0] + roundUnivariate[1]; + if (totalSum != roundTargetSum) revert SumcheckFailed(); - Honk.G1Point memory quotient_commitment = proof.kzgQuotient; + Fr roundChallenge = tp.sumCheckUChallenges[round]; - commitments[boundary] = quotient_commitment; - scalars[boundary] = tp.shplonkZ; // evaluation challenge + // Update the round target for the next rounf + roundTargetSum = computeNextTargetSum(roundUnivariate, roundChallenge); + powPartialEvaluation = + powPartialEvaluation * (Fr.wrap(1) + roundChallenge * (tp.gateChallenges[round] - Fr.wrap(1))); + } - PairingInputs memory pair; - pair.P_0 = batchMul(commitments, scalars); - pair.P_1 = negateInplace(quotient_commitment); + // Last round + Fr grandHonkRelationSum = RelationsLib.accumulateRelationEvaluations( + proof.sumcheckEvaluations, tp.relationParameters, tp.alphas, powPartialEvaluation + ); - // Aggregate pairing points - Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); - (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = convertPairingPointsToG1(proof.pairingPointObject); - - // Validate the points from the proof are on the curve - validateOnCurve(P_0_other); - validateOnCurve(P_1_other); - - // accumulate with aggregate points in proof - pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator); - pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator); - - return pairing(pair.P_0, pair.P_1); - } - - struct SmallSubgroupIpaIntermediates { - Fr[SUBGROUP_SIZE] challengePolyLagrange; - Fr challengePolyEval; - Fr lagrangeFirst; - Fr lagrangeLast; - Fr rootPower; - Fr[SUBGROUP_SIZE] denominators; // this has to disappear - Fr diff; - } - - function checkEvalsConsistency( - Fr[LIBRA_EVALUATIONS] memory libraPolyEvals, - Fr geminiR, - Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, - Fr libraEval - ) internal view returns (bool check) { - Fr one = Fr.wrap(1); - Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one; - if (vanishingPolyEval == Fr.wrap(0)) { - revert GeminiChallengeInSubgroup(); - } + Fr evaluation = Fr.wrap(1); + for (uint256 i = 2; i < $LOG_N; i++) { + evaluation = evaluation * tp.sumCheckUChallenges[i]; + } - SmallSubgroupIpaIntermediates memory mem; - mem.challengePolyLagrange[0] = one; - for (uint256 round = 0; round < $LOG_N; round++) { - uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; - mem.challengePolyLagrange[currIdx] = one; - for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { - mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; - } + grandHonkRelationSum = + grandHonkRelationSum * (Fr.wrap(1) - evaluation) + proof.libraEvaluation * tp.libraChallenge; + verified = (grandHonkRelationSum == roundTargetSum); } - mem.rootPower = one; - mem.challengePolyEval = Fr.wrap(0); - for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { - mem.denominators[idx] = mem.rootPower * geminiR - one; - mem.denominators[idx] = mem.denominators[idx].invert(); - mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; - mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; - } - - Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); - mem.challengePolyEval = mem.challengePolyEval * numerator; - mem.lagrangeFirst = mem.denominators[0] * numerator; - mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; - - mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; - - mem.diff = - mem.diff + - (geminiR - SUBGROUP_GENERATOR_INVERSE) * - (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); - mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; - - check = mem.diff == Fr.wrap(0); - } + // Return the new target sum for the next sumcheck round + function computeNextTargetSum(Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory roundUnivariates, Fr roundChallenge) + internal + view + returns (Fr targetSum) + { + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory BARYCENTRIC_LAGRANGE_DENOMINATORS = [ + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000000240), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593effffd31), + Fr.wrap(0x00000000000000000000000000000000000000000000000000000000000005a0), + Fr.wrap(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffec51), + Fr.wrap(0x0000000000000000000000000000000000000000000000000000000000009d80) + ]; + + // To compute the next target sum, we evaluate the given univariate at a point u (challenge). + + // Performing Barycentric evaluations + // Compute B(x) + Fr numeratorValue = Fr.wrap(1); + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + numeratorValue = numeratorValue * (roundChallenge - Fr.wrap(i)); + } + + Fr[ZK_BATCHED_RELATION_PARTIAL_LENGTH] memory denominatorInverses; + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + denominatorInverses[i] = FrLib.invert(BARYCENTRIC_LAGRANGE_DENOMINATORS[i] * (roundChallenge - Fr.wrap(i))); + } + + for (uint256 i = 0; i < ZK_BATCHED_RELATION_PARTIAL_LENGTH; ++i) { + targetSum = targetSum + roundUnivariates[i] * denominatorInverses[i]; + } + + // Scale the sum by the value of B(x) + targetSum = targetSum * numeratorValue; + } + + uint256 constant LIBRA_COMMITMENTS = 3; + uint256 constant LIBRA_EVALUATIONS = 4; + uint256 constant LIBRA_UNIVARIATES_LENGTH = 9; + + struct PairingInputs { + Honk.G1Point P_0; + Honk.G1Point P_1; + } + + function verifyShplemini(Honk.ZKProof memory proof, Honk.VerificationKey memory vk, ZKTranscript memory tp) + internal + view + returns (bool verified) + { + CommitmentSchemeLib.ShpleminiIntermediates memory mem; // stack + + // - Compute vector (r, r², ... , r²⁽ⁿ⁻¹⁾), where n = log_circuit_size + Fr[] memory powers_of_evaluation_challenge = CommitmentSchemeLib.computeSquares(tp.geminiR, $LOG_N); + // Arrays hold values that will be linearly combined for the gemini and shplonk batch openings + Fr[] memory scalars = new Fr[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3); + Honk.G1Point[] memory commitments = new Honk.G1Point[](NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3); + + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[0]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[0]).invert(); + + mem.unshiftedScalar = mem.posInvertedDenominator + (tp.shplonkNu * mem.negInvertedDenominator); + mem.shiftedScalar = + tp.geminiR.invert() * (mem.posInvertedDenominator - (tp.shplonkNu * mem.negInvertedDenominator)); + + scalars[0] = Fr.wrap(1); + commitments[0] = proof.shplonkQ; + + /* Batch multivariate opening claims, shifted and unshifted + * The vector of scalars is populated as follows: + * \f[ + * \left( + * - \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{i+k-1} \times \left(\frac{1}{z-r} + \nu \times \frac{1}{z+r}\right), + * - \rho^{i+k} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right), + * \ldots, + * - \rho^{k+m-1} \times \frac{1}{r} \times \left(\frac{1}{z-r} - \nu \times \frac{1}{z+r}\right) + * \right) + * \f] + * + * The following vector is concatenated to the vector of commitments: + * \f[ + * f_0, \ldots, f_{m-1}, f_{\text{shift}, 0}, \ldots, f_{\text{shift}, k-1} + * \f] + * + * Simultaneously, the evaluation of the multilinear polynomial + * \f[ + * \sum \rho^i \cdot f_i + \sum \rho^{i+k} \cdot f_{\text{shift}, i} + * \f] + * at the challenge point \f$ (u_0,\ldots, u_{n-1}) \f$ is computed. + * + * This approach minimizes the number of iterations over the commitments to multilinear polynomials + * and eliminates the need to store the powers of \f$ \rho \f$. + */ + mem.batchedEvaluation = proof.geminiMaskingEval; + mem.batchingChallenge = tp.rho; + mem.unshiftedScalarNeg = mem.unshiftedScalar.neg(); + mem.shiftedScalarNeg = mem.shiftedScalar.neg(); + + scalars[1] = mem.unshiftedScalarNeg; + for (uint256 i = 0; i < NUMBER_UNSHIFTED; ++i) { + scalars[i + 2] = mem.unshiftedScalarNeg * mem.batchingChallenge; + mem.batchedEvaluation = mem.batchedEvaluation + (proof.sumcheckEvaluations[i] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + // g commitments are accumulated at r + // For each of the to be shifted commitments perform the shift in place by + // adding to the unshifted value. + // We do so, as the values are to be used in batchMul later, and as + // `a * c + b * c = (a + b) * c` this will allow us to reduce memory and compute. + // Applied to w1, w2, w3, w4 and zPerm + for (uint256 i = 0; i < NUMBER_TO_BE_SHIFTED; ++i) { + uint256 scalarOff = i + SHIFTED_COMMITMENTS_START; + uint256 evaluationOff = i + NUMBER_UNSHIFTED; + + scalars[scalarOff] = scalars[scalarOff] + (mem.shiftedScalarNeg * mem.batchingChallenge); + mem.batchedEvaluation = + mem.batchedEvaluation + (proof.sumcheckEvaluations[evaluationOff] * mem.batchingChallenge); + mem.batchingChallenge = mem.batchingChallenge * tp.rho; + } + + commitments[1] = proof.geminiMaskingPoly; + + commitments[2] = vk.qm; + commitments[3] = vk.qc; + commitments[4] = vk.ql; + commitments[5] = vk.qr; + commitments[6] = vk.qo; + commitments[7] = vk.q4; + commitments[8] = vk.qLookup; + commitments[9] = vk.qArith; + commitments[10] = vk.qDeltaRange; + commitments[11] = vk.qElliptic; + commitments[12] = vk.qMemory; + commitments[13] = vk.qNnf; + commitments[14] = vk.qPoseidon2External; + commitments[15] = vk.qPoseidon2Internal; + commitments[16] = vk.s1; + commitments[17] = vk.s2; + commitments[18] = vk.s3; + commitments[19] = vk.s4; + commitments[20] = vk.id1; + commitments[21] = vk.id2; + commitments[22] = vk.id3; + commitments[23] = vk.id4; + commitments[24] = vk.t1; + commitments[25] = vk.t2; + commitments[26] = vk.t3; + commitments[27] = vk.t4; + commitments[28] = vk.lagrangeFirst; + commitments[29] = vk.lagrangeLast; + + // Accumulate proof points + commitments[30] = proof.w1; + commitments[31] = proof.w2; + commitments[32] = proof.w3; + commitments[33] = proof.w4; + commitments[34] = proof.zPerm; + commitments[35] = proof.lookupInverses; + commitments[36] = proof.lookupReadCounts; + commitments[37] = proof.lookupReadTags; + + /* Batch gemini claims from the prover + * place the commitments to gemini aᵢ to the vector of commitments, compute the contributions from + * aᵢ(−r²ⁱ) for i=1, … , n−1 to the constant term accumulator, add corresponding scalars + * + * 1. Moves the vector + * \f[ + * \left( \text{com}(A_1), \text{com}(A_2), \ldots, \text{com}(A_{n-1}) \right) + * \f] + * to the 'commitments' vector. + * + * 2. Computes the scalars: + * \f[ + * \frac{\nu^{2}}{z + r^2}, \frac{\nu^3}{z + r^4}, \ldots, \frac{\nu^{n-1}}{z + r^{2^{n-1}}} + * \f] + * and places them into the 'scalars' vector. + * + * 3. Accumulates the summands of the constant term: + * \f[ + * \sum_{i=2}^{n-1} \frac{\nu^{i} \cdot A_i(-r^{2^i})}{z + r^{2^i}} + * \f] + * and adds them to the 'constant_term_accumulator'. + */ + + // Add contributions from A₀(r) and A₀(-r) to constant_term_accumulator: + // Compute the evaluations Aₗ(r^{2ˡ}) for l = 0, ..., $LOG_N - 1 + Fr[] memory foldPosEvaluations = CommitmentSchemeLib.computeFoldPosEvaluations( + tp.sumCheckUChallenges, + mem.batchedEvaluation, + proof.geminiAEvaluations, + powers_of_evaluation_challenge, + $LOG_N + ); + + mem.constantTermAccumulator = foldPosEvaluations[0] * mem.posInvertedDenominator; + mem.constantTermAccumulator = + mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * mem.negInvertedDenominator); + + mem.batchingChallenge = tp.shplonkNu.sqr(); + uint256 boundary = NUMBER_UNSHIFTED + 2; + + // Compute Shplonk constant term contributions from Aₗ(± r^{2ˡ}) for l = 1, ..., m-1; + // Compute scalar multipliers for each fold commitment + for (uint256 i = 0; i < $LOG_N - 1; ++i) { + bool dummy_round = i >= ($LOG_N - 1); + + if (!dummy_round) { + // Update inverted denominators + mem.posInvertedDenominator = (tp.shplonkZ - powers_of_evaluation_challenge[i + 1]).invert(); + mem.negInvertedDenominator = (tp.shplonkZ + powers_of_evaluation_challenge[i + 1]).invert(); + + // Compute the scalar multipliers for Aₗ(± r^{2ˡ}) and [Aₗ] + mem.scalingFactorPos = mem.batchingChallenge * mem.posInvertedDenominator; + mem.scalingFactorNeg = mem.batchingChallenge * tp.shplonkNu * mem.negInvertedDenominator; + scalars[boundary + i] = mem.scalingFactorNeg.neg() + mem.scalingFactorPos.neg(); + + // Accumulate the const term contribution given by + // v^{2l} * Aₗ(r^{2ˡ}) /(z-r^{2^l}) + v^{2l+1} * Aₗ(-r^{2ˡ}) /(z+ r^{2^l}) + Fr accumContribution = mem.scalingFactorNeg * proof.geminiAEvaluations[i + 1]; + accumContribution = accumContribution + mem.scalingFactorPos * foldPosEvaluations[i + 1]; + mem.constantTermAccumulator = mem.constantTermAccumulator + accumContribution; + } + // Update the running power of v + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + + commitments[boundary + i] = proof.geminiFoldComms[i]; + } + + boundary += $LOG_N - 1; + + // Finalize the batch opening claim + mem.denominators[0] = Fr.wrap(1).div(tp.shplonkZ - tp.geminiR); + mem.denominators[1] = Fr.wrap(1).div(tp.shplonkZ - SUBGROUP_GENERATOR * tp.geminiR); + mem.denominators[2] = mem.denominators[0]; + mem.denominators[3] = mem.denominators[0]; + + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu * tp.shplonkNu; + for (uint256 i = 0; i < LIBRA_EVALUATIONS; i++) { + Fr scalingFactor = mem.denominators[i] * mem.batchingChallenge; + mem.batchingScalars[i] = scalingFactor.neg(); + mem.batchingChallenge = mem.batchingChallenge * tp.shplonkNu; + mem.constantTermAccumulator = mem.constantTermAccumulator + scalingFactor * proof.libraPolyEvals[i]; + } + scalars[boundary] = mem.batchingScalars[0]; + scalars[boundary + 1] = mem.batchingScalars[1] + mem.batchingScalars[2]; + scalars[boundary + 2] = mem.batchingScalars[3]; + + for (uint256 i = 0; i < LIBRA_COMMITMENTS; i++) { + commitments[boundary++] = proof.libraCommitments[i]; + } + + commitments[boundary] = Honk.G1Point({x: 1, y: 2}); + scalars[boundary++] = mem.constantTermAccumulator; + + if (!checkEvalsConsistency(proof.libraPolyEvals, tp.geminiR, tp.sumCheckUChallenges, proof.libraEvaluation)) { + revert ConsistencyCheckFailed(); + } + + Honk.G1Point memory quotient_commitment = proof.kzgQuotient; + + commitments[boundary] = quotient_commitment; + scalars[boundary] = tp.shplonkZ; // evaluation challenge + + PairingInputs memory pair; + pair.P_0 = batchMul(commitments, scalars); + pair.P_1 = negateInplace(quotient_commitment); + + // Aggregate pairing points + Fr recursionSeparator = generateRecursionSeparator(proof.pairingPointObject, pair.P_0, pair.P_1); + (Honk.G1Point memory P_0_other, Honk.G1Point memory P_1_other) = + convertPairingPointsToG1(proof.pairingPointObject); + + // Validate the points from the proof are on the curve + validateOnCurve(P_0_other); + validateOnCurve(P_1_other); + + // accumulate with aggregate points in proof + pair.P_0 = mulWithSeperator(pair.P_0, P_0_other, recursionSeparator); + pair.P_1 = mulWithSeperator(pair.P_1, P_1_other, recursionSeparator); + + return pairing(pair.P_0, pair.P_1); + } + + struct SmallSubgroupIpaIntermediates { + Fr[SUBGROUP_SIZE] challengePolyLagrange; + Fr challengePolyEval; + Fr lagrangeFirst; + Fr lagrangeLast; + Fr rootPower; + Fr[SUBGROUP_SIZE] denominators; // this has to disappear + Fr diff; + } + + function checkEvalsConsistency( + Fr[LIBRA_EVALUATIONS] memory libraPolyEvals, + Fr geminiR, + Fr[CONST_PROOF_SIZE_LOG_N] memory uChallenges, + Fr libraEval + ) internal view returns (bool check) { + Fr one = Fr.wrap(1); + Fr vanishingPolyEval = geminiR.pow(SUBGROUP_SIZE) - one; + if (vanishingPolyEval == Fr.wrap(0)) { + revert GeminiChallengeInSubgroup(); + } + + SmallSubgroupIpaIntermediates memory mem; + mem.challengePolyLagrange[0] = one; + for (uint256 round = 0; round < $LOG_N; round++) { + uint256 currIdx = 1 + LIBRA_UNIVARIATES_LENGTH * round; + mem.challengePolyLagrange[currIdx] = one; + for (uint256 idx = currIdx + 1; idx < currIdx + LIBRA_UNIVARIATES_LENGTH; idx++) { + mem.challengePolyLagrange[idx] = mem.challengePolyLagrange[idx - 1] * uChallenges[round]; + } + } + + mem.rootPower = one; + mem.challengePolyEval = Fr.wrap(0); + for (uint256 idx = 0; idx < SUBGROUP_SIZE; idx++) { + mem.denominators[idx] = mem.rootPower * geminiR - one; + mem.denominators[idx] = mem.denominators[idx].invert(); + mem.challengePolyEval = mem.challengePolyEval + mem.challengePolyLagrange[idx] * mem.denominators[idx]; + mem.rootPower = mem.rootPower * SUBGROUP_GENERATOR_INVERSE; + } + + Fr numerator = vanishingPolyEval * Fr.wrap(SUBGROUP_SIZE).invert(); + mem.challengePolyEval = mem.challengePolyEval * numerator; + mem.lagrangeFirst = mem.denominators[0] * numerator; + mem.lagrangeLast = mem.denominators[SUBGROUP_SIZE - 1] * numerator; + + mem.diff = mem.lagrangeFirst * libraPolyEvals[2]; + + mem.diff = mem.diff + + (geminiR - SUBGROUP_GENERATOR_INVERSE) + * (libraPolyEvals[1] - libraPolyEvals[2] - libraPolyEvals[0] * mem.challengePolyEval); + mem.diff = mem.diff + mem.lagrangeLast * (libraPolyEvals[2] - libraEval) - vanishingPolyEval * libraPolyEvals[3]; + + check = mem.diff == Fr.wrap(0); + } + + // This implementation is the same as above with different constants + function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) + internal + view + returns (Honk.G1Point memory result) + { + uint256 limit = NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3; - // This implementation is the same as above with different constants - function batchMul(Honk.G1Point[] memory base, Fr[] memory scalars) internal view returns (Honk.G1Point memory result) { - uint256 limit = NUMBER_UNSHIFTED + $LOG_N + LIBRA_COMMITMENTS + 3; + // Validate all points are on the curve + for (uint256 i = 0; i < limit; ++i) { + validateOnCurve(base[i]); + } - // Validate all points are on the curve - for (uint256 i = 0; i < limit; ++i) { - validateOnCurve(base[i]); - } + bool success = true; + assembly { + let free := mload(0x40) - bool success = true; - assembly { - let free := mload(0x40) + let count := 0x01 + for {} lt(count, add(limit, 1)) { count := add(count, 1) } { + // Get loop offsets + let base_base := add(base, mul(count, 0x20)) + let scalar_base := add(scalars, mul(count, 0x20)) - let count := 0x01 - for {} lt(count, add(limit, 1)) { - count := add(count, 1) - } { - // Get loop offsets - let base_base := add(base, mul(count, 0x20)) - let scalar_base := add(scalars, mul(count, 0x20)) + mstore(add(free, 0x40), mload(mload(base_base))) + mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) + // Add scalar + mstore(add(free, 0x80), mload(scalar_base)) - mstore(add(free, 0x40), mload(mload(base_base))) - mstore(add(free, 0x60), mload(add(0x20, mload(base_base)))) - // Add scalar - mstore(add(free, 0x80), mload(scalar_base)) + success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) + // accumulator = accumulator + accumulator_2 + success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) + } - success := and(success, staticcall(gas(), 7, add(free, 0x40), 0x60, add(free, 0x40), 0x40)) - // accumulator = accumulator + accumulator_2 - success := and(success, staticcall(gas(), 6, free, 0x80, free, 0x40)) - } + // Return the result + mstore(result, mload(free)) + mstore(add(result, 0x20), mload(add(free, 0x20))) + } - // Return the result - mstore(result, mload(free)) - mstore(add(result, 0x20), mload(add(free, 0x20))) + require(success, ShpleminiFailed()); } - - require(success, ShpleminiFailed()); - } } contract HonkVerifier is BaseZKHonkVerifier(N, LOG_N, VK_HASH, NUMBER_OF_PUBLIC_INPUTS) { - function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { - return HonkVerificationKey.loadVerificationKey(); - } + function loadVerificationKey() internal pure override returns (Honk.VerificationKey memory) { + return HonkVerificationKey.loadVerificationKey(); + } } From 1ee5231238153c8048bb55b2e40dba5103aba501 Mon Sep 17 00:00:00 2001 From: 0xjei Date: Fri, 13 Feb 2026 16:06:01 +0100 Subject: [PATCH 3/3] update failing test and benchmarks --- .../benchmarks/results_insecure/report.md | 335 +++++++++--------- circuits/benchmarks/results_secure/report.md | 322 ++++++++--------- circuits/benchmarks/scripts/run_benchmarks.sh | 9 +- crates/zk-prover/src/circuits/utils.rs | 15 +- 4 files changed, 330 insertions(+), 351 deletions(-) diff --git a/circuits/benchmarks/results_insecure/report.md b/circuits/benchmarks/results_insecure/report.md index 3dc7ee236e..3b7e19d687 100644 --- a/circuits/benchmarks/results_insecure/report.md +++ b/circuits/benchmarks/results_insecure/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-02-12 16:42:19 UTC +**Generated:** 2026-02-13 12:59:43 UTC -**Git Branch:** `refactor/input-computation` -**Git Commit:** `2b72de5d2b119a672a6da46c355db03862614ac7` +**Git Branch:** `ref/circuits-comp` +**Git Commit:** `2403461592f5c628cb22b493586995f79bb698e1` --- @@ -13,45 +13,45 @@ #### Timing Metrics -| Circuit | Compile | Execute | Prove | Verify | -| ---------------------- | ------- | ------- | ------ | ------ | -| e_sm_share_computation | 0.23 s | 0.42 s | 1.71 s | 0.03 s | -| pk | 0.19 s | 0.19 s | 0.12 s | 0.02 s | -| share_decryption | 0.61 s | 0.23 s | 0.30 s | 0.03 s | -| share_encryption | 0.22 s | 0.36 s | 0.65 s | 0.03 s | -| sk_share_computation | 0.23 s | 0.42 s | 1.71 s | 0.03 s | +| Circuit | Compile | Execute | Prove | Verify | +|---------|---------|---------|-------|--------| +| e_sm_share_computation | 0.31 s | 0.52 s | 1.55 s | 0.02 s | +| pk | 0.25 s | 0.27 s | 0.12 s | 0.03 s | +| share_decryption | 0.25 s | 0.29 s | 0.23 s | 0.02 s | +| share_encryption | 0.29 s | 0.44 s | 0.61 s | 0.03 s | +| sk_share_computation | 0.30 s | 0.51 s | 1.54 s | 0.03 s | #### Size & Circuit Metrics -| Circuit | Opcodes | Gates | Circuit Size | Witness | VK Size | Proof Size | -| ---------------------- | ------- | ------- | ------------ | --------- | ------- | ---------- | -| e_sm_share_computation | 90956 | 328.74K | 1.39 MB | 477.83 KB | 3.59 KB | 15.88 KB | -| pk | 344 | 6.85K | 87.81 KB | 29.06 KB | 3.59 KB | 15.88 KB | -| share_decryption | 3093 | 28.72K | 158.24 KB | 148.90 KB | 3.59 KB | 15.88 KB | -| share_encryption | 47758 | 127.69K | 798.18 KB | 512.13 KB | 3.59 KB | 15.88 KB | -| sk_share_computation | 90827 | 326.14K | 1.38 MB | 463.68 KB | 3.59 KB | 15.88 KB | +| Circuit | Opcodes | Gates | Circuit Size | Witness | VK Size | Proof Size | +|---------|---------|-------|--------------|---------|---------|------------| +| e_sm_share_computation | 90956 | 328.74K | 1.39 MB | 477.72 KB | 3.59 KB | 15.88 KB | +| pk | 344 | 6.85K | 87.84 KB | 29.07 KB | 3.59 KB | 15.88 KB | +| share_decryption | 3093 | 28.72K | 158.27 KB | 148.91 KB | 3.59 KB | 15.88 KB | +| share_encryption | 47758 | 127.69K | 798.14 KB | 512.11 KB | 3.59 KB | 15.88 KB | +| sk_share_computation | 90827 | 326.14K | 1.38 MB | 463.68 KB | 3.59 KB | 15.88 KB | ### Threshold #### Timing Metrics -| Circuit | Compile | Execute | Prove | Verify | -| -------------------------------- | ------- | ------- | ------ | ------ | -| decrypted_shares_aggregation_mod | 0.21 s | 0.25 s | 0.51 s | 0.03 s | -| pk_aggregation | 0.22 s | 0.37 s | 0.95 s | 0.03 s | -| pk_generation | 0.21 s | 0.31 s | 0.55 s | 0.03 s | -| share_decryption | 0.21 s | 0.33 s | 0.58 s | 0.03 s | -| user_data_encryption | 0.23 s | 0.40 s | 0.64 s | 0.03 s | +| Circuit | Compile | Execute | Prove | Verify | +|---------|---------|---------|-------|--------| +| decrypted_shares_aggregation_mod | 0.27 s | 0.33 s | 0.46 s | 0.02 s | +| pk_aggregation | 0.29 s | 0.45 s | 0.87 s | 0.02 s | +| pk_generation | 0.28 s | 0.38 s | 0.48 s | 0.03 s | +| share_decryption | 0.28 s | 0.40 s | 0.53 s | 0.03 s | +| user_data_encryption | 0.30 s | 0.47 s | 0.58 s | 0.02 s | #### Size & Circuit Metrics -| Circuit | Opcodes | Gates | Circuit Size | Witness | VK Size | Proof Size | -| -------------------------------- | ------- | ------- | ------------ | --------- | ------- | ---------- | -| decrypted_shares_aggregation_mod | 31544 | 80.74K | 509.82 KB | 77.42 KB | 3.59 KB | 15.88 KB | -| pk_aggregation | 47817 | 169.89K | 884.07 KB | 360.64 KB | 3.59 KB | 15.88 KB | -| pk_generation | 30019 | 65.61K | 542.13 KB | 446.90 KB | 3.59 KB | 15.88 KB | -| share_decryption | 30570 | 85.48K | 541.52 KB | 522.88 KB | 3.59 KB | 15.88 KB | -| user_data_encryption | 56601 | 106.72K | 847.64 KB | 690.54 KB | 3.59 KB | 15.88 KB | +| Circuit | Opcodes | Gates | Circuit Size | Witness | VK Size | Proof Size | +|---------|---------|-------|--------------|---------|---------|------------| +| decrypted_shares_aggregation_mod | 31544 | 80.74K | 509.84 KB | 77.56 KB | 3.59 KB | 15.88 KB | +| pk_aggregation | 47817 | 169.89K | 884.11 KB | 360.86 KB | 3.59 KB | 15.88 KB | +| pk_generation | 30019 | 65.61K | 542.16 KB | 447.05 KB | 3.59 KB | 15.88 KB | +| share_decryption | 30570 | 85.48K | 541.56 KB | 522.86 KB | 3.59 KB | 15.88 KB | +| user_data_encryption | 56601 | 106.72K | 847.64 KB | 691.13 KB | 3.59 KB | 15.88 KB | ## Circuit Details @@ -59,179 +59,180 @@ #### e_sm_share_computation -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.23 s | -| **Execution** | 0.42 s | -| **VK Generation** | 0.59 s | -| **Proof Generation** | 1.71 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "90956" | -| **Total Gates** | "328743" | -| **Circuit Size** | 1.39 MB | -| **Witness Size** | 477.83 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.31 s | +| **Execution** | 0.52 s | +| **VK Generation** | 0.59 s | +| **Proof Generation** | 1.55 s | +| **Verification** | 0.02 s | +| **ACIR Opcodes** | "90956" | +| **Total Gates** | "328743" | +| **Circuit Size** | 1.39 MB | +| **Witness Size** | 477.72 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### pk -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 0.19 s | -| **Execution** | 0.19 s | -| **VK Generation** | 0.05 s | -| **Proof Generation** | 0.12 s | -| **Verification** | 0.02 s | -| **ACIR Opcodes** | "344" | -| **Total Gates** | "6846" | -| **Circuit Size** | 87.81 KB | -| **Witness Size** | 29.06 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.25 s | +| **Execution** | 0.27 s | +| **VK Generation** | 0.05 s | +| **Proof Generation** | 0.12 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "344" | +| **Total Gates** | "6846" | +| **Circuit Size** | 87.84 KB | +| **Witness Size** | 29.07 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### share_decryption -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.61 s | -| **Execution** | 0.23 s | -| **VK Generation** | 0.09 s | -| **Proof Generation** | 0.30 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "3093" | -| **Total Gates** | "28720" | -| **Circuit Size** | 158.24 KB | -| **Witness Size** | 148.90 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.25 s | +| **Execution** | 0.29 s | +| **VK Generation** | 0.09 s | +| **Proof Generation** | 0.23 s | +| **Verification** | 0.02 s | +| **ACIR Opcodes** | "3093" | +| **Total Gates** | "28720" | +| **Circuit Size** | 158.27 KB | +| **Witness Size** | 148.91 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### share_encryption -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.22 s | -| **Execution** | 0.36 s | -| **VK Generation** | 0.26 s | -| **Proof Generation** | 0.65 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "47758" | -| **Total Gates** | "127691" | -| **Circuit Size** | 798.18 KB | -| **Witness Size** | 512.13 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.29 s | +| **Execution** | 0.44 s | +| **VK Generation** | 0.26 s | +| **Proof Generation** | 0.61 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "47758" | +| **Total Gates** | "127691" | +| **Circuit Size** | 798.14 KB | +| **Witness Size** | 512.11 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### sk_share_computation -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.23 s | -| **Execution** | 0.42 s | -| **VK Generation** | 0.62 s | -| **Proof Generation** | 1.71 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "90827" | -| **Total Gates** | "326138" | -| **Circuit Size** | 1.38 MB | -| **Witness Size** | 463.68 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.30 s | +| **Execution** | 0.51 s | +| **VK Generation** | 0.66 s | +| **Proof Generation** | 1.54 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "90827" | +| **Total Gates** | "326138" | +| **Circuit Size** | 1.38 MB | +| **Witness Size** | 463.68 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | + ### Threshold #### decrypted_shares_aggregation_mod -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.21 s | -| **Execution** | 0.25 s | -| **VK Generation** | 0.19 s | -| **Proof Generation** | 0.51 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "31544" | -| **Total Gates** | "80740" | -| **Circuit Size** | 509.82 KB | -| **Witness Size** | 77.42 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.27 s | +| **Execution** | 0.33 s | +| **VK Generation** | 0.18 s | +| **Proof Generation** | 0.46 s | +| **Verification** | 0.02 s | +| **ACIR Opcodes** | "31544" | +| **Total Gates** | "80740" | +| **Circuit Size** | 509.84 KB | +| **Witness Size** | 77.56 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### pk_aggregation -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.22 s | -| **Execution** | 0.37 s | -| **VK Generation** | 0.36 s | -| **Proof Generation** | 0.95 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "47817" | -| **Total Gates** | "169890" | -| **Circuit Size** | 884.07 KB | -| **Witness Size** | 360.64 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.29 s | +| **Execution** | 0.45 s | +| **VK Generation** | 0.35 s | +| **Proof Generation** | 0.87 s | +| **Verification** | 0.02 s | +| **ACIR Opcodes** | "47817" | +| **Total Gates** | "169890" | +| **Circuit Size** | 884.11 KB | +| **Witness Size** | 360.86 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### pk_generation -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.21 s | -| **Execution** | 0.31 s | -| **VK Generation** | 0.18 s | -| **Proof Generation** | 0.55 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "30019" | -| **Total Gates** | "65606" | -| **Circuit Size** | 542.13 KB | -| **Witness Size** | 446.90 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.28 s | +| **Execution** | 0.38 s | +| **VK Generation** | 0.16 s | +| **Proof Generation** | 0.48 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "30019" | +| **Total Gates** | "65606" | +| **Circuit Size** | 542.16 KB | +| **Witness Size** | 447.05 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### share_decryption -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.21 s | -| **Execution** | 0.33 s | -| **VK Generation** | 0.19 s | -| **Proof Generation** | 0.58 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "30570" | -| **Total Gates** | "85478" | -| **Circuit Size** | 541.52 KB | -| **Witness Size** | 522.88 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.28 s | +| **Execution** | 0.40 s | +| **VK Generation** | 0.19 s | +| **Proof Generation** | 0.53 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "30570" | +| **Total Gates** | "85478" | +| **Circuit Size** | 541.56 KB | +| **Witness Size** | 522.86 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### user_data_encryption -| Metric | Value | -| -------------------- | --------- | -| **Compilation** | 0.23 s | -| **Execution** | 0.40 s | -| **VK Generation** | 0.24 s | -| **Proof Generation** | 0.64 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | "56601" | -| **Total Gates** | "106725" | -| **Circuit Size** | 847.64 KB | -| **Witness Size** | 690.54 KB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +|--------|-------| +| **Compilation** | 0.30 s | +| **Execution** | 0.47 s | +| **VK Generation** | 0.23 s | +| **Proof Generation** | 0.58 s | +| **Verification** | 0.02 s | +| **ACIR Opcodes** | "56601" | +| **Total Gates** | "106725" | +| **Circuit Size** | 847.64 KB | +| **Witness Size** | 691.13 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | + ## System Information ### Hardware - **CPU:** Apple M4 Pro -- **CPU Cores:** 12 -- **RAM:** 24.00 GB +- **CPU Cores:** 14 +- **RAM:** 48.00 GB - **OS:** Darwin - **Architecture:** arm64 ### Software -- **Nargo Version:** nargo version = 1.0.0-beta.15 noirc version = - 1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663 (git version hash: - 83245db91dcf63420ef4bcbbd85b98f397fee663, is dirty: false) -- **Barretenberg Version:** 3.0.0-nightly.20251104 +- **Nargo Version:** nargo version = 1.0.0-beta.15 noirc version = 1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663 (git version hash: 83245db91dcf63420ef4bcbbd85b98f397fee663, is dirty: false) +- **Barretenberg Version:** 3.0.0-nightly.20251104 + diff --git a/circuits/benchmarks/results_secure/report.md b/circuits/benchmarks/results_secure/report.md index 00188be565..d2431eee22 100644 --- a/circuits/benchmarks/results_secure/report.md +++ b/circuits/benchmarks/results_secure/report.md @@ -1,9 +1,9 @@ # Enclave ZK Circuit Benchmarks -**Generated:** 2026-02-11 09:44:15 UTC +**Generated:** 2026-02-13 14:38:47 UTC -**Git Branch:** `circuits/configs-benches` -**Git Commit:** `eb62e90b8e36fedfd255a2dc8e6867296c2dc379` +**Git Branch:** `ref/circuits-comp` +**Git Commit:** `2403461592f5c628cb22b493586995f79bb698e1` --- @@ -15,25 +15,21 @@ | Circuit | Compile | Execute | Prove | Verify | | ---------------------- | -------- | ------- | ------- | ------ | -| e_sm_share_computation | 744.00 s | 10.10 s | 40.60 s | 0.03 s | -| e_sm_share_decryption | 31.68 s | 1.25 s | 3.33 s | 0.02 s | -| e_sm_share_encryption | 473.70 s | 5.09 s | 12.15 s | 0.09 s | -| pk | 10.58 s | 0.48 s | 1.11 s | 0.02 s | -| sk_share_computation | 536.29 s | 9.23 s | 38.13 s | 0.03 s | -| sk_share_decryption | 32.84 s | 1.26 s | 3.32 s | 0.02 s | -| sk_share_encryption | 498.52 s | 5.18 s | 12.07 s | 0.09 s | +| e_sm_share_computation | 1.94 s | 9.97 s | 38.67 s | 0.03 s | +| pk | 0.26 s | 0.47 s | 1.00 s | 0.02 s | +| share_decryption | 45.18 s | 1.90 s | 5.71 s | 0.02 s | +| share_encryption | 462.32 s | 5.14 s | 11.85 s | 0.09 s | +| sk_share_computation | 1.80 s | 9.05 s | 37.63 s | 0.03 s | #### Size & Circuit Metrics | Circuit | Opcodes | Gates | Circuit Size | Witness | VK Size | Proof Size | | ---------------------- | ------- | ------- | ------------ | --------- | ------- | ---------- | | e_sm_share_computation | 2949141 | 11.54M | 39.14 MB | 17.63 MB | 3.59 KB | 15.88 KB | -| e_sm_share_decryption | 51902 | 879.66K | 1.70 MB | 3.55 MB | 3.59 KB | 15.88 KB | -| e_sm_share_encryption | 1151876 | 3.20M | 14.36 MB | 14.19 MB | 3.59 KB | 15.88 KB | -| pk | 10925 | 215.80K | 442.45 KB | 952.21 KB | 3.59 KB | 15.88 KB | +| pk | 10925 | 215.80K | 442.45 KB | 952.12 KB | 3.59 KB | 15.88 KB | +| share_decryption | 81950 | 1.33M | 2.65 MB | 5.57 MB | 3.59 KB | 15.88 KB | +| share_encryption | 1151876 | 3.20M | 14.36 MB | 14.19 MB | 3.59 KB | 15.88 KB | | sk_share_computation | 2905804 | 10.72M | 38.25 MB | 15.36 MB | 3.59 KB | 15.88 KB | -| sk_share_decryption | 51902 | 879.66K | 1.70 MB | 3.55 MB | 3.59 KB | 15.88 KB | -| sk_share_encryption | 1151876 | 3.20M | 14.36 MB | 14.19 MB | 3.59 KB | 15.88 KB | ### Threshold @@ -41,18 +37,18 @@ | Circuit | Compile | Execute | Prove | Verify | | ------------------------------- | -------- | ------- | ------- | ------ | -| decrypted_shares_aggregation_bn | 0.30 s | 0.58 s | 0.80 s | 0.02 s | -| pk_aggregation | 116.13 s | 6.22 s | 20.25 s | 0.02 s | -| pk_generation | 388.08 s | 4.88 s | 12.30 s | 0.09 s | -| share_decryption | 430.14 s | 5.55 s | 12.41 s | 0.16 s | -| user_data_encryption | 409.30 s | 7.78 s | 13.37 s | 0.02 s | +| decrypted_shares_aggregation_bn | 1.40 s | 0.73 s | 0.89 s | 0.03 s | +| pk_aggregation | 153.74 s | 7.90 s | 21.98 s | 0.03 s | +| pk_generation | 419.13 s | 4.95 s | 22.79 s | 0.10 s | +| share_decryption | 520.26 s | 6.24 s | 13.80 s | 0.17 s | +| user_data_encryption | 434.14 s | 7.88 s | 15.04 s | 0.03 s | #### Size & Circuit Metrics | Circuit | Opcodes | Gates | Circuit Size | Witness | VK Size | Proof Size | | ------------------------------- | ------- | ------- | ------------ | --------- | ------- | ---------- | -| decrypted_shares_aggregation_bn | 61568 | 154.96K | 1.29 MB | 194.35 KB | 3.59 KB | 15.88 KB | -| pk_aggregation | 1529181 | 5.27M | 21.69 MB | 11.06 MB | 3.59 KB | 15.88 KB | +| decrypted_shares_aggregation_bn | 61568 | 154.96K | 1.29 MB | 194.16 KB | 3.59 KB | 15.88 KB | +| pk_aggregation | 1572875 | 6.13M | 23.79 MB | 14.66 MB | 3.59 KB | 15.88 KB | | pk_generation | 948955 | 3.49M | 12.31 MB | 16.86 MB | 3.59 KB | 15.88 KB | | share_decryption | 1012104 | 3.54M | 12.98 MB | 19.20 MB | 3.59 KB | 15.88 KB | | user_data_encryption | 1684299 | 4.02M | 20.75 MB | 23.82 MB | 3.59 KB | 15.88 KB | @@ -63,115 +59,83 @@ #### e_sm_share_computation -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 744.00 s | -| **Execution** | 10.10 s | -| **VK Generation** | 16.53 s | -| **Proof Generation** | 40.60 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | 2949141 | -| **Total Gates** | 11539441 | -| **Circuit Size** | 39.14 MB | -| **Witness Size** | 17.63 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | - -#### e_sm_share_decryption - -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 31.68 s | -| **Execution** | 1.25 s | -| **VK Generation** | 1.25 s | -| **Proof Generation** | 3.33 s | -| **Verification** | 0.02 s | -| **ACIR Opcodes** | 51902 | -| **Total Gates** | 879661 | -| **Circuit Size** | 1.70 MB | -| **Witness Size** | 3.55 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | - -#### e_sm_share_encryption - -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 473.70 s | -| **Execution** | 5.09 s | -| **VK Generation** | 5.28 s | -| **Proof Generation** | 12.15 s | -| **Verification** | 0.09 s | -| **ACIR Opcodes** | 1151876 | -| **Total Gates** | 3204716 | -| **Circuit Size** | 14.36 MB | -| **Witness Size** | 14.19 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +| -------------------- | ---------- | +| **Compilation** | 1.94 s | +| **Execution** | 9.97 s | +| **VK Generation** | 16.31 s | +| **Proof Generation** | 38.67 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "2949141" | +| **Total Gates** | "11539441" | +| **Circuit Size** | 39.14 MB | +| **Witness Size** | 17.63 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### pk | Metric | Value | | -------------------- | --------- | -| **Compilation** | 10.58 s | -| **Execution** | 0.48 s | -| **VK Generation** | 0.38 s | -| **Proof Generation** | 1.11 s | +| **Compilation** | 0.26 s | +| **Execution** | 0.47 s | +| **VK Generation** | 0.34 s | +| **Proof Generation** | 1.00 s | | **Verification** | 0.02 s | -| **ACIR Opcodes** | 10925 | -| **Total Gates** | 215803 | +| **ACIR Opcodes** | "10925" | +| **Total Gates** | "215803" | | **Circuit Size** | 442.45 KB | -| **Witness Size** | 952.21 KB | +| **Witness Size** | 952.12 KB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | + +#### share_decryption + +| Metric | Value | +| -------------------- | --------- | +| **Compilation** | 45.18 s | +| **Execution** | 1.90 s | +| **VK Generation** | 1.92 s | +| **Proof Generation** | 5.71 s | +| **Verification** | 0.02 s | +| **ACIR Opcodes** | "81950" | +| **Total Gates** | "1327693" | +| **Circuit Size** | 2.65 MB | +| **Witness Size** | 5.57 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | + +#### share_encryption + +| Metric | Value | +| -------------------- | --------- | +| **Compilation** | 462.32 s | +| **Execution** | 5.14 s | +| **VK Generation** | 5.26 s | +| **Proof Generation** | 11.85 s | +| **Verification** | 0.09 s | +| **ACIR Opcodes** | "1151876" | +| **Total Gates** | "3204716" | +| **Circuit Size** | 14.36 MB | +| **Witness Size** | 14.19 MB | | **VK Size** | 3.59 KB | | **Proof Size** | 15.88 KB | #### sk_share_computation -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 536.29 s | -| **Execution** | 9.23 s | -| **VK Generation** | 15.57 s | -| **Proof Generation** | 38.13 s | -| **Verification** | 0.03 s | -| **ACIR Opcodes** | 2905804 | -| **Total Gates** | 10718698 | -| **Circuit Size** | 38.25 MB | -| **Witness Size** | 15.36 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | - -#### sk_share_decryption - -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 32.84 s | -| **Execution** | 1.26 s | -| **VK Generation** | 1.28 s | -| **Proof Generation** | 3.32 s | -| **Verification** | 0.02 s | -| **ACIR Opcodes** | 51902 | -| **Total Gates** | 879661 | -| **Circuit Size** | 1.70 MB | -| **Witness Size** | 3.55 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | - -#### sk_share_encryption - -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 498.52 s | -| **Execution** | 5.18 s | -| **VK Generation** | 5.46 s | -| **Proof Generation** | 12.07 s | -| **Verification** | 0.09 s | -| **ACIR Opcodes** | 1151876 | -| **Total Gates** | 3204716 | -| **Circuit Size** | 14.36 MB | -| **Witness Size** | 14.19 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +| -------------------- | ---------- | +| **Compilation** | 1.80 s | +| **Execution** | 9.05 s | +| **VK Generation** | 15.19 s | +| **Proof Generation** | 37.63 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "2905804" | +| **Total Gates** | "10718698" | +| **Circuit Size** | 38.25 MB | +| **Witness Size** | 15.36 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | ### Threshold @@ -179,81 +143,81 @@ | Metric | Value | | -------------------- | --------- | -| **Compilation** | 0.30 s | -| **Execution** | 0.58 s | -| **VK Generation** | 0.32 s | -| **Proof Generation** | 0.80 s | -| **Verification** | 0.02 s | -| **ACIR Opcodes** | 61568 | -| **Total Gates** | 154955 | +| **Compilation** | 1.40 s | +| **Execution** | 0.73 s | +| **VK Generation** | 0.35 s | +| **Proof Generation** | 0.89 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "61568" | +| **Total Gates** | "154955" | | **Circuit Size** | 1.29 MB | -| **Witness Size** | 194.35 KB | +| **Witness Size** | 194.16 KB | | **VK Size** | 3.59 KB | | **Proof Size** | 15.88 KB | #### pk_aggregation -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 116.13 s | -| **Execution** | 6.22 s | -| **VK Generation** | 8.28 s | -| **Proof Generation** | 20.25 s | -| **Verification** | 0.02 s | -| **ACIR Opcodes** | 1529181 | -| **Total Gates** | 5267720 | -| **Circuit Size** | 21.69 MB | -| **Witness Size** | 11.06 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +| -------------------- | --------- | +| **Compilation** | 153.74 s | +| **Execution** | 7.90 s | +| **VK Generation** | 9.48 s | +| **Proof Generation** | 21.98 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "1572875" | +| **Total Gates** | "6130710" | +| **Circuit Size** | 23.79 MB | +| **Witness Size** | 14.66 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### pk_generation -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 388.08 s | -| **Execution** | 4.88 s | -| **VK Generation** | 5.17 s | -| **Proof Generation** | 12.30 s | -| **Verification** | 0.09 s | -| **ACIR Opcodes** | 948955 | -| **Total Gates** | 3485220 | -| **Circuit Size** | 12.31 MB | -| **Witness Size** | 16.86 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +| -------------------- | --------- | +| **Compilation** | 419.13 s | +| **Execution** | 4.95 s | +| **VK Generation** | 5.33 s | +| **Proof Generation** | 22.79 s | +| **Verification** | 0.10 s | +| **ACIR Opcodes** | "948955" | +| **Total Gates** | "3485220" | +| **Circuit Size** | 12.31 MB | +| **Witness Size** | 16.86 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### share_decryption -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 430.14 s | -| **Execution** | 5.55 s | -| **VK Generation** | 5.37 s | -| **Proof Generation** | 12.41 s | -| **Verification** | 0.16 s | -| **ACIR Opcodes** | 1012104 | -| **Total Gates** | 3543998 | -| **Circuit Size** | 12.98 MB | -| **Witness Size** | 19.20 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +| -------------------- | --------- | +| **Compilation** | 520.26 s | +| **Execution** | 6.24 s | +| **VK Generation** | 6.06 s | +| **Proof Generation** | 13.80 s | +| **Verification** | 0.17 s | +| **ACIR Opcodes** | "1012104" | +| **Total Gates** | "3543998" | +| **Circuit Size** | 12.98 MB | +| **Witness Size** | 19.20 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | #### user_data_encryption -| Metric | Value | -| -------------------- | -------- | -| **Compilation** | 409.30 s | -| **Execution** | 7.78 s | -| **VK Generation** | 6.35 s | -| **Proof Generation** | 13.37 s | -| **Verification** | 0.02 s | -| **ACIR Opcodes** | 1684299 | -| **Total Gates** | 4021683 | -| **Circuit Size** | 20.75 MB | -| **Witness Size** | 23.82 MB | -| **VK Size** | 3.59 KB | -| **Proof Size** | 15.88 KB | +| Metric | Value | +| -------------------- | --------- | +| **Compilation** | 434.14 s | +| **Execution** | 7.88 s | +| **VK Generation** | 6.55 s | +| **Proof Generation** | 15.04 s | +| **Verification** | 0.03 s | +| **ACIR Opcodes** | "1684299" | +| **Total Gates** | "4021683" | +| **Circuit Size** | 20.75 MB | +| **Witness Size** | 23.82 MB | +| **VK Size** | 3.59 KB | +| **Proof Size** | 15.88 KB | ## System Information diff --git a/circuits/benchmarks/scripts/run_benchmarks.sh b/circuits/benchmarks/scripts/run_benchmarks.sh index dd586b44f2..cd53bcc2ad 100755 --- a/circuits/benchmarks/scripts/run_benchmarks.sh +++ b/circuits/benchmarks/scripts/run_benchmarks.sh @@ -213,6 +213,14 @@ REPORT_FILE="${BENCHMARKS_DIR}/${OUTPUT_DIR}/report.md" echo "✓ Report generated: ${REPORT_FILE}" echo "" +# Remove raw JSON folder after report is generated +RAW_DIR="${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw" +if [ -d "$RAW_DIR" ]; then + rm -rf "$RAW_DIR" + echo "✓ Removed raw data: ${RAW_DIR}" + echo "" +fi + # Clean artifacts if requested if [ "$CLEAN_ARTIFACTS" = true ]; then echo "Cleaning circuit artifacts..." @@ -233,7 +241,6 @@ echo "║ Benchmark Complete! ║" echo "╚════════════════════════════════════════════════╝" echo "" echo "Results:" -echo " Raw data: ${BENCHMARKS_DIR}/${OUTPUT_DIR}/raw/" echo " Report: ${REPORT_FILE}" echo "" echo "To view the report:" diff --git a/crates/zk-prover/src/circuits/utils.rs b/crates/zk-prover/src/circuits/utils.rs index bba6009963..2464a52cc1 100644 --- a/crates/zk-prover/src/circuits/utils.rs +++ b/crates/zk-prover/src/circuits/utils.rs @@ -47,10 +47,17 @@ fn json_value_to_input_value(v: &serde_json::Value) -> Result