- use sumcheck to batch open PCS - split Prod and witness into two batches - benchmark codemain
@ -0,0 +1,51 @@ |
|||||
|
filename = 'pie_chart.txt' |
||||
|
set terminal postscript eps enhanced color font "18" |
||||
|
set size square |
||||
|
set output "components.eps" |
||||
|
|
||||
|
rowi = 0 |
||||
|
rowf = 7 |
||||
|
|
||||
|
# obtain sum(column(2)) from rows `rowi` to `rowf` |
||||
|
set datafile separator ',' |
||||
|
stats filename u 2 every ::rowi::rowf noout prefix "A" |
||||
|
|
||||
|
# rowf should not be greater than length of file |
||||
|
rowf = (rowf-rowi > A_records - 1 ? A_records + rowi - 1 : rowf) |
||||
|
|
||||
|
angle(x)=x*360/A_sum |
||||
|
percentage(x)=x*100/A_sum |
||||
|
|
||||
|
# circumference dimensions for pie-chart |
||||
|
centerX=0 |
||||
|
centerY=0 |
||||
|
radius=1 |
||||
|
|
||||
|
# label positions |
||||
|
yposmin = 0.0 |
||||
|
yposmax = 0.95*radius |
||||
|
xpos = -0.8*radius |
||||
|
ypos(i) = -2.2*radius + yposmax - i*(yposmax-yposmin)/(1.0*rowf-rowi) |
||||
|
|
||||
|
#------------------------------------------------------------------- |
||||
|
# now we can configure the canvas |
||||
|
set style fill solid 1 # filled pie-chart |
||||
|
unset key # no automatic labels |
||||
|
unset tics # remove tics |
||||
|
unset border # remove borders; if some label is missing, comment to see what is happening |
||||
|
|
||||
|
set size ratio -1 # equal scale length |
||||
|
set xrange [-radius:3*radius] # [-1:2] leaves space for labels |
||||
|
set yrange [-3*radius:radius] # [-1:1] |
||||
|
|
||||
|
#------------------------------------------------------------------- |
||||
|
pos = 0 # init angle |
||||
|
colour = 0 # init colour |
||||
|
|
||||
|
# 1st line: plot pie-chart |
||||
|
# 2nd line: draw colored boxes at (xpos):(ypos) |
||||
|
# 3rd line: place labels at (xpos+offset):(ypos) |
||||
|
plot filename u (centerX):(centerY):(radius):(pos):(pos=pos+angle($2)):(colour=colour+1) every ::rowi::rowf w circle lc var,\ |
||||
|
for [i=0:rowf-rowi] '+' u (xpos):(ypos(i)) w p pt 5 ps 4 lc i+1,\ |
||||
|
for [i=0:rowf-rowi] filename u (xpos):(ypos(i)):(sprintf('%04.1f%% %s', percentage($2), stringcolumn(1))) every ::i+rowi::i+rowi w labels left offset 3,0 |
||||
|
|
@ -0,0 +1,31 @@ |
|||||
|
set terminal postscript eps enhanced color font "18" |
||||
|
filename = '64threads_growing_degree.txt' |
||||
|
set output "grow_degree.eps" |
||||
|
|
||||
|
# set font "32" |
||||
|
|
||||
|
set key left top |
||||
|
set grid |
||||
|
# set logscale y |
||||
|
# set logscale x |
||||
|
|
||||
|
|
||||
|
set title font ",64" |
||||
|
set key font ",18" |
||||
|
set xtics font ",20" |
||||
|
set ytics font ",20" |
||||
|
set xlabel font ",20" |
||||
|
set ylabel font ",20" |
||||
|
|
||||
|
# set key title "IOP proving time" |
||||
|
set key title font ", 20" |
||||
|
# set key title "2^{15} constraints" |
||||
|
set xlabel "degree d" |
||||
|
set ylabel 'time (us)' |
||||
|
# set yrange [] |
||||
|
# set xrange [500000:1100000] |
||||
|
# set xtics (0, 1,2,4,8,16,32) |
||||
|
plot filename using 1:2 w lp t "q_Lw_1 + q_Rw_2 + q_Mw_1^{d-1}w_2 + q_C = 0", |
||||
|
|
||||
|
|
||||
|
reset |
@ -0,0 +1,59 @@ |
|||||
|
set terminal postscript eps enhanced color font "18" |
||||
|
sumcheck = 'iop/sum_check.txt' |
||||
|
zerocheck = 'iop/zero_check.txt' |
||||
|
permcheck = 'iop/perm_check.txt' |
||||
|
prodcheck = 'iop/prod_check.txt' |
||||
|
|
||||
|
set output "iop_prover.eps" |
||||
|
|
||||
|
set font "64" |
||||
|
|
||||
|
set key left |
||||
|
set grid |
||||
|
set logscale y |
||||
|
|
||||
|
set title font ",64" |
||||
|
set key font ",18" |
||||
|
set xtics font ",20" |
||||
|
set ytics font ",20" |
||||
|
set xlabel font ",20" |
||||
|
set ylabel font ",20" |
||||
|
|
||||
|
set key title "IOP proving time" |
||||
|
set key title font ", 20" |
||||
|
set xlabel "\#variables" |
||||
|
set ylabel 'time (ms)' |
||||
|
# set xtics (4,8,16,32,64) |
||||
|
plot sumcheck using 1:2 w lp t "Sum Check",\ |
||||
|
zerocheck using 1:2 w lp t "Zero Check",\ |
||||
|
prodcheck using 1:2 w lp t "Prod Check",\ |
||||
|
permcheck using 1:2 w lp t "Perm Check", |
||||
|
reset |
||||
|
|
||||
|
|
||||
|
# set terminal postscript eps enhanced color |
||||
|
# sumcheck = 'iop/sum_check.txt' |
||||
|
# zerocheck = 'iop/zero_check.txt' |
||||
|
# permcheck = 'iop/perm_check.txt' |
||||
|
# prodcheck = 'iop/prod_check.txt' |
||||
|
|
||||
|
# set output "iop_verifier.eps" |
||||
|
|
||||
|
# set font "32" |
||||
|
|
||||
|
# set key left |
||||
|
# set grid |
||||
|
# set logscale y |
||||
|
|
||||
|
# set title font ",10" |
||||
|
# set key title "IOP verifier time" |
||||
|
# set xlabel "\#variables" |
||||
|
# set ylabel 'log time (us)' |
||||
|
# # set xtics (4,8,16,32,64) |
||||
|
# plot sumcheck using 1:3 w lp t "Sum Check",\ |
||||
|
# zerocheck using 1:3 w lp t "Zero Check",\ |
||||
|
# prodcheck using 1:3 w lp t "Prod Check",\ |
||||
|
# permcheck using 1:3 w lp t "Perm Check", |
||||
|
# reset |
||||
|
|
||||
|
|
@ -0,0 +1,50 @@ |
|||||
|
filename = 'msm_vs_eval.txt' |
||||
|
set terminal postscript eps enhanced color font "18" |
||||
|
set size square |
||||
|
set output "msm_vs_eval.eps" |
||||
|
|
||||
|
rowi = 0 |
||||
|
rowf = 3 |
||||
|
|
||||
|
# obtain sum(column(2)) from rows `rowi` to `rowf` |
||||
|
set datafile separator ',' |
||||
|
stats filename u 2 every ::rowi::rowf noout prefix "A" |
||||
|
|
||||
|
# rowf should not be greater than length of file |
||||
|
rowf = (rowf-rowi > A_records - 1 ? A_records + rowi - 1 : rowf) |
||||
|
|
||||
|
angle(x)=x*360/A_sum |
||||
|
percentage(x)=x*100/A_sum |
||||
|
|
||||
|
# circumference dimensions for pie-chart |
||||
|
centerX=0 |
||||
|
centerY=0 |
||||
|
radius=1 |
||||
|
|
||||
|
# label positions |
||||
|
yposmin = 0.0 |
||||
|
yposmax = 0.95*radius |
||||
|
xpos = -0.8*radius |
||||
|
ypos(i) = -2.2*radius + yposmax - i*(yposmax-yposmin)/(1.0*rowf-rowi) |
||||
|
|
||||
|
#------------------------------------------------------------------- |
||||
|
# now we can configure the canvas |
||||
|
set style fill solid 1 # filled pie-chart |
||||
|
unset key # no automatic labels |
||||
|
unset tics # remove tics |
||||
|
unset border # remove borders; if some label is missing, comment to see what is happening |
||||
|
|
||||
|
set size ratio -1 # equal scale length |
||||
|
set xrange [-radius:radius] # [-1:2] leaves space for labels |
||||
|
set yrange [-3*radius:radius] # [-1:1] |
||||
|
|
||||
|
#------------------------------------------------------------------- |
||||
|
pos = 0 # init angle |
||||
|
colour = 0 # init colour |
||||
|
|
||||
|
# 1st line: plot pie-chart |
||||
|
# 2nd line: draw colored boxes at (xpos):(ypos) |
||||
|
# 3rd line: place labels at (xpos+offset):(ypos) |
||||
|
plot filename u (centerX):(centerY):(radius):(pos):(pos=pos+angle($2)):(colour=colour+1) every ::rowi::rowf w circle lc var,\ |
||||
|
for [i=0:rowf-rowi] '+' u (xpos):(ypos(i)) w p pt 5 ps 4 lc i+1,\ |
||||
|
for [i=0:rowf-rowi] filename u (xpos):(ypos(i)):(sprintf('%05.2f%% %s', percentage($2), stringcolumn(1))) every ::i+rowi::i+rowi w labels left offset 3,0 |
@ -0,0 +1,32 @@ |
|||||
|
set terminal postscript eps enhanced color "18" |
||||
|
filename = 'degree_16_grow_threads_1006.txt' |
||||
|
set output "vanilla_multi_threads.eps" |
||||
|
|
||||
|
# set font "32" |
||||
|
|
||||
|
# set font "32" |
||||
|
|
||||
|
set key left |
||||
|
set grid |
||||
|
set logscale y |
||||
|
|
||||
|
set title font ",64" |
||||
|
set key font ",18" |
||||
|
set xtics font ",20" |
||||
|
set ytics font ",20" |
||||
|
set xlabel font ",20" |
||||
|
set ylabel font ",20" |
||||
|
set key title font ", 20" |
||||
|
|
||||
|
set key title "Multi-threading\n performance" |
||||
|
set xlabel "\#threads" |
||||
|
set ylabel 'time (us)' |
||||
|
plot filename using 1:2 w lp t "1 thread",\ |
||||
|
filename using 1:3 w lp t "2 threads",\ |
||||
|
filename using 1:4 w lp t "4 threads",\ |
||||
|
filename using 1:5 w lp t "8 threads",\ |
||||
|
filename using 1:6 w lp t "16 threads",\ |
||||
|
filename using 1:7 w lp t "32 threads",\ |
||||
|
|
||||
|
|
||||
|
reset |
@ -0,0 +1,55 @@ |
|||||
|
set terminal postscript eps enhanced color font "18" |
||||
|
spartan = 'comparison/spartan.txt' |
||||
|
hyperplonk = 'comparison/hyperplonk.txt' |
||||
|
jellyfish = 'comparison/jellyfish.txt' |
||||
|
|
||||
|
set output "spartan_prover.eps" |
||||
|
|
||||
|
set title font ",64" |
||||
|
set key font ",18" |
||||
|
set xtics font ",20" |
||||
|
set ytics font ",20" |
||||
|
set xlabel font ",20" |
||||
|
set ylabel font ",20" |
||||
|
|
||||
|
set key left |
||||
|
set grid |
||||
|
set logscale y |
||||
|
|
||||
|
set xrange [9:20] |
||||
|
# set title font ",10" |
||||
|
# set key title "Proving time" |
||||
|
set xlabel "log \# constraits" |
||||
|
set ylabel 'time (sec)' |
||||
|
# set xtics (4,8,16,32,64) |
||||
|
plot spartan using 1:2 w lp t "spartan",\ |
||||
|
jellyfish using 1:2 w lp t "jellyfish plonk",\ |
||||
|
hyperplonk using 1:2 w lp t "hyperplonk", |
||||
|
reset |
||||
|
|
||||
|
set terminal postscript eps enhanced color font "18" |
||||
|
ratio = 'comparison/ratio.txt' |
||||
|
|
||||
|
set output "ratio.eps" |
||||
|
|
||||
|
|
||||
|
set title font ",64" |
||||
|
set key font ",18" |
||||
|
set xtics font ",20" |
||||
|
set ytics font ",20" |
||||
|
set xlabel font ",20" |
||||
|
set ylabel font ",20" |
||||
|
# set font "32" |
||||
|
|
||||
|
set key left |
||||
|
set grid |
||||
|
# set logscale y |
||||
|
|
||||
|
set xrange [9:20] |
||||
|
set title font ",10" |
||||
|
# set key title "Proving time" |
||||
|
set xlabel "log \# constraits" |
||||
|
set ylabel 'ratio ' |
||||
|
# set xtics (4,8,16,32,64) |
||||
|
plot ratio using 1:6 w lp t "Jellyfish/Hyperplonk",\ |
||||
|
ratio using 1:7 w lp t "Spartan/Hyperplonk" |
@ -1,51 +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"] } |
|
||||
|
|
||||
arithmetic = { path = "../arithmetic" } |
|
||||
transcript = { path = "../transcript" } |
|
||||
util = { path = "../util" } |
|
||||
|
|
||||
rayon = { version = "1.5.2", default-features = false, optional = true } |
|
||||
itertools = { version = "0.10.4", optional = true } |
|
||||
|
|
||||
# Benchmarks |
|
||||
[[bench]] |
|
||||
name = "pcs-benches" |
|
||||
path = "benches/bench.rs" |
|
||||
harness = false |
|
||||
|
|
||||
[features] |
|
||||
# default = [ "parallel", "print-trace" ] |
|
||||
default = [ "parallel",] |
|
||||
extensive_sanity_checks = [ ] |
|
||||
parallel = [ |
|
||||
"rayon", |
|
||||
"itertools", |
|
||||
"ark-std/parallel", |
|
||||
"ark-ff/parallel", |
|
||||
"ark-poly/parallel", |
|
||||
"ark-ec/parallel", |
|
||||
"util/parallel", |
|
||||
"arithmetic/parallel", |
|
||||
] |
|
||||
print-trace = [ |
|
||||
"ark-std/print-trace", |
|
||||
"arithmetic/print-trace", |
|
||||
] |
|
@ -1,11 +0,0 @@ |
|||||
// Copyright (c) 2022 Espresso Systems (espressosys.com)
|
|
||||
// This file is part of the Jellyfish library.
|
|
||||
|
|
||||
// You should have received a copy of the MIT License
|
|
||||
// along with the Jellyfish library. If not, see <https://mit-license.org/>.
|
|
||||
|
|
||||
mod multi_poly;
|
|
||||
mod single_poly;
|
|
||||
|
|
||||
pub(crate) use multi_poly::*;
|
|
||||
pub(crate) use single_poly::*;
|
|
@ -1,426 +0,0 @@ |
|||||
use crate::{
|
|
||||
multilinear_kzg::{
|
|
||||
open_internal,
|
|
||||
srs::{MultilinearProverParam, MultilinearVerifierParam},
|
|
||||
util::compute_w_circ_l,
|
|
||||
verify_internal, MultilinearKzgBatchProof,
|
|
||||
},
|
|
||||
prelude::{Commitment, UnivariateProverParam, UnivariateVerifierParam},
|
|
||||
univariate_kzg::UnivariateKzgPCS,
|
|
||||
PCSError, PolynomialCommitmentScheme,
|
|
||||
};
|
|
||||
use arithmetic::{build_l, get_uni_domain, merge_polynomials};
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_poly::{DenseMultilinearExtension, EvaluationDomain, MultilinearExtension, Polynomial};
|
|
||||
use ark_std::{end_timer, format, rc::Rc, start_timer, string::ToString, vec, 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, and utilize 'multi_open_same_poly_internal'
|
|
||||
/// API.
|
|
||||
///
|
|
||||
/// 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(crate) 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<(MultilinearKzgBatchProof<E>, Vec<E::Fr>), PCSError> {
|
|
||||
let open_timer = start_timer!(|| "multi open");
|
|
||||
|
|
||||
// ===================================
|
|
||||
// Sanity checks on inputs
|
|
||||
// ===================================
|
|
||||
let points_len = points.len();
|
|
||||
if points_len == 0 {
|
|
||||
return Err(PCSError::InvalidParameters("points is empty".to_string()));
|
|
||||
}
|
|
||||
|
|
||||
if points_len != polynomials.len() {
|
|
||||
return Err(PCSError::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(PCSError::InvalidParameters(
|
|
||||
"polynomials do not have same num_vars".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
}
|
|
||||
for point in points.iter() {
|
|
||||
if point.len() != num_var {
|
|
||||
return Err(PCSError::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(points, &domain, true)?;
|
|
||||
|
|
||||
// 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, points.len(), true)?;
|
|
||||
|
|
||||
// 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 = UnivariateKzgPCS::<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) =
|
|
||||
UnivariateKzgPCS::<E>::open(uni_prover_param, &q_x, &domain.element(i))?;
|
|
||||
q_x_opens.push(q_x_open);
|
|
||||
q_x_evals.push(q_x_eval);
|
|
||||
#[cfg(feature = "extensive_sanity_checks")]
|
|
||||
{
|
|
||||
// sanity check
|
|
||||
let point: Vec<E::Fr> = uni_polys
|
|
||||
.iter()
|
|
||||
.map(|poly| poly.evaluate(&domain.element(i)))
|
|
||||
.collect();
|
|
||||
let mle_eval = merge_poly.evaluate(&point).unwrap();
|
|
||||
if mle_eval != q_x_eval {
|
|
||||
return Err(PCSError::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) = UnivariateKzgPCS::<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().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(PCSError::InvalidProver(
|
|
||||
"Q(r) does not match W(l(r))".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
end_timer!(open_timer);
|
|
||||
Ok((
|
|
||||
MultilinearKzgBatchProof {
|
|
||||
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(crate) 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: &MultilinearKzgBatchProof<E>,
|
|
||||
) -> Result<bool, PCSError> {
|
|
||||
let verify_timer = start_timer!(|| "batch verify");
|
|
||||
|
|
||||
// ===================================
|
|
||||
// Sanity checks on inputs
|
|
||||
// ===================================
|
|
||||
let points_len = points.len();
|
|
||||
if points_len == 0 {
|
|
||||
return Err(PCSError::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(PCSError::InvalidParameters(format!(
|
|
||||
"openings length {} does not match point length {}",
|
|
||||
points_len + 1,
|
|
||||
batch_proof.q_x_opens.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
if points_len + 1 != values.len() {
|
|
||||
return Err(PCSError::InvalidParameters(format!(
|
|
||||
"values length {} does not match point length {}",
|
|
||||
points_len + 1,
|
|
||||
values.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
let num_var = points[0].len();
|
|
||||
for point in points.iter().skip(1) {
|
|
||||
if point.len() != num_var {
|
|
||||
return Err(PCSError::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 !UnivariateKzgPCS::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 !UnivariateKzgPCS::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(points, &domain, true)?;
|
|
||||
|
|
||||
// 5. get a point `p := l(r)`
|
|
||||
let point: Vec<E::Fr> = uni_polys.iter().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::*;
|
|
||||
use crate::{
|
|
||||
multilinear_kzg::{
|
|
||||
srs::MultilinearUniversalParams,
|
|
||||
util::{compute_qx_degree, generate_evaluations_multi_poly},
|
|
||||
MultilinearKzgPCS, MultilinearKzgProof,
|
|
||||
},
|
|
||||
prelude::UnivariateUniversalParams,
|
|
||||
StructuredReferenceString,
|
|
||||
};
|
|
||||
use arithmetic::get_batched_nv;
|
|
||||
use ark_bls12_381::Bls12_381 as E;
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
|
||||
use ark_std::{
|
|
||||
log2,
|
|
||||
rand::{CryptoRng, RngCore},
|
|
||||
test_rng,
|
|
||||
vec::Vec,
|
|
||||
UniformRand,
|
|
||||
};
|
|
||||
|
|
||||
type Fr = <E as PairingEngine>::Fr;
|
|
||||
|
|
||||
fn test_multi_open_helper<R: RngCore + CryptoRng>(
|
|
||||
uni_params: &UnivariateUniversalParams<E>,
|
|
||||
ml_params: &MultilinearUniversalParams<E>,
|
|
||||
polys: &[Rc<DenseMultilinearExtension<Fr>>],
|
|
||||
rng: &mut R,
|
|
||||
) -> Result<(), PCSError> {
|
|
||||
let merged_nv = get_batched_nv(polys[0].num_vars(), polys.len());
|
|
||||
let qx_degree = compute_qx_degree(merged_nv, polys.len());
|
|
||||
let padded_qx_degree = 1usize << log2(qx_degree);
|
|
||||
|
|
||||
let (uni_ck, uni_vk) = uni_params.trim(padded_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_multi_poly(polys, &points)?;
|
|
||||
|
|
||||
let com = MultilinearKzgPCS::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(<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,
|
|
||||
&MultilinearKzgBatchProof {
|
|
||||
proof: MultilinearKzgProof { proofs: Vec::new() },
|
|
||||
q_x_commit: 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;
|
|
||||
wrong_proof.q_x_commit = Commitment(<E as PairingEngine>::G1Affine::default());
|
|
||||
assert!(!batch_verify_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&com,
|
|
||||
&points,
|
|
||||
&evaluations,
|
|
||||
&wrong_proof,
|
|
||||
)?);
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_multi_open_internal() -> Result<(), PCSError> {
|
|
||||
let mut rng = test_rng();
|
|
||||
|
|
||||
let uni_params =
|
|
||||
UnivariateUniversalParams::<E>::gen_srs_for_testing(&mut rng, 1usize << 15)?;
|
|
||||
let ml_params = MultilinearUniversalParams::<E>::gen_srs_for_testing(&mut rng, 15)?;
|
|
||||
for num_poly in 1..10 {
|
|
||||
for nv in 1..5 {
|
|
||||
let polys1: Vec<_> = (0..num_poly)
|
|
||||
.map(|_| Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)))
|
|
||||
.collect();
|
|
||||
test_multi_open_helper(&uni_params, &ml_params, &polys1, &mut rng)?;
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
@ -1,368 +0,0 @@ |
|||||
// Copyright (c) 2022 Espresso Systems (espressosys.com)
|
|
||||
// This file is part of the Jellyfish library.
|
|
||||
|
|
||||
// You should have received a copy of the MIT License
|
|
||||
// along with the Jellyfish library. If not, see <https://mit-license.org/>.
|
|
||||
|
|
||||
use crate::{
|
|
||||
multilinear_kzg::{
|
|
||||
open_internal,
|
|
||||
srs::{MultilinearProverParam, MultilinearVerifierParam},
|
|
||||
util::compute_w_circ_l,
|
|
||||
verify_internal, MultilinearKzgBatchProof,
|
|
||||
},
|
|
||||
prelude::{Commitment, UnivariateProverParam, UnivariateVerifierParam},
|
|
||||
univariate_kzg::UnivariateKzgPCS,
|
|
||||
PCSError, PolynomialCommitmentScheme,
|
|
||||
};
|
|
||||
use arithmetic::{build_l, get_uni_domain};
|
|
||||
use ark_ec::PairingEngine;
|
|
||||
use ark_poly::{DenseMultilinearExtension, EvaluationDomain, MultilinearExtension, Polynomial};
|
|
||||
use ark_std::{end_timer, format, rc::Rc, start_timer, string::ToString, vec, vec::Vec};
|
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
|
||||
use transcript::IOPTranscript;
|
|
||||
|
|
||||
/// Input
|
|
||||
/// - the prover parameters for univariate KZG,
|
|
||||
/// - the prover parameters for multilinear KZG,
|
|
||||
/// - a single MLE,
|
|
||||
/// - a commitment to the MLE
|
|
||||
/// - and a list of points,
|
|
||||
/// compute a multi-opening for this polynomial.
|
|
||||
///
|
|
||||
/// 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 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
|
|
||||
/// 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(crate) fn multi_open_same_poly_internal<E: PairingEngine>(
|
|
||||
uni_prover_param: &UnivariateProverParam<E::G1Affine>,
|
|
||||
ml_prover_param: &MultilinearProverParam<E>,
|
|
||||
polynomial: &Rc<DenseMultilinearExtension<E::Fr>>,
|
|
||||
commitment: &Commitment<E>,
|
|
||||
points: &[Vec<E::Fr>],
|
|
||||
) -> Result<(MultilinearKzgBatchProof<E>, Vec<E::Fr>), PCSError> {
|
|
||||
let open_timer = start_timer!(|| "multi open");
|
|
||||
|
|
||||
// ===================================
|
|
||||
// Sanity checks on inputs
|
|
||||
// ===================================
|
|
||||
let points_len = points.len();
|
|
||||
if points_len == 0 {
|
|
||||
return Err(PCSError::InvalidParameters("points is empty".to_string()));
|
|
||||
}
|
|
||||
|
|
||||
let num_var = polynomial.num_vars();
|
|
||||
for point in points.iter() {
|
|
||||
if point.len() != num_var {
|
|
||||
return Err(PCSError::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(points, &domain, false)?;
|
|
||||
|
|
||||
// 3. build `q(x)` which is a univariate polynomial `W circ l`
|
|
||||
let q_x = compute_w_circ_l(polynomial, &uni_polys, points.len(), false)?;
|
|
||||
|
|
||||
// 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", commitment)?;
|
|
||||
for point in points {
|
|
||||
transcript.append_serializable_element(b"w", point)?;
|
|
||||
}
|
|
||||
|
|
||||
let q_x_commit = UnivariateKzgPCS::<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) =
|
|
||||
UnivariateKzgPCS::<E>::open(uni_prover_param, &q_x, &domain.element(i))?;
|
|
||||
q_x_opens.push(q_x_open);
|
|
||||
q_x_evals.push(q_x_eval);
|
|
||||
|
|
||||
#[cfg(feature = "extensive_sanity_checks")]
|
|
||||
{
|
|
||||
// sanity check
|
|
||||
let point: Vec<E::Fr> = uni_polys
|
|
||||
.iter()
|
|
||||
.map(|poly| poly.evaluate(&domain.element(i)))
|
|
||||
.collect();
|
|
||||
let mle_eval = polynomial.evaluate(&point).unwrap();
|
|
||||
if mle_eval != q_x_eval {
|
|
||||
return Err(PCSError::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) = UnivariateKzgPCS::<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
|
|
||||
.into_par_iter()
|
|
||||
.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, polynomial, &point)?;
|
|
||||
|
|
||||
// 9. output value that is `w` evaluated at `p` (which should match `q(r)`)
|
|
||||
if mle_eval != q_r_value {
|
|
||||
return Err(PCSError::InvalidProver(
|
|
||||
"Q(r) does not match W(l(r))".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
end_timer!(open_timer);
|
|
||||
Ok((
|
|
||||
MultilinearKzgBatchProof {
|
|
||||
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
|
|
||||
#[allow(dead_code)]
|
|
||||
pub(crate) fn batch_verify_same_poly_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: &MultilinearKzgBatchProof<E>,
|
|
||||
) -> Result<bool, PCSError> {
|
|
||||
let verify_timer = start_timer!(|| "batch verify");
|
|
||||
|
|
||||
// ===================================
|
|
||||
// Sanity checks on inputs
|
|
||||
// ===================================
|
|
||||
let points_len = points.len();
|
|
||||
if points_len == 0 {
|
|
||||
return Err(PCSError::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(PCSError::InvalidParameters(format!(
|
|
||||
"openings length {} does not match point length {}",
|
|
||||
points_len + 1,
|
|
||||
batch_proof.q_x_opens.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
if points_len + 1 != values.len() {
|
|
||||
return Err(PCSError::InvalidParameters(format!(
|
|
||||
"values length {} does not match point length {}",
|
|
||||
points_len + 1,
|
|
||||
values.len()
|
|
||||
)));
|
|
||||
}
|
|
||||
|
|
||||
let num_var = points[0].len();
|
|
||||
for point in points.iter().skip(1) {
|
|
||||
if point.len() != num_var {
|
|
||||
return Err(PCSError::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 !UnivariateKzgPCS::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 !UnivariateKzgPCS::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(points, &domain, false)?;
|
|
||||
|
|
||||
// 5. get a point `p := l(r)`
|
|
||||
let point: Vec<E::Fr> = uni_polys.iter().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::*;
|
|
||||
use crate::{
|
|
||||
multilinear_kzg::{
|
|
||||
srs::MultilinearUniversalParams,
|
|
||||
util::{compute_qx_degree, generate_evaluations_single_poly},
|
|
||||
MultilinearKzgPCS,
|
|
||||
},
|
|
||||
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::{CryptoRng, RngCore},
|
|
||||
test_rng,
|
|
||||
vec::Vec,
|
|
||||
UniformRand,
|
|
||||
};
|
|
||||
type Fr = <E as PairingEngine>::Fr;
|
|
||||
|
|
||||
fn test_same_poly_multi_open_internal_helper<R: RngCore + CryptoRng>(
|
|
||||
uni_params: &UnivariateUniversalParams<E>,
|
|
||||
ml_params: &MultilinearUniversalParams<E>,
|
|
||||
poly: &Rc<DenseMultilinearExtension<Fr>>,
|
|
||||
point_len: usize,
|
|
||||
rng: &mut R,
|
|
||||
) -> Result<(), PCSError> {
|
|
||||
let nv = poly.num_vars;
|
|
||||
let qx_degree = compute_qx_degree(nv, point_len);
|
|
||||
let padded_qx_degree = 1usize << log2(qx_degree);
|
|
||||
|
|
||||
let (uni_ck, uni_vk) = uni_params.trim(padded_qx_degree)?;
|
|
||||
let (ml_ck, ml_vk) = ml_params.trim(nv)?;
|
|
||||
|
|
||||
let mut points = Vec::new();
|
|
||||
let mut eval = Vec::new();
|
|
||||
for _ in 0..point_len {
|
|
||||
let point = (0..nv).map(|_| Fr::rand(rng)).collect::<Vec<Fr>>();
|
|
||||
eval.push(poly.evaluate(&point).unwrap());
|
|
||||
points.push(point);
|
|
||||
}
|
|
||||
|
|
||||
let evals = generate_evaluations_single_poly(poly, &points)?;
|
|
||||
let com = MultilinearKzgPCS::commit(&(ml_ck.clone(), uni_ck.clone()), poly)?;
|
|
||||
let (batch_proof, evaluations) =
|
|
||||
multi_open_same_poly_internal(&uni_ck, &ml_ck, poly, &com, &points)?;
|
|
||||
|
|
||||
for (a, b) in evals.iter().zip(evaluations.iter()) {
|
|
||||
assert_eq!(a, b)
|
|
||||
}
|
|
||||
|
|
||||
// good path
|
|
||||
assert!(batch_verify_same_poly_internal(
|
|
||||
&uni_vk,
|
|
||||
&ml_vk,
|
|
||||
&com,
|
|
||||
&points,
|
|
||||
&evaluations,
|
|
||||
&batch_proof,
|
|
||||
)?);
|
|
||||
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_same_poly_multi_open_internal() -> Result<(), PCSError> {
|
|
||||
let mut rng = test_rng();
|
|
||||
|
|
||||
let uni_params =
|
|
||||
UnivariateUniversalParams::<E>::gen_srs_for_testing(&mut rng, 1usize << 15)?;
|
|
||||
let ml_params = MultilinearUniversalParams::<E>::gen_srs_for_testing(&mut rng, 15)?;
|
|
||||
for nv in 1..10 {
|
|
||||
for point_len in 1..10 {
|
|
||||
// normal polynomials
|
|
||||
let polys1 = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
|
|
||||
test_same_poly_multi_open_internal_helper(
|
|
||||
&uni_params,
|
|
||||
&ml_params,
|
|
||||
&polys1,
|
|
||||
point_len,
|
|
||||
&mut rng,
|
|
||||
)?;
|
|
||||
}
|
|
||||
}
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
@ -1,432 +0,0 @@ |
|||||
// Copyright (c) 2022 Espresso Systems (espressosys.com)
|
|
||||
// This file is part of the Jellyfish library.
|
|
||||
|
|
||||
// You should have received a copy of the MIT License
|
|
||||
// along with the Jellyfish library. If not, see <https://mit-license.org/>.
|
|
||||
|
|
||||
//! Useful utilities for KZG PCS
|
|
||||
use crate::prelude::PCSError;
|
|
||||
use arithmetic::evaluate_no_par;
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_poly::{
|
|
||||
univariate::DensePolynomial, DenseMultilinearExtension, EvaluationDomain, Evaluations,
|
|
||||
MultilinearExtension, Polynomial, Radix2EvaluationDomain,
|
|
||||
};
|
|
||||
use ark_std::{end_timer, format, log2, start_timer, string::ToString, vec::Vec};
|
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
|
||||
|
|
||||
/// 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 * ((1 << log2(point_len)) - 1) + 1
|
|
||||
}
|
|
||||
|
|
||||
/// 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>],
|
|
||||
num_points: usize,
|
|
||||
with_suffix: bool,
|
|
||||
) -> Result<DensePolynomial<F>, PCSError> {
|
|
||||
let timer = start_timer!(|| "compute W \\circ l");
|
|
||||
|
|
||||
if w.num_vars != l.len() {
|
|
||||
return Err(PCSError::InvalidParameters(format!(
|
|
||||
"l's length ({}) does not match num_variables ({})",
|
|
||||
l.len(),
|
|
||||
w.num_vars(),
|
|
||||
)));
|
|
||||
}
|
|
||||
let uni_degree = if with_suffix {
|
|
||||
compute_qx_degree(w.num_vars() + log2(num_points) as usize, num_points)
|
|
||||
} else {
|
|
||||
compute_qx_degree(w.num_vars(), num_points)
|
|
||||
};
|
|
||||
|
|
||||
let domain = match Radix2EvaluationDomain::<F>::new(uni_degree) {
|
|
||||
Some(p) => p,
|
|
||||
None => {
|
|
||||
return Err(PCSError::InvalidParameters(
|
|
||||
"failed to build radix 2 domain".to_string(),
|
|
||||
))
|
|
||||
},
|
|
||||
};
|
|
||||
|
|
||||
let step = start_timer!(|| format!("compute eval {}-dim domain", domain.size()));
|
|
||||
let res_eval = (0..domain.size())
|
|
||||
.into_par_iter()
|
|
||||
.map(|i| {
|
|
||||
let l_eval: Vec<F> = l.iter().map(|x| x.evaluate(&domain.element(i))).collect();
|
|
||||
evaluate_no_par(w, &l_eval)
|
|
||||
})
|
|
||||
.collect();
|
|
||||
end_timer!(step);
|
|
||||
|
|
||||
let evaluation = Evaluations::from_vec_and_domain(res_eval, domain);
|
|
||||
let res = evaluation.interpolate();
|
|
||||
|
|
||||
end_timer!(timer);
|
|
||||
Ok(res)
|
|
||||
}
|
|
||||
|
|
||||
/// 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_multi_poly<F: PrimeField>(
|
|
||||
polynomials: &[std::rc::Rc<DenseMultilinearExtension<F>>],
|
|
||||
points: &[Vec<F>],
|
|
||||
) -> Result<Vec<F>, PCSError> {
|
|
||||
use arithmetic::{build_l, get_uni_domain, merge_polynomials};
|
|
||||
|
|
||||
if polynomials.len() != points.len() {
|
|
||||
return Err(PCSError::InvalidParameters(
|
|
||||
"polynomial length does not match point length".to_string(),
|
|
||||
));
|
|
||||
}
|
|
||||
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(points, &domain, true)?;
|
|
||||
let mut mle_values = vec![];
|
|
||||
|
|
||||
for i in 0..uni_poly_degree {
|
|
||||
let point: Vec<F> = uni_polys
|
|
||||
.iter()
|
|
||||
.map(|poly| poly.evaluate(&domain.element(i)))
|
|
||||
.collect();
|
|
||||
|
|
||||
let mle_value = merge_poly.evaluate(&point).unwrap();
|
|
||||
mle_values.push(mle_value)
|
|
||||
}
|
|
||||
Ok(mle_values)
|
|
||||
}
|
|
||||
|
|
||||
/// 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_single_poly<F: PrimeField>(
|
|
||||
polynomial: &std::rc::Rc<DenseMultilinearExtension<F>>,
|
|
||||
points: &[Vec<F>],
|
|
||||
) -> Result<Vec<F>, PCSError> {
|
|
||||
use arithmetic::{build_l, get_uni_domain};
|
|
||||
|
|
||||
let uni_poly_degree = points.len();
|
|
||||
|
|
||||
let domain = get_uni_domain::<F>(uni_poly_degree)?;
|
|
||||
let uni_polys = build_l(points, &domain, false)?;
|
|
||||
let mut mle_values = vec![];
|
|
||||
|
|
||||
for i in 0..uni_poly_degree {
|
|
||||
let point: Vec<F> = uni_polys
|
|
||||
.iter()
|
|
||||
.map(|poly| poly.evaluate(&domain.element(i)))
|
|
||||
.collect();
|
|
||||
|
|
||||
let mle_value = polynomial.evaluate(&point).unwrap();
|
|
||||
mle_values.push(mle_value)
|
|
||||
}
|
|
||||
Ok(mle_values)
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod test {
|
|
||||
use super::*;
|
|
||||
use arithmetic::{build_l, get_uni_domain, merge_polynomials};
|
|
||||
use ark_bls12_381::Fr;
|
|
||||
use ark_poly::UVPolynomial;
|
|
||||
use ark_std::{One, Zero};
|
|
||||
use std::rc::Rc;
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_w_circ_l() -> Result<(), PCSError> {
|
|
||||
test_w_circ_l_helper::<Fr>()
|
|
||||
}
|
|
||||
|
|
||||
fn test_w_circ_l_helper<F: PrimeField>() -> Result<(), PCSError> {
|
|
||||
{
|
|
||||
// Example from page 53:
|
|
||||
// W = 3x1x2 + 2x2 whose evaluations are
|
|
||||
// 0, 0 |-> 0
|
|
||||
// 1, 0 |-> 0
|
|
||||
// 0, 1 |-> 2
|
|
||||
// 1, 1 |-> 5
|
|
||||
let w_eval = vec![F::zero(), F::zero(), F::from(2u64), 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(), 4, false)?;
|
|
||||
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
|
|
||||
// 1, 0, 0 |-> 5
|
|
||||
// 0, 1, 0 |-> -6
|
|
||||
// 1, 1, 0 |-> -3
|
|
||||
// 0, 0, 1 |-> 7
|
|
||||
// 1, 0, 1 |-> 8
|
|
||||
// 0, 1, 1 |-> 4
|
|
||||
// 1, 1, 1 |-> 4
|
|
||||
let w_eval = vec![
|
|
||||
F::zero(),
|
|
||||
F::from(5u64),
|
|
||||
-F::from(6u64),
|
|
||||
-F::from(3u64),
|
|
||||
F::from(7u64),
|
|
||||
F::from(8u64),
|
|
||||
F::from(4u64),
|
|
||||
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(), 8, false)?;
|
|
||||
|
|
||||
// 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_w_circ_l_with_prefix() -> Result<(), PCSError> {
|
|
||||
test_w_circ_l_with_prefix_helper::<Fr>()
|
|
||||
}
|
|
||||
|
|
||||
fn test_w_circ_l_with_prefix_helper<F: PrimeField>() -> Result<(), PCSError> {
|
|
||||
{
|
|
||||
// Example from page 53:
|
|
||||
// W = 3x1x2 + 2x2 whose evaluations are
|
|
||||
// 0, 0 |-> 0
|
|
||||
// 1, 0 |-> 0
|
|
||||
// 0, 1 |-> 2
|
|
||||
// 1, 1 |-> 5
|
|
||||
let w_eval = vec![F::zero(), F::zero(), F::from(2u64), 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(), 4, true)?;
|
|
||||
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
|
|
||||
// 1, 0, 0 |-> 5
|
|
||||
// 0, 1, 0 |-> -6
|
|
||||
// 1, 1, 0 |-> -3
|
|
||||
// 0, 0, 1 |-> 7
|
|
||||
// 1, 0, 1 |-> 8
|
|
||||
// 0, 1, 1 |-> 4
|
|
||||
// 1, 1, 1 |-> 4
|
|
||||
let w_eval = vec![
|
|
||||
F::zero(),
|
|
||||
F::from(5u64),
|
|
||||
-F::from(6u64),
|
|
||||
-F::from(3u64),
|
|
||||
F::from(7u64),
|
|
||||
F::from(8u64),
|
|
||||
F::from(4u64),
|
|
||||
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(), 8, true)?;
|
|
||||
|
|
||||
// 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_qx() -> Result<(), PCSError> {
|
|
||||
// Example from page 53:
|
|
||||
// W1 = 3x1x2 + 2x2
|
|
||||
let w_eval = vec![Fr::zero(), Fr::from(2u64), Fr::zero(), Fr::from(5u64)];
|
|
||||
let w = 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)?;
|
|
||||
let l = build_l(&[point1.clone(), point2.clone()], &domain, false)?;
|
|
||||
|
|
||||
let q_x = compute_w_circ_l(&w, &l, 2, false)?;
|
|
||||
|
|
||||
let point: Vec<Fr> = l.iter().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)?;
|
|
||||
|
|
||||
let l = build_l(&[point1, point2, point3], &domain, false)?;
|
|
||||
let q_x = compute_w_circ_l(&w, &l, 3, false)?;
|
|
||||
|
|
||||
let point: Vec<Fr> = vec![l[0].evaluate(&r), l[1].evaluate(&r)];
|
|
||||
|
|
||||
assert_eq!(
|
|
||||
q_x.evaluate(&r),
|
|
||||
w.evaluate(&point).unwrap(),
|
|
||||
"q(r) != w(l(r))"
|
|
||||
);
|
|
||||
}
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
|
|
||||
#[test]
|
|
||||
fn test_qx_with_prefix() -> Result<(), PCSError> {
|
|
||||
// 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(&[point1.clone(), point2.clone()], &domain, true)?;
|
|
||||
|
|
||||
// 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, 2, true)?;
|
|
||||
|
|
||||
let point: Vec<Fr> = l.iter().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)?;
|
|
||||
let w = merge_polynomials(&[w1, w2, w3])?;
|
|
||||
|
|
||||
let l = build_l(&[point1, point2, point3], &domain, true)?;
|
|
||||
let q_x = compute_w_circ_l(&w, &l, 3, true)?;
|
|
||||
|
|
||||
let point: Vec<Fr> = vec![
|
|
||||
l[0].evaluate(&r),
|
|
||||
l[1].evaluate(&r),
|
|
||||
l[2].evaluate(&r),
|
|
||||
l[3].evaluate(&r),
|
|
||||
];
|
|
||||
|
|
||||
assert_eq!(
|
|
||||
q_x.evaluate(&r),
|
|
||||
w.evaluate(&point).unwrap(),
|
|
||||
"q(r) != w(l(r))"
|
|
||||
);
|
|
||||
}
|
|
||||
Ok(())
|
|
||||
}
|
|
||||
}
|
|
@ -1,4 +0,0 @@ |
|||||
pub use crate::{
|
|
||||
errors::PolyIOPErrors, perm_check::PermutationCheck, prod_check::ProductCheck,
|
|
||||
sum_check::SumCheck, utils::*, zero_check::ZeroCheck, PolyIOP,
|
|
||||
};
|
|
@ -1,37 +0,0 @@ |
|||||
#!/bin/bash |
|
||||
|
|
||||
# m4_ignore( |
|
||||
echo "This is just a script template, not the script (yet) - pass it to 'argbash' to fix this." >&2 |
|
||||
exit 11 #)Created by argbash-init v2.10.0 |
|
||||
# ARG_OPTIONAL_BOOLEAN([asm]) |
|
||||
# ARG_OPTIONAL_BOOLEAN([multi_threads]) |
|
||||
# ARG_HELP([<Hyperplonk benchmarks>]) |
|
||||
# ARGBASH_GO |
|
||||
|
|
||||
# [ <-- needed because of Argbash |
|
||||
|
|
||||
if [ "$_arg_multi_threads" = on ] |
|
||||
then |
|
||||
echo "Multi-threads: ON" |
|
||||
# Do nothing |
|
||||
else |
|
||||
echo "Multi-threads: OFF" |
|
||||
export RAYON_NUM_THREADS=1 |
|
||||
fi |
|
||||
|
|
||||
if [ "$_arg_asm" = on ] |
|
||||
then |
|
||||
echo "Asm feature: ON" |
|
||||
export RUSTFLAGS="-C target-feature=+bmi2,+adx" |
|
||||
else |
|
||||
echo "Asm feature: OFF" |
|
||||
# Do nothing |
|
||||
fi |
|
||||
|
|
||||
# Run the benchmark binary |
|
||||
cargo +nightly bench |
|
||||
|
|
||||
|
|
||||
# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^ |
|
||||
|
|
||||
# ] <-- needed because of Argbash |
|
@ -1,106 +1,12 @@ |
|||||
#!/usr/bin/env bash |
|
||||
|
#!/bin/bash |
||||
|
|
||||
# Created by argbash-init v2.10.0 |
|
||||
# ARG_OPTIONAL_BOOLEAN([asm]) |
|
||||
# ARG_OPTIONAL_BOOLEAN([multi_threads]) |
|
||||
# ARG_HELP([<Hyperplonk benchmarks>]) |
|
||||
# ARGBASH_GO() |
|
||||
# needed because of Argbash --> m4_ignore([ |
|
||||
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ### |
|
||||
# Argbash is a bash code generator used to get arguments parsing right. |
|
||||
# Argbash is FREE SOFTWARE, see https://argbash.io for more info |
|
||||
|
|
||||
|
|
||||
die() |
|
||||
{ |
|
||||
local _ret="${2:-1}" |
|
||||
test "${_PRINT_HELP:-no}" = yes && print_help >&2 |
|
||||
echo "$1" >&2 |
|
||||
exit "${_ret}" |
|
||||
} |
|
||||
|
|
||||
|
|
||||
begins_with_short_option() |
|
||||
{ |
|
||||
local first_option all_short_options='h' |
|
||||
first_option="${1:0:1}" |
|
||||
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0 |
|
||||
} |
|
||||
|
|
||||
# THE DEFAULTS INITIALIZATION - OPTIONALS |
|
||||
_arg_asm="off" |
|
||||
_arg_multi_threads="off" |
|
||||
|
|
||||
|
|
||||
print_help() |
|
||||
{ |
|
||||
printf '%s\n' "<Hyperplonk benchmarks>" |
|
||||
printf 'Usage: %s [--(no-)asm] [--(no-)multi_threads] [-h|--help]\n' "$0" |
|
||||
printf '\t%s\n' "-h, --help: Prints help" |
|
||||
} |
|
||||
|
|
||||
|
|
||||
parse_commandline() |
|
||||
{ |
|
||||
while test $# -gt 0 |
|
||||
do |
|
||||
_key="$1" |
|
||||
case "$_key" in |
|
||||
--no-asm|--asm) |
|
||||
_arg_asm="on" |
|
||||
test "${1:0:5}" = "--no-" && _arg_asm="off" |
|
||||
;; |
|
||||
--no-multi_threads|--multi_threads) |
|
||||
_arg_multi_threads="on" |
|
||||
test "${1:0:5}" = "--no-" && _arg_multi_threads="off" |
|
||||
;; |
|
||||
-h|--help) |
|
||||
print_help |
|
||||
exit 0 |
|
||||
;; |
|
||||
-h*) |
|
||||
print_help |
|
||||
exit 0 |
|
||||
;; |
|
||||
*) |
|
||||
_PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1 |
|
||||
;; |
|
||||
esac |
|
||||
shift |
|
||||
done |
|
||||
} |
|
||||
|
|
||||
parse_commandline "$@" |
|
||||
|
|
||||
# OTHER STUFF GENERATED BY Argbash |
|
||||
|
|
||||
### END OF CODE GENERATED BY Argbash (sortof) ### ]) |
|
||||
# [ <-- needed because of Argbash |
|
||||
|
|
||||
cargo clean |
|
||||
|
|
||||
if [ "$_arg_multi_threads" = on ] |
|
||||
then |
|
||||
echo "Multi-threads: ON" |
|
||||
# Do nothing |
|
||||
else |
|
||||
echo "Multi-threads: OFF" |
|
||||
export RAYON_NUM_THREADS=1 |
|
||||
fi |
|
||||
|
|
||||
if [ "$_arg_asm" = on ] |
|
||||
then |
|
||||
echo "Asm feature: ON" |
|
||||
export RUSTFLAGS="-C target-feature=+bmi2,+adx" |
|
||||
else |
|
||||
echo "Asm feature: OFF" |
|
||||
# Do nothing |
|
||||
fi |
|
||||
|
cd hyperplonk |
||||
|
|
||||
# Run the benchmark binary |
# Run the benchmark binary |
||||
cargo bench |
|
||||
|
|
||||
|
|
||||
# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^ |
|
||||
|
|
||||
# ] <-- needed because of Argbash |
|
||||
|
cargo bench 64 --no-default-features --features=bench |
||||
|
cargo bench 32 --no-default-features --features=bench |
||||
|
cargo bench 16 --no-default-features --features=bench |
||||
|
cargo bench 8 --no-default-features --features=bench |
||||
|
cargo bench 4 --no-default-features --features=bench |
||||
|
cargo bench 2 --no-default-features --features=bench |
||||
|
cargo bench 1 --no-default-features --features=bench |
@ -0,0 +1,5 @@ |
|||||
|
pub mod pcs;
|
||||
|
pub mod poly_iop;
|
||||
|
|
||||
|
pub use pcs::prelude::*;
|
||||
|
pub use poly_iop::prelude::*;
|
@ -0,0 +1,329 @@ |
|||||
|
//! Sumcheck based batch opening and verify commitment.
|
||||
|
// TODO: refactoring this code to somewhere else
|
||||
|
// currently IOP depends on PCS because perm check requires commitment.
|
||||
|
// The sumcheck based batch opening therefore cannot stay in the PCS repo --
|
||||
|
// which creates a cyclic dependency.
|
||||
|
|
||||
|
use crate::{
|
||||
|
pcs::{
|
||||
|
multilinear_kzg::util::eq_eval,
|
||||
|
prelude::{Commitment, PCSError},
|
||||
|
PolynomialCommitmentScheme,
|
||||
|
},
|
||||
|
poly_iop::{prelude::SumCheck, PolyIOP},
|
||||
|
IOPProof,
|
||||
|
};
|
||||
|
use arithmetic::{
|
||||
|
build_eq_x_r_vec, fix_last_variables, DenseMultilinearExtension, VPAuxInfo, VirtualPolynomial,
|
||||
|
};
|
||||
|
use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve};
|
||||
|
use ark_std::{end_timer, log2, start_timer, One, Zero};
|
||||
|
use std::{marker::PhantomData, rc::Rc};
|
||||
|
use transcript::IOPTranscript;
|
||||
|
|
||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
|
pub struct BatchProof<E, PCS>
|
||||
|
where
|
||||
|
E: PairingEngine,
|
||||
|
PCS: PolynomialCommitmentScheme<E>,
|
||||
|
{
|
||||
|
/// A sum check proof proving tilde g's sum
|
||||
|
pub(crate) sum_check_proof: IOPProof<E::Fr>,
|
||||
|
/// f_i(point_i)
|
||||
|
pub f_i_eval_at_point_i: Vec<E::Fr>,
|
||||
|
/// proof for g'(a_2)
|
||||
|
pub(crate) g_prime_proof: PCS::Proof,
|
||||
|
}
|
||||
|
|
||||
|
/// Steps:
|
||||
|
/// 1. get challenge point t from transcript
|
||||
|
/// 2. build eq(t,i) for i in [0..k]
|
||||
|
/// 3. build \tilde g(i, b) = eq(t, i) * f_i(b)
|
||||
|
/// 4. compute \tilde eq
|
||||
|
/// 5. run sumcheck on \tilde eq * \tilde g(i, b)
|
||||
|
/// 6. build g'(a2) where (a1, a2) is the sumcheck's point
|
||||
|
pub(crate) fn multi_open_internal<E, PCS>(
|
||||
|
prover_param: &PCS::ProverParam,
|
||||
|
polynomials: &[PCS::Polynomial],
|
||||
|
points: &[PCS::Point],
|
||||
|
evals: &[PCS::Evaluation],
|
||||
|
transcript: &mut IOPTranscript<E::Fr>,
|
||||
|
) -> Result<BatchProof<E, PCS>, PCSError>
|
||||
|
where
|
||||
|
E: PairingEngine,
|
||||
|
PCS: PolynomialCommitmentScheme<
|
||||
|
E,
|
||||
|
Polynomial = Rc<DenseMultilinearExtension<E::Fr>>,
|
||||
|
Point = Vec<E::Fr>,
|
||||
|
Evaluation = E::Fr,
|
||||
|
>,
|
||||
|
{
|
||||
|
let open_timer = start_timer!(|| format!("multi open {} points", points.len()));
|
||||
|
|
||||
|
// TODO: sanity checks
|
||||
|
let num_var = polynomials[0].num_vars;
|
||||
|
let k = polynomials.len();
|
||||
|
let ell = log2(k) as usize;
|
||||
|
let merged_num_var = num_var + ell;
|
||||
|
|
||||
|
// challenge point t
|
||||
|
let t = transcript.get_and_append_challenge_vectors("t".as_ref(), ell)?;
|
||||
|
|
||||
|
// eq(t, i) for i in [0..k]
|
||||
|
let eq_t_i_list = build_eq_x_r_vec(t.as_ref())?;
|
||||
|
|
||||
|
// \tilde g(i, b) = eq(t, i) * f_i(b)
|
||||
|
let timer = start_timer!(|| format!("compute tilde g for {} points", points.len()));
|
||||
|
let mut tilde_g_eval = vec![E::Fr::zero(); 1 << (ell + num_var)];
|
||||
|
let block_size = 1 << num_var;
|
||||
|
for (index, f_i) in polynomials.iter().enumerate() {
|
||||
|
for (j, &f_i_eval) in f_i.iter().enumerate() {
|
||||
|
tilde_g_eval[index * block_size + j] = f_i_eval * eq_t_i_list[index];
|
||||
|
}
|
||||
|
}
|
||||
|
let tilde_g = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
|
||||
|
merged_num_var,
|
||||
|
tilde_g_eval,
|
||||
|
));
|
||||
|
end_timer!(timer);
|
||||
|
|
||||
|
let timer = start_timer!(|| format!("compute tilde eq for {} points", points.len()));
|
||||
|
let mut tilde_eq_eval = vec![E::Fr::zero(); 1 << (ell + num_var)];
|
||||
|
for (index, point) in points.iter().enumerate() {
|
||||
|
let eq_b_zi = build_eq_x_r_vec(point)?;
|
||||
|
let start = index * block_size;
|
||||
|
tilde_eq_eval[start..start + block_size].copy_from_slice(eq_b_zi.as_slice());
|
||||
|
}
|
||||
|
let tilde_eq = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
|
||||
|
merged_num_var,
|
||||
|
tilde_eq_eval,
|
||||
|
));
|
||||
|
end_timer!(timer);
|
||||
|
|
||||
|
// built the virtual polynomial for SumCheck
|
||||
|
let timer = start_timer!(|| format!("sum check prove of {} variables", num_var + ell));
|
||||
|
|
||||
|
let step = start_timer!(|| "add mle");
|
||||
|
let mut sum_check_vp = VirtualPolynomial::new(num_var + ell);
|
||||
|
sum_check_vp.add_mle_list([tilde_g.clone(), tilde_eq], E::Fr::one())?;
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
let proof = match <PolyIOP<E::Fr> as SumCheck<E::Fr>>::prove(&sum_check_vp, transcript) {
|
||||
|
Ok(p) => p,
|
||||
|
Err(_e) => {
|
||||
|
// cannot wrap IOPError with PCSError due to cyclic dependency
|
||||
|
return Err(PCSError::InvalidProver(
|
||||
|
"Sumcheck in batch proving Failed".to_string(),
|
||||
|
));
|
||||
|
},
|
||||
|
};
|
||||
|
|
||||
|
end_timer!(timer);
|
||||
|
|
||||
|
// (a1, a2) := sumcheck's point
|
||||
|
let step = start_timer!(|| "open at a2");
|
||||
|
let a1 = &proof.point[num_var..];
|
||||
|
let a2 = &proof.point[..num_var];
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
// build g'(a2)
|
||||
|
let step = start_timer!(|| "evaluate at a2");
|
||||
|
let g_prime = Rc::new(fix_last_variables(&tilde_g, a1));
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
let step = start_timer!(|| "pcs open");
|
||||
|
let (g_prime_proof, _g_prime_eval) = PCS::open(prover_param, &g_prime, a2.to_vec().as_ref())?;
|
||||
|
// assert_eq!(g_prime_eval, tilde_g_eval);
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
let step = start_timer!(|| "evaluate fi(pi)");
|
||||
|
end_timer!(step);
|
||||
|
end_timer!(open_timer);
|
||||
|
|
||||
|
Ok(BatchProof {
|
||||
|
sum_check_proof: proof,
|
||||
|
f_i_eval_at_point_i: evals.to_vec(),
|
||||
|
g_prime_proof,
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
/// Steps:
|
||||
|
/// 1. get challenge point t from transcript
|
||||
|
/// 2. build g' commitment
|
||||
|
/// 3. ensure \sum_i eq(t, <i>) * f_i_evals matches the sum via SumCheck
|
||||
|
/// verification 4. verify commitment
|
||||
|
pub(crate) fn batch_verify_internal<E, PCS>(
|
||||
|
verifier_param: &PCS::VerifierParam,
|
||||
|
f_i_commitments: &[Commitment<E>],
|
||||
|
points: &[PCS::Point],
|
||||
|
proof: &BatchProof<E, PCS>,
|
||||
|
transcript: &mut IOPTranscript<E::Fr>,
|
||||
|
) -> Result<bool, PCSError>
|
||||
|
where
|
||||
|
E: PairingEngine,
|
||||
|
PCS: PolynomialCommitmentScheme<
|
||||
|
E,
|
||||
|
Polynomial = Rc<DenseMultilinearExtension<E::Fr>>,
|
||||
|
Point = Vec<E::Fr>,
|
||||
|
Evaluation = E::Fr,
|
||||
|
Commitment = Commitment<E>,
|
||||
|
>,
|
||||
|
{
|
||||
|
let open_timer = start_timer!(|| "batch verification");
|
||||
|
|
||||
|
// TODO: sanity checks
|
||||
|
|
||||
|
let k = f_i_commitments.len();
|
||||
|
let ell = log2(k) as usize;
|
||||
|
let num_var = proof.sum_check_proof.point.len() - ell;
|
||||
|
|
||||
|
// challenge point t
|
||||
|
let t = transcript.get_and_append_challenge_vectors("t".as_ref(), ell)?;
|
||||
|
|
||||
|
// sum check point (a1, a2)
|
||||
|
let a1 = &proof.sum_check_proof.point[num_var..];
|
||||
|
let a2 = &proof.sum_check_proof.point[..num_var];
|
||||
|
|
||||
|
// build g' commitment
|
||||
|
let eq_a1_list = build_eq_x_r_vec(a1)?;
|
||||
|
let eq_t_list = build_eq_x_r_vec(t.as_ref())?;
|
||||
|
|
||||
|
let mut g_prime_commit = E::G1Affine::zero().into_projective();
|
||||
|
for i in 0..k {
|
||||
|
let tmp = eq_a1_list[i] * eq_t_list[i];
|
||||
|
g_prime_commit += &f_i_commitments[i].0.mul(tmp);
|
||||
|
}
|
||||
|
|
||||
|
// ensure \sum_i eq(t, <i>) * f_i_evals matches the sum via SumCheck
|
||||
|
// verification
|
||||
|
let mut sum = E::Fr::zero();
|
||||
|
for (i, &e) in eq_t_list.iter().enumerate().take(k) {
|
||||
|
sum += e * proof.f_i_eval_at_point_i[i];
|
||||
|
}
|
||||
|
let aux_info = VPAuxInfo {
|
||||
|
max_degree: 2,
|
||||
|
num_variables: num_var + ell,
|
||||
|
phantom: PhantomData,
|
||||
|
};
|
||||
|
let subclaim = match <PolyIOP<E::Fr> as SumCheck<E::Fr>>::verify(
|
||||
|
sum,
|
||||
|
&proof.sum_check_proof,
|
||||
|
&aux_info,
|
||||
|
transcript,
|
||||
|
) {
|
||||
|
Ok(p) => p,
|
||||
|
Err(_e) => {
|
||||
|
// cannot wrap IOPError with PCSError due to cyclic dependency
|
||||
|
return Err(PCSError::InvalidProver(
|
||||
|
"Sumcheck in batch verification failed".to_string(),
|
||||
|
));
|
||||
|
},
|
||||
|
};
|
||||
|
let mut eq_tilde_eval = E::Fr::zero();
|
||||
|
for (point, &coef) in points.iter().zip(eq_a1_list.iter()) {
|
||||
|
eq_tilde_eval += coef * eq_eval(a2, point)?;
|
||||
|
}
|
||||
|
let tilde_g_eval = subclaim.expected_evaluation / eq_tilde_eval;
|
||||
|
|
||||
|
// verify commitment
|
||||
|
let res = PCS::verify(
|
||||
|
verifier_param,
|
||||
|
&Commitment(g_prime_commit.into_affine()),
|
||||
|
a2.to_vec().as_ref(),
|
||||
|
&tilde_g_eval,
|
||||
|
&proof.g_prime_proof,
|
||||
|
)?;
|
||||
|
|
||||
|
end_timer!(open_timer);
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod tests {
|
||||
|
use super::*;
|
||||
|
use crate::pcs::{
|
||||
|
prelude::{MultilinearKzgPCS, MultilinearUniversalParams},
|
||||
|
StructuredReferenceString,
|
||||
|
};
|
||||
|
use arithmetic::get_batched_nv;
|
||||
|
use ark_bls12_381::Bls12_381 as E;
|
||||
|
use ark_ec::PairingEngine;
|
||||
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
|
use ark_std::{
|
||||
|
rand::{CryptoRng, RngCore},
|
||||
|
test_rng,
|
||||
|
vec::Vec,
|
||||
|
UniformRand,
|
||||
|
};
|
||||
|
|
||||
|
type Fr = <E as PairingEngine>::Fr;
|
||||
|
|
||||
|
fn test_multi_open_helper<R: RngCore + CryptoRng>(
|
||||
|
ml_params: &MultilinearUniversalParams<E>,
|
||||
|
polys: &[Rc<DenseMultilinearExtension<Fr>>],
|
||||
|
rng: &mut R,
|
||||
|
) -> Result<(), PCSError> {
|
||||
|
let merged_nv = get_batched_nv(polys[0].num_vars(), polys.len());
|
||||
|
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 = polys
|
||||
|
.iter()
|
||||
|
.zip(points.iter())
|
||||
|
.map(|(f, p)| f.evaluate(p).unwrap())
|
||||
|
.collect::<Vec<_>>();
|
||||
|
|
||||
|
let commitments = polys
|
||||
|
.iter()
|
||||
|
.map(|poly| MultilinearKzgPCS::commit(&ml_ck.clone(), poly).unwrap())
|
||||
|
.collect::<Vec<_>>();
|
||||
|
|
||||
|
let mut transcript = IOPTranscript::new("test transcript".as_ref());
|
||||
|
transcript.append_field_element("init".as_ref(), &Fr::zero())?;
|
||||
|
|
||||
|
let batch_proof = multi_open_internal::<E, MultilinearKzgPCS<E>>(
|
||||
|
&ml_ck,
|
||||
|
polys,
|
||||
|
&points,
|
||||
|
&evals,
|
||||
|
&mut transcript,
|
||||
|
)?;
|
||||
|
|
||||
|
// good path
|
||||
|
let mut transcript = IOPTranscript::new("test transcript".as_ref());
|
||||
|
transcript.append_field_element("init".as_ref(), &Fr::zero())?;
|
||||
|
assert!(batch_verify_internal::<E, MultilinearKzgPCS<E>>(
|
||||
|
&ml_vk,
|
||||
|
&commitments,
|
||||
|
&points,
|
||||
|
&batch_proof,
|
||||
|
&mut transcript
|
||||
|
)?);
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_multi_open_internal() -> Result<(), PCSError> {
|
||||
|
let mut rng = test_rng();
|
||||
|
|
||||
|
let ml_params = MultilinearUniversalParams::<E>::gen_srs_for_testing(&mut rng, 20)?;
|
||||
|
for num_poly in 5..6 {
|
||||
|
for nv in 15..16 {
|
||||
|
let polys1: Vec<_> = (0..num_poly)
|
||||
|
.map(|_| Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)))
|
||||
|
.collect();
|
||||
|
test_multi_open_helper(&ml_params, &polys1, &mut rng)?;
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,51 @@ |
|||||
|
// Copyright (c) 2022 Espresso Systems (espressosys.com)
|
||||
|
// This file is part of the Jellyfish library.
|
||||
|
|
||||
|
// You should have received a copy of the MIT License
|
||||
|
// along with the Jellyfish library. If not, see <https://mit-license.org/>.
|
||||
|
|
||||
|
//! Useful utilities for KZG PCS
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::DenseMultilinearExtension;
|
||||
|
use ark_std::{end_timer, start_timer, vec::Vec};
|
||||
|
|
||||
|
use crate::PCSError;
|
||||
|
|
||||
|
/// 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.
|
||||
|
pub(crate) 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
|
||||
|
}
|
||||
|
|
||||
|
/// Evaluate eq polynomial. use the public one later
|
||||
|
pub(crate) fn eq_eval<F: PrimeField>(x: &[F], y: &[F]) -> Result<F, PCSError> {
|
||||
|
if x.len() != y.len() {
|
||||
|
return Err(PCSError::InvalidParameters(
|
||||
|
"x and y have different length".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
let start = start_timer!(|| "eq_eval");
|
||||
|
let mut res = F::one();
|
||||
|
for (&xi, &yi) in x.iter().zip(y.iter()) {
|
||||
|
let xi_yi = xi * yi;
|
||||
|
res *= xi_yi + xi_yi - xi - yi + F::one();
|
||||
|
}
|
||||
|
end_timer!(start);
|
||||
|
Ok(res)
|
||||
|
}
|
@ -1,9 +1,9 @@ |
|||||
//! Error module.
|
//! Error module.
|
||||
|
|
||||
|
use crate::pcs::prelude::PCSError;
|
||||
use arithmetic::ArithErrors;
|
use arithmetic::ArithErrors;
|
||||
use ark_std::string::String;
|
use ark_std::string::String;
|
||||
use displaydoc::Display;
|
use displaydoc::Display;
|
||||
use pcs::prelude::PCSError;
|
|
||||
use transcript::TranscriptError;
|
use transcript::TranscriptError;
|
||||
|
|
||||
/// A `enum` specifying the possible failure modes of the PolyIOP.
|
/// A `enum` specifying the possible failure modes of the PolyIOP.
|
@ -1,6 +1,6 @@ |
|||||
//! This module implements useful functions for the permutation check protocol.
|
//! This module implements useful functions for the permutation check protocol.
|
||||
|
|
||||
use crate::errors::PolyIOPErrors;
|
|
||||
|
use crate::poly_iop::errors::PolyIOPErrors;
|
||||
use arithmetic::identity_permutation_mle;
|
use arithmetic::identity_permutation_mle;
|
||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
use ark_poly::DenseMultilinearExtension;
|
@ -0,0 +1,4 @@ |
|||||
|
pub use crate::poly_iop::{
|
||||
|
errors::PolyIOPErrors, perm_check::PermutationCheck, prod_check::ProductCheck,
|
||||
|
structs::IOPProof, sum_check::SumCheck, utils::*, zero_check::ZeroCheck, PolyIOP,
|
||||
|
};
|
@ -1,6 +1,6 @@ |
|||||
//! This module implements useful functions for the product check protocol.
|
//! This module implements useful functions for the product check protocol.
|
||||
|
|
||||
use crate::{errors::PolyIOPErrors, structs::IOPProof, zero_check::ZeroCheck, PolyIOP};
|
|
||||
|
use crate::poly_iop::{errors::PolyIOPErrors, structs::IOPProof, zero_check::ZeroCheck, PolyIOP};
|
||||
use arithmetic::{get_index, VirtualPolynomial};
|
use arithmetic::{get_index, VirtualPolynomial};
|
||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
use ark_poly::DenseMultilinearExtension;
|
@ -1,6 +1,6 @@ |
|||||
//! This module implements the sum check protocol.
|
//! This module implements the sum check protocol.
|
||||
|
|
||||
use crate::{
|
|
||||
|
use crate::poly_iop::{
|
||||
errors::PolyIOPErrors,
|
errors::PolyIOPErrors,
|
||||
structs::{IOPProof, IOPProverState, IOPVerifierState},
|
structs::{IOPProof, IOPProverState, IOPVerifierState},
|
||||
PolyIOP,
|
PolyIOP,
|