From 5342d8b32ff09996a134f90ec756cbd07ea37852 Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 10:47:31 +0100 Subject: [PATCH 1/8] refactor: extract a `sum_ci_mul_prod_thetaj` function for testing --- src/folding/hypernova/utils.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/folding/hypernova/utils.rs b/src/folding/hypernova/utils.rs index 8fd8b8be..5f0141a7 100644 --- a/src/folding/hypernova/utils.rs +++ b/src/folding/hypernova/utils.rs @@ -102,6 +102,22 @@ pub fn sum_muls_gamma_pows_eq_sigma( result } +/// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j +pub fn sum_ci_mul_prod_thetaj( + ccs: &CCS, + thetas: &Vec, +) -> C::ScalarField { + let mut result = C::ScalarField::zero(); + for i in 0..ccs.q { + let mut prod = C::ScalarField::one(); + for j in ccs.S[i].clone() { + prod *= thetas[j]; + } + result += ccs.c[i] * prod; + } + result +} + /// Compute the right-hand-side of step 5 of the multifolding scheme pub fn compute_c_from_sigmas_and_thetas( ccs: &CCS, @@ -127,14 +143,7 @@ pub fn compute_c_from_sigmas_and_thetas( let e2 = eq_eval(beta, r_x_prime).unwrap(); for (k, thetas) in vec_thetas.iter().enumerate() { // + gamma^{t+1} * e2 * sum c_i * prod theta_j - let mut lhs = C::ScalarField::zero(); - for i in 0..ccs.q { - let mut prod = C::ScalarField::one(); - for j in ccs.S[i].clone() { - prod *= thetas[j]; - } - lhs += ccs.c[i] * prod; - } + let lhs = sum_ci_mul_prod_thetaj(ccs, thetas); let gamma_t1 = gamma.pow([(mu * ccs.t + k) as u64]); c += gamma_t1 * e2 * lhs; } From bdfedbc760dfd3774100b7434d78d9ab9ce50ca3 Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 12:09:16 +0100 Subject: [PATCH 2/8] chore: update comment doc --- src/folding/hypernova/circuit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index 1d6d7771..2851ea70 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -21,7 +21,7 @@ impl SumMulsGammaPowsEqSigmaGadget { /// # Arguments /// - `sigmas`: vector of $\sigma_j$ values /// - `eq_eval`: the value of $\tilde{eq}(x_j, x^{\prime})$ - /// - `gamma`: a `GammaVar`, which supports a `pow` method, representing $\gamma$ + /// - `gamma`: value $\gamma$ /// - `j`: the power at which we start to compute $\gamma^{j}$. This is needed in the contexxt of multifolding. /// /// # Notes From 8c1ba6646a3b883da93dff4c3735d9a11fe82000 Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 16:22:34 +0100 Subject: [PATCH 3/8] feat: `test_sum_ci_mul_prod_thetaj_gadget` passing --- src/folding/hypernova/circuit.rs | 76 +++++++++++++++++++++++++++++++- src/folding/hypernova/utils.rs | 2 +- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index 2851ea70..23386621 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -42,15 +42,44 @@ impl SumMulsGammaPowsEqSigmaGadget { } } +/// Gadget to compute $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$. +pub struct SumCiMulProdThetajGadget { + _f: PhantomData, +} + +impl SumCiMulProdThetajGadget { + /// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$ + /// + /// # Arguments + /// - `c_i`: vector of $c_i$ values + /// - `thetas`: vector of pre-processed $\thetas[j]$ values, i.e. corresponding to a ccs.S[i] + /// + /// # Notes + /// This is a part of the second term of the sum that $\mathcal{V}$ has to compute at section 5, step 5 of "A multi-folding scheme for CCS". + /// The first term is computed by `SumMulsGammaPowsEqSigmaGadget::sum_muls_gamma_pows_eq_sigma`. + /// This is a doct product between a vector of c_i values and a vector of pre-processed $\theta_j$ values, where $j$ is a value from $S_i$. + /// Hence, this requires some pre-processing of the $\theta_j$ values, before running this gadget. + pub fn sum_ci_mul_prod_thetaj(c_i: Vec>, thetas: Vec>>) -> FpVar { + let mut result = FpVar::::zero(); + for (i, c_i) in c_i.iter().enumerate() { + let prod = &thetas[i].iter().fold(FpVar::one(), |acc, e| acc * e); + result += c_i * prod; + } + result + } +} + #[cfg(test)] mod tests { - use super::SumMulsGammaPowsEqSigmaGadget; + use super::{SumCiMulProdThetajGadget, SumMulsGammaPowsEqSigmaGadget}; use crate::{ ccs::{ tests::{get_test_ccs, get_test_z}, CCS, }, - folding::hypernova::utils::{compute_sigmas_and_thetas, sum_muls_gamma_pows_eq_sigma}, + folding::hypernova::utils::{ + compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma, + }, pedersen::Pedersen, utils::virtual_polynomial::eq_eval, }; @@ -102,4 +131,47 @@ mod tests { assert_eq!(expected, computed.value().unwrap()); } } + + #[test] + pub fn test_sum_ci_mul_prod_thetaj_gadget() { + let mut rng = test_rng(); + let ccs: CCS = get_test_ccs(); + let z1 = get_test_z(3); + let z2 = get_test_z(4); + + let r_x_prime: Vec = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); + + // Initialize a multifolding object + let pedersen_params = Pedersen::new_params(&mut rng, ccs.n - ccs.l - 1); + let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); + let sigmas_thetas = + compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime); + + let mut e_lcccs = Vec::new(); + for r_x in &vec![lcccs_instance.r_x] { + e_lcccs.push(eq_eval(r_x, &r_x_prime).unwrap()); + } + + // Initialize cs and gamma + let cs = ConstraintSystem::::new_ref(); + let vec_thetas = sigmas_thetas.1; + for (_, thetas) in vec_thetas.iter().enumerate() { + // + gamma^{t+1} * e2 * sum c_i * prod theta_j + let expected = sum_ci_mul_prod_thetaj(&ccs, thetas); + let mut prepared_thetas = Vec::new(); + for i in 0..ccs.q { + let mut prepared: Vec = Vec::new(); + for j in ccs.S[i].clone() { + prepared.push(thetas[j]); + } + prepared_thetas + .push(Vec::>::new_witness(cs.clone(), || Ok(prepared)).unwrap()); + } + let computed = SumCiMulProdThetajGadget::sum_ci_mul_prod_thetaj( + Vec::>::new_witness(cs.clone(), || Ok(ccs.c.clone())).unwrap(), + prepared_thetas, + ); + assert_eq!(expected, computed.value().unwrap()); + } + } } diff --git a/src/folding/hypernova/utils.rs b/src/folding/hypernova/utils.rs index 5f0141a7..f4363d8e 100644 --- a/src/folding/hypernova/utils.rs +++ b/src/folding/hypernova/utils.rs @@ -102,7 +102,7 @@ pub fn sum_muls_gamma_pows_eq_sigma( result } -/// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j +/// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$ pub fn sum_ci_mul_prod_thetaj( ccs: &CCS, thetas: &Vec, From 804ca145d4f42015d04e6ab6624374baf1b44004 Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 16:59:06 +0100 Subject: [PATCH 4/8] refactor: update docs and add a helper `get_prepared_thetas` function --- src/folding/hypernova/circuit.rs | 34 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index 23386621..2be4401f 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -1,6 +1,7 @@ // hypernova nimfs verifier circuit // see section 5 in https://eprint.iacr.org/2023/573.pdf +use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_r1cs_std::{ fields::{fp::FpVar, FieldVar}, @@ -52,7 +53,7 @@ impl SumCiMulProdThetajGadget { /// /// # Arguments /// - `c_i`: vector of $c_i$ values - /// - `thetas`: vector of pre-processed $\thetas[j]$ values, i.e. corresponding to a ccs.S[i] + /// - `thetas`: vector of pre-processed $\thetas[j]$ values corresponding to a particular `ccs.S[i]` /// /// # Notes /// This is a part of the second term of the sum that $\mathcal{V}$ has to compute at section 5, step 5 of "A multi-folding scheme for CCS". @@ -69,6 +70,19 @@ impl SumCiMulProdThetajGadget { } } +/// Returns a vector of thetas for a corresponding $S_i$ +/// An helper function to run before computing $\Pi_{j \in S_i} \theta_j$ in a circuit. +pub fn get_prepared_thetas( + S: &Vec, + thetas: &Vec, +) -> Vec { + let mut prepared: Vec = Vec::new(); + for j in S { + prepared.push(thetas[*j]); + } + prepared +} + #[cfg(test)] mod tests { use super::{SumCiMulProdThetajGadget, SumMulsGammaPowsEqSigmaGadget}; @@ -77,8 +91,11 @@ mod tests { tests::{get_test_ccs, get_test_z}, CCS, }, - folding::hypernova::utils::{ - compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma, + folding::hypernova::{ + circuit::get_prepared_thetas, + utils::{ + compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma, + }, }, pedersen::Pedersen, utils::virtual_polynomial::eq_eval, @@ -152,18 +169,15 @@ mod tests { e_lcccs.push(eq_eval(r_x, &r_x_prime).unwrap()); } - // Initialize cs and gamma + // Initialize cs let cs = ConstraintSystem::::new_ref(); let vec_thetas = sigmas_thetas.1; for (_, thetas) in vec_thetas.iter().enumerate() { - // + gamma^{t+1} * e2 * sum c_i * prod theta_j - let expected = sum_ci_mul_prod_thetaj(&ccs, thetas); + // sum c_i * prod theta_j + let expected = sum_ci_mul_prod_thetaj(&ccs, thetas); // from `compute_c_from_sigmas_and_thetas` let mut prepared_thetas = Vec::new(); for i in 0..ccs.q { - let mut prepared: Vec = Vec::new(); - for j in ccs.S[i].clone() { - prepared.push(thetas[j]); - } + let prepared = get_prepared_thetas::(&ccs.S[i], thetas); prepared_thetas .push(Vec::>::new_witness(cs.clone(), || Ok(prepared)).unwrap()); } From 588877165dc8b7f2d8dd751f027b4ca79f71d21c Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 17:01:05 +0100 Subject: [PATCH 5/8] refactor: clearer arg name --- src/folding/hypernova/circuit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index 2be4401f..37df9540 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -73,11 +73,11 @@ impl SumCiMulProdThetajGadget { /// Returns a vector of thetas for a corresponding $S_i$ /// An helper function to run before computing $\Pi_{j \in S_i} \theta_j$ in a circuit. pub fn get_prepared_thetas( - S: &Vec, + S_i: &Vec, thetas: &Vec, ) -> Vec { let mut prepared: Vec = Vec::new(); - for j in S { + for j in S_i { prepared.push(thetas[*j]); } prepared From 0a2ea74599f2837e4bb2dc908132b66ed9db41dd Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 17:05:38 +0100 Subject: [PATCH 6/8] fix: clippy typing --- src/folding/hypernova/circuit.rs | 2 +- src/folding/hypernova/utils.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index 37df9540..e2f58370 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -74,7 +74,7 @@ impl SumCiMulProdThetajGadget { /// An helper function to run before computing $\Pi_{j \in S_i} \theta_j$ in a circuit. pub fn get_prepared_thetas( S_i: &Vec, - thetas: &Vec, + thetas: &[C::ScalarField], ) -> Vec { let mut prepared: Vec = Vec::new(); for j in S_i { diff --git a/src/folding/hypernova/utils.rs b/src/folding/hypernova/utils.rs index f4363d8e..8f6250be 100644 --- a/src/folding/hypernova/utils.rs +++ b/src/folding/hypernova/utils.rs @@ -105,7 +105,7 @@ pub fn sum_muls_gamma_pows_eq_sigma( /// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$ pub fn sum_ci_mul_prod_thetaj( ccs: &CCS, - thetas: &Vec, + thetas: &[C::ScalarField], ) -> C::ScalarField { let mut result = C::ScalarField::zero(); for i in 0..ccs.q { From bdfd7bd93ee38e20f06498581f517ad243e4d6f2 Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 18:48:32 +0100 Subject: [PATCH 7/8] chore: correct latex comments --- src/folding/circuits/utils.rs | 2 +- src/folding/hypernova/circuit.rs | 8 ++++---- src/folding/hypernova/utils.rs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/folding/circuits/utils.rs b/src/folding/circuits/utils.rs index 6f74ddae..50b954a0 100644 --- a/src/folding/circuits/utils.rs +++ b/src/folding/circuits/utils.rs @@ -3,7 +3,7 @@ use ark_r1cs_std::fields::{fp::FpVar, FieldVar}; use ark_relations::r1cs::SynthesisError; use std::marker::PhantomData; -/// EqEval is a gadget for computing $\tilde{eq}(a, b) = \Pi_{i=1}^{l}(a_i \cdot b_i + (1 - a_i)(1 - b_i))$ +/// EqEval is a gadget for computing $\tilde{eq}(a, b) = \prod_{i=1}^{l}(a_i \cdot b_i + (1 - a_i)(1 - b_i))$ /// :warning: This is not the ark_r1cs_std::eq::EqGadget pub struct EqEvalGadget { _f: PhantomData, diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index e2f58370..5c2a9b77 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -16,7 +16,7 @@ pub struct SumMulsGammaPowsEqSigmaGadget { } impl SumMulsGammaPowsEqSigmaGadget { - /// Computes the sum $\Sigma_{j}^{j + n} \gamma^{j} \cdot eq_eval \cdot \sigma_{j}$, where $n$ is the length of the `sigmas` vector + /// Computes the sum $\sum_{j}^{j + n} \gamma^{j} \cdot eq_eval \cdot \sigma_{j}$, where $n$ is the length of the `sigmas` vector /// It corresponds to the first term of the sum that $\mathcal{V}$ has to compute at section 5, step 5 of "A multi-folding scheme for CCS". /// /// # Arguments @@ -43,13 +43,13 @@ impl SumMulsGammaPowsEqSigmaGadget { } } -/// Gadget to compute $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$. +/// Gadget to compute $\sum_{i=1}^{q} c_i * \prod_{j \in S_i} theta_j$. pub struct SumCiMulProdThetajGadget { _f: PhantomData, } impl SumCiMulProdThetajGadget { - /// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$ + /// Computes $\sum_{i=1}^{q} c_i * \prod_{j \in S_i} theta_j$ /// /// # Arguments /// - `c_i`: vector of $c_i$ values @@ -71,7 +71,7 @@ impl SumCiMulProdThetajGadget { } /// Returns a vector of thetas for a corresponding $S_i$ -/// An helper function to run before computing $\Pi_{j \in S_i} \theta_j$ in a circuit. +/// An helper function to run before computing $\prod_{j \in S_i} \theta_j$ in a circuit. pub fn get_prepared_thetas( S_i: &Vec, thetas: &[C::ScalarField], diff --git a/src/folding/hypernova/utils.rs b/src/folding/hypernova/utils.rs index 8f6250be..0db62f13 100644 --- a/src/folding/hypernova/utils.rs +++ b/src/folding/hypernova/utils.rs @@ -86,7 +86,7 @@ pub fn compute_sigmas_and_thetas( SigmasThetas(sigmas, thetas) } -/// Computes the sum $\Sigma_{j = 0}^{n} \gamma^{\text{pow} + j} \cdot eq_eval \cdot \sigma_{j}$ +/// Computes the sum $\sum_{j = 0}^{n} \gamma^{\text{pow} + j} \cdot eq_eval \cdot \sigma_{j}$ /// `pow` corresponds to `i * ccs.t` in `compute_c_from_sigmas_and_thetas` pub fn sum_muls_gamma_pows_eq_sigma( gamma: F, @@ -102,7 +102,7 @@ pub fn sum_muls_gamma_pows_eq_sigma( result } -/// Computes $\Sigma_{i=1}^{q} c_i * \Pi_{j \in S_i} theta_j$ +/// Computes $\sum_{i=1}^{q} c_i * \prod_{j \in S_i} theta_j$ pub fn sum_ci_mul_prod_thetaj( ccs: &CCS, thetas: &[C::ScalarField], From ac92a8b38aae2a58964000c77805286b635f8a38 Mon Sep 17 00:00:00 2001 From: dmpierre Date: Wed, 10 Jan 2024 18:53:08 +0100 Subject: [PATCH 8/8] refactor: remove unncessary `get_prepared_thetas` fn --- src/folding/hypernova/circuit.rs | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/folding/hypernova/circuit.rs b/src/folding/hypernova/circuit.rs index 5c2a9b77..e1a0e71d 100644 --- a/src/folding/hypernova/circuit.rs +++ b/src/folding/hypernova/circuit.rs @@ -1,7 +1,6 @@ // hypernova nimfs verifier circuit // see section 5 in https://eprint.iacr.org/2023/573.pdf -use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_r1cs_std::{ fields::{fp::FpVar, FieldVar}, @@ -70,19 +69,6 @@ impl SumCiMulProdThetajGadget { } } -/// Returns a vector of thetas for a corresponding $S_i$ -/// An helper function to run before computing $\prod_{j \in S_i} \theta_j$ in a circuit. -pub fn get_prepared_thetas( - S_i: &Vec, - thetas: &[C::ScalarField], -) -> Vec { - let mut prepared: Vec = Vec::new(); - for j in S_i { - prepared.push(thetas[*j]); - } - prepared -} - #[cfg(test)] mod tests { use super::{SumCiMulProdThetajGadget, SumMulsGammaPowsEqSigmaGadget}; @@ -91,11 +77,8 @@ mod tests { tests::{get_test_ccs, get_test_z}, CCS, }, - folding::hypernova::{ - circuit::get_prepared_thetas, - utils::{ - compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma, - }, + folding::hypernova::utils::{ + compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma, }, pedersen::Pedersen, utils::virtual_polynomial::eq_eval, @@ -177,7 +160,7 @@ mod tests { let expected = sum_ci_mul_prod_thetaj(&ccs, thetas); // from `compute_c_from_sigmas_and_thetas` let mut prepared_thetas = Vec::new(); for i in 0..ccs.q { - let prepared = get_prepared_thetas::(&ccs.S[i], thetas); + let prepared: Vec = ccs.S[i].iter().map(|j| thetas[*j]).collect(); prepared_thetas .push(Vec::>::new_witness(cs.clone(), || Ok(prepared)).unwrap()); }