@ -1,40 +0,0 @@ |
|||||
[package] |
|
||||
name = "pcs" |
|
||||
version = "0.1.0" |
|
||||
edition = "2021" |
|
||||
|
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
|
||||
|
|
||||
[dependencies] |
|
||||
|
|
||||
ark-std = { version = "^0.3.0", default-features = false } |
|
||||
ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } |
|
||||
ark-ff = { version = "^0.3.0", default-features = false } |
|
||||
ark-ec = { version = "^0.3.0", default-features = false } |
|
||||
ark-poly = {version = "^0.3.0", default-features = false } |
|
||||
ark-sponge = {version = "^0.3.0", default-features = false} |
|
||||
ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } |
|
||||
|
|
||||
displaydoc = { version = "0.2.3", default-features = false } |
|
||||
derivative = { version = "2", features = ["use_core"] } |
|
||||
|
|
||||
transcript = { path = "../transcript" } |
|
||||
|
|
||||
# Benchmarks |
|
||||
[[bench]] |
|
||||
name = "pcs-benches" |
|
||||
path = "benches/bench.rs" |
|
||||
harness = false |
|
||||
|
|
||||
[features] |
|
||||
# default = [ "parallel", "print-trace" ] |
|
||||
default = [ "parallel" ] |
|
||||
parallel = [ |
|
||||
"ark-std/parallel", |
|
||||
"ark-ff/parallel", |
|
||||
"ark-poly/parallel", |
|
||||
"ark-ec/parallel", |
|
||||
] |
|
||||
print-trace = [ |
|
||||
"ark-std/print-trace", |
|
||||
] |
|
@ -1,88 +0,0 @@ |
|||||
use ark_bls12_381::{Bls12_381, Fr};
|
|
||||
use ark_ff::UniformRand;
|
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
|
||||
use ark_std::{rc::Rc, test_rng};
|
|
||||
use pcs::{
|
|
||||
prelude::{KZGMultilinearPCS, PCSErrors, PolynomialCommitmentScheme},
|
|
||||
StructuredReferenceString,
|
|
||||
};
|
|
||||
use std::time::Instant;
|
|
||||
|
|
||||
fn main() -> Result<(), PCSErrors> {
|
|
||||
bench_pcs()
|
|
||||
}
|
|
||||
|
|
||||
fn bench_pcs() -> Result<(), PCSErrors> {
|
|
||||
let mut rng = test_rng();
|
|
||||
|
|
||||
// normal polynomials
|
|
||||
let uni_params = KZGMultilinearPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 18)?;
|
|
||||
|
|
||||
for nv in 4..19 {
|
|
||||
let repetition = if nv < 10 {
|
|
||||
100
|
|
||||
} else if nv < 20 {
|
|
||||
50
|
|
||||
} else {
|
|
||||
10
|
|
||||
};
|
|
||||
|
|
||||
let poly = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
|
|
||||
let (ml_ck, ml_vk) = uni_params.0.trim(nv)?;
|
|
||||
let (uni_ck, uni_vk) = uni_params.1.trim(nv)?;
|
|
||||
let ck = (ml_ck, uni_ck);
|
|
||||
let vk = (ml_vk, uni_vk);
|
|
||||
|
|
||||
let point: Vec<_> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
|
||||
|
|
||||
// commit
|
|
||||
let com = {
|
|
||||
let start = Instant::now();
|
|
||||
for _ in 0..repetition {
|
|
||||
let _commit = KZGMultilinearPCS::commit(&ck, &poly)?;
|
|
||||
}
|
|
||||
|
|
||||
println!(
|
|
||||
"KZG commit for {} variables: {} ns",
|
|
||||
nv,
|
|
||||
start.elapsed().as_nanos() / repetition as u128
|
|
||||
);
|
|
||||
|
|
||||
KZGMultilinearPCS::commit(&ck, &poly)?
|
|
||||
};
|
|
||||
|
|
||||
// open
|
|
||||
let (proof, value) = {
|
|
||||
let start = Instant::now();
|
|
||||
for _ in 0..repetition {
|
|
||||
let _open = KZGMultilinearPCS::open(&ck, &poly, &point)?;
|
|
||||
}
|
|
||||
|
|
||||
println!(
|
|
||||
"KZG open for {} variables: {} ns",
|
|
||||
nv,
|
|
||||
start.elapsed().as_nanos() / repetition as u128
|
|
||||
);
|
|
||||
KZGMultilinearPCS::open(&ck, &poly, &point)?
|
|
||||
};
|
|
||||
|
|
||||
// verify
|
|
||||
{
|
|
||||
let start = Instant::now();
|
|
||||
for _ in 0..repetition {
|
|
||||
assert!(KZGMultilinearPCS::verify(
|
|
||||
&vk, &com, &point, &value, &proof
|
|
||||
)?);
|
|
||||
}
|
|
||||
println!(
|
|
||||
"KZG verify for {} variables: {} ns",
|
|
||||
nv,
|
|
||||
start.elapsed().as_nanos() / repetition as u128
|
|
||||
);
|
|
||||
}
|
|
||||
|
|
||||
println!("====================================");
|
|
||||
}
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
@ -1,6 +0,0 @@ |
|||||
KZG based multilinear polynomial commitment |
|
||||
----- |
|
||||
|
|
||||
# Compiling features: |
|
||||
- `parallel`: use multi-threading when possible. |
|
||||
- `print-trace`: print out user friendly information about the running time for each micro component. |
|
@ -1,35 +0,0 @@ |
|||||
//! Error module.
|
|
||||
|
|
||||
use ark_serialize::SerializationError;
|
|
||||
use ark_std::string::String;
|
|
||||
use displaydoc::Display;
|
|
||||
use transcript::TranscriptErrors;
|
|
||||
|
|
||||
/// A `enum` specifying the possible failure modes of the PCS.
|
|
||||
#[derive(Display, Debug)]
|
|
||||
pub enum PCSErrors {
|
|
||||
/// Invalid Prover: {0}
|
|
||||
InvalidProver(String),
|
|
||||
/// Invalid Verifier: {0}
|
|
||||
InvalidVerifier(String),
|
|
||||
/// Invalid Proof: {0}
|
|
||||
InvalidProof(String),
|
|
||||
/// Invalid parameters: {0}
|
|
||||
InvalidParameters(String),
|
|
||||
/// An error during (de)serialization: {0}
|
|
||||
SerializationErrors(SerializationError),
|
|
||||
/// Transcript error {0}
|
|
||||
TranscriptErrors(TranscriptErrors),
|
|
||||
}
|
|
||||
|
|
||||
impl From<SerializationError> for PCSErrors {
|
|
||||
fn from(e: ark_serialize::SerializationError) -> Self {
|
|
||||
Self::SerializationErrors(e)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl From<TranscriptErrors> for PCSErrors {
|
|
||||
fn from(e: TranscriptErrors) -> Self {
|
|
||||
Self::TranscriptErrors(e)
|
|
||||
}
|
|
||||
}
|
|
@ -1,135 +0,0 @@ |
|||||
mod errors;
|
|
||||
mod multilinear_kzg;
|
|
||||
mod structs;
|
|
||||
mod univariate_kzg;
|
|
||||
|
|
||||
pub mod prelude;
|
|
||||
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_serialize::CanonicalSerialize;
|
|
||||
use ark_std::rand::RngCore;
|
|
||||
use errors::PCSErrors;
|
|
||||
|
|
||||
/// This trait defines APIs for polynomial commitment schemes.
|
|
||||
/// Note that for our usage of PCS, we do not require the hiding property.
|
|
||||
pub trait PolynomialCommitmentScheme<E: PairingEngine> {
|
|
||||
// Parameters
|
|
||||
type ProverParam;
|
|
||||
type VerifierParam;
|
|
||||
type SRS;
|
|
||||
// Polynomial and its associated types
|
|
||||
type Polynomial;
|
|
||||
type Point;
|
|
||||
type Evaluation;
|
|
||||
// Commitments and proofs
|
|
||||
type Commitment: CanonicalSerialize;
|
|
||||
type BatchCommitment: CanonicalSerialize;
|
|
||||
type Proof;
|
|
||||
type BatchProof;
|
|
||||
|
|
||||
/// Build SRS for testing.
|
|
||||
///
|
|
||||
/// - For univariate polynomials, `log_size` is the log of maximum degree.
|
|
||||
/// - For multilinear polynomials, `log_size` is the number of variables.
|
|
||||
///
|
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
|
||||
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
|
|
||||
fn gen_srs_for_testing<R: RngCore>(
|
|
||||
rng: &mut R,
|
|
||||
log_size: usize,
|
|
||||
) -> Result<Self::SRS, PCSErrors>;
|
|
||||
|
|
||||
/// Trim the universal parameters to specialize the public parameters.
|
|
||||
/// Input both `supported_log_degree` for univariate and
|
|
||||
/// `supported_num_vars` for multilinear.
|
|
||||
fn trim(
|
|
||||
srs: &Self::SRS,
|
|
||||
supported_log_degree: usize,
|
|
||||
supported_num_vars: Option<usize>,
|
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors>;
|
|
||||
|
|
||||
/// Generate a commitment for a polynomial
|
|
||||
fn commit(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
poly: &Self::Polynomial,
|
|
||||
) -> Result<Self::Commitment, PCSErrors>;
|
|
||||
|
|
||||
/// Generate a commitment for a list of polynomials
|
|
||||
fn multi_commit(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
polys: &[Self::Polynomial],
|
|
||||
) -> Result<Self::BatchCommitment, PCSErrors>;
|
|
||||
|
|
||||
/// On input a polynomial `p` and a point `point`, outputs a proof for the
|
|
||||
/// same.
|
|
||||
fn open(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
polynomial: &Self::Polynomial,
|
|
||||
point: &Self::Point,
|
|
||||
) -> Result<(Self::Proof, Self::Evaluation), PCSErrors>;
|
|
||||
|
|
||||
/// Input a list of MLEs, and a same number of points, and a transcript,
|
|
||||
/// compute a multi-opening for all the polynomials.
|
|
||||
fn multi_open(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
multi_commitment: &Self::Commitment,
|
|
||||
polynomials: &[Self::Polynomial],
|
|
||||
points: &[Self::Point],
|
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), 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: &Self::Point,
|
|
||||
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<R: RngCore>(
|
|
||||
verifier_param: &Self::VerifierParam,
|
|
||||
multi_commitment: &Self::BatchCommitment,
|
|
||||
points: &[Self::Point],
|
|
||||
values: &[E::Fr],
|
|
||||
batch_proof: &Self::BatchProof,
|
|
||||
rng: &mut R,
|
|
||||
) -> Result<bool, PCSErrors>;
|
|
||||
}
|
|
||||
|
|
||||
/// API definitions for structured reference string
|
|
||||
pub trait StructuredReferenceString<E: PairingEngine>: Sized {
|
|
||||
type ProverParam;
|
|
||||
type VerifierParam;
|
|
||||
|
|
||||
/// Extract the prover parameters from the public parameters.
|
|
||||
fn extract_prover_param(&self, supported_log_size: usize) -> Self::ProverParam;
|
|
||||
/// Extract the verifier parameters from the public parameters.
|
|
||||
fn extract_verifier_param(&self, supported_log_size: usize) -> Self::VerifierParam;
|
|
||||
|
|
||||
/// Trim the universal parameters to specialize the public parameters
|
|
||||
/// for polynomials to the given `supported_log_size`, and
|
|
||||
/// returns committer key and verifier key.
|
|
||||
///
|
|
||||
/// - For univariate polynomials, `supported_log_size` is the log of maximum
|
|
||||
/// degree.
|
|
||||
/// - For multilinear polynomials, `supported_log_size` is the number of
|
|
||||
/// variables.
|
|
||||
///
|
|
||||
/// `supported_log_size` should be in range `1..=params.log_size`
|
|
||||
fn trim(
|
|
||||
&self,
|
|
||||
supported_log_size: usize,
|
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors>;
|
|
||||
|
|
||||
/// Build SRS for testing.
|
|
||||
///
|
|
||||
/// - For univariate polynomials, `log_size` is the log of maximum degree.
|
|
||||
/// - For multilinear polynomials, `log_size` is the number of variables.
|
|
||||
///
|
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
|
||||
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
|
|
||||
fn gen_srs_for_testing<R: RngCore>(rng: &mut R, log_size: usize) -> Result<Self, PCSErrors>;
|
|
||||
}
|
|
@ -1,433 +0,0 @@ |
|||||
use super::{
|
|
||||
open_internal,
|
|
||||
srs::{MultilinearProverParam, MultilinearVerifierParam},
|
|
||||
util::{build_l, compute_w_circ_l, merge_polynomials},
|
|
||||
verify_internal, BatchProof,
|
|
||||
};
|
|
||||
use crate::{
|
|
||||
multilinear_kzg::util::get_uni_domain,
|
|
||||
prelude::{Commitment, UnivariateProverParam, UnivariateVerifierParam},
|
|
||||
univariate_kzg::KZGUnivariatePCS,
|
|
||||
PCSErrors, PolynomialCommitmentScheme,
|
|
||||
};
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_poly::{DenseMultilinearExtension, EvaluationDomain, MultilinearExtension, Polynomial};
|
|
||||
use ark_std::{end_timer, rc::Rc, start_timer, vec::Vec};
|
|
||||
use transcript::IOPTranscript;
|
|
||||
|
|
||||
/// Input
|
|
||||
/// - the prover parameters for univariate KZG,
|
|
||||
/// - the prover parameters for multilinear KZG,
|
|
||||
/// - a list of MLEs,
|
|
||||
/// - a commitment to all MLEs
|
|
||||
/// - and a same number of points,
|
|
||||
/// 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, consists of
|
|
||||
/// - the multilinear KZG opening
|
|
||||
/// - the univariate KZG commitment to q(x)
|
|
||||
/// - the openings and evaluations of q(x) at omega^i and r
|
|
||||
///
|
|
||||
/// 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. commit to q(x) and sample r from transcript
|
|
||||
/// transcript contains: w commitment, points, q(x)'s commitment
|
|
||||
/// 5. build q(omega^i) and their openings
|
|
||||
/// 6. build q(r) and its opening
|
|
||||
/// 7. get a point `p := l(r)`
|
|
||||
/// 8. output an opening of `w` over point `p`
|
|
||||
/// 9. output `w(p)`
|
|
||||
pub(super) fn multi_open_internal<E: PairingEngine>(
|
|
||||
uni_prover_param: &UnivariateProverParam<E::G1Affine>,
|
|
||||
ml_prover_param: &MultilinearProverParam<E>,
|
|
||||
polynomials: &[Rc<DenseMultilinearExtension<E::Fr>>],
|
|
||||
multi_commitment: &Commitment<E>,
|
|
||||
points: &[Vec<E::Fr>],
|
|
||||
) -> Result<(BatchProof<E>, Vec<E::Fr>), PCSErrors> {
|
|
||||
let open_timer = start_timer!(|| "multi open");
|
|
||||
|
|
||||
// ===================================
|
|
||||
// Sanity checks on inputs
|
|
||||
// ===================================
|
|
||||
let points_len = points.len();
|
|
||||
if points_len == 0 {
|
|
||||
return Err(PCSErrors::InvalidParameters("points is empty".to_string()));
|
|
||||
}
|
|
||||
|
|
||||
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(),
|
|
||||
));
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
let domain = get_uni_domain::<E::Fr>(points_len)?;
|
|
||||
|
|
||||
// 1. build `l(points)` which is a list of univariate polynomials that goes
|
|
||||
// through the points
|
|
||||
let uni_polys = build_l(num_var, points, &domain)?;
|
|
||||
|
|
||||
// 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. commit to q(x) and sample r from transcript
|
|
||||
// transcript contains: w commitment, points, q(x)'s commitment
|
|
||||
let mut transcript = IOPTranscript::new(b"ml kzg");
|
|
||||
transcript.append_serializable_element(b"w", multi_commitment)?;
|
|
||||
for point in points {
|
|
||||
transcript.append_serializable_element(b"w", point)?;
|
|
||||
}
|
|
||||
|
|
||||
let q_x_commit = KZGUnivariatePCS::<E>::commit(uni_prover_param, &q_x)?;
|
|
||||
transcript.append_serializable_element(b"q(x)", &q_x_commit)?;
|
|
||||
let r = transcript.get_and_append_challenge(b"r")?;
|
|
||||
|
|
||||
// 5. build q(omega^i) and their openings
|
|
||||
let mut q_x_opens = vec![];
|
|
||||
let mut q_x_evals = vec![];
|
|
||||
for i in 0..points_len {
|
|
||||
let (q_x_open, q_x_eval) =
|
|
||||
KZGUnivariatePCS::<E>::open(uni_prover_param, &q_x, &domain.element(i))?;
|
|
||||
q_x_opens.push(q_x_open);
|
|
||||
q_x_evals.push(q_x_eval);
|
|
||||
|
|
||||
// sanity check
|
|
||||
let point: Vec<E::Fr> = uni_polys
|
|
||||
.iter()
|
|
||||
.rev()
|
|
||||
.map(|poly| poly.evaluate(&domain.element(i)))
|
|
||||
.collect();
|
|
||||
let mle_eval = merge_poly.evaluate(&point).unwrap();
|
|
||||
if mle_eval != q_x_eval {
|
|
||||
return Err(PCSErrors::InvalidProver(
|
|
||||
"Q(omega) does not match W(l(omega))".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
// 6. build q(r) and its opening
|
|
||||
let (q_x_open, q_r_value) = KZGUnivariatePCS::<E>::open(uni_prover_param, &q_x, &r)?;
|
|
||||
q_x_opens.push(q_x_open);
|
|
||||
q_x_evals.push(q_r_value);
|
|
||||
|
|
||||
// 7. get a point `p := l(r)`
|
|
||||
let point: Vec<E::Fr> = uni_polys
|
|
||||
.iter()
|
|
||||
.rev()
|
|
||||
.map(|poly| poly.evaluate(&r))
|
|
||||
.collect();
|
|
||||
|
|
||||
// 8. output an opening of `w` over point `p`
|
|
||||
let (mle_opening, mle_eval) = open_internal(ml_prover_param, &merge_poly, &point)?;
|
|
||||
|
|
||||
// 9. output value that is `w` evaluated at `p` (which should match `q(r)`)
|
|
||||
if mle_eval != q_r_value {
|
|
||||
return Err(PCSErrors::InvalidProver(
|
|
||||
"Q(r) does not match W(l(r))".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
end_timer!(open_timer);
|
|
||||
|
|
||||
Ok((
|
|
||||
BatchProof {
|
|
||||
proof: mle_opening,
|
|
||||
q_x_commit,
|
|
||||
q_x_opens,
|
|
||||
},
|
|
||||
q_x_evals,
|
|
||||
))
|
|
||||
}
|
|
||||
|
|
||||
/// Verifies that the `multi_commitment` is a valid commitment
|
|
||||
/// to a list of MLEs for the given openings and evaluations in
|
|
||||
/// the batch_proof.
|
|
||||
///
|
|
||||
/// steps:
|
|
||||
///
|
|
||||
/// 1. push w, points and q_com into transcript
|
|
||||
/// 2. sample `r` from transcript
|
|
||||
/// 3. check `q(r) == batch_proof.q_x_value.last` and
|
|
||||
/// `q(omega^i) == batch_proof.q_x_value[i]`
|
|
||||
/// 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 valid against multilinear KZG proof
|
|
||||
pub(super) fn batch_verify_internal<E: PairingEngine>(
|
|
||||
uni_verifier_param: &UnivariateVerifierParam<E>,
|
|
||||
ml_verifier_param: &MultilinearVerifierParam<E>,
|
|
||||
multi_commitment: &Commitment<E>,
|
|
||||
points: &[Vec<E::Fr>],
|
|
||||
values: &[E::Fr],
|
|
||||
batch_proof: &BatchProof<E>,
|
|
||||
) -> Result<bool, PCSErrors> {
|
|
||||
let verify_timer = start_timer!(|| "batch verify");
|
|
||||
|
|
||||
// ===================================
|
|
||||
// Sanity checks on inputs
|
|
||||
// ===================================
|
|
||||
let points_len = points.len();
|
|
||||
if points_len == 0 {
|
|
||||
return Err(PCSErrors::InvalidParameters("points is empty".to_string()));
|
|
||||
}
|
|
||||
|
|
||||
// add one here because we also have q(r) and its opening
|
|
||||
if points_len + 1 != batch_proof.q_x_opens.len() {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"openings length does not match point length".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
|
|
||||
if points_len + 1 != values.len() {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"values length does not match point length".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
|
|
||||
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,
|
|
||||
)));
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
let domain = get_uni_domain::<E::Fr>(points_len)?;
|
|
||||
|
|
||||
// 1. push w, points and q_com into transcript
|
|
||||
let mut transcript = IOPTranscript::new(b"ml kzg");
|
|
||||
transcript.append_serializable_element(b"w", multi_commitment)?;
|
|
||||
for point in points {
|
|
||||
transcript.append_serializable_element(b"w", point)?;
|
|
||||
}
|
|
||||
|
|
||||
transcript.append_serializable_element(b"q(x)", &batch_proof.q_x_commit)?;
|
|
||||
|
|
||||
// 2. sample `r` from transcript
|
|
||||
let r = transcript.get_and_append_challenge(b"r")?;
|
|
||||
|
|
||||
// 3. check `q(r) == batch_proof.q_x_value.last` and `q(omega^i) =
|
|
||||
// batch_proof.q_x_value[i]`
|
|
||||
for (i, value) in values.iter().enumerate().take(points_len) {
|
|
||||
if !KZGUnivariatePCS::verify(
|
|
||||
uni_verifier_param,
|
|
||||
&batch_proof.q_x_commit,
|
|
||||
&domain.element(i),
|
|
||||
value,
|
|
||||
&batch_proof.q_x_opens[i],
|
|
||||
)? {
|
|
||||
#[cfg(debug_assertion)]
|
|
||||
println!("q(omega^{}) verification failed", i);
|
|
||||
return Ok(false);
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
if !KZGUnivariatePCS::verify(
|
|
||||
uni_verifier_param,
|
|
||||
&batch_proof.q_x_commit,
|
|
||||
&r,
|
|
||||
&values[points_len],
|
|
||||
&batch_proof.q_x_opens[points_len],
|
|
||||
)? {
|
|
||||
#[cfg(debug_assertion)]
|
|
||||
println!("q(r) verification failed");
|
|
||||
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, &domain)?;
|
|
||||
|
|
||||
// 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 valid against multilinear KZG proof
|
|
||||
let res = verify_internal(
|
|
||||
ml_verifier_param,
|
|
||||
multi_commitment,
|
|
||||
&point,
|
|
||||
&values[points_len],
|
|
||||
&batch_proof.proof,
|
|
||||
)?;
|
|
||||
|
|
||||
#[cfg(debug_assertion)]
|
|
||||
if !res {
|
|
||||
println!("multilinear KZG verification failed");
|
|
||||
}
|
|
||||
|
|
||||
end_timer!(verify_timer);
|
|
||||
|
|
||||
Ok(res)
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod tests {
|
|
||||
use super::{
|
|
||||
super::{util::get_batched_nv, *},
|
|
||||
*,
|
|
||||
};
|
|
||||
use crate::{
|
|
||||
multilinear_kzg::util::{compute_qx_degree, generate_evaluations},
|
|
||||
prelude::UnivariateUniversalParams,
|
|
||||
StructuredReferenceString,
|
|
||||
};
|
|
||||
use ark_bls12_381::Bls12_381 as E;
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
|
||||
use ark_std::{log2, rand::RngCore, test_rng, vec::Vec, UniformRand};
|
|
||||
type Fr = <E as PairingEngine>::Fr;
|
|
||||
|
|
||||
fn test_multi_commit_helper<R: RngCore>(
|
|
||||
uni_params: &UnivariateUniversalParams<E>,
|
|
||||
ml_params: &MultilinearUniversalParams<E>,
|
|
||||
polys: &[Rc<DenseMultilinearExtension<Fr>>],
|
|
||||
rng: &mut R,
|
|
||||
) -> Result<(), PCSErrors> {
|
|
||||
let merged_nv = get_batched_nv(polys[0].num_vars(), polys.len());
|
|
||||
let qx_degree = compute_qx_degree(merged_nv, polys.len());
|
|
||||
let log_qx_degree = log2(qx_degree) as usize;
|
|
||||
|
|
||||
let (uni_ck, uni_vk) = uni_params.trim(log_qx_degree)?;
|
|
||||
let (ml_ck, ml_vk) = ml_params.trim(merged_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 evals = generate_evaluations(polys, &points)?;
|
|
||||
|
|
||||
let com = KZGMultilinearPCS::multi_commit(&(ml_ck.clone(), uni_ck.clone()), polys)?;
|
|
||||
let (batch_proof, evaluations) =
|
|
||||
multi_open_internal(&uni_ck, &ml_ck, polys, &com, &points)?;
|
|
||||
|
|
||||
for (a, b) in evals.iter().zip(evaluations.iter()) {
|
|
||||
assert_eq!(a, b)
|
|
||||
}
|
|
||||
|
|
||||
// good path
|
|
||||
assert!(batch_verify_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&com,
|
|
||||
&points,
|
|
||||
&evaluations,
|
|
||||
&batch_proof,
|
|
||||
)?);
|
|
||||
|
|
||||
// bad commitment
|
|
||||
assert!(!batch_verify_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&Commitment {
|
|
||||
commitment: <E as PairingEngine>::G1Affine::default()
|
|
||||
},
|
|
||||
&points,
|
|
||||
&evaluations,
|
|
||||
&batch_proof,
|
|
||||
)?);
|
|
||||
|
|
||||
// bad points
|
|
||||
assert!(
|
|
||||
batch_verify_internal(&uni_vk, &ml_vk, &com, &points[1..], &[], &batch_proof,).is_err()
|
|
||||
);
|
|
||||
|
|
||||
// bad proof
|
|
||||
assert!(batch_verify_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&com,
|
|
||||
&points,
|
|
||||
&evaluations,
|
|
||||
&BatchProof {
|
|
||||
proof: Proof { proofs: Vec::new() },
|
|
||||
q_x_commit: Commitment {
|
|
||||
commitment: <E as PairingEngine>::G1Affine::default()
|
|
||||
},
|
|
||||
q_x_opens: vec![],
|
|
||||
},
|
|
||||
)
|
|
||||
.is_err());
|
|
||||
|
|
||||
// bad value
|
|
||||
let mut wrong_evals = evaluations.clone();
|
|
||||
wrong_evals[0] = Fr::default();
|
|
||||
assert!(!batch_verify_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&com,
|
|
||||
&points,
|
|
||||
&wrong_evals,
|
|
||||
&batch_proof
|
|
||||
)?);
|
|
||||
|
|
||||
// bad q(x) commit
|
|
||||
let mut wrong_proof = batch_proof.clone();
|
|
||||
wrong_proof.q_x_commit = Commitment {
|
|
||||
commitment: <E as PairingEngine>::G1Affine::default(),
|
|
||||
};
|
|
||||
assert!(!batch_verify_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&com,
|
|
||||
&points,
|
|
||||
&evaluations,
|
|
||||
&wrong_proof,
|
|
||||
)?);
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_multi_commit_internal() -> Result<(), PCSErrors> {
|
|
||||
let mut rng = test_rng();
|
|
||||
|
|
||||
let uni_params = UnivariateUniversalParams::<E>::gen_srs_for_testing(&mut rng, 15)?;
|
|
||||
let ml_params = MultilinearUniversalParams::<E>::gen_srs_for_testing(&mut rng, 15)?;
|
|
||||
|
|
||||
// normal polynomials
|
|
||||
let polys1: Vec<_> = (0..5)
|
|
||||
.map(|_| Rc::new(DenseMultilinearExtension::rand(4, &mut rng)))
|
|
||||
.collect();
|
|
||||
test_multi_commit_helper(&uni_params, &ml_params, &polys1, &mut rng)?;
|
|
||||
|
|
||||
// single-variate polynomials
|
|
||||
let polys1: Vec<_> = (0..5)
|
|
||||
.map(|_| Rc::new(DenseMultilinearExtension::rand(1, &mut rng)))
|
|
||||
.collect();
|
|
||||
test_multi_commit_helper(&uni_params, &ml_params, &polys1, &mut rng)?;
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
@ -1,480 +0,0 @@ |
|||||
//! Main module for multilinear KZG commitment scheme
|
|
||||
|
|
||||
mod batching;
|
|
||||
pub(crate) mod srs;
|
|
||||
pub(crate) mod util;
|
|
||||
|
|
||||
use crate::{
|
|
||||
prelude::{
|
|
||||
Commitment, UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam,
|
|
||||
},
|
|
||||
univariate_kzg::KZGUnivariateOpening,
|
|
||||
PCSErrors, PolynomialCommitmentScheme, StructuredReferenceString,
|
|
||||
};
|
|
||||
use ark_ec::{
|
|
||||
msm::{FixedBaseMSM, VariableBaseMSM},
|
|
||||
AffineCurve, PairingEngine, ProjectiveCurve,
|
|
||||
};
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
|
||||
use ark_std::{end_timer, rand::RngCore, rc::Rc, start_timer, vec::Vec, One, Zero};
|
|
||||
use batching::{batch_verify_internal, multi_open_internal};
|
|
||||
use srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam};
|
|
||||
use std::marker::PhantomData;
|
|
||||
use util::merge_polynomials;
|
|
||||
|
|
||||
/// KZG Polynomial Commitment Scheme on multilinear polynomials.
|
|
||||
pub struct KZGMultilinearPCS<E: PairingEngine> {
|
|
||||
#[doc(hidden)]
|
|
||||
phantom: PhantomData<E>,
|
|
||||
}
|
|
||||
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
|
||||
/// proof of opening
|
|
||||
pub struct Proof<E: PairingEngine> {
|
|
||||
/// Evaluation of quotients
|
|
||||
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>,
|
|
||||
/// Commitment to q(x):= w(l(x)) where
|
|
||||
/// - `w` is the merged MLE
|
|
||||
/// - `l` is the list of univariate polys that goes through all points
|
|
||||
pub q_x_commit: Commitment<E>,
|
|
||||
/// openings of q(x) at 1, omega, ..., and r
|
|
||||
pub q_x_opens: Vec<KZGUnivariateOpening<E>>,
|
|
||||
}
|
|
||||
|
|
||||
impl<E: PairingEngine> PolynomialCommitmentScheme<E> for KZGMultilinearPCS<E> {
|
|
||||
// Parameters
|
|
||||
type ProverParam = (
|
|
||||
MultilinearProverParam<E>,
|
|
||||
UnivariateProverParam<E::G1Affine>,
|
|
||||
);
|
|
||||
type VerifierParam = (MultilinearVerifierParam<E>, UnivariateVerifierParam<E>);
|
|
||||
type SRS = (MultilinearUniversalParams<E>, UnivariateUniversalParams<E>);
|
|
||||
// Polynomial and its associated types
|
|
||||
type Polynomial = Rc<DenseMultilinearExtension<E::Fr>>;
|
|
||||
type Point = Vec<E::Fr>;
|
|
||||
type Evaluation = E::Fr;
|
|
||||
// Commitments and proofs
|
|
||||
type Commitment = Commitment<E>;
|
|
||||
type BatchCommitment = Commitment<E>;
|
|
||||
type Proof = Proof<E>;
|
|
||||
type BatchProof = BatchProof<E>;
|
|
||||
|
|
||||
/// Build SRS for testing.
|
|
||||
///
|
|
||||
/// - For univariate polynomials, `log_size` is the log of maximum degree.
|
|
||||
/// - For multilinear polynomials, `log_size` is the number of variables.
|
|
||||
///
|
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
|
||||
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
|
|
||||
fn gen_srs_for_testing<R: RngCore>(
|
|
||||
rng: &mut R,
|
|
||||
log_size: usize,
|
|
||||
) -> Result<Self::SRS, PCSErrors> {
|
|
||||
Ok((
|
|
||||
MultilinearUniversalParams::<E>::gen_srs_for_testing(rng, log_size)?,
|
|
||||
UnivariateUniversalParams::<E>::gen_srs_for_testing(rng, log_size)?,
|
|
||||
))
|
|
||||
}
|
|
||||
|
|
||||
/// Trim the universal parameters to specialize the public parameters.
|
|
||||
/// Input both `supported_log_degree` for univariate and
|
|
||||
/// `supported_num_vars` for multilinear.
|
|
||||
fn trim(
|
|
||||
srs: &Self::SRS,
|
|
||||
supported_log_degree: usize,
|
|
||||
supported_num_vars: Option<usize>,
|
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> {
|
|
||||
let supported_num_vars = match supported_num_vars {
|
|
||||
Some(p) => p,
|
|
||||
None => {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"multilinear should receive a num_var param".to_string(),
|
|
||||
))
|
|
||||
},
|
|
||||
};
|
|
||||
let (uni_ck, uni_vk) = srs.1.trim(supported_log_degree)?;
|
|
||||
let (ml_ck, ml_vk) = srs.0.trim(supported_num_vars)?;
|
|
||||
|
|
||||
Ok(((ml_ck, uni_ck), (ml_vk, uni_vk)))
|
|
||||
}
|
|
||||
|
|
||||
/// Generate a commitment for a polynomial.
|
|
||||
///
|
|
||||
/// This function takes `2^num_vars` number of scalar multiplications over
|
|
||||
/// G1.
|
|
||||
fn commit(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
poly: &Self::Polynomial,
|
|
||||
) -> Result<Self::Commitment, PCSErrors> {
|
|
||||
let commit_timer = start_timer!(|| "commit");
|
|
||||
if prover_param.0.num_vars < poly.num_vars {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"Poly length ({}) exceeds param limit ({})",
|
|
||||
poly.num_vars, prover_param.0.num_vars
|
|
||||
)));
|
|
||||
}
|
|
||||
let ignored = prover_param.0.num_vars - poly.num_vars;
|
|
||||
let scalars: Vec<_> = poly
|
|
||||
.to_evaluations()
|
|
||||
.into_iter()
|
|
||||
.map(|x| x.into_repr())
|
|
||||
.collect();
|
|
||||
let commitment = VariableBaseMSM::multi_scalar_mul(
|
|
||||
&prover_param.0.powers_of_g[ignored].evals,
|
|
||||
scalars.as_slice(),
|
|
||||
)
|
|
||||
.into_affine();
|
|
||||
|
|
||||
end_timer!(commit_timer);
|
|
||||
Ok(Commitment { commitment })
|
|
||||
}
|
|
||||
|
|
||||
/// 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: &[Self::Polynomial],
|
|
||||
) -> 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 commitment = VariableBaseMSM::multi_scalar_mul(
|
|
||||
&prover_param.0.powers_of_g[0].evals,
|
|
||||
scalars.as_slice(),
|
|
||||
)
|
|
||||
.into_affine();
|
|
||||
|
|
||||
end_timer!(commit_timer);
|
|
||||
Ok(Commitment { commitment })
|
|
||||
}
|
|
||||
|
|
||||
/// 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
|
|
||||
/// G1:
|
|
||||
/// - it prodceeds with `num_var` number of rounds,
|
|
||||
/// - at round i, we compute an MSM for `2^{num_var - i + 1}` number of G2
|
|
||||
/// elements.
|
|
||||
fn open(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
polynomial: &Self::Polynomial,
|
|
||||
point: &Self::Point,
|
|
||||
) -> Result<(Self::Proof, Self::Evaluation), PCSErrors> {
|
|
||||
open_internal(&prover_param.0, polynomial, point)
|
|
||||
}
|
|
||||
|
|
||||
/// Input
|
|
||||
/// - the prover parameters for univariate KZG,
|
|
||||
/// - the prover parameters for multilinear KZG,
|
|
||||
/// - a list of MLEs,
|
|
||||
/// - a commitment to all MLEs
|
|
||||
/// - and a same number of points,
|
|
||||
/// 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, consists of
|
|
||||
/// - the multilinear KZG opening
|
|
||||
/// - the univariate KZG commitment to q(x)
|
|
||||
/// - the openings and evaluations of q(x) at omega^i and r
|
|
||||
///
|
|
||||
/// 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. commit to q(x) and sample r from transcript
|
|
||||
/// transcript contains: w commitment, points, q(x)'s commitment
|
|
||||
/// 5. build q(omega^i) and their openings
|
|
||||
/// 6. build q(r) and its opening
|
|
||||
/// 7. get a point `p := l(r)`
|
|
||||
/// 8. output an opening of `w` over point `p`
|
|
||||
/// 9. output `w(p)`
|
|
||||
fn multi_open(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
multi_commitment: &Self::Commitment,
|
|
||||
polynomials: &[Self::Polynomial],
|
|
||||
points: &[Self::Point],
|
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSErrors> {
|
|
||||
multi_open_internal::<E>(
|
|
||||
&prover_param.1,
|
|
||||
&prover_param.0,
|
|
||||
polynomials,
|
|
||||
multi_commitment,
|
|
||||
points,
|
|
||||
)
|
|
||||
}
|
|
||||
|
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
|
||||
/// committed inside `comm`.
|
|
||||
///
|
|
||||
/// This function takes
|
|
||||
/// - num_var number of pairing product.
|
|
||||
/// - num_var number of MSM
|
|
||||
fn verify(
|
|
||||
verifier_param: &Self::VerifierParam,
|
|
||||
commitment: &Self::Commitment,
|
|
||||
point: &Self::Point,
|
|
||||
value: &E::Fr,
|
|
||||
proof: &Self::Proof,
|
|
||||
) -> Result<bool, PCSErrors> {
|
|
||||
verify_internal(&verifier_param.0, commitment, point, value, proof)
|
|
||||
}
|
|
||||
|
|
||||
/// 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<R: RngCore>(
|
|
||||
verifier_param: &Self::VerifierParam,
|
|
||||
multi_commitment: &Self::BatchCommitment,
|
|
||||
points: &[Self::Point],
|
|
||||
values: &[E::Fr],
|
|
||||
batch_proof: &Self::BatchProof,
|
|
||||
_rng: &mut R,
|
|
||||
) -> Result<bool, PCSErrors> {
|
|
||||
batch_verify_internal(
|
|
||||
&verifier_param.1,
|
|
||||
&verifier_param.0,
|
|
||||
multi_commitment,
|
|
||||
points,
|
|
||||
values,
|
|
||||
batch_proof,
|
|
||||
)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
/// 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
|
|
||||
/// 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.
|
|
||||
fn open_internal<E: PairingEngine>(
|
|
||||
prover_param: &MultilinearProverParam<E>,
|
|
||||
polynomial: &DenseMultilinearExtension<E::Fr>,
|
|
||||
point: &[E::Fr],
|
|
||||
) -> Result<(Proof<E>, E::Fr), PCSErrors> {
|
|
||||
let open_timer = start_timer!(|| format!("open mle with {} variable", polynomial.num_vars));
|
|
||||
|
|
||||
if polynomial.num_vars() > prover_param.num_vars {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"Polynomial num_vars {} exceed the limit {}",
|
|
||||
polynomial.num_vars, prover_param.num_vars
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
if polynomial.num_vars() != point.len() {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"Polynomial num_vars {} does not match point len {}",
|
|
||||
polynomial.num_vars,
|
|
||||
point.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
let nv = polynomial.num_vars();
|
|
||||
let ignored = prover_param.num_vars - nv;
|
|
||||
let mut r: Vec<Vec<E::Fr>> = (0..nv + 1).map(|_| Vec::new()).collect();
|
|
||||
let mut q: Vec<Vec<E::Fr>> = (0..nv + 1).map(|_| Vec::new()).collect();
|
|
||||
|
|
||||
r[nv] = polynomial.to_evaluations();
|
|
||||
|
|
||||
let mut proofs = Vec::new();
|
|
||||
|
|
||||
for (i, (&point_at_k, gi)) in point
|
|
||||
.iter()
|
|
||||
.zip(prover_param.powers_of_g[ignored..].iter())
|
|
||||
.take(nv)
|
|
||||
.enumerate()
|
|
||||
{
|
|
||||
let ith_round = start_timer!(|| format!("{}-th round", i));
|
|
||||
|
|
||||
let k = nv - i;
|
|
||||
let cur_dim = 1 << (k - 1);
|
|
||||
let mut cur_q = vec![E::Fr::zero(); cur_dim];
|
|
||||
let mut cur_r = vec![E::Fr::zero(); cur_dim];
|
|
||||
|
|
||||
for b in 0..(1 << (k - 1)) {
|
|
||||
// q_b = pre_r [2^b + 1] - pre_r [2^b]
|
|
||||
cur_q[b] = r[k][(b << 1) + 1] - r[k][b << 1];
|
|
||||
|
|
||||
// r_b = pre_r [2^b]*(1-p) + pre_r [2^b + 1] * p
|
|
||||
cur_r[b] =
|
|
||||
r[k][b << 1] * (E::Fr::one() - point_at_k) + (r[k][(b << 1) + 1] * point_at_k);
|
|
||||
}
|
|
||||
|
|
||||
let scalars: Vec<_> = (0..(1 << k)).map(|x| cur_q[x >> 1].into_repr()).collect();
|
|
||||
|
|
||||
q[k] = cur_q;
|
|
||||
r[k - 1] = cur_r;
|
|
||||
|
|
||||
// this is a MSM over G1 and is likely to be the bottleneck
|
|
||||
proofs.push(VariableBaseMSM::multi_scalar_mul(&gi.evals, &scalars).into_affine());
|
|
||||
end_timer!(ith_round);
|
|
||||
}
|
|
||||
let eval = polynomial.evaluate(point).ok_or_else(|| {
|
|
||||
PCSErrors::InvalidParameters("fail to evaluate the polynomial".to_string())
|
|
||||
})?;
|
|
||||
end_timer!(open_timer);
|
|
||||
Ok((Proof { proofs }, eval))
|
|
||||
}
|
|
||||
|
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
|
||||
/// committed inside `comm`.
|
|
||||
///
|
|
||||
/// This function takes
|
|
||||
/// - num_var number of pairing product.
|
|
||||
/// - num_var number of MSM
|
|
||||
fn verify_internal<E: PairingEngine>(
|
|
||||
verifier_param: &MultilinearVerifierParam<E>,
|
|
||||
commitment: &Commitment<E>,
|
|
||||
point: &[E::Fr],
|
|
||||
value: &E::Fr,
|
|
||||
proof: &Proof<E>,
|
|
||||
) -> Result<bool, PCSErrors> {
|
|
||||
let verify_timer = start_timer!(|| "verify");
|
|
||||
let num_var = point.len();
|
|
||||
|
|
||||
if num_var > verifier_param.num_vars {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"point length ({}) exceeds param limit ({})",
|
|
||||
num_var, verifier_param.num_vars
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
let ignored = verifier_param.num_vars - num_var;
|
|
||||
let prepare_inputs_timer = start_timer!(|| "prepare pairing inputs");
|
|
||||
|
|
||||
let scalar_size = E::Fr::size_in_bits();
|
|
||||
let window_size = FixedBaseMSM::get_mul_window_size(num_var);
|
|
||||
|
|
||||
let h_table = FixedBaseMSM::get_window_table(
|
|
||||
scalar_size,
|
|
||||
window_size,
|
|
||||
verifier_param.h.into_projective(),
|
|
||||
);
|
|
||||
let h_mul: Vec<E::G2Projective> =
|
|
||||
FixedBaseMSM::multi_scalar_mul(scalar_size, window_size, &h_table, point);
|
|
||||
|
|
||||
let h_vec: Vec<_> = (0..num_var)
|
|
||||
.map(|i| verifier_param.h_mask[ignored + i].into_projective() - h_mul[i])
|
|
||||
.collect();
|
|
||||
let h_vec: Vec<E::G2Affine> = E::G2Projective::batch_normalization_into_affine(&h_vec);
|
|
||||
end_timer!(prepare_inputs_timer);
|
|
||||
|
|
||||
let pairing_product_timer = start_timer!(|| "pairing product");
|
|
||||
|
|
||||
let mut pairings: Vec<_> = proof
|
|
||||
.proofs
|
|
||||
.iter()
|
|
||||
.map(|&x| E::G1Prepared::from(x))
|
|
||||
.zip(h_vec.into_iter().take(num_var).map(E::G2Prepared::from))
|
|
||||
.collect();
|
|
||||
|
|
||||
pairings.push((
|
|
||||
E::G1Prepared::from(
|
|
||||
(verifier_param.g.mul(*value) - commitment.commitment.into_projective()).into_affine(),
|
|
||||
),
|
|
||||
E::G2Prepared::from(verifier_param.h),
|
|
||||
));
|
|
||||
|
|
||||
let res = E::product_of_pairings(pairings.iter()) == E::Fqk::one();
|
|
||||
|
|
||||
end_timer!(pairing_product_timer);
|
|
||||
end_timer!(verify_timer);
|
|
||||
Ok(res)
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod tests {
|
|
||||
use super::*;
|
|
||||
use ark_bls12_381::Bls12_381;
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
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_single_helper<R: RngCore>(
|
|
||||
params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
|
|
||||
poly: &Rc<DenseMultilinearExtension<Fr>>,
|
|
||||
rng: &mut R,
|
|
||||
) -> Result<(), PCSErrors> {
|
|
||||
let nv = poly.num_vars();
|
|
||||
assert_ne!(nv, 0);
|
|
||||
let uni_degree = 1;
|
|
||||
let (ck, vk) = KZGMultilinearPCS::trim(params, uni_degree, Some(nv + 1))?;
|
|
||||
let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
|
|
||||
let com = KZGMultilinearPCS::commit(&ck, poly)?;
|
|
||||
let (proof, value) = KZGMultilinearPCS::open(&ck, poly, &point)?;
|
|
||||
|
|
||||
assert!(KZGMultilinearPCS::verify(
|
|
||||
&vk, &com, &point, &value, &proof
|
|
||||
)?);
|
|
||||
|
|
||||
let value = Fr::rand(rng);
|
|
||||
assert!(!KZGMultilinearPCS::verify(
|
|
||||
&vk, &com, &point, &value, &proof
|
|
||||
)?);
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_single_commit() -> Result<(), PCSErrors> {
|
|
||||
let mut rng = test_rng();
|
|
||||
|
|
||||
let params = KZGMultilinearPCS::<E>::gen_srs_for_testing(&mut rng, 10)?;
|
|
||||
|
|
||||
// normal polynomials
|
|
||||
let poly1 = Rc::new(DenseMultilinearExtension::rand(8, &mut rng));
|
|
||||
test_single_helper(¶ms, &poly1, &mut rng)?;
|
|
||||
|
|
||||
// single-variate polynomials
|
|
||||
let poly2 = Rc::new(DenseMultilinearExtension::rand(1, &mut rng));
|
|
||||
test_single_helper(¶ms, &poly2, &mut rng)?;
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn setup_commit_verify_constant_polynomial() {
|
|
||||
let mut rng = test_rng();
|
|
||||
|
|
||||
// normal polynomials
|
|
||||
assert!(KZGMultilinearPCS::<E>::gen_srs_for_testing(&mut rng, 0).is_err());
|
|
||||
}
|
|
||||
}
|
|
@ -1,258 +0,0 @@ |
|||||
//! Implementing Structured Reference Strings for multilinear polynomial KZG
|
|
||||
|
|
||||
use crate::{prelude::PCSErrors, StructuredReferenceString};
|
|
||||
use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve};
|
|
||||
use ark_ff::{Field, PrimeField};
|
|
||||
use ark_poly::DenseMultilinearExtension;
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
|
||||
use ark_std::{end_timer, rand::RngCore, start_timer, vec::Vec, UniformRand};
|
|
||||
use std::collections::LinkedList;
|
|
||||
|
|
||||
/// 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>,
|
|
||||
}
|
|
||||
|
|
||||
/// Universal Parameter
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
|
||||
pub struct MultilinearUniversalParams<E: PairingEngine> {
|
|
||||
/// prover parameters
|
|
||||
pub prover_param: MultilinearProverParam<E>,
|
|
||||
/// h^randomness: h^t1, h^t2, ..., **h^{t_nv}**
|
|
||||
pub h_mask: Vec<E::G2Affine>,
|
|
||||
}
|
|
||||
|
|
||||
/// Prover Parameters
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
|
||||
pub struct MultilinearProverParam<E: PairingEngine> {
|
|
||||
/// number of variables
|
|
||||
pub num_vars: usize,
|
|
||||
/// `pp_{num_vars}`, `pp_{num_vars - 1}`, `pp_{num_vars - 2}`, ..., defined
|
|
||||
/// by XZZPD19
|
|
||||
pub powers_of_g: Vec<Evaluations<E::G1Affine>>,
|
|
||||
/// generator for G1
|
|
||||
pub g: E::G1Affine,
|
|
||||
/// generator for G2
|
|
||||
pub h: E::G2Affine,
|
|
||||
}
|
|
||||
|
|
||||
/// Verifier Parameters
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
|
||||
pub struct MultilinearVerifierParam<E: PairingEngine> {
|
|
||||
/// number of variables
|
|
||||
pub num_vars: usize,
|
|
||||
/// generator of G1
|
|
||||
pub g: E::G1Affine,
|
|
||||
/// generator of G2
|
|
||||
pub h: E::G2Affine,
|
|
||||
/// h^randomness: h^t1, h^t2, ..., **h^{t_nv}**
|
|
||||
pub h_mask: Vec<E::G2Affine>,
|
|
||||
}
|
|
||||
|
|
||||
impl<E: PairingEngine> StructuredReferenceString<E> for MultilinearUniversalParams<E> {
|
|
||||
type ProverParam = MultilinearProverParam<E>;
|
|
||||
type VerifierParam = MultilinearVerifierParam<E>;
|
|
||||
|
|
||||
/// Extract the prover parameters from the public parameters.
|
|
||||
fn extract_prover_param(&self, supported_num_vars: usize) -> Self::ProverParam {
|
|
||||
let to_reduce = self.prover_param.num_vars - supported_num_vars;
|
|
||||
|
|
||||
Self::ProverParam {
|
|
||||
powers_of_g: self.prover_param.powers_of_g[to_reduce..].to_vec(),
|
|
||||
g: self.prover_param.g,
|
|
||||
h: self.prover_param.h,
|
|
||||
num_vars: supported_num_vars,
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
/// Extract the verifier parameters from the public parameters.
|
|
||||
fn extract_verifier_param(&self, supported_num_vars: usize) -> Self::VerifierParam {
|
|
||||
let to_reduce = self.prover_param.num_vars - supported_num_vars;
|
|
||||
Self::VerifierParam {
|
|
||||
num_vars: supported_num_vars,
|
|
||||
g: self.prover_param.g,
|
|
||||
h: self.prover_param.h,
|
|
||||
h_mask: self.h_mask[to_reduce..].to_vec(),
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
/// Trim the universal parameters to specialize the public parameters
|
|
||||
/// for multilinear polynomials to the given `supported_num_vars`, and
|
|
||||
/// returns committer key and verifier key. `supported_num_vars` should
|
|
||||
/// be in range `1..=params.num_vars`
|
|
||||
fn trim(
|
|
||||
&self,
|
|
||||
supported_num_vars: usize,
|
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> {
|
|
||||
if supported_num_vars > self.prover_param.num_vars {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"SRS does not support target number of vars {}",
|
|
||||
supported_num_vars
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
let to_reduce = self.prover_param.num_vars - supported_num_vars;
|
|
||||
let ck = Self::ProverParam {
|
|
||||
powers_of_g: self.prover_param.powers_of_g[to_reduce..].to_vec(),
|
|
||||
g: self.prover_param.g,
|
|
||||
h: self.prover_param.h,
|
|
||||
num_vars: supported_num_vars,
|
|
||||
};
|
|
||||
let vk = Self::VerifierParam {
|
|
||||
num_vars: supported_num_vars,
|
|
||||
g: self.prover_param.g,
|
|
||||
h: self.prover_param.h,
|
|
||||
h_mask: self.h_mask[to_reduce..].to_vec(),
|
|
||||
};
|
|
||||
Ok((ck, vk))
|
|
||||
}
|
|
||||
|
|
||||
/// Build SRS for testing.
|
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
|
||||
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
|
|
||||
fn gen_srs_for_testing<R: RngCore>(rng: &mut R, num_vars: usize) -> Result<Self, PCSErrors> {
|
|
||||
if num_vars == 0 {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"constant polynomial not supported".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
|
|
||||
let total_timer = start_timer!(|| "SRS generation");
|
|
||||
|
|
||||
let pp_generation_timer = start_timer!(|| "Prover Param generation");
|
|
||||
|
|
||||
let g = E::G1Projective::rand(rng);
|
|
||||
let h = E::G2Projective::rand(rng);
|
|
||||
|
|
||||
let mut powers_of_g = Vec::new();
|
|
||||
|
|
||||
let t: Vec<_> = (0..num_vars).map(|_| E::Fr::rand(rng)).collect();
|
|
||||
let scalar_bits = E::Fr::size_in_bits();
|
|
||||
|
|
||||
let mut eq: LinkedList<DenseMultilinearExtension<E::Fr>> =
|
|
||||
LinkedList::from_iter(eq_extension(&t).into_iter());
|
|
||||
let mut eq_arr = LinkedList::new();
|
|
||||
let mut base = eq.pop_back().unwrap().evaluations;
|
|
||||
|
|
||||
for i in (0..num_vars).rev() {
|
|
||||
eq_arr.push_front(remove_dummy_variable(&base, i)?);
|
|
||||
if i != 0 {
|
|
||||
let mul = eq.pop_back().unwrap().evaluations;
|
|
||||
base = base
|
|
||||
.into_iter()
|
|
||||
.zip(mul.into_iter())
|
|
||||
.map(|(a, b)| a * b)
|
|
||||
.collect();
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
let mut pp_powers = Vec::new();
|
|
||||
let mut total_scalars = 0;
|
|
||||
for i in 0..num_vars {
|
|
||||
let eq = eq_arr.pop_front().unwrap();
|
|
||||
let pp_k_powers = (0..(1 << (num_vars - i))).map(|x| eq[x]);
|
|
||||
pp_powers.extend(pp_k_powers);
|
|
||||
total_scalars += 1 << (num_vars - i);
|
|
||||
}
|
|
||||
let window_size = FixedBaseMSM::get_mul_window_size(total_scalars);
|
|
||||
let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g);
|
|
||||
|
|
||||
let pp_g = E::G1Projective::batch_normalization_into_affine(
|
|
||||
&FixedBaseMSM::multi_scalar_mul(scalar_bits, window_size, &g_table, &pp_powers),
|
|
||||
);
|
|
||||
|
|
||||
let mut start = 0;
|
|
||||
for i in 0..num_vars {
|
|
||||
let size = 1 << (num_vars - i);
|
|
||||
let pp_k_g = Evaluations {
|
|
||||
evals: pp_g[start..(start + size)].to_vec(),
|
|
||||
};
|
|
||||
powers_of_g.push(pp_k_g);
|
|
||||
start += size;
|
|
||||
}
|
|
||||
|
|
||||
let pp = Self::ProverParam {
|
|
||||
num_vars,
|
|
||||
g: g.into_affine(),
|
|
||||
h: h.into_affine(),
|
|
||||
powers_of_g,
|
|
||||
};
|
|
||||
|
|
||||
end_timer!(pp_generation_timer);
|
|
||||
|
|
||||
let vp_generation_timer = start_timer!(|| "VP generation");
|
|
||||
let h_mask = {
|
|
||||
let window_size = FixedBaseMSM::get_mul_window_size(num_vars);
|
|
||||
let h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h);
|
|
||||
E::G2Projective::batch_normalization_into_affine(&FixedBaseMSM::multi_scalar_mul(
|
|
||||
scalar_bits,
|
|
||||
window_size,
|
|
||||
&h_table,
|
|
||||
&t,
|
|
||||
))
|
|
||||
};
|
|
||||
end_timer!(vp_generation_timer);
|
|
||||
end_timer!(total_timer);
|
|
||||
Ok(Self {
|
|
||||
prover_param: pp,
|
|
||||
h_mask,
|
|
||||
})
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
/// fix first `pad` variables of `poly` represented in evaluation form to zero
|
|
||||
fn remove_dummy_variable<F: Field>(poly: &[F], pad: usize) -> Result<Vec<F>, PCSErrors> {
|
|
||||
if pad == 0 {
|
|
||||
return Ok(poly.to_vec());
|
|
||||
}
|
|
||||
if !poly.len().is_power_of_two() {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"Size of polynomial should be power of two.".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
let nv = ark_std::log2(poly.len()) as usize - pad;
|
|
||||
|
|
||||
Ok((0..(1 << nv)).map(|x| poly[x << pad]).collect())
|
|
||||
}
|
|
||||
|
|
||||
/// Generate eq(t,x), a product of multilinear polynomials with fixed t.
|
|
||||
/// eq(a,b) is takes extensions of a,b in {0,1}^num_vars such that if a and b in
|
|
||||
/// {0,1}^num_vars are equal then this polynomial evaluates to 1.
|
|
||||
fn eq_extension<F: PrimeField>(t: &[F]) -> Vec<DenseMultilinearExtension<F>> {
|
|
||||
let start = start_timer!(|| "eq extension");
|
|
||||
|
|
||||
let dim = t.len();
|
|
||||
let mut result = Vec::new();
|
|
||||
for (i, &ti) in t.iter().enumerate().take(dim) {
|
|
||||
let mut poly = Vec::with_capacity(1 << dim);
|
|
||||
for x in 0..(1 << dim) {
|
|
||||
let xi = if x >> i & 1 == 1 { F::one() } else { F::zero() };
|
|
||||
let ti_xi = ti * xi;
|
|
||||
poly.push(ti_xi + ti_xi - xi - ti + F::one());
|
|
||||
}
|
|
||||
result.push(DenseMultilinearExtension::from_evaluations_vec(dim, poly));
|
|
||||
}
|
|
||||
|
|
||||
end_timer!(start);
|
|
||||
result
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod tests {
|
|
||||
use super::*;
|
|
||||
use ark_bls12_381::Bls12_381;
|
|
||||
use ark_std::test_rng;
|
|
||||
type E = Bls12_381;
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_srs_gen() -> Result<(), PCSErrors> {
|
|
||||
let mut rng = test_rng();
|
|
||||
for nv in 4..10 {
|
|
||||
let _ = MultilinearUniversalParams::<E>::gen_srs_for_testing(&mut rng, nv)?;
|
|
||||
}
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
@ -1,674 +0,0 @@ |
|||||
//! 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, rc::Rc, start_timer};
|
|
||||
|
|
||||
/// 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> {
|
|
||||
let mut res = Vec::with_capacity(num_var);
|
|
||||
let mut i = input;
|
|
||||
for _ in 0..num_var {
|
|
||||
res.push(i & 1 == 1);
|
|
||||
i >>= 1;
|
|
||||
}
|
|
||||
res
|
|
||||
}
|
|
||||
|
|
||||
/// For an MLE w with `mle_num_vars` variables, and `point_len` number of
|
|
||||
/// points, compute the degree of the univariate polynomial `q(x):= w(l(x))`
|
|
||||
/// where l(x) is a list of polynomials that go through all points.
|
|
||||
// uni_degree is computed as `mle_num_vars * point_len`:
|
|
||||
// - each l(x) is of degree `point_len`
|
|
||||
// - mle has degree one
|
|
||||
// - worst case is `\prod_{i=0}^{mle_num_vars-1} l_i(x) < point_len * mle_num_vars`
|
|
||||
#[inline]
|
|
||||
pub fn compute_qx_degree(mle_num_vars: usize, point_len: usize) -> usize {
|
|
||||
mle_num_vars * point_len
|
|
||||
}
|
|
||||
|
|
||||
/// get the domain for the univariate polynomial
|
|
||||
#[inline]
|
|
||||
pub(crate) fn get_uni_domain<F: PrimeField>(
|
|
||||
uni_poly_degree: usize,
|
|
||||
) -> Result<Radix2EvaluationDomain<F>, PCSErrors> {
|
|
||||
let domain = match Radix2EvaluationDomain::<F>::new(uni_poly_degree) {
|
|
||||
Some(p) => p,
|
|
||||
None => {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"failed to build radix 2 domain".to_string(),
|
|
||||
))
|
|
||||
},
|
|
||||
};
|
|
||||
Ok(domain)
|
|
||||
}
|
|
||||
|
|
||||
/// 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 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 fn merge_polynomials<F: PrimeField>(
|
|
||||
polynomials: &[Rc<DenseMultilinearExtension<F>>],
|
|
||||
) -> Result<Rc<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(Rc::new(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: &[Vec<F>],
|
|
||||
domain: &Radix2EvaluationDomain<F>,
|
|
||||
) -> Result<Vec<DensePolynomial<F>>, PCSErrors> {
|
|
||||
let prefix_len = log2(points.len()) as usize;
|
|
||||
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, *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(); domain.size as usize - eval.len()].as_slice());
|
|
||||
uni_polys.push(Evaluations::from_vec_and_domain(eval, *domain).interpolate())
|
|
||||
}
|
|
||||
|
|
||||
Ok(uni_polys)
|
|
||||
}
|
|
||||
|
|
||||
/// Input a list of multilinear polynomials and a list of points,
|
|
||||
/// generate a list of evaluations.
|
|
||||
// Note that this function is only used for testing verifications.
|
|
||||
// In practice verifier does not see polynomials, and the `mle_values`
|
|
||||
// are included in the `batch_proof`.
|
|
||||
#[cfg(test)]
|
|
||||
pub(crate) fn generate_evaluations<F: PrimeField>(
|
|
||||
polynomials: &[Rc<DenseMultilinearExtension<F>>],
|
|
||||
points: &[Vec<F>],
|
|
||||
) -> Result<Vec<F>, PCSErrors> {
|
|
||||
if polynomials.len() != points.len() {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"polynomial length does not match point length".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
|
|
||||
let num_var = polynomials[0].num_vars;
|
|
||||
let uni_poly_degree = points.len();
|
|
||||
let merge_poly = merge_polynomials(polynomials)?;
|
|
||||
|
|
||||
let domain = get_uni_domain::<F>(uni_poly_degree)?;
|
|
||||
let uni_polys = build_l(num_var, points, &domain)?;
|
|
||||
let mut mle_values = vec![];
|
|
||||
|
|
||||
for i in 0..uni_poly_degree {
|
|
||||
let point: Vec<F> = uni_polys
|
|
||||
.iter()
|
|
||||
.rev()
|
|
||||
.map(|poly| poly.evaluate(&domain.element(i)))
|
|
||||
.collect();
|
|
||||
|
|
||||
let mle_value = merge_poly.evaluate(&point).unwrap();
|
|
||||
mle_values.push(mle_value)
|
|
||||
}
|
|
||||
Ok(mle_values)
|
|
||||
}
|
|
||||
|
|
||||
#[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 = Rc::new(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 = Rc::new(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 = Rc::new(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 = Rc::new(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 = Rc::new(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 = vec![Fr::from(1u64), Fr::from(2u64)];
|
|
||||
|
|
||||
// point 2 is [3, 4]
|
|
||||
let point2 = vec![Fr::from(3u64), Fr::from(4u64)];
|
|
||||
|
|
||||
// point 3 is [5, 6]
|
|
||||
let point3 = vec![Fr::from(5u64), Fr::from(6u64)];
|
|
||||
|
|
||||
{
|
|
||||
let domain = get_uni_domain::<Fr>(2)?;
|
|
||||
let l = build_l(2, &[point1.clone(), point2.clone()], &domain)?;
|
|
||||
|
|
||||
// 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 domain = get_uni_domain::<Fr>(3)?;
|
|
||||
let l = build_l(2, &[point1, point2, point3], &domain)?;
|
|
||||
|
|
||||
// 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 = Rc::new(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 = Rc::new(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 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
|
|
||||
|
|
||||
let r = Fr::from(42u64);
|
|
||||
|
|
||||
// point 1 is [1, 2]
|
|
||||
let point1 = vec![Fr::from(1u64), Fr::from(2u64)];
|
|
||||
|
|
||||
// point 2 is [3, 4]
|
|
||||
let point2 = vec![Fr::from(3u64), Fr::from(4u64)];
|
|
||||
|
|
||||
// point 3 is [5, 6]
|
|
||||
let point3 = vec![Fr::from(5u64), Fr::from(6u64)];
|
|
||||
|
|
||||
{
|
|
||||
let domain = get_uni_domain::<Fr>(2)?;
|
|
||||
// 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.clone(), point2.clone()], &domain)?;
|
|
||||
|
|
||||
// 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))"
|
|
||||
);
|
|
||||
}
|
|
||||
|
|
||||
{
|
|
||||
let domain = get_uni_domain::<Fr>(3)?;
|
|
||||
// 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], &domain)?;
|
|
||||
|
|
||||
// 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(())
|
|
||||
}
|
|
||||
}
|
|
@ -1,13 +0,0 @@ |
|||||
pub use crate::{
|
|
||||
errors::PCSErrors,
|
|
||||
multilinear_kzg::{
|
|
||||
srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam},
|
|
||||
util::{compute_qx_degree, get_batched_nv, merge_polynomials},
|
|
||||
BatchProof, KZGMultilinearPCS, Proof,
|
|
||||
},
|
|
||||
structs::Commitment,
|
|
||||
univariate_kzg::srs::{
|
|
||||
UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam,
|
|
||||
},
|
|
||||
PolynomialCommitmentScheme, StructuredReferenceString,
|
|
||||
};
|
|
@ -1,9 +0,0 @@ |
|||||
use ark_ec::PairingEngine;
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
|
||||
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, Default, PartialEq, Eq)]
|
|
||||
/// A commitment is an Affine point.
|
|
||||
pub struct Commitment<E: PairingEngine> {
|
|
||||
/// the actual commitment is an affine point.
|
|
||||
pub commitment: E::G1Affine,
|
|
||||
}
|
|
@ -1,400 +0,0 @@ |
|||||
//! Main module for univariate KZG commitment scheme
|
|
||||
|
|
||||
use crate::{
|
|
||||
prelude::{Commitment, PCSErrors},
|
|
||||
PolynomialCommitmentScheme, StructuredReferenceString,
|
|
||||
};
|
|
||||
use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve};
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial};
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
|
||||
use ark_std::{end_timer, rand::RngCore, start_timer, One, UniformRand, Zero};
|
|
||||
use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam};
|
|
||||
use std::marker::PhantomData;
|
|
||||
|
|
||||
pub(crate) mod srs;
|
|
||||
|
|
||||
/// KZG Polynomial Commitment Scheme on univariate polynomial.
|
|
||||
pub struct KZGUnivariatePCS<E: PairingEngine> {
|
|
||||
#[doc(hidden)]
|
|
||||
phantom: PhantomData<E>,
|
|
||||
}
|
|
||||
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
|
||||
/// proof of opening
|
|
||||
pub struct KZGUnivariateOpening<E: PairingEngine> {
|
|
||||
/// Evaluation of quotients
|
|
||||
pub proof: E::G1Affine,
|
|
||||
}
|
|
||||
|
|
||||
impl<E: PairingEngine> PolynomialCommitmentScheme<E> for KZGUnivariatePCS<E> {
|
|
||||
// Parameters
|
|
||||
type ProverParam = UnivariateProverParam<E::G1Affine>;
|
|
||||
type VerifierParam = UnivariateVerifierParam<E>;
|
|
||||
type SRS = UnivariateUniversalParams<E>;
|
|
||||
// Polynomial and its associated types
|
|
||||
type Polynomial = DensePolynomial<E::Fr>;
|
|
||||
type Point = E::Fr;
|
|
||||
type Evaluation = E::Fr;
|
|
||||
// Polynomial and its associated types
|
|
||||
type Commitment = Commitment<E>;
|
|
||||
type BatchCommitment = Vec<Self::Commitment>;
|
|
||||
type Proof = KZGUnivariateOpening<E>;
|
|
||||
type BatchProof = Vec<Self::Proof>;
|
|
||||
|
|
||||
/// Build SRS for testing.
|
|
||||
///
|
|
||||
/// - For univariate polynomials, `log_size` is the log of maximum degree.
|
|
||||
/// - For multilinear polynomials, `log_size` is the number of variables.
|
|
||||
///
|
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
|
||||
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
|
|
||||
fn gen_srs_for_testing<R: RngCore>(
|
|
||||
rng: &mut R,
|
|
||||
log_size: usize,
|
|
||||
) -> Result<Self::SRS, PCSErrors> {
|
|
||||
Self::SRS::gen_srs_for_testing(rng, log_size)
|
|
||||
}
|
|
||||
|
|
||||
/// Trim the universal parameters to specialize the public parameters.
|
|
||||
/// Input `supported_log_degree` for univariate.
|
|
||||
/// `supported_num_vars` must be None or an error is returned.
|
|
||||
fn trim(
|
|
||||
srs: &Self::SRS,
|
|
||||
supported_log_degree: usize,
|
|
||||
supported_num_vars: Option<usize>,
|
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> {
|
|
||||
if supported_num_vars.is_some() {
|
|
||||
return Err(PCSErrors::InvalidParameters(
|
|
||||
"univariate should not receive a num_var param".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
srs.trim(supported_log_degree)
|
|
||||
}
|
|
||||
|
|
||||
/// Generate a commitment for a polynomial
|
|
||||
/// Note that the scheme is not hidding
|
|
||||
fn commit(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
poly: &Self::Polynomial,
|
|
||||
) -> Result<Self::Commitment, PCSErrors> {
|
|
||||
let commit_time =
|
|
||||
start_timer!(|| format!("Committing to polynomial of degree {} ", poly.degree()));
|
|
||||
|
|
||||
if poly.degree() > prover_param.powers_of_g.len() {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"poly degree {} is larger than allowed {}",
|
|
||||
poly.degree(),
|
|
||||
prover_param.powers_of_g.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
let (num_leading_zeros, plain_coeffs) = skip_leading_zeros_and_convert_to_bigints(poly);
|
|
||||
|
|
||||
let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly");
|
|
||||
let commitment = VariableBaseMSM::multi_scalar_mul(
|
|
||||
&prover_param.powers_of_g[num_leading_zeros..],
|
|
||||
&plain_coeffs,
|
|
||||
)
|
|
||||
.into_affine();
|
|
||||
end_timer!(msm_time);
|
|
||||
|
|
||||
end_timer!(commit_time);
|
|
||||
Ok(Commitment { commitment })
|
|
||||
}
|
|
||||
|
|
||||
/// Generate a commitment for a list of polynomials
|
|
||||
fn multi_commit(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
polys: &[Self::Polynomial],
|
|
||||
) -> Result<Self::BatchCommitment, PCSErrors> {
|
|
||||
let commit_time = start_timer!(|| format!("batch commit {} polynomials", polys.len()));
|
|
||||
let res = polys
|
|
||||
.iter()
|
|
||||
.map(|poly| Self::commit(prover_param, poly))
|
|
||||
.collect::<Result<Vec<Self::Commitment>, PCSErrors>>()?;
|
|
||||
|
|
||||
end_timer!(commit_time);
|
|
||||
Ok(res)
|
|
||||
}
|
|
||||
|
|
||||
/// On input a polynomial `p` and a point `point`, outputs a proof for the
|
|
||||
/// same.
|
|
||||
fn open(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
polynomial: &Self::Polynomial,
|
|
||||
point: &Self::Point,
|
|
||||
) -> Result<(Self::Proof, Self::Evaluation), PCSErrors> {
|
|
||||
let open_time =
|
|
||||
start_timer!(|| format!("Opening polynomial of degree {}", polynomial.degree()));
|
|
||||
let divisor = Self::Polynomial::from_coefficients_vec(vec![-*point, E::Fr::one()]);
|
|
||||
|
|
||||
let witness_time = start_timer!(|| "Computing witness polynomial");
|
|
||||
let witness_polynomial = polynomial / &divisor;
|
|
||||
end_timer!(witness_time);
|
|
||||
|
|
||||
let (num_leading_zeros, witness_coeffs) =
|
|
||||
skip_leading_zeros_and_convert_to_bigints(&witness_polynomial);
|
|
||||
|
|
||||
let proof = VariableBaseMSM::multi_scalar_mul(
|
|
||||
&prover_param.powers_of_g[num_leading_zeros..],
|
|
||||
&witness_coeffs,
|
|
||||
)
|
|
||||
.into_affine();
|
|
||||
|
|
||||
let eval = polynomial.evaluate(point);
|
|
||||
|
|
||||
end_timer!(open_time);
|
|
||||
Ok((Self::Proof { proof }, eval))
|
|
||||
}
|
|
||||
|
|
||||
/// Input a list of polynomials, and a same number of points,
|
|
||||
/// compute a multi-opening for all the polynomials.
|
|
||||
// This is a naive approach
|
|
||||
// TODO: to implement the more efficient batch opening algorithm
|
|
||||
// (e.g., the appendix C.4 in https://eprint.iacr.org/2020/1536.pdf)
|
|
||||
fn multi_open(
|
|
||||
prover_param: &Self::ProverParam,
|
|
||||
_multi_commitment: &Self::Commitment,
|
|
||||
polynomials: &[Self::Polynomial],
|
|
||||
points: &[Self::Point],
|
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSErrors> {
|
|
||||
let open_time = start_timer!(|| format!("batch opening {} polynomials", polynomials.len()));
|
|
||||
if polynomials.len() != points.len() {
|
|
||||
return Err(PCSErrors::InvalidParameters(format!(
|
|
||||
"poly length {} is different from points length {}",
|
|
||||
polynomials.len(),
|
|
||||
points.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
let mut batch_proof = vec![];
|
|
||||
let mut evals = vec![];
|
|
||||
for (poly, point) in polynomials.iter().zip(points.iter()) {
|
|
||||
let (proof, eval) = Self::open(prover_param, poly, point)?;
|
|
||||
batch_proof.push(proof);
|
|
||||
evals.push(eval);
|
|
||||
}
|
|
||||
|
|
||||
end_timer!(open_time);
|
|
||||
Ok((batch_proof, evals))
|
|
||||
}
|
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
|
||||
/// committed inside `comm`.
|
|
||||
fn verify(
|
|
||||
verifier_param: &Self::VerifierParam,
|
|
||||
commitment: &Self::Commitment,
|
|
||||
point: &Self::Point,
|
|
||||
value: &E::Fr,
|
|
||||
proof: &Self::Proof,
|
|
||||
) -> Result<bool, PCSErrors> {
|
|
||||
let check_time = start_timer!(|| "Checking evaluation");
|
|
||||
let pairing_inputs: Vec<(E::G1Prepared, E::G2Prepared)> = vec![
|
|
||||
(
|
|
||||
(verifier_param.g.mul(value.into_repr())
|
|
||||
- proof.proof.mul(point.into_repr())
|
|
||||
- commitment.commitment.into_projective())
|
|
||||
.into_affine()
|
|
||||
.into(),
|
|
||||
verifier_param.h.into(),
|
|
||||
),
|
|
||||
(proof.proof.into(), verifier_param.beta_h.into()),
|
|
||||
];
|
|
||||
|
|
||||
let res = E::product_of_pairings(pairing_inputs.iter()).is_one();
|
|
||||
|
|
||||
end_timer!(check_time, || format!("Result: {}", res));
|
|
||||
Ok(res)
|
|
||||
}
|
|
||||
|
|
||||
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
|
|
||||
/// `poly_i` committed inside `comm`.
|
|
||||
// This is a naive approach
|
|
||||
// TODO: to implement the more efficient batch verification algorithm
|
|
||||
// (e.g., the appendix C.4 in https://eprint.iacr.org/2020/1536.pdf)
|
|
||||
fn batch_verify<R: RngCore>(
|
|
||||
verifier_param: &Self::VerifierParam,
|
|
||||
multi_commitment: &Self::BatchCommitment,
|
|
||||
points: &[Self::Point],
|
|
||||
values: &[E::Fr],
|
|
||||
batch_proof: &Self::BatchProof,
|
|
||||
rng: &mut R,
|
|
||||
) -> Result<bool, PCSErrors> {
|
|
||||
let check_time =
|
|
||||
start_timer!(|| format!("Checking {} evaluation proofs", multi_commitment.len()));
|
|
||||
|
|
||||
let mut total_c = <E::G1Projective>::zero();
|
|
||||
let mut total_w = <E::G1Projective>::zero();
|
|
||||
|
|
||||
let combination_time = start_timer!(|| "Combining commitments and proofs");
|
|
||||
let mut randomizer = E::Fr::one();
|
|
||||
// Instead of multiplying g and gamma_g in each turn, we simply accumulate
|
|
||||
// their coefficients and perform a final multiplication at the end.
|
|
||||
let mut g_multiplier = E::Fr::zero();
|
|
||||
for (((c, z), v), proof) in multi_commitment
|
|
||||
.iter()
|
|
||||
.zip(points)
|
|
||||
.zip(values)
|
|
||||
.zip(batch_proof)
|
|
||||
{
|
|
||||
let w = proof.proof;
|
|
||||
let mut temp = w.mul(*z);
|
|
||||
temp.add_assign_mixed(&c.commitment);
|
|
||||
let c = temp;
|
|
||||
g_multiplier += &(randomizer * v);
|
|
||||
total_c += &c.mul(randomizer.into_repr());
|
|
||||
total_w += &w.mul(randomizer.into_repr());
|
|
||||
// We don't need to sample randomizers from the full field,
|
|
||||
// only from 128-bit strings.
|
|
||||
randomizer = u128::rand(rng).into();
|
|
||||
}
|
|
||||
total_c -= &verifier_param.g.mul(g_multiplier);
|
|
||||
end_timer!(combination_time);
|
|
||||
|
|
||||
let to_affine_time = start_timer!(|| "Converting results to affine for pairing");
|
|
||||
let affine_points = E::G1Projective::batch_normalization_into_affine(&[-total_w, total_c]);
|
|
||||
let (total_w, total_c) = (affine_points[0], affine_points[1]);
|
|
||||
end_timer!(to_affine_time);
|
|
||||
|
|
||||
let pairing_time = start_timer!(|| "Performing product of pairings");
|
|
||||
let result = E::product_of_pairings(&[
|
|
||||
(total_w.into(), verifier_param.beta_h.into()),
|
|
||||
(total_c.into(), verifier_param.h.into()),
|
|
||||
])
|
|
||||
.is_one();
|
|
||||
end_timer!(pairing_time);
|
|
||||
end_timer!(check_time, || format!("Result: {}", result));
|
|
||||
Ok(result)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
fn skip_leading_zeros_and_convert_to_bigints<F: PrimeField, P: UVPolynomial<F>>(
|
|
||||
p: &P,
|
|
||||
) -> (usize, Vec<F::BigInt>) {
|
|
||||
let mut num_leading_zeros = 0;
|
|
||||
while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() {
|
|
||||
num_leading_zeros += 1;
|
|
||||
}
|
|
||||
let coeffs = convert_to_bigints(&p.coeffs()[num_leading_zeros..]);
|
|
||||
(num_leading_zeros, coeffs)
|
|
||||
}
|
|
||||
|
|
||||
fn convert_to_bigints<F: PrimeField>(p: &[F]) -> Vec<F::BigInt> {
|
|
||||
let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to bigints");
|
|
||||
let coeffs = p.iter().map(|s| s.into_repr()).collect::<Vec<_>>();
|
|
||||
end_timer!(to_bigint_time);
|
|
||||
coeffs
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod tests {
|
|
||||
use super::*;
|
|
||||
use crate::StructuredReferenceString;
|
|
||||
use ark_bls12_381::Bls12_381;
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_poly::univariate::DensePolynomial;
|
|
||||
use ark_std::{log2, test_rng, UniformRand};
|
|
||||
|
|
||||
fn end_to_end_test_template<E>() -> Result<(), PCSErrors>
|
|
||||
where
|
|
||||
E: PairingEngine,
|
|
||||
{
|
|
||||
let rng = &mut test_rng();
|
|
||||
for _ in 0..100 {
|
|
||||
let mut degree = 0;
|
|
||||
while degree <= 1 {
|
|
||||
degree = usize::rand(rng) % 20;
|
|
||||
}
|
|
||||
let log_degree = log2(degree) as usize;
|
|
||||
let pp = KZGUnivariatePCS::<E>::gen_srs_for_testing(rng, log_degree)?;
|
|
||||
let (ck, vk) = pp.trim(log_degree)?;
|
|
||||
let p = <DensePolynomial<E::Fr> as UVPolynomial<E::Fr>>::rand(degree, rng);
|
|
||||
let comm = KZGUnivariatePCS::<E>::commit(&ck, &p)?;
|
|
||||
let point = E::Fr::rand(rng);
|
|
||||
let (proof, value) = KZGUnivariatePCS::<E>::open(&ck, &p, &point)?;
|
|
||||
assert!(
|
|
||||
KZGUnivariatePCS::<E>::verify(&vk, &comm, &point, &value, &proof)?,
|
|
||||
"proof was incorrect for max_degree = {}, polynomial_degree = {}",
|
|
||||
degree,
|
|
||||
p.degree(),
|
|
||||
);
|
|
||||
}
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
fn linear_polynomial_test_template<E>() -> Result<(), PCSErrors>
|
|
||||
where
|
|
||||
E: PairingEngine,
|
|
||||
{
|
|
||||
let rng = &mut test_rng();
|
|
||||
for _ in 0..100 {
|
|
||||
let degree = 50;
|
|
||||
let log_degree = log2(degree) as usize;
|
|
||||
|
|
||||
let pp = KZGUnivariatePCS::<E>::gen_srs_for_testing(rng, log_degree)?;
|
|
||||
let (ck, vk) = pp.trim(log_degree)?;
|
|
||||
let p = <DensePolynomial<E::Fr> as UVPolynomial<E::Fr>>::rand(degree, rng);
|
|
||||
let comm = KZGUnivariatePCS::<E>::commit(&ck, &p)?;
|
|
||||
let point = E::Fr::rand(rng);
|
|
||||
let (proof, value) = KZGUnivariatePCS::<E>::open(&ck, &p, &point)?;
|
|
||||
assert!(
|
|
||||
KZGUnivariatePCS::<E>::verify(&vk, &comm, &point, &value, &proof)?,
|
|
||||
"proof was incorrect for max_degree = {}, polynomial_degree = {}",
|
|
||||
degree,
|
|
||||
p.degree(),
|
|
||||
);
|
|
||||
}
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
fn batch_check_test_template<E>() -> Result<(), PCSErrors>
|
|
||||
where
|
|
||||
E: PairingEngine,
|
|
||||
{
|
|
||||
let rng = &mut test_rng();
|
|
||||
for _ in 0..10 {
|
|
||||
let mut degree = 0;
|
|
||||
while degree <= 1 {
|
|
||||
degree = usize::rand(rng) % 20;
|
|
||||
}
|
|
||||
let log_degree = log2(degree) as usize;
|
|
||||
let pp = KZGUnivariatePCS::<E>::gen_srs_for_testing(rng, log_degree)?;
|
|
||||
let (ck, vk) = KZGUnivariatePCS::<E>::trim(&pp, log_degree, None)?;
|
|
||||
let mut comms = Vec::new();
|
|
||||
let mut values = Vec::new();
|
|
||||
let mut points = Vec::new();
|
|
||||
let mut proofs = Vec::new();
|
|
||||
for _ in 0..10 {
|
|
||||
let p = <DensePolynomial<E::Fr> as UVPolynomial<E::Fr>>::rand(degree, rng);
|
|
||||
let comm = KZGUnivariatePCS::<E>::commit(&ck, &p)?;
|
|
||||
let point = E::Fr::rand(rng);
|
|
||||
let (proof, value) = KZGUnivariatePCS::<E>::open(&ck, &p, &point)?;
|
|
||||
|
|
||||
assert!(KZGUnivariatePCS::<E>::verify(
|
|
||||
&vk, &comm, &point, &value, &proof
|
|
||||
)?);
|
|
||||
comms.push(comm);
|
|
||||
values.push(value);
|
|
||||
points.push(point);
|
|
||||
proofs.push(proof);
|
|
||||
}
|
|
||||
assert!(KZGUnivariatePCS::<E>::batch_verify(
|
|
||||
&vk, &comms, &points, &values, &proofs, rng
|
|
||||
)?);
|
|
||||
}
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn end_to_end_test() {
|
|
||||
end_to_end_test_template::<Bls12_381>().expect("test failed for bls12-381");
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn linear_polynomial_test() {
|
|
||||
linear_polynomial_test_template::<Bls12_381>().expect("test failed for bls12-381");
|
|
||||
}
|
|
||||
#[test]
|
|
||||
fn batch_check_test() {
|
|
||||
batch_check_test_template::<Bls12_381>().expect("test failed for bls12-381");
|
|
||||
}
|
|
||||
}
|
|
@ -1,127 +0,0 @@ |
|||||
//! Implementing Structured Reference Strings for univariate polynomial KZG
|
|
||||
|
|
||||
use crate::{prelude::PCSErrors, StructuredReferenceString};
|
|
||||
use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve};
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
|
||||
use ark_std::{end_timer, rand::RngCore, start_timer, One, UniformRand};
|
|
||||
|
|
||||
/// `UniversalParams` are the universal parameters for the KZG10 scheme.
|
|
||||
/// Adapted from
|
|
||||
/// https://github.com/arkworks-rs/poly-commit/blob/master/src/kzg10/data_structures.rs#L20
|
|
||||
#[derive(Clone, Debug)]
|
|
||||
pub struct UnivariateUniversalParams<E: PairingEngine> {
|
|
||||
/// Group elements of the form `{ \beta^i G }`, where `i` ranges from 0 to
|
|
||||
/// `degree`.
|
|
||||
pub powers_of_g: Vec<E::G1Affine>,
|
|
||||
/// The generator of G2.
|
|
||||
pub h: E::G2Affine,
|
|
||||
/// \beta times the above generator of G2.
|
|
||||
pub beta_h: E::G2Affine,
|
|
||||
}
|
|
||||
|
|
||||
/// Prover Parameters
|
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)]
|
|
||||
pub struct UnivariateProverParam<C: AffineCurve> {
|
|
||||
pub powers_of_g: Vec<C>,
|
|
||||
}
|
|
||||
|
|
||||
/// `VerifierKey` is used to check evaluation proofs for a given commitment.
|
|
||||
/// https://github.com/arkworks-rs/poly-commit/blob/master/src/kzg10/data_structures.rs#L236
|
|
||||
#[derive(Clone, Debug)]
|
|
||||
pub struct UnivariateVerifierParam<E: PairingEngine> {
|
|
||||
/// The generator of G1.
|
|
||||
pub g: E::G1Affine,
|
|
||||
/// The generator of G2.
|
|
||||
pub h: E::G2Affine,
|
|
||||
/// \beta times the above generator of G2.
|
|
||||
pub beta_h: E::G2Affine,
|
|
||||
}
|
|
||||
|
|
||||
impl<E: PairingEngine> StructuredReferenceString<E> for UnivariateUniversalParams<E> {
|
|
||||
type ProverParam = UnivariateProverParam<E::G1Affine>;
|
|
||||
type VerifierParam = UnivariateVerifierParam<E>;
|
|
||||
|
|
||||
/// Extract the prover parameters from the public parameters.
|
|
||||
fn extract_prover_param(&self, supported_log_size: usize) -> Self::ProverParam {
|
|
||||
let support_size = 1usize << supported_log_size;
|
|
||||
let powers_of_g = self.powers_of_g[..=support_size].to_vec();
|
|
||||
|
|
||||
Self::ProverParam { powers_of_g }
|
|
||||
}
|
|
||||
|
|
||||
/// Extract the verifier parameters from the public parameters.
|
|
||||
fn extract_verifier_param(&self, _supported_log_size: usize) -> Self::VerifierParam {
|
|
||||
Self::VerifierParam {
|
|
||||
g: self.powers_of_g[0],
|
|
||||
h: self.h,
|
|
||||
beta_h: self.beta_h,
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
/// Trim the universal parameters to specialize the public parameters
|
|
||||
/// for univariate polynomials to the given `supported_log_size`, and
|
|
||||
/// returns committer key and verifier key. `supported_log_size` should
|
|
||||
/// be in range `1..log(params.len())`
|
|
||||
fn trim(
|
|
||||
&self,
|
|
||||
supported_log_size: usize,
|
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> {
|
|
||||
let support_size = 1usize << supported_log_size;
|
|
||||
let powers_of_g = self.powers_of_g[..=support_size].to_vec();
|
|
||||
|
|
||||
let pk = Self::ProverParam { powers_of_g };
|
|
||||
let vk = Self::VerifierParam {
|
|
||||
g: self.powers_of_g[0],
|
|
||||
h: self.h,
|
|
||||
beta_h: self.beta_h,
|
|
||||
};
|
|
||||
Ok((pk, vk))
|
|
||||
}
|
|
||||
|
|
||||
/// Build SRS for testing.
|
|
||||
/// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY.
|
|
||||
/// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION.
|
|
||||
fn gen_srs_for_testing<R: RngCore>(rng: &mut R, log_degree: usize) -> Result<Self, PCSErrors> {
|
|
||||
let max_degree = 1usize << log_degree;
|
|
||||
let setup_time = start_timer!(|| format!("KZG10::Setup with degree {}", max_degree));
|
|
||||
let beta = E::Fr::rand(rng);
|
|
||||
let g = E::G1Projective::rand(rng);
|
|
||||
let h = E::G2Projective::rand(rng);
|
|
||||
|
|
||||
let mut powers_of_beta = vec![E::Fr::one()];
|
|
||||
|
|
||||
let mut cur = beta;
|
|
||||
for _ in 0..max_degree {
|
|
||||
powers_of_beta.push(cur);
|
|
||||
cur *= β
|
|
||||
}
|
|
||||
|
|
||||
let window_size = FixedBaseMSM::get_mul_window_size(max_degree + 1);
|
|
||||
|
|
||||
let scalar_bits = E::Fr::size_in_bits();
|
|
||||
let g_time = start_timer!(|| "Generating powers of G");
|
|
||||
// TODO: parallelization
|
|
||||
let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g);
|
|
||||
let powers_of_g = FixedBaseMSM::multi_scalar_mul::<E::G1Projective>(
|
|
||||
scalar_bits,
|
|
||||
window_size,
|
|
||||
&g_table,
|
|
||||
&powers_of_beta,
|
|
||||
);
|
|
||||
end_timer!(g_time);
|
|
||||
|
|
||||
let powers_of_g = E::G1Projective::batch_normalization_into_affine(&powers_of_g);
|
|
||||
|
|
||||
let h = h.into_affine();
|
|
||||
let beta_h = h.mul(beta).into_affine();
|
|
||||
|
|
||||
let pp = Self {
|
|
||||
powers_of_g,
|
|
||||
h,
|
|
||||
beta_h,
|
|
||||
};
|
|
||||
end_timer!(setup_time);
|
|
||||
Ok(pp)
|
|
||||
}
|
|
||||
}
|
|