diff --git a/src/hypernova/README.md b/src/hypernova/README.md new file mode 100644 index 0000000..d01e239 --- /dev/null +++ b/src/hypernova/README.md @@ -0,0 +1,5 @@ +### hypernova-study + +https://eprint.iacr.org/2023/573.pdf + +> Warning: Implementation just to learn the internals of HyperNova. Do not use. diff --git a/src/hypernova/ccs.rs b/src/hypernova/ccs.rs index 30dc1fd..dce804d 100644 --- a/src/hypernova/ccs.rs +++ b/src/hypernova/ccs.rs @@ -4,14 +4,14 @@ use crate::nifs::R1CS; use crate::utils::{hadamard_product, matrix_vector_product, vec_add, vector_elem_product}; pub struct CCS { - m: usize, - n: usize, - t: usize, - q: usize, - d: usize, - S: Vec>, - c: Vec, - M: Vec>>, + pub m: usize, + pub n: usize, + pub t: usize, + pub q: usize, + pub d: usize, + pub S: Vec>, + pub c: Vec, + pub M: Vec>>, } impl R1CS { diff --git a/src/hypernova/mod.rs b/src/hypernova/mod.rs index ce82ffd..aafa865 100644 --- a/src/hypernova/mod.rs +++ b/src/hypernova/mod.rs @@ -1 +1,3 @@ pub mod ccs; +pub mod multifolding; +pub mod sumcheck; diff --git a/src/hypernova/multifolding.rs b/src/hypernova/multifolding.rs new file mode 100644 index 0000000..dc1f95e --- /dev/null +++ b/src/hypernova/multifolding.rs @@ -0,0 +1,198 @@ +use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; +use ark_ec::{CurveGroup, Group}; +use ark_ff::fields::PrimeField; +use ark_poly::{ + evaluations::multivariate::multilinear::{MultilinearExtension, SparseMultilinearExtension}, + multivariate::{SparsePolynomial, SparseTerm, Term}, + univariate::DensePolynomial, + DenseMVPolynomial, DenseUVPolynomial, Polynomial, +}; +use ark_std::log2; + +use std::marker::PhantomData; + +use crate::hypernova::ccs::CCS; +use crate::hypernova::sumcheck::{Point, SumCheck}; +use crate::pedersen::Commitment; +use crate::transcript::Transcript; + +use ark_std::{One, Zero}; + +// Committed CCS instance +pub struct CCCS { + C: Commitment, + x: Vec, +} + +// Linearized Committed CCS instance +pub struct LCCCS { + C: Commitment, + u: C::ScalarField, + x: Vec, + r: Vec, + v: Vec, +} + +// NIMFS: Non Interactive Multifolding Scheme +pub struct NIMFS { + _c: PhantomData, +} + +impl NIMFS +where + ::ScalarField: Absorb, + ::BaseField: Absorb, +{ + // proof method folds and returns the proof of the multifolding + pub fn proof( + tr: &mut Transcript, + poseidon_config: &PoseidonConfig, + ccs: CCS, + lcccs: LCCCS, + cccs: CCCS, + z1: Vec, + z2: Vec, + ) -> LCCCS { + let s = log2(ccs.m) as usize; // s + let s_ = log2(ccs.n) as usize; // s' + let gamma = tr.get_challenge(); + let beta = tr.get_challenge_vec(s); + + // get MLE of M_i + let mut MLEs: Vec> = Vec::new(); + let n_vars = (s + s_) as usize; + for i in 0..ccs.M.len() { + let M_i_MLE = matrix_to_mle(n_vars, ccs.m, ccs.n, &ccs.M[i]); + MLEs.push(M_i_MLE); + } + // get MLE of z1 & z2 + let z1_MLE = vector_to_mle(s_, ccs.n, z1); + let z2_MLE = vector_to_mle(s_, ccs.n, z2); + + // compute Lj = eq(r_x,x) * \sum Mj * z1 + let mut Lj_evals: Vec<(usize, C::ScalarField)> = Vec::new(); + for i in 0..s_ {} + // compute Q = eq(beta, x) * ( \sum c_i * \prod( \sum Mj * z1 ) ) + // compute g + // let g: SparsePolynomial; + // let proof = SC::::prove(&poseidon_config, g); + // fold C, u, x, v, w + + unimplemented!(); + } +} + +fn matrix_to_mle( + n_vars: usize, // log2(m) + log2(n) + m: usize, + n: usize, + M: &Vec>, +) -> SparseMultilinearExtension { + let mut M_evals: Vec<(usize, F)> = Vec::new(); + for i in 0..m { + for j in 0..n { + if !M[i][j].is_zero() { + M_evals.push((i * n + j, M[i][j])); + } + } + } + SparseMultilinearExtension::::from_evaluations(n_vars, M_evals.iter()) +} + +fn vector_to_mle(s: usize, n: usize, z: Vec) -> SparseMultilinearExtension { + let mut z_evals: Vec<(usize, F)> = Vec::new(); + for i in 0..n { + if !z[i].is_zero() { + z_evals.push((i, z[i])); + } + } + SparseMultilinearExtension::::from_evaluations(s, z_evals.iter()) +} + +type SC = SumCheck< + C::ScalarField, + C, + DensePolynomial, + SparsePolynomial, +>; + +#[cfg(test)] +mod tests { + use super::*; + use crate::transcript::poseidon_test_config; + use ark_mnt4_298::{Fr, G1Projective}; + use ark_std::One; + use ark_std::UniformRand; + + use crate::nifs::gen_test_values; + + type P = Point; + + #[test] + fn test_cccs_mles() { + let (r1cs, ws, _) = gen_test_values(2); + let z1: Vec = ws[0].clone(); + println!("z1 {:?}", z1); + + let ccs = r1cs.to_ccs(); + let s = log2(ccs.m) as usize; // s + let s_ = log2(ccs.n) as usize; // s' + let pow_s_ = (2 as usize).pow(s_ as u32); + + let mut M_MLEs: Vec> = Vec::new(); + let n_vars = (s + s_) as usize; + for i in 0..ccs.M.len() { + let M_i_MLE = matrix_to_mle(n_vars, ccs.m, ccs.n, &ccs.M[i]); + println!("i:{}, M_i_mle: {:?}", i, M_i_MLE); + M_MLEs.push(M_i_MLE); + } + + let z1_MLE = vector_to_mle(s_, ccs.n, z1); + println!("z1_MLE: {:?}", z1_MLE); + + let beta = Point::::point_normal(s, 2); // imagine that this comes from random + println!("beta: {:?}", beta); + + // check Committed CCS relation + let mut r: Fr = Fr::zero(); + for i in 0..ccs.q { + let mut prod_res = Fr::one(); + // for j in 0..ccs.S.len() { + for j in ccs.S[i].clone() { + let mut Mj_z_eval = Fr::zero(); + // for k in 0..s_ { + // over the boolean hypercube un s' vars, but only the combinations that lead to + // some non-zero z() + for k in 0..ccs.n { + // over the whole boolean hypercube on s' vars + // for k in 0..pow_s_ { + let point_in_s_ = Point::::point_normal(s_, k); + // println!("point_in_s {:?}", point_in_s_); + let z_eval = z1_MLE.evaluate(&point_in_s_).unwrap(); + // println!(" ===================================z_eval {:?}", z_eval); + + // let point_in_s_plus_s_ = Point::::point_complete(beta.clone(), s + s_, k); + let mut point_in_s_plus_s_ = Point::::point_normal(s_, i); + point_in_s_plus_s_.append(&mut beta.clone()); + // println!("point_in_s_plus_s_ {:?}", point_in_s_plus_s_); + // println!("j: {}, Mj {:?}", j, M_MLEs[j]); + let Mj_eval = M_MLEs[j].evaluate(&point_in_s_plus_s_).unwrap(); + + if Mj_eval * z_eval != Fr::zero() { + println!(" j: {}, Mj_eval {:?}", j, Mj_eval); + println!(" z_eval {:?}", z_eval); + println!(" =(Mj*z)_eval {:?}", Mj_eval * z_eval); + } + + Mj_z_eval += Mj_eval * z_eval; + } + println!("j: {}, {:?}\n", j, Mj_z_eval); + prod_res += Mj_z_eval; + } + println!("i:{}, c: {:?}, {:?}\n", i, ccs.c[i], prod_res); + r += ccs.c[i] * prod_res; + } + println!("r {:?}", r); + assert!(r.is_zero()); + } +} diff --git a/src/sumcheck.rs b/src/hypernova/sumcheck.rs similarity index 87% rename from src/sumcheck.rs rename to src/hypernova/sumcheck.rs index 18a622a..c151d21 100644 --- a/src/sumcheck.rs +++ b/src/hypernova/sumcheck.rs @@ -14,6 +14,59 @@ use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use crate::transcript::Transcript; +pub struct Point { + _f: PhantomData, +} +impl Point { + pub fn point_normal(n_elems: usize, iter_num: usize) -> Vec { + let p = Self::point(vec![], false, n_elems, iter_num); + let mut r = vec![F::zero(); n_elems]; + for i in 0..n_elems { + r[i] = p[i].unwrap(); + } + r + } + pub fn point_complete(challenges: Vec, n_elems: usize, iter_num: usize) -> Vec { + let p = Self::point(challenges, false, n_elems, iter_num); + let mut r = vec![F::zero(); n_elems]; + for i in 0..n_elems { + r[i] = p[i].unwrap(); + } + r + } + fn point(challenges: Vec, none: bool, n_elems: usize, iter_num: usize) -> Vec> { + let mut n_vars = n_elems - challenges.len(); + assert!(n_vars >= log2(iter_num + 1) as usize); + + if none { + // WIP + if n_vars == 0 { + panic!("err"); // or return directly challenges vector + } + n_vars -= 1; + } + let none_pos = if none { + challenges.len() + 1 + } else { + challenges.len() + }; + let mut p: Vec> = vec![None; n_elems]; + for i in 0..challenges.len() { + p[i] = Some(challenges[i]); + } + for i in 0..n_vars { + let k = F::from(iter_num as u64).into_bigint().to_bytes_le(); + let bit = k[i / 8] & (1 << (i % 8)); + if bit == 0 { + p[none_pos + i] = Some(F::zero()); + } else { + p[none_pos + i] = Some(F::one()); + } + } + p + } +} + pub struct SumCheck< F: PrimeField + Absorb, C: CurveGroup, @@ -84,46 +137,6 @@ where UV::from_coefficients_vec(univ_coeffs) } - fn point_complete(challenges: Vec, n_elems: usize, iter_num: usize) -> Vec { - let p = Self::point(challenges, false, n_elems, iter_num); - let mut r = vec![F::zero(); n_elems]; - for i in 0..n_elems { - r[i] = p[i].unwrap(); - } - r - } - fn point(challenges: Vec, none: bool, n_elems: usize, iter_num: usize) -> Vec> { - let mut n_vars = n_elems - challenges.len(); - assert!(n_vars >= log2(iter_num + 1) as usize); - - if none { - // WIP - if n_vars == 0 { - panic!("err"); // or return directly challenges vector - } - n_vars -= 1; - } - let none_pos = if none { - challenges.len() + 1 - } else { - challenges.len() - }; - let mut p: Vec> = vec![None; n_elems]; - for i in 0..challenges.len() { - p[i] = Some(challenges[i]); - } - for i in 0..n_vars { - let k = F::from(iter_num as u64).into_bigint().to_bytes_le(); - let bit = k[i / 8] & (1 << (i % 8)); - if bit == 0 { - p[none_pos + i] = Some(F::zero()); - } else { - p[none_pos + i] = Some(F::one()); - } - } - p - } - pub fn prove(poseidon_config: &PoseidonConfig, g: MV) -> (F, Vec, F) where >::Point: From>, @@ -133,14 +146,14 @@ where let v = g.num_vars(); - // compute H - let mut H = F::zero(); + // compute T + let mut T = F::zero(); for i in 0..(2_u64.pow(v as u32) as usize) { - let p = Self::point_complete(vec![], v, i); + let p = Point::::point_complete(vec![], v, i); - H += g.evaluate(&p.into()); + T += g.evaluate(&p.into()); } - transcript.add(&H); + transcript.add(&T); let mut ss: Vec = Vec::new(); let mut r: Vec = vec![]; @@ -153,7 +166,7 @@ where let mut s_i = UV::zero(); for j in 0..n_points { - let point = Self::point(r[..i].to_vec(), true, v, j); + let point = Point::::point(r[..i].to_vec(), true, v, j); s_i = s_i + Self::partial_evaluate(&g, &point); } transcript.add_vec(s_i.coeffs()); @@ -161,7 +174,8 @@ where } let last_g_eval = g.evaluate(&r.into()); - (H, ss, last_g_eval) + // ss: intermediate univariate polynomials + (T, ss, last_g_eval) } pub fn verify(poseidon_config: &PoseidonConfig, proof: (F, Vec, F)) -> bool { @@ -218,45 +232,46 @@ mod tests { let f1 = Fr::from(1); let f0 = Fr::from(0); type SC = SumCheck, SparsePolynomial>; + type P = Point; - let p = SC::point(vec![Fr::from(4_u32)], true, 5, 0); + let p = P::point(vec![Fr::from(4_u32)], true, 5, 0); assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], true, 5, 1); + let p = P::point(vec![Fr::from(4_u32)], true, 5, 1); assert_eq!(vec![Some(f4), None, Some(f1), Some(f0), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], true, 5, 2); + let p = P::point(vec![Fr::from(4_u32)], true, 5, 2); assert_eq!(vec![Some(f4), None, Some(f0), Some(f1), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], true, 5, 3); + let p = P::point(vec![Fr::from(4_u32)], true, 5, 3); assert_eq!(vec![Some(f4), None, Some(f1), Some(f1), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], true, 5, 4); + let p = P::point(vec![Fr::from(4_u32)], true, 5, 4); assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f1),], p); // without None - let p = SC::point(vec![], false, 4, 0); + let p = P::point(vec![], false, 4, 0); assert_eq!(vec![Some(f0), Some(f0), Some(f0), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], false, 5, 0); + let p = P::point(vec![Fr::from(4_u32)], false, 5, 0); assert_eq!(vec![Some(f4), Some(f0), Some(f0), Some(f0), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], false, 5, 1); + let p = P::point(vec![Fr::from(4_u32)], false, 5, 1); assert_eq!(vec![Some(f4), Some(f1), Some(f0), Some(f0), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], false, 5, 3); + let p = P::point(vec![Fr::from(4_u32)], false, 5, 3); assert_eq!(vec![Some(f4), Some(f1), Some(f1), Some(f0), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], false, 5, 4); + let p = P::point(vec![Fr::from(4_u32)], false, 5, 4); assert_eq!(vec![Some(f4), Some(f0), Some(f0), Some(f1), Some(f0),], p); - let p = SC::point(vec![Fr::from(4_u32)], false, 5, 10); + let p = P::point(vec![Fr::from(4_u32)], false, 5, 10); assert_eq!(vec![Some(f4), Some(f0), Some(f1), Some(f0), Some(f1),], p); - let p = SC::point(vec![Fr::from(4_u32)], false, 5, 15); + let p = P::point(vec![Fr::from(4_u32)], false, 5, 15); assert_eq!(vec![Some(f4), Some(f1), Some(f1), Some(f1), Some(f1),], p); - // let p = SC::point(vec![Fr::from(4_u32)], false, 4, 16); // TODO expect error + // let p = P::point(vec![Fr::from(4_u32)], false, 4, 16); // TODO expect error } #[test] diff --git a/src/lib.rs b/src/lib.rs index 0342e9a..4aa96ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,6 @@ mod circuits; mod ivc; mod nifs; mod pedersen; -mod sumcheck; mod transcript; mod utils;