diff --git a/src/aggregation/multiple.rs b/src/aggregation/multiple.rs index 3f8171c..4e5a81f 100644 --- a/src/aggregation/multiple.rs +++ b/src/aggregation/multiple.rs @@ -28,6 +28,19 @@ pub fn aggregate_polys, T: ShplonkTranscript>( xss: &[BTreeSet], transcript: &mut T, ) -> (Poly, F, CS::C) { + let bfs = vec![F::zero(); fs.len()]; + let (agg_poly, _agg_bf, eval_coord, q_comm) = + aggregate_polys_with_bfs(ck, fs, &bfs, xss, transcript); + (agg_poly, eval_coord, q_comm) +} + +pub fn aggregate_polys_with_bfs, T: ShplonkTranscript>( + ck: &CS::CK, + fs: &[Poly], + bfs: &[F], + xss: &[BTreeSet], + transcript: &mut T, +) -> (Poly, F, F, CS::C) { assert_eq!( xss.len(), fs.len(), @@ -35,6 +48,13 @@ pub fn aggregate_polys, T: ShplonkTranscript>( xss.len(), fs.len() ); + assert_eq!( + bfs.len(), + fs.len(), + "{} blinding factors specified for {} polynomials", + bfs.len(), + fs.len() + ); // Both Halo-inf and fflonk/shplonk use the notation "complement" in set-theoretical sense to that used in the code. // The papers consider vanishing polynomials of the complements of the opening sets, // while in the code vanishing polynomials of the opening sets are used directly. @@ -66,6 +86,7 @@ pub fn aggregate_polys, T: ShplonkTranscript>( let q = poly::sum_with_powers(gamma, &qs); let t_commit = start_timer!(|| ark_std::format!("commitment to a degree-{} polynomial", q.degree())); + // TODO: blinding factor? let qc = CS::commit(ck, &q).unwrap(); // "W" in the paper end_timer!(t_commit); @@ -94,11 +115,16 @@ pub fn aggregate_polys, T: ShplonkTranscript>( // coeff_i := gamma^i * z0(zeta) / zi(zeta) let (coeffs, normalizer) = get_coeffs(zs_at_zeta, gamma); let t_combine = start_timer!(|| "linear combination of polynomials"); - let l_norm = &poly::sum_with_coeffs(coeffs, &ps) - &(&q * normalizer); + let l_norm = &poly::sum_with_coeffs(coeffs.clone(), &ps) - &(&q * normalizer); end_timer!(t_combine); + let agg_bf = coeffs + .into_iter() + .zip(bfs.iter()) + .map(|(c, bf)| c * bf) + .sum(); // It remains to notice that "W'" is a KZG opening proof for polynomial l_norm in point zeta. - (l_norm, zeta, qc) + (l_norm, agg_bf, zeta, qc) } /// Takes evaluations of vanishing polynomials at a random point `zeta`, and a random challenge `gamma`, diff --git a/src/pcs/ipa/hiding.rs b/src/pcs/ipa/hiding.rs new file mode 100644 index 0000000..8748604 --- /dev/null +++ b/src/pcs/ipa/hiding.rs @@ -0,0 +1,187 @@ +use crate::pcs::commitment::WrappedAffine; +use crate::pcs::ipa::ipa_pc; +use crate::pcs::PCS; +use crate::pcs::{ipa, CommitterKey, PcsParams, RawVerifierKey, VerifierKey}; +use crate::Poly; +use ark_ec::CurveGroup; +use ark_ff::{PrimeField, Zero}; +use ark_poly::{DenseUVPolynomial, Polynomial}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::rand::Rng; +use ark_std::vec::Vec; +use ark_std::UniformRand; + +// To open a hiding commitment `Cp = Commit(p, t1) = (p0.G0 + ... + pn.Gn) + t1.H` at `z`, +// the prover: +// 1. Computes a hiding commitment to a random `q` such that `deg(q) = deg(p)` and `q(z) = 0` +// `Cq = Commit(q, t2) = (q0.G0 + ... + qn.Gn) + t2.H`. +// 2. Computes the blinded polynomial `p' = p + a.q`, p'(z) = p(z) + a.q(z) = p(z)`. +// 3. Opens the non-hiding commitment `Cp' = Commit(p', 0)` to the blinded polynomial `p'`. +// 4. Reveals `Cq` and `t = t1 + a.t2` to the verifier. +// The verifier +// 1. Computes the non-hiding commitment `Cp'` as `Cp + a.Cq - t'H = (Cp - t1.H) + a.(Cq - t2.H)`. +// 2. Verifies the opening against `Cp'`. +#[derive(Clone, Debug, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct HidingIpa { + pub ipa_pcs: ipa::IPA, + pub h: C::Affine, +} + +/// `Cp = Commit(p, t1) = (p0.G0 + ... + pn.Gn) + t1.H`. +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct HidingProof { + /// A hiding commitment to the blinding polynomial `q`, `deg(q) = deg(p), q(z) = 0`. + /// `Commit(q, t2) = (q0.G0 + ... + qn.Gn) + t2.H`. + q: C::Affine, + /// The blinding factor `t` in the hiding commitment `Cp' = Cp + a.Cq` to the blinded polynomial `p' = p + a.q`. + /// `t = t1 + a.t2`, `Cp' = Commit(p', t) = Commit(p', 0) + (t1 + a.t2).H` + t: C::ScalarField, + /// Opening proof for the non-hiding commitment `Commit(p', 0)` to `p'`. + ipa_pcs_proof: ipa_pc::Proof, + // TODO: remove + a: C::ScalarField, +} + +impl> HidingIpa { + pub fn commit_hiding(&self, p: &Poly, bf: F) -> Result, ()> { + let c = ipa::IPA::commit(&self.ipa_pcs, p)?; + self.reblind(c.0, F::zero(), bf) + } + + pub fn reblind(&self, c: C::Affine, bf_old: F, bf_new: F) -> Result, ()> { + let c = c + self.h * (bf_new - bf_old); + let c = c.into_affine(); + Ok(WrappedAffine(c)) + } +} + +impl CommitterKey for HidingIpa { + fn max_degree(&self) -> usize { + self.ipa_pcs.max_degree() + } +} + +impl VerifierKey for HidingIpa {} + +impl RawVerifierKey for HidingIpa { + type VK = Self; + + fn prepare(&self) -> Self::VK { + self.clone() + } +} + +impl PcsParams for HidingIpa { + type CK = Self; + type VK = Self; + type RVK = Self; + + fn ck(&self) -> Self::CK { + self.clone() + } + + fn vk(&self) -> Self::VK { + self.clone() + } + + fn raw_vk(&self) -> Self::RVK { + self.clone() + } +} + +impl PCS for HidingIpa { + type C = WrappedAffine; + type Proof = HidingProof; + type CK = Self; + type VK = Self; + type Params = Self; + + fn setup(max_degree: usize, rng: &mut R) -> Self::Params { + let ipa_pcs = ipa::IPA::setup(max_degree, rng); + let h = C::Affine::rand(rng); + Self { ipa_pcs, h } + } + + fn commit(ck: &Self::CK, p: &Poly) -> Result { + Self::commit_hiding(ck, p, C::ScalarField::zero()) + } + + fn open( + _ck: &Self::CK, + _p: &Poly, + _x: C::ScalarField, + ) -> Result { + todo!() + } + + fn open_hiding( + ck: &Self::CK, + p: &Poly, + bf: C::ScalarField, + x: C::ScalarField, + rng: &mut R, + ) -> Result { + let t1 = bf; + let mut q = Poly::rand(p.degree(), rng); + let q_at_z = q.evaluate(&x); + q[0] -= q_at_z; + debug_assert!(q.evaluate(&x).is_zero()); + let t2 = C::ScalarField::rand(rng); + let cq = ck.commit_hiding(&q, t2)?.0; + let a = C::ScalarField::rand(rng); // TODO: that's a FS point + let p = p + q * a; + let t = t1 + t2 * a; + let ipa_pcs_proof = ipa::IPA::open(&ck.ipa_pcs, &p, x)?; + Ok(HidingProof { + q: cq, + t, + ipa_pcs_proof, + a, + }) + } + + fn verify( + vk: &Self::VK, + c: Self::C, + x: C::ScalarField, + z: C::ScalarField, + proof: Self::Proof, + ) -> Result<(), ()> { + let c = c.0 + proof.q * proof.a - vk.h * proof.t; + ipa::IPA::verify( + &vk.ipa_pcs, + WrappedAffine(c.into_affine()), + x, + z, + proof.ipa_pcs_proof, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Fr, G1Projective}; + use ark_std::{test_rng, UniformRand}; + + #[test] + fn test_hiding_ipa_opening() { + let rng = &mut test_rng(); + + let max_coeffs = 2usize.pow(6); + let max_degree = max_coeffs - 1; + + let hiding_pcs = HidingIpa::::setup(max_degree, rng); + + let p = Poly::rand(max_degree, rng); + let bf = Fr::rand(rng); + + let c = hiding_pcs.commit_hiding(&p, bf).unwrap(); + + let x = Fr::rand(rng); + let pi = HidingIpa::::open_hiding(&hiding_pcs, &p, bf, x, rng).unwrap(); + let v = p.evaluate(&x); + + assert!(HidingIpa::::verify(&hiding_pcs, c, x, v, pi).is_ok()); + } +} diff --git a/src/pcs/ipa/ipa_pc.rs b/src/pcs/ipa/ipa_pc.rs new file mode 100644 index 0000000..2154ea8 --- /dev/null +++ b/src/pcs/ipa/ipa_pc.rs @@ -0,0 +1,166 @@ +use crate::pcs::ipa::{ + evaluate_final_poly, final_folding_exponents, fold_points, fold_scalars, scalar_prod, +}; +use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; +use ark_ff::{batch_inversion, Field, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::rand::Rng; +use ark_std::test_rng; +use ark_std::vec; +use ark_std::vec::Vec; + +#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +pub struct Proof { + l: Vec, + r: Vec, + g_final: C, // aka U + f_final: C::ScalarField, + xs: Vec, //TODO: + pub log_n: usize, +} + +pub fn open( + log_n: usize, + g: Vec, + h: C, + f: Vec, + z: Vec, +) -> Proof { + let n = 1 << log_n; + let mut f = f; + f.resize(n, C::ScalarField::zero()); + assert_eq!(g.len(), n); + assert_eq!(f.len(), n); + assert_eq!(z.len(), n); + + let mut g_folded = g; + let mut f_folded = f; + let mut z_folded = z; + + let mut l = Vec::::with_capacity(log_n); + let mut r = Vec::::with_capacity(log_n); + + let xs: Vec<_> = (0..log_n) + .map(|_| C::ScalarField::from(test_rng().gen::())) + .collect(); + + let mut n1 = n; + for x in xs.iter() { + n1 /= 2; + + let gl = &g_folded[..n1]; + let gr = &g_folded[n1..]; + let fl = &f_folded[..n1]; + let fr = &f_folded[n1..]; + let zl = &z_folded[..n1]; + let zr = &z_folded[n1..]; + + let cl = scalar_prod(fl, zr); + let cr = scalar_prod(fr, zl); + + let points = [gr, &[h]].concat(); + let scalars = [fl, &[cl]].concat(); + l.push(C::Group::msm(&points, &scalars).unwrap().into()); + + let points = [gl, &[h]].concat(); + let scalars = [fr, &[cr]].concat(); + r.push(C::Group::msm(&points, &scalars).unwrap().into()); + + let x_inv = x.inverse().unwrap(); + + g_folded = fold_points(gl, gr, &x_inv); + f_folded = fold_scalars(fl, fr, &x); + z_folded = fold_scalars(zl, zr, &x_inv); + } + + let g_final = g_folded[0]; + let f_final = f_folded[0]; + + Proof { + l, + r, + g_final, + f_final, + xs, + log_n, + } +} + +/// `g_final` -- folded vector of Pedersen bases `G1,...,Gn` +/// `u` -- the extra base used to commit to the scalar products in Bulletproofs +/// `cf` -- Pedersen commitment to the polynomial, `Cf = f0.G1 + ...+ fd.Gn`, where `n = d+1` +/// `f(z) = v` +pub fn check_assuming_g_final( + g_final: C, + u: C, + cf: C, + z: C::ScalarField, + v: C::ScalarField, + proof: Proof, +) -> bool { + let xs = proof.xs; + let mut xs_inv = xs.clone(); + batch_inversion(xs_inv.as_mut_slice()); + + // the main Bulletproof commitment is `P = Cf + vU` + // The folded one is then `P' = P + (1/x_i).Li + x_i.R_i` + let bases = [proof.l, proof.r, vec![u]].concat(); + let exps = [xs_inv.as_slice(), xs.as_slice(), &[v]].concat(); + let cp_final = cf + C::Group::msm(&bases, &exps).unwrap(); + + // now we check that it's consistent with the other values + let z_final = evaluate_final_poly(&xs_inv, &z); + let sp_final = proof.f_final * z_final; // scalar product of size 1 + let rhs = g_final * proof.f_final + u * sp_final; + cp_final == rhs //TODO: merge into single msm +} + +pub fn check( + g: Vec, + u: C, + cf: C, + z: C::ScalarField, + v: C::ScalarField, + proof: Proof, +) -> bool { + // TODO: duplicate + let xs = proof.xs.clone(); + let mut xs_inv = xs.clone(); + batch_inversion(xs_inv.as_mut_slice()); + + let final_exps = final_folding_exponents(&xs_inv); + let g_final = C::Group::msm(&g, &final_exps).unwrap().into_affine(); + check_assuming_g_final(g_final, u, cf, z, v, proof) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::utils; + use ark_bls12_381::{Fr, G1Affine, G1Projective}; + use ark_poly::univariate::DensePolynomial; + use ark_poly::DenseUVPolynomial; + use ark_poly::Polynomial; + use ark_std::{test_rng, UniformRand}; + + #[test] + fn ipa_pc() { + let rng = &mut test_rng(); + + let log_n = 2; + let n = 2usize.pow(log_n as u32); + + let g = (0..n).map(|_| G1Affine::rand(rng)).collect::>(); + let h = G1Affine::rand(rng); + + let f = DensePolynomial::::rand(n - 1, rng); + let f_comm: G1Affine = G1Projective::msm(&g, &f.coeffs).unwrap().into_affine(); + + let z = Fr::rand(rng); + let z_powers: Vec = utils::powers(z).take(n).collect(); + let v = f.evaluate(&z); + + let proof = open(log_n, g.clone(), h, f.coeffs, z_powers); + assert!(check(g, h, f_comm, z, v, proof)); + } +} diff --git a/src/pcs/ipa/mod.rs b/src/pcs/ipa/mod.rs new file mode 100644 index 0000000..c3124c9 --- /dev/null +++ b/src/pcs/ipa/mod.rs @@ -0,0 +1,208 @@ +pub mod hiding; +pub mod ipa_pc; +mod trait_impl; + +use ark_ec::{AffineRepr, CurveGroup, VariableBaseMSM}; +use ark_ff::{batch_inversion, Field, One}; +use ark_std::rand::Rng; +use ark_std::test_rng; +use ark_std::vec; +use ark_std::vec::Vec; +#[cfg(feature = "parallel")] +use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; + +pub use trait_impl::IPA; + +fn scalar_prod(a: &[F], b: &[F]) -> F { + ark_std::cfg_iter!(a).zip(b).map(|(a, b)| *a * b).sum() +} + +// Computes `l + xr` pointwise. +fn fold_points(l: &[C], r: &[C], x: &C::ScalarField) -> Vec { + assert_eq!(l.len(), r.len()); + let proj: Vec = ark_std::cfg_iter!(l) + .zip(r) + .map(|(&l, &r)| l + r * x) + .collect(); + C::Group::normalize_batch(&proj) +} + +// Computes `l + xr` pointwise. +fn fold_scalars(l: &[F], r: &[F], x: &F) -> Vec { + assert_eq!(l.len(), r.len()); + ark_std::cfg_iter!(l) + .zip(r) + .map(|(&l, &r)| l + r * x) + .collect() +} + +// n = 2^m +// Folding elements V = [A1, ..., An] with scalars [x1, ..., xm] recursively m times using formula +// V = VL || VR, Vi = VL + xi * VR pointwise, V := Vi, i = 1,...,m +// results in V = Vm = [c1A1 + ... + cnAn], where ci = prod({xj | if j-th bit of i-1 is set}). +// This function computes these ci-s. +fn final_folding_exponents(xs: &[F]) -> Vec { + let m = xs.len(); + let mut n = 2usize.pow(m as u32); + let mut res = vec![F::one(); n]; + for x in xs { + n = n / 2; + for chunk in res.rchunks_mut(n).step_by(2) { + for elem in chunk.iter_mut() { + *elem *= x; + } + } + } + res +} + +// Computes (1 + x_m z)(1 + x_{m-1} z^2) ... (1 + x_1 z^{2^{m-1}}). +fn evaluate_final_poly(xs: &[F], z: &F) -> F { + let mut res = F::one(); + let mut z_i = z.clone(); + for x in xs.iter().rev() { + res *= z_i * x + F::one(); + z_i = z_i.square(); //TODO: remove extra squaring + } + res +} + +pub struct Proof { + ls: Vec, + rs: Vec, + _final_p: C, + final_a: C::ScalarField, + final_b: C::ScalarField, + xs: Vec, +} + +pub fn bullet_prove( + log_n: usize, + g: &[C], + h: &[C], + a: &[C::ScalarField], + b: &[C::ScalarField], + p: C, + u: C, +) -> Proof { + let n = 2usize.pow(log_n as u32); + assert_eq!(g.len(), n); + assert_eq!(h.len(), n); + assert_eq!(a.len(), n); + assert_eq!(b.len(), n); + + let mut g_folded = g.to_vec(); + let mut h_folded = h.to_vec(); + let mut a_folded = a.to_vec(); + let mut b_folded = b.to_vec(); + + let mut ls = Vec::::with_capacity(log_n); + let mut rs = Vec::::with_capacity(log_n); + + let xs: Vec<_> = (0..log_n) + .map(|_| C::ScalarField::from(test_rng().gen::())) + .collect(); + + let mut p1 = p; + + let mut n1 = n; + for x in xs.iter() { + n1 /= 2; + + let gl = &g_folded[..n1]; + let gr = &g_folded[n1..]; + let hl = &h_folded[..n1]; + let hr = &h_folded[n1..]; + let al = &a_folded[..n1]; + let ar = &a_folded[n1..]; + let bl = &b_folded[..n1]; + let br = &b_folded[n1..]; + + let cl = scalar_prod(al, br); + let cr = scalar_prod(ar, bl); + + let points = [gr, hl, &[u]].concat(); + let scalars = [al, br, &[cl]].concat(); + let l = C::Group::msm(&points, &scalars).unwrap(); + ls.push(l.into()); + + let points = [gl, hr, &[u]].concat(); + let scalars = [ar, bl, &[cr]].concat(); + let r = C::Group::msm(&points, &scalars).unwrap(); + rs.push(r.into()); + + let x_inv = x.inverse().unwrap(); + + g_folded = fold_points(gl, gr, &x_inv); + h_folded = fold_points(hl, hr, &x); + a_folded = fold_scalars(al, ar, &x); + b_folded = fold_scalars(bl, br, &x_inv); + + p1 = ((l * x_inv) + (r * x) + p1).into_affine(); + } + + let final_a = a_folded[0]; + let final_b = b_folded[0]; + + Proof { + ls, + rs, + _final_p: p1, + final_a, + final_b, + xs, + } +} + +pub fn verify(g: &[C], h: &[C], p: C, u: C, proof: Proof) { + let xs = proof.xs; + let mut xs_inv = xs.clone(); + batch_inversion(xs_inv.as_mut_slice()); + + let g_exps = final_folding_exponents(&xs_inv); + let h_exps = final_folding_exponents(&xs); + + let g_exps = g_exps.iter().map(|e| proof.final_a * e).collect(); + let h_exps = h_exps.iter().map(|e| proof.final_b * e).collect(); + + let final_c = proof.final_a * proof.final_b; + + let points = [g, h, &[u]].concat(); + let scalars = [g_exps, h_exps, vec![final_c]].concat(); + let res1 = C::Group::msm(&points, &scalars).unwrap(); + + let points = [&[p], proof.ls.as_slice(), proof.rs.as_slice()].concat(); + let scalars = [vec![C::ScalarField::one()], xs_inv, xs].concat(); + let res2 = C::Group::msm(&points, &scalars).unwrap(); + assert_eq!(res1, res2); +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::{Fr, G1Affine, G1Projective}; + use ark_ff::vec; + use ark_std::{test_rng, UniformRand}; + + #[test] + fn bullet() { + let rng = &mut test_rng(); + + let log_n = 2; + let n = 2usize.pow(log_n as u32); + + let g = (0..n).map(|_| G1Affine::rand(rng)).collect::>(); + let h = (0..n).map(|_| G1Affine::rand(rng)).collect::>(); + let a = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let b = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let c = scalar_prod(&a, &b); + let u = G1Affine::rand(rng); + + let points = [g.clone(), h.clone(), vec![u]].concat(); + let scalars = [a.clone(), b.clone(), vec![c]].concat(); + let p: G1Affine = G1Projective::msm(&points, &scalars).unwrap().into_affine(); + + let proof = bullet_prove(log_n, &g, &h, &a, &b, p, u); + verify(&g, &h, p, u, proof); + } +} diff --git a/src/pcs/ipa/trait_impl.rs b/src/pcs/ipa/trait_impl.rs new file mode 100644 index 0000000..1252296 --- /dev/null +++ b/src/pcs/ipa/trait_impl.rs @@ -0,0 +1,107 @@ +use crate::pcs::commitment::WrappedAffine; +use crate::pcs::ipa::ipa_pc; +use crate::pcs::{CommitterKey, PcsParams, RawVerifierKey, VerifierKey, PCS}; +use crate::Poly; +use ark_ec::CurveGroup; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::rand::Rng; +use ark_std::vec::Vec; +use ark_std::UniformRand; + +#[derive(Clone, Debug, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct IPA { + log_n: usize, + n: usize, + pub g: Vec, + h: C::Affine, +} + +impl CommitterKey for IPA { + fn max_degree(&self) -> usize { + self.g.len() - 1 + } +} + +impl VerifierKey for IPA {} + +impl RawVerifierKey for IPA { + type VK = Self; + + fn prepare(&self) -> Self::VK { + self.clone() + } +} + +impl PcsParams for IPA { + type CK = Self; + type VK = Self; + type RVK = Self; + + fn ck(&self) -> Self::CK { + self.clone() + } + + fn vk(&self) -> Self::VK { + self.clone() + } + + fn raw_vk(&self) -> Self::RVK { + self.clone() + } +} + +impl PCS for IPA { + type C = WrappedAffine; + type Proof = ipa_pc::Proof; + type CK = Self; + type VK = Self; + type Params = Self; + + fn setup(max_degree: usize, rng: &mut R) -> Self::Params { + let log_n = ark_std::log2(max_degree + 1); + let n = 2usize.pow(log_n); + assert!(max_degree + 1 <= n); + let g = (0..n).map(|_| C::Affine::rand(rng)).collect::>(); //TODO: proj + batch affine + let h = C::Affine::rand(rng); + Self { + log_n: log_n as usize, + n, + g, + h, + } + } + + fn commit(ck: &Self, p: &Poly) -> Result { + if ck.max_evals() < p.coeffs.len() { + return Err(()); + } + let p_comm: C::Affine = C::msm(&ck.g[..p.coeffs.len()], &p.coeffs) + .unwrap() + .into_affine(); + Ok(WrappedAffine(p_comm)) + } + + fn open(ck: &Self, p: &Poly, x: C::ScalarField) -> Result { + let n_coeffs = p.coeffs.len(); + let log_n = ark_std::log2(n_coeffs) as usize; + let n = 1 << log_n; + assert!(n_coeffs <= n); + let x_powers: Vec = crate::utils::powers(x).take(n).collect(); + let proof = ipa_pc::open(log_n, ck.g[..n].to_vec(), ck.h, p.coeffs.clone(), x_powers); + Ok(proof) + } + + fn verify( + vk: &Self, + c: Self::C, + x: C::ScalarField, + z: C::ScalarField, + proof: Self::Proof, + ) -> Result<(), ()> { + let log_n = proof.log_n; + let n = 1 << log_n; + ipa_pc::check(vk.g[..n].to_vec(), vk.h, c.0, x, z, proof) + .then(|| ()) + .ok_or(()) + } +} diff --git a/src/pcs/mod.rs b/src/pcs/mod.rs index 4f64446..eeac76e 100644 --- a/src/pcs/mod.rs +++ b/src/pcs/mod.rs @@ -11,9 +11,10 @@ pub use id::IdentityCommitment; use crate::Poly; +pub mod commitment; pub mod id; +pub mod ipa; pub mod kzg; -pub mod commitment; pub trait Commitment: Eq @@ -98,7 +99,17 @@ pub trait PCS { fn open(ck: &Self::CK, p: &Poly, x: F) -> Result; - fn verify(vk: &Self::VK, c: Self::C, x: F, z: F, proof: Self::Proof) -> Result<(), ()>; + fn open_hiding( + ck: &Self::CK, + p: &Poly, + _bf: F, + x: F, + _rng: &mut R, + ) -> Result { + Self::open(ck, p, x) + } + + fn verify(vk: &Self::VK, c: Self::C, x: F, v: F, proof: Self::Proof) -> Result<(), ()>; // TODO: is the default implementation useful? fn batch_verify( diff --git a/src/shplonk.rs b/src/shplonk.rs index b3ac17e..4214f27 100644 --- a/src/shplonk.rs +++ b/src/shplonk.rs @@ -3,10 +3,12 @@ use ark_poly::{DenseUVPolynomial, Polynomial}; use ark_serialize::*; use ark_std::collections::BTreeSet; use ark_std::marker::PhantomData; +use ark_std::rand::Rng; use ark_std::vec::Vec; use crate::aggregation::multiple::{ - aggregate_claims, aggregate_polys, group_by_commitment, ShplonkTranscript, + aggregate_claims, aggregate_polys, aggregate_polys_with_bfs, group_by_commitment, + ShplonkTranscript, }; use crate::pcs::PCS; use crate::Poly; @@ -38,6 +40,27 @@ impl> Shplonk { } } + pub fn open_many_hiding, R: Rng>( + ck: &CS::CK, + // Polynomials to open. + fs: &[Poly], + // blinding factor per polynomial. + bfs: &[F], + // Coordinate sets to open at, per polynomial. + xss: &[BTreeSet], + transcript: &mut T, + rng: &mut R, + ) -> AggregateProof { + let (agg_poly, agg_bf, zeta, agg_proof) = + aggregate_polys_with_bfs::(ck, fs, bfs, xss, transcript); + assert!(agg_poly.evaluate(&zeta).is_zero()); + let opening_proof = CS::open_hiding(ck, &agg_poly, agg_bf, zeta, rng).unwrap(); + AggregateProof { + agg_proof, + opening_proof, + } + } + pub fn verify_many>( vk: &CS::VK, fcs: &[CS::C],