Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multi-commiting/opening #34

Merged
merged 10 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pcs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "cur

displaydoc = { version = "0.2.3", default-features = false }

poly-iop = { path = "../poly-iop" }


# Benchmarks
[[bench]]
name = "pcs-benches"
Expand All @@ -31,7 +34,9 @@ parallel = [
"ark-ff/parallel",
"ark-poly/parallel",
"ark-ec/parallel",
"poly-iop/parallel"
]
print-trace = [
"ark-std/print-trace"
"ark-std/print-trace",
"poly-iop/print-trace"
]
2 changes: 1 addition & 1 deletion pcs/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn bench_pcs() -> Result<(), PCSErrors> {
{
let start = Instant::now();
for _ in 0..repetition {
assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?);
assert!(KZGMultilinearPC::verify(&vk, &com, &point, &value, &proof)?);
}
println!(
"KZG verify for {} variables: {} ns",
Expand Down
278 changes: 256 additions & 22 deletions pcs/src/commit.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use crate::{
util::{build_l, compute_w_circ_l, merge_polynomials},
KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, UniversalParams,
VerifierParam,
};
use ark_ec::{
msm::{FixedBaseMSM, VariableBaseMSM},
AffineCurve, PairingEngine, ProjectiveCurve,
};
use ark_ff::PrimeField;
use ark_poly::MultilinearExtension;
use ark_poly::{MultilinearExtension, Polynomial};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
use ark_std::{end_timer, rand::RngCore, start_timer, vec::Vec, One, Zero};

use crate::{
KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, UniversalParams,
VerifierParam,
};
use ark_std::{end_timer, log2, rand::RngCore, start_timer, vec::Vec, One, Zero};
use poly_iop::IOPTranscript;

#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
/// commitment
Expand Down Expand Up @@ -71,12 +72,42 @@ impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {
Ok(Commitment { nv, g_product })
}

/// Generate a commitment for a list of polynomials.
///
/// This function takes `2^(num_vars + log(polys.len())` number of scalar
/// multiplications over G1.
fn multi_commit(
prover_param: &Self::ProverParam,
polys: &[impl MultilinearExtension<E::Fr>],
) -> Result<Self::Commitment, PCSErrors> {
let commit_timer = start_timer!(|| "commit");
let poly = merge_polynomials(polys)?;

let scalars: Vec<_> = poly
.to_evaluations()
.iter()
.map(|x| x.into_repr())
.collect();

let g_product = VariableBaseMSM::multi_scalar_mul(
&prover_param.powers_of_g[0].evals,
scalars.as_slice(),
)
.into_affine();

end_timer!(commit_timer);
Ok(Commitment {
nv: poly.num_vars,
g_product,
})
}

/// On input a polynomial `p` and a point `point`, outputs a proof for the
/// same. This function does not need to take the evaluation value as an
/// input.
///
/// This function takes 2^{num_var +1} number of scalar multiplications over
/// G2:
/// G1:
/// - it proceeds with `num_var` number of rounds,
/// - at round i, we compute an MSM for `2^{num_var - i + 1}` number of G2
/// elements.
Expand Down Expand Up @@ -136,6 +167,92 @@ impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {
Ok(Proof { proofs })
}

/// Input a list of MLEs, and a same number of points, and a transcript,
chancharles92 marked this conversation as resolved.
Show resolved Hide resolved
/// compute a multi-opening for all the polynomials
///
/// Returns an error if the lengths do not match.
///
/// Returns
/// - the proof,
/// - q(x),
chancharles92 marked this conversation as resolved.
Show resolved Hide resolved
/// - and a value which is merged MLE evaluated at the consolidated point.
///
/// Steps:
/// 1. build `l(points)` which is a list of univariate polynomials that goes
/// through the points
/// 2. build MLE `w` which is the merge of all MLEs.
/// 3. build `q(x)` which is a univariate polynomial `W circ l`
/// 4. output `q(x)`' and put it into transcript
/// 5. sample `r` from transcript
/// 6. get a point `p := l(r)`
/// 7. output an opening of `w` over point `p`
/// 8. output `w(p)`
#[allow(clippy::type_complexity)]
// TODO: remove after we KZG-commit q(x)
fn multi_open(
prover_param: &Self::ProverParam,
polynomials: &[impl MultilinearExtension<E::Fr>],
points: &[&[E::Fr]],
chancharles92 marked this conversation as resolved.
Show resolved Hide resolved
transcript: &mut IOPTranscript<E::Fr>,
) -> Result<(Self::Proof, Vec<E::Fr>, E::Fr), PCSErrors> {
let open_timer = start_timer!(|| "open");

if points.len() != polynomials.len() {
return Err(PCSErrors::InvalidParameters(
"polynomial length does not match point length".to_string(),
));
}

let num_var = polynomials[0].num_vars();
for poly in polynomials.iter().skip(1) {
if poly.num_vars() != num_var {
return Err(PCSErrors::InvalidParameters(
"polynomials do not have same num_vars".to_string(),
));
}
}
for &point in points.iter() {
if point.len() != num_var {
return Err(PCSErrors::InvalidParameters(
"points do not have same num_vars".to_string(),
));
}
}

// 1. build `l(points)` which is a list of univariate polynomials that goes
// through the points
let uni_polys = build_l(num_var, points)?;

// 2. build MLE `w` which is the merge of all MLEs.
let merge_poly = merge_polynomials(polynomials)?;

// 3. build `q(x)` which is a univariate polynomial `W circ l`
let q_x = compute_w_circ_l(&merge_poly, &uni_polys)?;

// 4. output `q(x)`' and put it into transcript
//
// TODO: use KZG commit for q(x)
// TODO: unwrap
q_x.coeffs
.iter()
.for_each(|x| transcript.append_field_element(b"q(x)", x).unwrap());

// 5. sample `r` from transcript
let r = transcript.get_and_append_challenge(b"r")?;

// 6. get a point `p := l(r)`
let point: Vec<E::Fr> = uni_polys.iter().map(|poly| poly.evaluate(&r)).collect();

// 7. output an opening of `w` over point `p`
let opening = Self::open(prover_param, &merge_poly, &point)?;

// 8. output value that is `w` evaluated at `p`
let value = merge_poly.evaluate(&point).unwrap();
end_timer!(open_timer);

Ok((opening, q_x.coeffs, value))
}

/// Verifies that `value` is the evaluation at `x` of the polynomial
/// committed inside `comm`.
///
Expand All @@ -146,7 +263,7 @@ impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {
verifier_param: &Self::VerifierParam,
commitment: &Self::Commitment,
point: &[E::Fr],
value: E::Fr,
value: &E::Fr,
proof: &Self::Proof,
) -> Result<bool, PCSErrors> {
let verify_timer = start_timer!(|| "verify");
Expand Down Expand Up @@ -185,7 +302,7 @@ impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {

pairings.push((
E::G1Prepared::from(
(verifier_param.g.mul(value) - commitment.g_product.into_projective())
(verifier_param.g.mul(*value) - commitment.g_product.into_projective())
.into_affine(),
),
E::G2Prepared::from(verifier_param.h),
Expand All @@ -197,19 +314,81 @@ impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {
end_timer!(verify_timer);
Ok(res)
}

/// Verifies that `value` is the evaluation at `x_i` of the polynomial
/// `poly_i` committed inside `comm`.
/// steps:
///
/// 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript
/// 2. sample `r` from transcript
/// 3. build `l(points)` which is a list of univariate polynomials that goes
/// through the points
/// 4. get a point `p := l(r)`
/// 5. verifies `p` is verifies against proof
fn batch_verify(
verifier_param: &Self::VerifierParam,
multi_commitment: &Self::Commitment,
points: &[&[E::Fr]],
value: &E::Fr,
chancharles92 marked this conversation as resolved.
Show resolved Hide resolved
proof: &Self::Proof,
q_x_coeff: &[E::Fr],
transcript: &mut IOPTranscript<E::Fr>,
) -> Result<bool, PCSErrors> {
let num_var = points[0].len();

for &point in points.iter().skip(1) {
if point.len() != num_var {
return Err(PCSErrors::InvalidParameters(format!(
"points do not have same num_vars ({} vs {})",
point.len(),
num_var,
)));
}
}
if num_var + log2(points.len()) as usize != multi_commitment.nv {
return Err(PCSErrors::InvalidParameters(format!(
"points and multi_commitment do not have same num_vars ({} vs {})",
num_var + log2(points.len()) as usize,
num_var,
)));
}

// TODO: verify commitment of `q(x)` instead of receiving full `q(x)`

// 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript
// TODO: unwrap
q_x_coeff
.iter()
.for_each(|x| transcript.append_field_element(b"q(x)", x).unwrap());

// 2. sample `r` from transcript
let r = transcript.get_and_append_challenge(b"r")?;

chancharles92 marked this conversation as resolved.
Show resolved Hide resolved
// 3. build `l(points)` which is a list of univariate polynomials that goes
// through the points
let uni_polys = build_l(num_var, points)?;

// 4. get a point `p := l(r)`
let point: Vec<E::Fr> = uni_polys.iter().map(|x| x.evaluate(&r)).collect();

// 5. verifies `p` is verifies against proof
Self::verify(verifier_param, multi_commitment, &point, value, proof)
}
}

#[cfg(test)]
mod tests {
use crate::util::get_batched_nv;

use super::*;
use ark_bls12_381::Bls12_381;
use ark_ec::PairingEngine;
use ark_poly::{DenseMultilinearExtension, MultilinearExtension, SparseMultilinearExtension};
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
use ark_std::{rand::RngCore, test_rng, vec::Vec, UniformRand};
type E = Bls12_381;
type Fr = <E as PairingEngine>::Fr;

fn test_kzg_mlpc_helper<R: RngCore>(
fn test_single_helper<R: RngCore>(
uni_params: &UniversalParams<E>,
poly: &impl MultilinearExtension<Fr>,
rng: &mut R,
Expand All @@ -222,33 +401,88 @@ mod tests {
let proof = KZGMultilinearPC::open(&ck, poly, &point)?;

let value = poly.evaluate(&point).unwrap();
assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?);
assert!(KZGMultilinearPC::verify(&vk, &com, &point, &value, &proof)?);

let value = Fr::rand(rng);
assert!(!KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?);
assert!(!KZGMultilinearPC::verify(
&vk, &com, &point, &value, &proof
)?);

Ok(())
}

#[test]
fn setup_commit_verify_correct_polynomials() -> Result<(), PCSErrors> {
fn test_single_commit() -> Result<(), PCSErrors> {
let mut rng = test_rng();

let uni_params = KZGMultilinearPC::<E>::setup(&mut rng, 10)?;

// normal polynomials
let poly1 = DenseMultilinearExtension::rand(8, &mut rng);
test_kzg_mlpc_helper(&uni_params, &poly1, &mut rng)?;
test_single_helper(&uni_params, &poly1, &mut rng)?;

let poly2 = SparseMultilinearExtension::rand_with_config(9, 1 << 5, &mut rng);
test_kzg_mlpc_helper(&uni_params, &poly2, &mut rng)?;
// single-variate polynomials
let poly2 = DenseMultilinearExtension::rand(1, &mut rng);
test_single_helper(&uni_params, &poly2, &mut rng)?;

Ok(())
}

fn test_multi_commit_helper<R: RngCore>(
uni_params: &UniversalParams<E>,
polys: &[impl MultilinearExtension<Fr>],
rng: &mut R,
) -> Result<(), PCSErrors> {
let mut transcript = IOPTranscript::new(b"test");

let nv = get_batched_nv(polys[0].num_vars(), polys.len());
let (ck, vk) = uni_params.trim(nv)?;
let mut points = Vec::new();

for poly in polys.iter() {
let point = (0..poly.num_vars())
.map(|_| Fr::rand(rng))
.collect::<Vec<Fr>>();
points.push(point);
}
let points_ref: Vec<&[Fr]> = points.iter().map(|x| x.as_ref()).collect();

let com = KZGMultilinearPC::multi_commit(&ck, polys)?;
let (proof, q_x_coeff, value) =
KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?;

let mut transcript = IOPTranscript::new(b"test");
assert!(KZGMultilinearPC::batch_verify(
&vk,
&com,
&points_ref,
&value,
&proof,
&q_x_coeff,
&mut transcript
)?);

chancharles92 marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}

#[test]
fn test_multi_commit() -> Result<(), PCSErrors> {
let mut rng = test_rng();

let uni_params = KZGMultilinearPC::<E>::setup(&mut rng, 15)?;

// normal polynomials
let polys1: Vec<_> = (0..5)
.map(|_| DenseMultilinearExtension::rand(4, &mut rng))
.collect();
test_multi_commit_helper(&uni_params, &polys1, &mut rng)?;

// single-variate polynomials
let poly3 = DenseMultilinearExtension::rand(1, &mut rng);
test_kzg_mlpc_helper(&uni_params, &poly3, &mut rng)?;
let polys1: Vec<_> = (0..5)
.map(|_| DenseMultilinearExtension::rand(1, &mut rng))
.collect();
test_multi_commit_helper(&uni_params, &polys1, &mut rng)?;

let poly4 = SparseMultilinearExtension::rand_with_config(1, 1 << 1, &mut rng);
test_kzg_mlpc_helper(&uni_params, &poly4, &mut rng)?;
Ok(())
}

Expand Down
Loading