diff --git a/README.md b/README.md index 5f9887d..ef9f9a7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # protogalaxy-poc -Proof of concept implementation of ProtoGalaxy (https://eprint.iacr.org/2023/1106.pdf). +Proof of concept implementation of ProtoGalaxy (https://eprint.iacr.org/2023/1106.pdf) using [arkworks](https://github.com/arkworks-rs). > Do not use in production. diff --git a/src/protogalaxy.rs b/src/protogalaxy.rs index 111b23d..b42c583 100644 --- a/src/protogalaxy.rs +++ b/src/protogalaxy.rs @@ -2,11 +2,15 @@ use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; use ark_ff::fields::PrimeField; use ark_std::log2; -use ark_std::{One, Zero}; +use ark_std::{cfg_into_iter, One, Zero}; use std::marker::PhantomData; -use std::ops::Add; +use std::ops::{Add, Mul}; -use ark_poly::{univariate::SparsePolynomial, Polynomial}; +use ark_ff::{batch_inversion, FftField}; +use ark_poly::{ + univariate::{DensePolynomial, SparsePolynomial}, + DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial, +}; use crate::pedersen::{Commitment, Params as PedersenParams, Pedersen, Proof as PedersenProof}; use crate::transcript::Transcript; @@ -38,25 +42,28 @@ where pub fn prover( tr: &mut Transcript, pedersen_params: &PedersenParams, - r1cs: R1CS, + r1cs: &R1CS, // running instance instance: CommittedInstance, w: Witness, // incomming instances vec_instances: Vec>, vec_w: Vec>, + ) -> ( + Vec, + Vec, + CommittedInstance, + Witness, ) { let t = instance.betas.len(); let n = r1cs.A[0].len(); + // TODO initialize transcript let delta = tr.get_challenge(); - let deltas = powers_of_beta(delta, t); let f_w = eval_f(&r1cs, &w.w); - dbg!(w.w.len()); - dbg!(f_w.len()); - dbg!(n); + // println!("is f(w) {:?}", f_w); // F(X) let mut F_X: SparsePolynomial = SparsePolynomial::zero(); @@ -65,9 +72,12 @@ where let curr = &lhs * f_w[i]; F_X = F_X.add(curr); } - // TODO return F(X) + + let F_X_dense = DensePolynomial::from(F_X.clone()); + tr.add_vec(&F_X_dense.coeffs); let alpha = tr.get_challenge(); + // eval F(alpha) let F_alpha = F_X.evaluate(&alpha); @@ -89,12 +99,122 @@ where assert!(check_instance( r1cs, CommittedInstance { - phi: instance.phi, - betas: betas_star, + phi: instance.phi.clone(), + betas: betas_star.clone(), e: F_alpha, }, w.clone(), )); + + let gamma = tr.get_challenge(); + + // TODO WIP compute G(X) & K(X) + let G_evals: Vec = vec![C::ScalarField::zero(); n]; + let G_X: DensePolynomial = + Evaluations::::from_vec_and_domain(G_evals.clone(), H).interpolate(); + // dbg!(&G_X); + let (K_X, remainder) = G_X.divide_by_vanishing_poly(H).unwrap(); + // dbg!(&K_X); + assert!(remainder.is_zero()); + + let Z_X: DensePolynomial = H.vanishing_polynomial().into(); + + let e_star = + F_alpha * L_X[0].evaluate(&gamma) + Z_X.evaluate(&gamma) * K_X.evaluate(&gamma); + + let mut phi_star: C = instance.phi.0 * L_X[0].evaluate(&gamma); + for i in 0..k { + phi_star += vec_instances[i].phi.0 * L_X[i].evaluate(&gamma); + } + let mut w_star: Vec = vec_scalar_mul(&w.w, &L_X[0].evaluate(&gamma)); + for i in 0..k { + w_star = vec_add( + &w_star, + &vec_scalar_mul(&vec_w[i].w, &L_X[i].evaluate(&gamma)), + ); + } + + ( + F_X_dense.coeffs, + K_X.coeffs, + CommittedInstance { + betas: betas_star, + phi: Commitment(phi_star), + e: e_star, + }, + Witness { + w: w_star, + r_w: w.r_w, + }, + ) + } + + pub fn verifier( + tr: &mut Transcript, + pedersen_params: &PedersenParams, + r1cs: R1CS, + // running instance + instance: CommittedInstance, + // incomming instances + vec_instances: Vec>, + // polys from P + F_coeffs: Vec, + K_coeffs: Vec, + ) -> CommittedInstance { + let t = instance.betas.len(); + let n = r1cs.A[0].len(); + + let delta = tr.get_challenge(); + let deltas = powers_of_beta(delta, t); + + tr.add_vec(&F_coeffs); + + let alpha = tr.get_challenge(); + let alphas = all_powers(alpha, n); + + // dbg!(instance.e); + // dbg!(F_coeffs[0]); + + // F(alpha) = e + \sum_t F_i * alpha^i + let mut F_alpha = instance.e; + for (i, F_i) in F_coeffs.iter().enumerate() { + F_alpha += *F_i * alphas[i]; + } + + let betas_star: Vec = instance + .betas + .iter() + .zip( + deltas + .iter() + .map(|delta_i| alpha * delta_i) + .collect::>(), + ) + .map(|(beta_i, delta_i_alpha)| *beta_i + delta_i_alpha) + .collect(); + + let gamma = tr.get_challenge(); + + let k = vec_instances.len(); + let domain_k = GeneralEvaluationDomain::::new(k).unwrap(); + let L_X: Vec> = lagrange_polys(domain_k); + let Z_X: DensePolynomial = domain_k.vanishing_polynomial().into(); + let K_X: DensePolynomial = + DensePolynomial::::from_coefficients_vec(K_coeffs); + let e_star = + F_alpha * L_X[0].evaluate(&gamma) + Z_X.evaluate(&gamma) * K_X.evaluate(&gamma); + + let mut phi_star: C = instance.phi.0 * L_X[0].evaluate(&gamma); + for i in 0..k { + phi_star += vec_instances[i].phi.0 * L_X[i].evaluate(&gamma); + } + + // return the folded instance + CommittedInstance { + betas: betas_star, + phi: Commitment(phi_star), + e: e_star, + } } } @@ -134,6 +254,19 @@ fn pow_i_over_x(i: usize, betas: &Vec, deltas: &Vec) -> Spa r } +// method from caulk: https://github.com/caulk-crypto/caulk/tree/8210b51fb8a9eef4335505d1695c44ddc7bf8170/src/multi/setup.rs#L300 +fn lagrange_polys(domain_n: GeneralEvaluationDomain) -> Vec> { + let mut lagrange_polynomials: Vec> = Vec::new(); + + for i in 0..domain_n.size() { + let evals: Vec = cfg_into_iter!(0..domain_n.size()) + .map(|k| if k == i { F::one() } else { F::zero() }) + .collect(); + lagrange_polynomials.push(Evaluations::from_vec_and_domain(evals, domain_n).interpolate()); + } + lagrange_polynomials +} + #[derive(Clone, Debug)] pub struct R1CS { pub A: Vec>, @@ -150,7 +283,7 @@ fn eval_f(r1cs: &R1CS, w: &Vec) -> Vec { } fn check_instance( - r1cs: R1CS, + r1cs: &R1CS, instance: CommittedInstance, w: Witness, ) -> bool { @@ -298,16 +431,24 @@ mod tests { } #[test] - fn test_fold_prover() { + fn test_fold() { let mut rng = ark_std::test_rng(); let pedersen_params = Pedersen::::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec let poseidon_config = poseidon_test_config::(); + let k = 5; + let r1cs = get_test_r1cs::(); let mut z = get_test_z::(3); + let mut zs: Vec> = Vec::new(); + for i in 0..k { + let z_i = get_test_z::(i + 4); + zs.push(z_i); + } - // init Prover's transcript + // init Prover & Verifier's transcript let mut transcript_p = Transcript::::new(&poseidon_config); + let mut transcript_v = Transcript::::new(&poseidon_config); let n = z.len(); let t = log2(n) as usize; @@ -318,24 +459,60 @@ mod tests { let betas = powers_of_beta(beta, t); let witness = Witness:: { - w: z, // WIP + w: z.clone(), // WIP r_w: Fr::rand(&mut rng), }; let phi = Pedersen::::commit(&pedersen_params, &witness.w, &witness.r_w); let instance = CommittedInstance:: { phi, - betas, + betas: betas.clone(), e: Fr::zero(), }; + // same for the other instances + let mut witnesses: Vec> = Vec::new(); + let mut instances: Vec> = Vec::new(); + for i in 0..k { + let witness_i = Witness:: { + w: zs[i].clone(), // WIP + r_w: Fr::rand(&mut rng), + }; + let phi_i = + Pedersen::::commit(&pedersen_params, &witness_i.w, &witness_i.r_w); + let instance_i = CommittedInstance:: { + phi: phi_i, + betas: betas.clone(), + e: Fr::zero(), + }; + witnesses.push(witness_i); + instances.push(instance_i); + } - Folding::::prover( + let (F_coeffs, K_coeffs, folded_instance, folded_witness) = Folding::::prover( &mut transcript_p, &pedersen_params, - r1cs, - instance, + &r1cs, + instance.clone(), witness, - Vec::new(), - Vec::new(), + instances.clone(), + witnesses, ); + dbg!(&F_coeffs); + + // veriier + let folded_instance_v = Folding::::verifier( + &mut transcript_v, + &pedersen_params, + r1cs.clone(), // TODO rm clone do borrow + instance, + instances, + F_coeffs, + K_coeffs, + ); + + assert_eq!(folded_instance.phi.0, folded_instance_v.phi.0); + assert_eq!(folded_instance.betas, folded_instance_v.betas); + assert_eq!(folded_instance.e, folded_instance_v.e); + + // assert!(check_instance(&r1cs, folded_instance, folded_witness)); } }