mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-10 08:01:28 +01:00
multi-commiting/opening (#34)
This commit is contained in:
@@ -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"
|
||||
@@ -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"
|
||||
]
|
||||
@@ -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",
|
||||
|
||||
@@ -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::{univariate::DensePolynomial, MultilinearExtension, Polynomial, UVPolynomial};
|
||||
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
|
||||
@@ -28,12 +29,30 @@ pub struct Proof<E: PairingEngine> {
|
||||
pub proofs: Vec<E::G1Affine>,
|
||||
}
|
||||
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
||||
/// proof of batch opening
|
||||
pub struct BatchProof<E: PairingEngine> {
|
||||
/// The actual proof
|
||||
pub proof: Proof<E>,
|
||||
/// The value which is `w` evaluated at `p:= l(r)`, where
|
||||
/// - `w` is the merged MLE
|
||||
/// - `l` is the list of univariate polys that goes through all points
|
||||
/// - `r` is sampled from the transcript.
|
||||
pub value: E::Fr,
|
||||
/// Commitment to q(x)
|
||||
// This is currently set to the entire coefficient list of q(x)
|
||||
// TODO: replace me with a KZG commit
|
||||
pub q_x_com: Vec<E::Fr>,
|
||||
}
|
||||
|
||||
impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {
|
||||
type ProverParam = ProverParam<E>;
|
||||
type VerifierParam = VerifierParam<E>;
|
||||
type SRS = UniversalParams<E>;
|
||||
type Commitment = Commitment<E>;
|
||||
type Proof = Proof<E>;
|
||||
type Transcript = IOPTranscript<E::Fr>;
|
||||
type BatchProof = BatchProof<E>;
|
||||
|
||||
/// Generate SRS from RNG.
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
||||
@@ -71,12 +90,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!(|| "multi 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.
|
||||
@@ -136,6 +185,117 @@ impl<E: PairingEngine> MultilinearCommitmentScheme<E> for KZGMultilinearPC<E> {
|
||||
Ok(Proof { proofs })
|
||||
}
|
||||
|
||||
/// Input
|
||||
/// - the prover parameters,
|
||||
/// - a list of MLEs,
|
||||
/// - and a same number of points,
|
||||
/// - and a transcript,
|
||||
/// compute a multi-opening for all the polynomials.
|
||||
///
|
||||
/// For simplicity, this API requires each MLE to have only one point. If
|
||||
/// the caller wish to use more than one points per MLE, it should be
|
||||
/// handled at the caller layer.
|
||||
///
|
||||
/// Returns an error if the lengths do not match.
|
||||
///
|
||||
/// Returns:
|
||||
/// - the proof,
|
||||
/// - q(x), which is a univariate polynomial `w circ l` where `w` is the
|
||||
/// merged MLE, and `l` is a list of polynomials that go through all the
|
||||
/// points. TODO: change this field to a commitment to `q(x)`
|
||||
/// - and a value which is `w` evaluated at `p:= l(r)` from some `r` from
|
||||
/// the transcript.
|
||||
///
|
||||
/// 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)`
|
||||
fn multi_open(
|
||||
prover_param: &Self::ProverParam,
|
||||
polynomials: &[impl MultilinearExtension<E::Fr>],
|
||||
points: &[&[E::Fr]],
|
||||
transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<Self::BatchProof, PCSErrors> {
|
||||
let open_timer = start_timer!(|| "multi 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()
|
||||
.rev()
|
||||
.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` (which should match `q(r)`)
|
||||
let value = merge_poly.evaluate(&point).unwrap();
|
||||
let value2 = q_x.evaluate(&r);
|
||||
|
||||
if value != value2 {
|
||||
return Err(PCSErrors::InvalidProver(
|
||||
"Q(r) does not match W(l(r))".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
end_timer!(open_timer);
|
||||
|
||||
Ok(Self::BatchProof {
|
||||
proof: opening,
|
||||
q_x_com: q_x.coeffs,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
||||
/// committed inside `comm`.
|
||||
///
|
||||
@@ -146,7 +306,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");
|
||||
@@ -185,7 +345,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),
|
||||
@@ -197,19 +357,98 @@ 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. check `q(r) == value`
|
||||
/// 4. build `l(points)` which is a list of univariate polynomials that goes
|
||||
/// through the points
|
||||
/// 5. get a point `p := l(r)`
|
||||
/// 6. verifies `p` is verifies against proof
|
||||
fn batch_verify(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
multi_commitment: &Self::Commitment,
|
||||
points: &[&[E::Fr]],
|
||||
batch_proof: &Self::BatchProof,
|
||||
transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<bool, PCSErrors> {
|
||||
let verify_timer = start_timer!(|| "batch verify");
|
||||
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
|
||||
batch_proof
|
||||
.q_x_com
|
||||
.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")?;
|
||||
|
||||
// 3. check `q(r) == value`
|
||||
let q_x = DensePolynomial::from_coefficients_slice(&batch_proof.q_x_com);
|
||||
let q_r = q_x.evaluate(&r);
|
||||
if q_r != batch_proof.value {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// 4. build `l(points)` which is a list of univariate polynomials that goes
|
||||
// through the points
|
||||
let uni_polys = build_l(num_var, points)?;
|
||||
|
||||
// 5. get a point `p := l(r)`
|
||||
let point: Vec<E::Fr> = uni_polys.iter().rev().map(|x| x.evaluate(&r)).collect();
|
||||
|
||||
// 6. verifies `p` is verifies against proof
|
||||
let res = Self::verify(
|
||||
verifier_param,
|
||||
multi_commitment,
|
||||
&point,
|
||||
&batch_proof.value,
|
||||
&batch_proof.proof,
|
||||
);
|
||||
end_timer!(verify_timer);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
@@ -222,33 +461,152 @@ 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)?;
|
||||
|
||||
let poly2 = SparseMultilinearExtension::rand_with_config(9, 1 << 5, &mut rng);
|
||||
test_kzg_mlpc_helper(&uni_params, &poly2, &mut rng)?;
|
||||
test_single_helper(&uni_params, &poly1, &mut rng)?;
|
||||
|
||||
// single-variate polynomials
|
||||
let poly3 = DenseMultilinearExtension::rand(1, &mut rng);
|
||||
test_kzg_mlpc_helper(&uni_params, &poly3, &mut rng)?;
|
||||
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 batch_proof = KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?;
|
||||
|
||||
// good path
|
||||
let mut transcript = IOPTranscript::new(b"test");
|
||||
assert!(KZGMultilinearPC::batch_verify(
|
||||
&vk,
|
||||
&com,
|
||||
&points_ref,
|
||||
&batch_proof,
|
||||
&mut transcript
|
||||
)?);
|
||||
|
||||
// bad commitment
|
||||
assert!(KZGMultilinearPC::batch_verify(
|
||||
&vk,
|
||||
&Commitment {
|
||||
nv: 0,
|
||||
g_product: <E as PairingEngine>::G1Affine::default()
|
||||
},
|
||||
&points_ref,
|
||||
&batch_proof,
|
||||
&mut transcript
|
||||
)
|
||||
.is_err());
|
||||
|
||||
// bad points
|
||||
let points_ref: Vec<&[Fr]> = points.iter().skip(1).map(|x| x.as_ref()).collect();
|
||||
assert!(KZGMultilinearPC::batch_verify(
|
||||
&vk,
|
||||
&com,
|
||||
&points_ref,
|
||||
&batch_proof,
|
||||
&mut transcript
|
||||
)
|
||||
.is_err());
|
||||
|
||||
// bad proof
|
||||
assert!(KZGMultilinearPC::batch_verify(
|
||||
&vk,
|
||||
&com,
|
||||
&points_ref,
|
||||
&BatchProof {
|
||||
proof: Proof { proofs: Vec::new() },
|
||||
value: batch_proof.value,
|
||||
q_x_com: batch_proof.q_x_com.clone()
|
||||
},
|
||||
&mut transcript
|
||||
)
|
||||
.is_err());
|
||||
|
||||
// bad value
|
||||
assert!(KZGMultilinearPC::batch_verify(
|
||||
&vk,
|
||||
&com,
|
||||
&points_ref,
|
||||
&BatchProof {
|
||||
proof: batch_proof.proof.clone(),
|
||||
value: Fr::one(),
|
||||
q_x_com: batch_proof.q_x_com
|
||||
},
|
||||
&mut transcript
|
||||
)
|
||||
.is_err());
|
||||
|
||||
// bad q(x) commit
|
||||
assert!(KZGMultilinearPC::batch_verify(
|
||||
&vk,
|
||||
&com,
|
||||
&points_ref,
|
||||
&BatchProof {
|
||||
proof: batch_proof.proof,
|
||||
value: batch_proof.value,
|
||||
q_x_com: Vec::new()
|
||||
},
|
||||
&mut transcript
|
||||
)
|
||||
.is_err());
|
||||
|
||||
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..2)
|
||||
.map(|_| DenseMultilinearExtension::rand(4, &mut rng))
|
||||
.collect();
|
||||
test_multi_commit_helper(&uni_params, &polys1, &mut rng)?;
|
||||
|
||||
// single-variate polynomials
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//! Error module.
|
||||
|
||||
use ark_serialize::SerializationError;
|
||||
use ark_std::string::String;
|
||||
use displaydoc::Display;
|
||||
use poly_iop::PolyIOPErrors;
|
||||
|
||||
/// A `enum` specifying the possible failure modes of the PCS.
|
||||
#[derive(Display, Debug)]
|
||||
@@ -15,11 +17,19 @@ pub enum PCSErrors {
|
||||
/// Invalid parameters: {0}
|
||||
InvalidParameters(String),
|
||||
/// An error during (de)serialization: {0}
|
||||
SerializationError(ark_serialize::SerializationError),
|
||||
SerializationError(SerializationError),
|
||||
/// PolyIOP error {0}
|
||||
PolyIOPErrors(PolyIOPErrors),
|
||||
}
|
||||
|
||||
impl From<ark_serialize::SerializationError> for PCSErrors {
|
||||
impl From<SerializationError> for PCSErrors {
|
||||
fn from(e: ark_serialize::SerializationError) -> Self {
|
||||
Self::SerializationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PolyIOPErrors> for PCSErrors {
|
||||
fn from(e: PolyIOPErrors) -> Self {
|
||||
Self::PolyIOPErrors(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
mod commit;
|
||||
mod errors;
|
||||
mod param;
|
||||
mod util;
|
||||
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_poly::MultilinearExtension;
|
||||
use ark_std::rand::RngCore;
|
||||
use poly_iop::IOPTranscript;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use errors::PCSErrors;
|
||||
@@ -12,6 +14,7 @@ pub use param::{ProverParam, UniversalParams, VerifierParam};
|
||||
|
||||
/// KZG Polynomial Commitment Scheme on multilinear extensions.
|
||||
pub struct KZGMultilinearPC<E: PairingEngine> {
|
||||
#[doc(hidden)]
|
||||
phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
@@ -21,6 +24,8 @@ pub trait MultilinearCommitmentScheme<E: PairingEngine> {
|
||||
type SRS;
|
||||
type Commitment;
|
||||
type Proof;
|
||||
type BatchProof;
|
||||
type Transcript;
|
||||
|
||||
/// Generate SRS from RNG.
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
||||
@@ -33,6 +38,12 @@ pub trait MultilinearCommitmentScheme<E: PairingEngine> {
|
||||
poly: &impl MultilinearExtension<E::Fr>,
|
||||
) -> Result<Self::Commitment, PCSErrors>;
|
||||
|
||||
/// Generate a commitment for a list of polynomials
|
||||
fn multi_commit(
|
||||
prover_param: &Self::ProverParam,
|
||||
polys: &[impl MultilinearExtension<E::Fr>],
|
||||
) -> Result<Self::Commitment, PCSErrors>;
|
||||
|
||||
/// On input a polynomial `p` and a point `point`, outputs a proof for the
|
||||
/// same.
|
||||
fn open(
|
||||
@@ -41,13 +52,34 @@ pub trait MultilinearCommitmentScheme<E: PairingEngine> {
|
||||
point: &[E::Fr],
|
||||
) -> Result<Self::Proof, PCSErrors>;
|
||||
|
||||
/// Input a list of MLEs, and a same number of points, and a transcript,
|
||||
/// compute a multi-opening for all the polynomials.
|
||||
#[allow(clippy::type_complexity)]
|
||||
// TODO: remove after we KZG-commit q(x)
|
||||
fn multi_open(
|
||||
prover_param: &Self::ProverParam,
|
||||
polynomials: &[impl MultilinearExtension<E::Fr>],
|
||||
point: &[&[E::Fr]],
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::BatchProof, PCSErrors>;
|
||||
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
||||
/// committed inside `comm`.
|
||||
fn verify(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
commitment: &Self::Commitment,
|
||||
point: &[E::Fr],
|
||||
value: E::Fr,
|
||||
value: &E::Fr,
|
||||
proof: &Self::Proof,
|
||||
) -> Result<bool, PCSErrors>;
|
||||
|
||||
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
|
||||
/// `poly_i` committed inside `comm`.
|
||||
fn batch_verify(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
multi_commitment: &Self::Commitment,
|
||||
points: &[&[E::Fr]],
|
||||
batch_proof: &Self::BatchProof,
|
||||
transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<bool, PCSErrors>;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use ark_std::{end_timer, rand::RngCore, start_timer, vec::Vec, UniformRand};
|
||||
/// Evaluations over {0,1}^n for G1 or G2
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
||||
pub struct Evaluations<C: AffineCurve> {
|
||||
/// The evaluations.
|
||||
pub evals: Vec<C>,
|
||||
}
|
||||
|
||||
|
||||
610
pcs/src/util.rs
Normal file
610
pcs/src/util.rs
Normal file
@@ -0,0 +1,610 @@
|
||||
//! Useful utilities for KZG PCS
|
||||
|
||||
use crate::PCSErrors;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::{
|
||||
univariate::DensePolynomial, DenseMultilinearExtension, EvaluationDomain, Evaluations,
|
||||
MultilinearExtension, Polynomial, Radix2EvaluationDomain,
|
||||
};
|
||||
use ark_std::{end_timer, log2, start_timer};
|
||||
use poly_iop::bit_decompose;
|
||||
|
||||
/// Compute W \circ l.
|
||||
///
|
||||
/// Given an MLE W, and a list of univariate polynomials l, generate the
|
||||
/// univariate polynomial that composes W with l.
|
||||
///
|
||||
/// Returns an error if l's length does not matches number of variables in W.
|
||||
pub(crate) fn compute_w_circ_l<F: PrimeField>(
|
||||
w: &DenseMultilinearExtension<F>,
|
||||
l: &[DensePolynomial<F>],
|
||||
) -> Result<DensePolynomial<F>, PCSErrors> {
|
||||
let timer = start_timer!(|| "compute W \\circ l");
|
||||
|
||||
if w.num_vars != l.len() {
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
||||
"l's length ({}) does not match num_variables ({})",
|
||||
l.len(),
|
||||
w.num_vars(),
|
||||
)));
|
||||
}
|
||||
|
||||
let mut res_eval: Vec<F> = vec![];
|
||||
|
||||
// TODO: consider to pass this in from caller
|
||||
// uni_degree is (product of each prefix's) + (2 * MLEs)
|
||||
// = (l.len() - (num_vars - log(l.len())) + 2) * l[0].degree
|
||||
let uni_degree = (l.len() - w.num_vars + log2(l.len()) as usize + 2) * l[0].degree();
|
||||
|
||||
let domain = match Radix2EvaluationDomain::<F>::new(uni_degree) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return Err(PCSErrors::InvalidParameters(
|
||||
"failed to build radix 2 domain".to_string(),
|
||||
))
|
||||
},
|
||||
};
|
||||
for point in domain.elements() {
|
||||
// we reverse the order here because the coefficient vec are stored in
|
||||
// bit-reversed order
|
||||
let l_eval: Vec<F> = l.iter().rev().map(|x| x.evaluate(&point)).collect();
|
||||
res_eval.push(w.evaluate(l_eval.as_ref()).unwrap())
|
||||
}
|
||||
let evaluation = Evaluations::from_vec_and_domain(res_eval, domain);
|
||||
let res = evaluation.interpolate();
|
||||
|
||||
end_timer!(timer);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Return the number of variables that one need for an MLE to
|
||||
/// batch the list of MLEs
|
||||
#[inline]
|
||||
pub(crate) fn get_batched_nv(num_var: usize, polynomials_len: usize) -> usize {
|
||||
num_var + log2(polynomials_len) as usize
|
||||
}
|
||||
|
||||
/// merge a set of polynomials. Returns an error if the
|
||||
/// polynomials do not share a same number of nvs.
|
||||
pub(crate) fn merge_polynomials<F: PrimeField>(
|
||||
polynomials: &[impl MultilinearExtension<F>],
|
||||
) -> Result<DenseMultilinearExtension<F>, PCSErrors> {
|
||||
let nv = polynomials[0].num_vars();
|
||||
for poly in polynomials.iter() {
|
||||
if nv != poly.num_vars() {
|
||||
return Err(PCSErrors::InvalidParameters(
|
||||
"num_vars do not match for polynomials".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let merged_nv = get_batched_nv(nv, polynomials.len());
|
||||
let mut scalars = vec![];
|
||||
for poly in polynomials.iter() {
|
||||
scalars.extend_from_slice(poly.to_evaluations().as_slice());
|
||||
}
|
||||
scalars.extend_from_slice(vec![F::zero(); (1 << merged_nv) - scalars.len()].as_ref());
|
||||
Ok(DenseMultilinearExtension::from_evaluations_vec(
|
||||
merged_nv, scalars,
|
||||
))
|
||||
}
|
||||
|
||||
/// Given a list of points, build `l(points)` which is a list of univariate
|
||||
/// polynomials that goes through the points
|
||||
pub(crate) fn build_l<F: PrimeField>(
|
||||
num_var: usize,
|
||||
points: &[&[F]],
|
||||
) -> Result<Vec<DensePolynomial<F>>, PCSErrors> {
|
||||
let prefix_len = log2(points.len()) as usize;
|
||||
|
||||
let uni_degree = points.len();
|
||||
let small_domain = match Radix2EvaluationDomain::<F>::new(uni_degree) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return Err(PCSErrors::InvalidParameters(
|
||||
"failed to build radix 2 domain".to_string(),
|
||||
))
|
||||
},
|
||||
};
|
||||
|
||||
// The following code print out the roots for testing
|
||||
// println!("domain root0: {}", small_domain.element(0));
|
||||
// println!("domain root1: {}", small_domain.element(1));
|
||||
// println!("domain root2: {}", small_domain.element(2));
|
||||
// println!("domain root3: {}", small_domain.element(3));
|
||||
|
||||
let mut uni_polys = Vec::new();
|
||||
|
||||
// 1.1 build the indexes and the univariate polys that go through the indexes
|
||||
let indexes: Vec<Vec<bool>> = (0..points.len())
|
||||
.map(|x| bit_decompose(x as u64, prefix_len))
|
||||
.collect();
|
||||
for i in 0..prefix_len {
|
||||
let eval: Vec<F> = indexes
|
||||
.iter()
|
||||
.map(|x| F::from(x[prefix_len - i - 1]))
|
||||
.collect();
|
||||
|
||||
uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate());
|
||||
}
|
||||
|
||||
// 1.2 build the actual univariate polys that go through the points
|
||||
for i in 0..num_var {
|
||||
let mut eval: Vec<F> = points.iter().map(|x| x[i]).collect();
|
||||
eval.extend_from_slice(vec![F::zero(); small_domain.size as usize - eval.len()].as_slice());
|
||||
uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate())
|
||||
}
|
||||
|
||||
Ok(uni_polys)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_ff::field_new;
|
||||
use ark_poly::UVPolynomial;
|
||||
use ark_std::{One, Zero};
|
||||
|
||||
#[test]
|
||||
fn test_w_circ_l() -> Result<(), PCSErrors> {
|
||||
test_w_circ_l_helper::<Fr>()
|
||||
}
|
||||
|
||||
fn test_w_circ_l_helper<F: PrimeField>() -> Result<(), PCSErrors> {
|
||||
{
|
||||
// Example from page 53:
|
||||
// W = 3x1x2 + 2x2 whose evaluations are
|
||||
// 0, 0 |-> 0
|
||||
// 0, 1 |-> 2
|
||||
// 1, 0 |-> 0
|
||||
// 1, 1 |-> 5
|
||||
let w_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)];
|
||||
let w = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
// l0 = t + 2
|
||||
// l1 = -2t + 4
|
||||
let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]);
|
||||
let l1 = DensePolynomial::from_coefficients_vec(vec![F::from(4u64), -F::from(2u64)]);
|
||||
|
||||
// res = -6t^2 - 4t + 32
|
||||
let res = compute_w_circ_l(&w, [l0, l1].as_ref())?;
|
||||
let res_rec = DensePolynomial::from_coefficients_vec(vec![
|
||||
F::from(32u64),
|
||||
-F::from(4u64),
|
||||
-F::from(6u64),
|
||||
]);
|
||||
assert_eq!(res, res_rec);
|
||||
}
|
||||
{
|
||||
// A random example
|
||||
// W = x1x2x3 - 2x1x2 + 3x2x3 - 4x1x3 + 5x1 - 6x2 + 7x3
|
||||
// 0, 0, 0 |-> 0
|
||||
// 0, 0, 1 |-> 7
|
||||
// 0, 1, 0 |-> -6
|
||||
// 0, 1, 1 |-> 4
|
||||
// 1, 0, 0 |-> 5
|
||||
// 1, 0, 1 |-> 8
|
||||
// 1, 1, 0 |-> -3
|
||||
// 1, 1, 1 |-> 4
|
||||
let w_eval = vec![
|
||||
F::zero(),
|
||||
F::from(7u64),
|
||||
-F::from(6u64),
|
||||
F::from(4u64),
|
||||
F::from(5u64),
|
||||
F::from(8u64),
|
||||
-F::from(3u64),
|
||||
F::from(4u64),
|
||||
];
|
||||
let w = DenseMultilinearExtension::from_evaluations_vec(3, w_eval);
|
||||
|
||||
// l0 = t + 2
|
||||
// l1 = 3t - 4
|
||||
// l2 = -5t + 6
|
||||
let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]);
|
||||
let l1 = DensePolynomial::from_coefficients_vec(vec![-F::from(4u64), F::from(3u64)]);
|
||||
let l2 = DensePolynomial::from_coefficients_vec(vec![F::from(6u64), -F::from(5u64)]);
|
||||
let res = compute_w_circ_l(&w, [l0, l1, l2].as_ref())?;
|
||||
|
||||
// res = -15t^3 - 23t^2 + 130t - 76
|
||||
let res_rec = DensePolynomial::from_coefficients_vec(vec![
|
||||
-F::from(76u64),
|
||||
F::from(130u64),
|
||||
-F::from(23u64),
|
||||
-F::from(15u64),
|
||||
]);
|
||||
|
||||
assert_eq!(res, res_rec);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_poly() -> Result<(), PCSErrors> {
|
||||
test_merge_poly_helper::<Fr>()
|
||||
}
|
||||
fn test_merge_poly_helper<F: PrimeField>() -> Result<(), PCSErrors> {
|
||||
// Example from page 53:
|
||||
// W1 = 3x1x2 + 2x2 whose evaluations are
|
||||
// 0, 0 |-> 0
|
||||
// 0, 1 |-> 2
|
||||
// 1, 0 |-> 0
|
||||
// 1, 1 |-> 5
|
||||
let w_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)];
|
||||
let w1 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
// W2 = x1x2 + x1 whose evaluations are
|
||||
// 0, 0 |-> 0
|
||||
// 0, 1 |-> 0
|
||||
// 1, 0 |-> 1
|
||||
// 1, 1 |-> 2
|
||||
let w_eval = vec![F::zero(), F::zero(), F::from(1u64), F::from(2u64)];
|
||||
let w2 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
// W3 = x1 + x2 whose evaluations are
|
||||
// 0, 0 |-> 0
|
||||
// 0, 1 |-> 1
|
||||
// 1, 0 |-> 1
|
||||
// 1, 1 |-> 2
|
||||
let w_eval = vec![F::zero(), F::one(), F::from(1u64), F::from(2u64)];
|
||||
let w3 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
{
|
||||
// W = (3x1x2 + 2x2)(1-x0) + (x1x2 + x1)x0
|
||||
// = -2x0x1x2 + x0x1 - 2x0x2 + 3x1x2 + 2x2
|
||||
// with evaluation map
|
||||
//
|
||||
// x0 x1 x2
|
||||
// 0, 0, 0 |-> 0
|
||||
// 0, 0, 1 |-> 2
|
||||
// 0, 1, 0 |-> 0
|
||||
// 0, 1, 1 |-> 5
|
||||
// 1, 0, 0 |-> 0
|
||||
// 1, 0, 1 |-> 0
|
||||
// 1, 1, 0 |-> 1
|
||||
// 1, 1, 1 |-> 2
|
||||
//
|
||||
let w = merge_polynomials(&[w1.clone(), w2.clone()])?;
|
||||
// w is [0,2,0,5,0,0,1,2]
|
||||
let w_eval = vec![
|
||||
F::zero(),
|
||||
F::from(2u64),
|
||||
F::zero(),
|
||||
F::from(5u64),
|
||||
F::zero(),
|
||||
F::zero(),
|
||||
F::from(1u64),
|
||||
F::from(2u64),
|
||||
];
|
||||
let w_rec = DenseMultilinearExtension::from_evaluations_vec(3, w_eval);
|
||||
|
||||
assert_eq!(w, w_rec);
|
||||
}
|
||||
|
||||
{
|
||||
// W = (3x1x2 + 2x2) * (1-y1) * (1-y2)
|
||||
// + (x1x2 + x1) * (1-y1) * y2
|
||||
// + (x1 + x2) * y1 * (1-y2)
|
||||
//
|
||||
// with evaluation map
|
||||
//
|
||||
// y1 y2 x1 x2
|
||||
// 0, 0, 0, 0 |-> 0
|
||||
// 0, 0, 0, 1 |-> 2
|
||||
// 0, 0, 1, 0 |-> 0
|
||||
// 0, 0, 1, 1 |-> 5
|
||||
// 0, 1, 0, 0 |-> 0
|
||||
// 0, 1, 0, 1 |-> 0
|
||||
// 0, 1, 1, 0 |-> 1
|
||||
// 0, 1, 1, 1 |-> 2
|
||||
// 1, 0, 0, 0 |-> 0
|
||||
// 1, 0, 0, 1 |-> 1
|
||||
// 1, 0, 1, 0 |-> 1
|
||||
// 1, 0, 1, 1 |-> 2
|
||||
// 1, 1, 0, 0 |-> 0
|
||||
// 1, 1, 0, 1 |-> 0
|
||||
// 1, 1, 1, 0 |-> 0
|
||||
// 1, 1, 1, 1 |-> 0
|
||||
//
|
||||
let w = merge_polynomials(&[w1, w2, w3])?;
|
||||
// w is [0,2,0,5,0,0,1,2, 0,1,1,2]
|
||||
let w_eval = vec![
|
||||
F::zero(),
|
||||
F::from(2u64),
|
||||
F::zero(),
|
||||
F::from(5u64),
|
||||
F::zero(),
|
||||
F::zero(),
|
||||
F::from(1u64),
|
||||
F::from(2u64),
|
||||
F::zero(),
|
||||
F::one(),
|
||||
F::from(1u64),
|
||||
F::from(2u64),
|
||||
F::zero(),
|
||||
F::zero(),
|
||||
F::zero(),
|
||||
F::zero(),
|
||||
];
|
||||
let w_rec = DenseMultilinearExtension::from_evaluations_vec(4, w_eval);
|
||||
|
||||
assert_eq!(w, w_rec);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_l() -> Result<(), PCSErrors> {
|
||||
test_build_l_helper::<Fr>()
|
||||
}
|
||||
|
||||
fn test_build_l_helper<F: PrimeField>() -> Result<(), PCSErrors> {
|
||||
// point 1 is [1, 2]
|
||||
let point1 = [Fr::from(1u64), Fr::from(2u64)];
|
||||
|
||||
// point 2 is [3, 4]
|
||||
let point2 = [Fr::from(3u64), Fr::from(4u64)];
|
||||
|
||||
// point 3 is [5, 6]
|
||||
let point3 = [Fr::from(5u64), Fr::from(6u64)];
|
||||
|
||||
{
|
||||
let l = build_l(2, &[&point1, &point2])?;
|
||||
|
||||
// roots: [1, -1]
|
||||
// l0 = -1/2 * x + 1/2
|
||||
// l1 = -x + 2
|
||||
// l2 = -x + 3
|
||||
let l0 = DensePolynomial::from_coefficients_vec(vec![
|
||||
Fr::one() / Fr::from(2u64),
|
||||
-Fr::one() / Fr::from(2u64),
|
||||
]);
|
||||
let l1 = DensePolynomial::from_coefficients_vec(vec![Fr::from(2u64), -Fr::one()]);
|
||||
let l2 = DensePolynomial::from_coefficients_vec(vec![Fr::from(3u64), -Fr::one()]);
|
||||
|
||||
assert_eq!(l0, l[0], "l0 not equal");
|
||||
assert_eq!(l1, l[1], "l1 not equal");
|
||||
assert_eq!(l2, l[2], "l2 not equal");
|
||||
}
|
||||
|
||||
{
|
||||
let l = build_l(2, &[&point1, &point2, &point3])?;
|
||||
|
||||
// sage: q = 52435875175126190479447740508185965837690552500527637822603658699938581184513
|
||||
// sage: P.<x> = PolynomialRing(Zmod(q))
|
||||
// sage: root1 = 1
|
||||
// sage: root2 = 0x8D51CCCE760304D0EC030002760300000001000000000000
|
||||
// sage: root3 = -1
|
||||
// sage: root4 = -root2
|
||||
// Arkwork's code is a bit wired: it also interpolate (root4, 0)
|
||||
// which returns a degree 3 polynomial, instead of degree 2
|
||||
|
||||
// ========================
|
||||
// l0: [0, 0, 1]
|
||||
// ========================
|
||||
// sage: points = [(root1, 0), (root2, 0), (root3, 1), (root4, 0)]
|
||||
// sage: P.lagrange_polynomial(points)
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^3 +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888385*x^2 +
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296128*x +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888385
|
||||
let l0 = DensePolynomial::from_coefficients_vec(vec![
|
||||
field_new!(
|
||||
Fr,
|
||||
"39326906381344642859585805381139474378267914375395728366952744024953935888385"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"13108968793781547619861935127046491459422638125131909455650914674984645296128"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"39326906381344642859585805381139474378267914375395728366952744024953935888385"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"13108968793781547619861935127046491459422638125131909455650914674984645296128"
|
||||
),
|
||||
]);
|
||||
|
||||
// ========================
|
||||
// l1: [0, 1, 0]
|
||||
// ========================
|
||||
// sage: points = [(root1, 0), (root2, 1), (root3, 0), (root4, 0)]
|
||||
// sage: P.lagrange_polynomial(points)
|
||||
// 866286206518413079694067382671935694567563117191340490752*x^3 +
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^2 +
|
||||
// 52435875175126190478581454301667552757996485117855702128036095582747240693761*x +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888385
|
||||
let l1 = DensePolynomial::from_coefficients_vec(vec![
|
||||
field_new!(
|
||||
Fr,
|
||||
"39326906381344642859585805381139474378267914375395728366952744024953935888385"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"52435875175126190478581454301667552757996485117855702128036095582747240693761"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"13108968793781547619861935127046491459422638125131909455650914674984645296128"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"866286206518413079694067382671935694567563117191340490752"
|
||||
),
|
||||
]);
|
||||
|
||||
// ========================
|
||||
// l2: [1, 3, 5]
|
||||
// ========================
|
||||
// sage: points = [(root1, 1), (root2, 3), (root3, 5), (root4, 0)]
|
||||
// sage: P.lagrange_polynomial(points)
|
||||
// 2598858619555239239082202148015807083702689351574021472255*x^3 +
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296129*x^2 +
|
||||
// 52435875175126190476848881888630726598608350352511830738900969348364559712256*x +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888387
|
||||
let l2 = DensePolynomial::from_coefficients_vec(vec![
|
||||
field_new!(
|
||||
Fr,
|
||||
"39326906381344642859585805381139474378267914375395728366952744024953935888387"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"52435875175126190476848881888630726598608350352511830738900969348364559712256"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"13108968793781547619861935127046491459422638125131909455650914674984645296129"
|
||||
),
|
||||
field_new!(
|
||||
Fr,
|
||||
"2598858619555239239082202148015807083702689351574021472255"
|
||||
),
|
||||
]);
|
||||
|
||||
// ========================
|
||||
// l3: [2, 4, 6]
|
||||
// ========================
|
||||
// sage: points = [(root1, 2), (root2, 4), (root3, 6), (root4, 0)]
|
||||
// sage: P.lagrange_polynomial(points)
|
||||
// 3465144826073652318776269530687742778270252468765361963007*x^3 +
|
||||
// x^2 +
|
||||
// 52435875175126190475982595682112313518914282969839895044333406231173219221504*x +
|
||||
// 3
|
||||
let l3 = DensePolynomial::from_coefficients_vec(vec![
|
||||
Fr::from(3u64),
|
||||
field_new!(
|
||||
Fr,
|
||||
"52435875175126190475982595682112313518914282969839895044333406231173219221504"
|
||||
),
|
||||
Fr::one(),
|
||||
field_new!(
|
||||
Fr,
|
||||
"3465144826073652318776269530687742778270252468765361963007"
|
||||
),
|
||||
]);
|
||||
|
||||
assert_eq!(l0, l[0], "l0 not equal");
|
||||
assert_eq!(l1, l[1], "l1 not equal");
|
||||
assert_eq!(l2, l[2], "l2 not equal");
|
||||
assert_eq!(l3, l[3], "l3 not equal");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_qx() -> Result<(), PCSErrors> {
|
||||
// Example from page 53:
|
||||
// W1 = 3x1x2 + 2x2
|
||||
let w_eval = vec![Fr::zero(), Fr::from(2u64), Fr::zero(), Fr::from(5u64)];
|
||||
let w1 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
// W2 = x1x2 + x1
|
||||
let w_eval = vec![Fr::zero(), Fr::zero(), Fr::from(1u64), Fr::from(2u64)];
|
||||
let w2 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
// W3 = x1 + x2
|
||||
let w_eval = vec![Fr::zero(), Fr::one(), Fr::from(1u64), Fr::from(2u64)];
|
||||
let w3 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval);
|
||||
|
||||
let r = Fr::from(42u64);
|
||||
|
||||
// point 1 is [1, 2]
|
||||
let point1 = [Fr::from(1u64), Fr::from(2u64)];
|
||||
|
||||
// point 2 is [3, 4]
|
||||
let point2 = [Fr::from(3u64), Fr::from(4u64)];
|
||||
|
||||
// point 3 is [5, 6]
|
||||
let point3 = [Fr::from(5u64), Fr::from(6u64)];
|
||||
|
||||
{
|
||||
// w = (3x1x2 + 2x2)(1-x0) + (x1x2 + x1)x0
|
||||
// with evaluations: [0,2,0,5,0,0,1,2]
|
||||
let w = merge_polynomials(&[w1.clone(), w2.clone()])?;
|
||||
|
||||
let l = build_l(2, &[&point1, &point2])?;
|
||||
|
||||
// sage: P.<x> = PolynomialRing(ZZ)
|
||||
// sage: l0 = -1/2 * x + 1/2
|
||||
// sage: l1 = -x + 2
|
||||
// sage: l2 = -x + 3
|
||||
// sage: w = (3 * l1 * l2 + 2 * l2) * (1-l0) + (l1 * l2 + l1) * l0
|
||||
// sage: w
|
||||
// x^3 - 7/2*x^2 - 7/2*x + 16
|
||||
//
|
||||
// q(x) = x^3 - 7/2*x^2 - 7/2*x + 16
|
||||
let q_x = compute_w_circ_l(&w, &l)?;
|
||||
|
||||
let point: Vec<Fr> = l.iter().rev().map(|poly| poly.evaluate(&r)).collect();
|
||||
|
||||
assert_eq!(
|
||||
q_x.evaluate(&r),
|
||||
w.evaluate(&point).unwrap(),
|
||||
"q(r) != w(l(r))"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// W = (3x1x2 + 2x2) * (1-y1) * (1-y2)
|
||||
// + (x1x2 + x1) * (1-y1) * y2
|
||||
// + (x1 + x2) * y1 * (1-y2)
|
||||
let w = merge_polynomials(&[w1, w2, w3])?;
|
||||
|
||||
let l = build_l(2, &[&point1, &point2, &point3])?;
|
||||
|
||||
// l0 =
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^3 +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888385*x^2 +
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296128*x +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888385
|
||||
//
|
||||
// l1 =
|
||||
// 866286206518413079694067382671935694567563117191340490752*x^3 +
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^2 +
|
||||
// 52435875175126190478581454301667552757996485117855702128036095582747240693761*x +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888385
|
||||
//
|
||||
// l2 =
|
||||
// 2598858619555239239082202148015807083702689351574021472255*x^3 +
|
||||
// 13108968793781547619861935127046491459422638125131909455650914674984645296129*x^2 +
|
||||
// 52435875175126190476848881888630726598608350352511830738900969348364559712256*x +
|
||||
// 39326906381344642859585805381139474378267914375395728366952744024953935888387
|
||||
//
|
||||
// l3 =
|
||||
// 3465144826073652318776269530687742778270252468765361963007*x^3 +
|
||||
// x^2 +
|
||||
// 52435875175126190475982595682112313518914282969839895044333406231173219221504*x +
|
||||
// 3
|
||||
//
|
||||
// q_x = (3*l2*l3 + 2*l3) * (1-l0) *(1-l1)
|
||||
// + (l2*l3+l2)*(1-l0)*l1
|
||||
// + (l2+l3)*l0*(1-l1)
|
||||
// q_x(42) = 42675783400755005965526147011103024780845819057955866345013183657072368533932
|
||||
let q_x = compute_w_circ_l(&w, &l)?;
|
||||
|
||||
let point: Vec<Fr> = vec![
|
||||
l[3].evaluate(&r),
|
||||
l[2].evaluate(&r),
|
||||
l[1].evaluate(&r),
|
||||
l[0].evaluate(&r),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
q_x.evaluate(&r),
|
||||
field_new!(
|
||||
Fr,
|
||||
"42675783400755005965526147011103024780845819057955866345013183657072368533932"
|
||||
),
|
||||
);
|
||||
assert_eq!(
|
||||
q_x.evaluate(&r),
|
||||
w.evaluate(&point).unwrap(),
|
||||
"q(r) != w(l(r))"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ pub use perm_check::{
|
||||
PermutationCheck,
|
||||
};
|
||||
pub use sum_check::SumCheck;
|
||||
pub use transcript::IOPTranscript;
|
||||
pub use utils::*;
|
||||
pub use virtual_poly::{VPAuxInfo, VirtualPolynomial};
|
||||
pub use zero_check::ZeroCheck;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct IOPTranscript<F: PrimeField> {
|
||||
|
||||
impl<F: PrimeField> IOPTranscript<F> {
|
||||
/// Create a new IOP transcript.
|
||||
pub(crate) fn new(label: &'static [u8]) -> Self {
|
||||
pub fn new(label: &'static [u8]) -> Self {
|
||||
Self {
|
||||
transcript: Transcript::new(label),
|
||||
is_empty: true,
|
||||
@@ -59,7 +59,7 @@ impl<F: PrimeField> IOPTranscript<F> {
|
||||
}
|
||||
|
||||
// Append the message to the transcript.
|
||||
pub(crate) fn append_field_element(
|
||||
pub fn append_field_element(
|
||||
&mut self,
|
||||
label: &'static [u8],
|
||||
field_elem: &F,
|
||||
@@ -83,10 +83,7 @@ impl<F: PrimeField> IOPTranscript<F> {
|
||||
//
|
||||
// The output field element is statistical uniform as long
|
||||
// as the field has a size less than 2^384.
|
||||
pub(crate) fn get_and_append_challenge(
|
||||
&mut self,
|
||||
label: &'static [u8],
|
||||
) -> Result<F, PolyIOPErrors> {
|
||||
pub fn get_and_append_challenge(&mut self, label: &'static [u8]) -> Result<F, PolyIOPErrors> {
|
||||
// we need to reject when transcript is empty
|
||||
if self.is_empty {
|
||||
return Err(PolyIOPErrors::InvalidTranscript(
|
||||
|
||||
@@ -13,7 +13,7 @@ macro_rules! to_bytes {
|
||||
|
||||
/// Decompose an integer into a binary vector in little endian.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
||||
pub fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
||||
let mut res = Vec::with_capacity(num_var);
|
||||
let mut i = input;
|
||||
for _ in 0..num_var {
|
||||
|
||||
Reference in New Issue
Block a user