mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-08 23:21:29 +01:00
Batch all (#89)
- use sumcheck to batch open PCS - split Prod and witness into two batches - benchmark code
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
members = [
|
||||
"arithmetic",
|
||||
"hyperplonk",
|
||||
"poly-iop",
|
||||
"subroutines",
|
||||
"transcript",
|
||||
"util"
|
||||
]
|
||||
|
||||
@@ -6,10 +6,10 @@ mod virtual_polynomial;
|
||||
|
||||
pub use errors::ArithErrors;
|
||||
pub use multilinear_polynomial::{
|
||||
evaluate_no_par, evaluate_opt, fix_first_variable, fix_variables, identity_permutation_mle,
|
||||
merge_polynomials, random_mle_list, random_permutation_mle, random_zero_mle_list,
|
||||
DenseMultilinearExtension,
|
||||
evaluate_no_par, evaluate_opt, fix_last_variables, fix_last_variables_no_par, fix_variables,
|
||||
identity_permutation_mle, merge_polynomials, random_mle_list, random_permutation_mle,
|
||||
random_zero_mle_list, DenseMultilinearExtension,
|
||||
};
|
||||
pub use univariate_polynomial::{build_l, get_uni_domain};
|
||||
pub use util::{bit_decompose, gen_eval_point, get_batched_nv, get_index};
|
||||
pub use virtual_polynomial::{build_eq_x_r, VPAuxInfo, VirtualPolynomial};
|
||||
pub use virtual_polynomial::{build_eq_x_r, build_eq_x_r_vec, VPAuxInfo, VirtualPolynomial};
|
||||
|
||||
@@ -123,36 +123,24 @@ pub fn fix_variables<F: Field>(
|
||||
DenseMultilinearExtension::<F>::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
|
||||
}
|
||||
|
||||
pub fn fix_first_variable<F: Field>(
|
||||
poly: &DenseMultilinearExtension<F>,
|
||||
partial_point: &F,
|
||||
) -> DenseMultilinearExtension<F> {
|
||||
assert!(poly.num_vars != 0, "invalid size of partial point");
|
||||
|
||||
let nv = poly.num_vars;
|
||||
let res = fix_one_variable_helper(&poly.evaluations, nv, partial_point);
|
||||
DenseMultilinearExtension::<F>::from_evaluations_slice(nv - 1, &res)
|
||||
}
|
||||
|
||||
fn fix_one_variable_helper<F: Field>(data: &[F], nv: usize, point: &F) -> Vec<F> {
|
||||
let mut res = vec![F::zero(); 1 << (nv - 1)];
|
||||
let one_minus_p = F::one() - point;
|
||||
|
||||
// evaluate single variable of partial point from left to right
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
for b in 0..(1 << (nv - 1)) {
|
||||
res[b] = data[b << 1] * one_minus_p + data[(b << 1) + 1] * point;
|
||||
for i in 0..(1 << (nv - 1)) {
|
||||
res[i] = data[i] + (data[(i << 1) + 1] - data[i << 1]) * point;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
if nv >= 13 {
|
||||
// on my computer we parallelization doesn't help till nv >= 13
|
||||
res.par_iter_mut().enumerate().for_each(|(i, x)| {
|
||||
*x = data[i << 1] * one_minus_p + data[(i << 1) + 1] * point;
|
||||
*x = data[i << 1] + (data[(i << 1) + 1] - data[i << 1]) * point;
|
||||
});
|
||||
} else {
|
||||
for b in 0..(1 << (nv - 1)) {
|
||||
res[b] = data[b << 1] * one_minus_p + data[(b << 1) + 1] * point;
|
||||
for i in 0..(1 << (nv - 1)) {
|
||||
res[i] = data[i << 1] + (data[(i << 1) + 1] - data[i << 1]) * point;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,9 +166,8 @@ fn fix_variables_no_par<F: Field>(
|
||||
// evaluate single variable of partial point from left to right
|
||||
for i in 1..dim + 1 {
|
||||
let r = partial_point[i - 1];
|
||||
let one_minus_r = F::one() - r;
|
||||
for b in 0..(1 << (nv - i)) {
|
||||
poly[b] = poly[b << 1] * one_minus_r + poly[(b << 1) + 1] * r;
|
||||
poly[b] = poly[b << 1] + (poly[(b << 1) + 1] - poly[b << 1]) * r;
|
||||
}
|
||||
}
|
||||
DenseMultilinearExtension::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
|
||||
@@ -210,3 +197,71 @@ pub fn merge_polynomials<F: PrimeField>(
|
||||
merged_nv, scalars,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn fix_last_variables_no_par<F: PrimeField>(
|
||||
poly: &DenseMultilinearExtension<F>,
|
||||
partial_point: &[F],
|
||||
) -> DenseMultilinearExtension<F> {
|
||||
let mut res = fix_last_variable_no_par(poly, partial_point.last().unwrap());
|
||||
for p in partial_point.iter().rev().skip(1) {
|
||||
res = fix_last_variable_no_par(&res, p);
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn fix_last_variable_no_par<F: PrimeField>(
|
||||
poly: &DenseMultilinearExtension<F>,
|
||||
partial_point: &F,
|
||||
) -> DenseMultilinearExtension<F> {
|
||||
let nv = poly.num_vars();
|
||||
let half_len = 1 << (nv - 1);
|
||||
let mut res = vec![F::zero(); half_len];
|
||||
for (i, e) in res.iter_mut().enumerate().take(half_len) {
|
||||
*e = poly.evaluations[i]
|
||||
+ *partial_point * (poly.evaluations[i + half_len] - poly.evaluations[i]);
|
||||
}
|
||||
DenseMultilinearExtension::from_evaluations_vec(nv - 1, res)
|
||||
}
|
||||
pub fn fix_last_variables<F: PrimeField>(
|
||||
poly: &DenseMultilinearExtension<F>,
|
||||
partial_point: &[F],
|
||||
) -> DenseMultilinearExtension<F> {
|
||||
assert!(
|
||||
partial_point.len() <= poly.num_vars,
|
||||
"invalid size of partial point"
|
||||
);
|
||||
let nv = poly.num_vars;
|
||||
let mut poly = poly.evaluations.to_vec();
|
||||
let dim = partial_point.len();
|
||||
// evaluate single variable of partial point from left to right
|
||||
for (i, point) in partial_point.iter().rev().enumerate().take(dim) {
|
||||
poly = fix_last_variable_helper(&poly, nv - i, point);
|
||||
}
|
||||
|
||||
DenseMultilinearExtension::<F>::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
|
||||
}
|
||||
|
||||
fn fix_last_variable_helper<F: Field>(data: &[F], nv: usize, point: &F) -> Vec<F> {
|
||||
let half_len = 1 << (nv - 1);
|
||||
let mut res = vec![F::zero(); half_len];
|
||||
|
||||
// evaluate single variable of partial point from left to right
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
for b in 0..half_len {
|
||||
res[b] = data[b] + (data[b + half_len] - data[b]) * point;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
if nv >= 13 {
|
||||
// on my computer we parallelization doesn't help till nv >= 13
|
||||
res.par_iter_mut().enumerate().for_each(|(i, x)| {
|
||||
*x = data[i] + (data[i + half_len] - data[i]) * point;
|
||||
});
|
||||
} else {
|
||||
for b in 0..(1 << (nv - 1)) {
|
||||
res[b] = data[b] + (data[b + half_len] - data[b]) * point;
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use ark_std::{
|
||||
rand::{Rng, RngCore},
|
||||
start_timer,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, rc::Rc};
|
||||
|
||||
#[rustfmt::skip]
|
||||
@@ -324,16 +325,29 @@ impl<F: PrimeField> VirtualPolynomial<F> {
|
||||
}
|
||||
}
|
||||
|
||||
// This function build the eq(x, r) polynomial for any given r.
|
||||
//
|
||||
// Evaluate
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
// over r, which is
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
/// This function build the eq(x, r) polynomial for any given r.
|
||||
///
|
||||
/// Evaluate
|
||||
/// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
/// over r, which is
|
||||
/// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
pub fn build_eq_x_r<F: PrimeField>(
|
||||
r: &[F],
|
||||
) -> Result<Rc<DenseMultilinearExtension<F>>, ArithErrors> {
|
||||
let start = start_timer!(|| "zero check build eq_x_r");
|
||||
let evals = build_eq_x_r_vec(r)?;
|
||||
let mle = DenseMultilinearExtension::from_evaluations_vec(r.len(), evals);
|
||||
|
||||
Ok(Rc::new(mle))
|
||||
}
|
||||
/// This function build the eq(x, r) polynomial for any given r, and output the
|
||||
/// evaluation of eq(x, r) in its vector form.
|
||||
///
|
||||
/// Evaluate
|
||||
/// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
/// over r, which is
|
||||
/// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
pub fn build_eq_x_r_vec<F: PrimeField>(r: &[F]) -> Result<Vec<F>, ArithErrors> {
|
||||
let start = start_timer!(|| format!("build eq_x_r of size {}", r.len()));
|
||||
|
||||
// we build eq(x,r) from its evaluations
|
||||
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
||||
@@ -349,11 +363,8 @@ pub fn build_eq_x_r<F: PrimeField>(
|
||||
let mut eval = Vec::new();
|
||||
build_eq_x_r_helper(r, &mut eval)?;
|
||||
|
||||
let mle = DenseMultilinearExtension::from_evaluations_vec(r.len(), eval);
|
||||
|
||||
let res = Rc::new(mle);
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
Ok(eval)
|
||||
}
|
||||
|
||||
/// A helper function to build eq(x, r) recursively.
|
||||
@@ -373,13 +384,24 @@ fn build_eq_x_r_helper<F: PrimeField>(r: &[F], buf: &mut Vec<F>) -> Result<(), A
|
||||
// for the current step we will need
|
||||
// if x_0 = 0: (1-r0) * [b_1, ..., b_k]
|
||||
// if x_0 = 1: r0 * [b_1, ..., b_k]
|
||||
// let mut res = vec![];
|
||||
// for &b_i in buf.iter() {
|
||||
// let tmp = r[0] * b_i;
|
||||
// res.push(b_i - tmp);
|
||||
// res.push(tmp);
|
||||
// }
|
||||
// *buf = res;
|
||||
|
||||
let mut res = vec![];
|
||||
for &b_i in buf.iter() {
|
||||
let tmp = r[0] * b_i;
|
||||
res.push(b_i - tmp);
|
||||
res.push(tmp);
|
||||
}
|
||||
let mut res = vec![F::zero(); buf.len() << 1];
|
||||
res.par_iter_mut().enumerate().for_each(|(i, val)| {
|
||||
let bi = buf[i >> 1];
|
||||
let tmp = r[0] * bi;
|
||||
if i & 1 == 0 {
|
||||
*val = bi - tmp;
|
||||
} else {
|
||||
*val = tmp;
|
||||
}
|
||||
});
|
||||
*buf = res;
|
||||
}
|
||||
|
||||
|
||||
51
bench_results/plot_component
Normal file
51
bench_results/plot_component
Normal file
@@ -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
|
||||
|
||||
31
bench_results/plot_high_degree
Normal file
31
bench_results/plot_high_degree
Normal file
@@ -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
|
||||
59
bench_results/plot_iop
Normal file
59
bench_results/plot_iop
Normal file
@@ -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
|
||||
|
||||
|
||||
50
bench_results/plot_msm
Normal file
50
bench_results/plot_msm
Normal file
@@ -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
|
||||
32
bench_results/plot_multi_thread
Normal file
32
bench_results/plot_multi_thread
Normal file
@@ -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
|
||||
55
bench_results/plot_spartan
Normal file
55
bench_results/plot_spartan
Normal file
@@ -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"
|
||||
@@ -15,8 +15,7 @@ ark-serialize = { version = "^0.3.0", default-features = false, features = [ "de
|
||||
|
||||
displaydoc = { version = "0.2.3", default-features = false }
|
||||
|
||||
poly-iop = { path = "../poly-iop" }
|
||||
pcs = { path = "../pcs" }
|
||||
subroutines = { path = "../subroutines" }
|
||||
transcript = { path = "../transcript" }
|
||||
arithmetic = { path = "../arithmetic" }
|
||||
util = { path = "../util" }
|
||||
@@ -33,15 +32,14 @@ path = "benches/bench.rs"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
# default = [ ]
|
||||
default = [ "parallel" ]
|
||||
# default = [ ]
|
||||
# default = [ "parallel" ]
|
||||
# default = [ "parallel", "print-trace" ]
|
||||
# default = [ "parallel", "extensive_sanity_checks" ]
|
||||
default = [ "parallel", "extensive_sanity_checks" ]
|
||||
bench = [ "parallel" ]
|
||||
# extensive sanity checks that are useful for debugging
|
||||
extensive_sanity_checks = [
|
||||
"poly-iop/extensive_sanity_checks",
|
||||
"pcs/extensive_sanity_checks",
|
||||
"subroutines/extensive_sanity_checks",
|
||||
]
|
||||
parallel = [
|
||||
"rayon",
|
||||
@@ -49,14 +47,13 @@ parallel = [
|
||||
"ark-ff/parallel",
|
||||
"ark-poly/parallel",
|
||||
"ark-ec/parallel",
|
||||
|
||||
"poly-iop/parallel",
|
||||
|
||||
"arithmetic/parallel",
|
||||
"pcs/parallel",
|
||||
"subroutines/parallel",
|
||||
"util/parallel"
|
||||
]
|
||||
print-trace = [
|
||||
"ark-std/print-trace",
|
||||
"poly-iop/print-trace",
|
||||
"arithmetic/print-trace",
|
||||
"subroutines/print-trace"
|
||||
]
|
||||
@@ -7,36 +7,45 @@ use hyperplonk::{
|
||||
prelude::{CustomizedGates, HyperPlonkErrors, MockCircuit},
|
||||
HyperPlonkSNARK,
|
||||
};
|
||||
use pcs::{
|
||||
prelude::{MultilinearKzgPCS, MultilinearUniversalParams, UnivariateUniversalParams},
|
||||
PolynomialCommitmentScheme,
|
||||
};
|
||||
use poly_iop::PolyIOP;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use subroutines::{
|
||||
pcs::{
|
||||
prelude::{MultilinearKzgPCS, MultilinearUniversalParams},
|
||||
PolynomialCommitmentScheme,
|
||||
},
|
||||
poly_iop::PolyIOP,
|
||||
};
|
||||
|
||||
const SUPPORTED_SIZE: usize = 20;
|
||||
const MIN_NUM_VARS: usize = 8;
|
||||
const MAX_NUM_VARS: usize = 15;
|
||||
const MIN_CUSTOM_DEGREE: usize = 1;
|
||||
const MAX_CUSTOM_DEGREE: usize = 32;
|
||||
|
||||
fn main() -> Result<(), HyperPlonkErrors> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let thread = args[1].parse().unwrap_or(12);
|
||||
|
||||
let thread = args[1].parse().unwrap_or(24);
|
||||
let mut rng = test_rng();
|
||||
let pcs_srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
|
||||
ThreadPoolBuilder::new()
|
||||
.num_threads(thread)
|
||||
.build_global()
|
||||
.unwrap();
|
||||
bench_vanilla_plonk(thread)?;
|
||||
for degree in [1, 2, 4, 8, 16, 32] {
|
||||
bench_high_degree_plonk(degree, thread)?;
|
||||
bench_vanilla_plonk(&pcs_srs, thread)?;
|
||||
for degree in MIN_CUSTOM_DEGREE..MAX_CUSTOM_DEGREE {
|
||||
bench_high_degree_plonk(&pcs_srs, degree, thread)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bench_vanilla_plonk(thread: usize) -> Result<(), HyperPlonkErrors> {
|
||||
let mut rng = test_rng();
|
||||
let pcs_srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 22)?;
|
||||
|
||||
let filename = format!("vanilla nv {}.txt", thread);
|
||||
fn bench_vanilla_plonk(
|
||||
pcs_srs: &MultilinearUniversalParams<Bls12_381>,
|
||||
thread: usize,
|
||||
) -> Result<(), HyperPlonkErrors> {
|
||||
let filename = format!("vanilla threads {}.txt", thread);
|
||||
let mut file = File::create(filename).unwrap();
|
||||
for nv in 1..16 {
|
||||
for nv in MIN_NUM_VARS..MAX_NUM_VARS {
|
||||
let vanilla_gate = CustomizedGates::vanilla_plonk_gate();
|
||||
bench_mock_circuit_zkp_helper(&mut file, nv, &vanilla_gate, &pcs_srs)?;
|
||||
}
|
||||
@@ -44,14 +53,15 @@ fn bench_vanilla_plonk(thread: usize) -> Result<(), HyperPlonkErrors> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bench_high_degree_plonk(degree: usize, thread: usize) -> Result<(), HyperPlonkErrors> {
|
||||
let mut rng = test_rng();
|
||||
let pcs_srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 22)?;
|
||||
|
||||
fn bench_high_degree_plonk(
|
||||
pcs_srs: &MultilinearUniversalParams<Bls12_381>,
|
||||
degree: usize,
|
||||
thread: usize,
|
||||
) -> Result<(), HyperPlonkErrors> {
|
||||
let filename = format!("high degree {} thread {}.txt", degree, thread);
|
||||
let mut file = File::create(filename).unwrap();
|
||||
for nv in 1..16 {
|
||||
let vanilla_gate = CustomizedGates::vanilla_plonk_gate();
|
||||
for nv in MIN_NUM_VARS..MAX_NUM_VARS {
|
||||
let vanilla_gate = CustomizedGates::mock_gate(2, degree);
|
||||
bench_mock_circuit_zkp_helper(&mut file, nv, &vanilla_gate, &pcs_srs)?;
|
||||
}
|
||||
|
||||
@@ -62,17 +72,14 @@ fn bench_mock_circuit_zkp_helper(
|
||||
file: &mut File,
|
||||
nv: usize,
|
||||
gate: &CustomizedGates,
|
||||
pcs_srs: &(
|
||||
MultilinearUniversalParams<Bls12_381>,
|
||||
UnivariateUniversalParams<Bls12_381>,
|
||||
),
|
||||
pcs_srs: &MultilinearUniversalParams<Bls12_381>,
|
||||
) -> Result<(), HyperPlonkErrors> {
|
||||
let repetition = if nv < 10 {
|
||||
10
|
||||
} else if nv < 20 {
|
||||
5
|
||||
} else {
|
||||
} else if nv < 20 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
//==========================================================
|
||||
@@ -120,7 +127,7 @@ fn bench_mock_circuit_zkp_helper(
|
||||
)?;
|
||||
}
|
||||
let t = start.elapsed().as_micros() / repetition as u128;
|
||||
println!("proving for {} variables: {} us", nv, t);
|
||||
|
||||
file.write_all(format!("{} {}\n", nv, t).as_ref()).unwrap();
|
||||
|
||||
let proof = <PolyIOP<Fr> as HyperPlonkSNARK<Bls12_381, MultilinearKzgPCS<Bls12_381>>>::prove(
|
||||
|
||||
@@ -145,14 +145,43 @@ impl CustomizedGates {
|
||||
pub fn mock_gate(num_witness: usize, degree: usize) -> Self {
|
||||
let mut gates = vec![];
|
||||
|
||||
let high_degree_term = vec![0; degree];
|
||||
let mut high_degree_term = vec![0; degree - 1];
|
||||
high_degree_term.push(1);
|
||||
|
||||
gates.push((1, Some(0), high_degree_term));
|
||||
for i in 1..num_witness {
|
||||
gates.push((1, Some(i), vec![i]))
|
||||
for i in 0..num_witness {
|
||||
gates.push((1, Some(i + 1), vec![i]))
|
||||
}
|
||||
gates.push((1, Some(num_witness), vec![]));
|
||||
gates.push((1, Some(num_witness + 1), vec![]));
|
||||
|
||||
CustomizedGates { gates }
|
||||
}
|
||||
|
||||
/// Return a plonk gate where #selector > #witness * 2
|
||||
/// ``` ignore
|
||||
/// q_1 w_1 + q_2 w_2 + q_3 w_3 +
|
||||
/// q_4 w1w2 + q_5 w1w3 + q_6 w2w3 +
|
||||
/// q_7 = 0
|
||||
/// ```
|
||||
/// which is
|
||||
/// ``` ignore
|
||||
/// (1, Some(id_qL), vec![id_W1]),
|
||||
/// (1, Some(id_qR), vec![id_W2]),
|
||||
/// (1, Some(id_qO), vec![id_W3]),
|
||||
/// (1, Some(id_qM), vec![id_W1, id_w2]),
|
||||
/// (1, Some(id_qC), vec![]),
|
||||
/// ```
|
||||
pub fn super_long_selector_gate() -> Self {
|
||||
Self {
|
||||
gates: vec![
|
||||
(1, Some(0), vec![0]),
|
||||
(1, Some(1), vec![1]),
|
||||
(1, Some(2), vec![2]),
|
||||
(1, Some(3), vec![0, 1]),
|
||||
(1, Some(4), vec![0, 2]),
|
||||
(1, Some(5), vec![1, 2]),
|
||||
(1, Some(6), vec![]),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ use arithmetic::ArithErrors;
|
||||
use ark_serialize::SerializationError;
|
||||
use ark_std::string::String;
|
||||
use displaydoc::Display;
|
||||
use pcs::prelude::PCSError;
|
||||
use poly_iop::prelude::PolyIOPErrors;
|
||||
use subroutines::{pcs::prelude::PCSError, poly_iop::prelude::PolyIOPErrors};
|
||||
use transcript::TranscriptError;
|
||||
|
||||
/// A `enum` specifying the possible failure modes of hyperplonk.
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
use ark_ec::PairingEngine;
|
||||
use errors::HyperPlonkErrors;
|
||||
use pcs::prelude::PolynomialCommitmentScheme;
|
||||
use poly_iop::prelude::PermutationCheck;
|
||||
use subroutines::{pcs::prelude::PolynomialCommitmentScheme, poly_iop::prelude::PermutationCheck};
|
||||
use witness::WitnessColumn;
|
||||
|
||||
mod custom_gate;
|
||||
|
||||
@@ -135,11 +135,18 @@ mod test {
|
||||
use super::*;
|
||||
use crate::{errors::HyperPlonkErrors, HyperPlonkSNARK};
|
||||
use ark_bls12_381::{Bls12_381, Fr};
|
||||
use pcs::{
|
||||
prelude::{MultilinearKzgPCS, MultilinearUniversalParams, UnivariateUniversalParams},
|
||||
PolynomialCommitmentScheme,
|
||||
use subroutines::{
|
||||
pcs::{
|
||||
prelude::{MultilinearKzgPCS, MultilinearUniversalParams},
|
||||
PolynomialCommitmentScheme,
|
||||
},
|
||||
poly_iop::PolyIOP,
|
||||
};
|
||||
use poly_iop::PolyIOP;
|
||||
|
||||
const SUPPORTED_SIZE: usize = 20;
|
||||
const MIN_NUM_VARS: usize = 8;
|
||||
const MAX_NUM_VARS: usize = 15;
|
||||
const CUSTOM_DEGREE: [usize; 6] = [1, 2, 4, 8, 16, 32];
|
||||
|
||||
#[test]
|
||||
fn test_mock_circuit_sat() {
|
||||
@@ -153,7 +160,7 @@ mod test {
|
||||
assert!(circuit.is_satisfied());
|
||||
|
||||
for num_witness in 2..10 {
|
||||
for degree in 1..10 {
|
||||
for degree in CUSTOM_DEGREE {
|
||||
let mock_gate = CustomizedGates::mock_gate(num_witness, degree);
|
||||
let circuit = MockCircuit::<Fr>::new(1 << i, &mock_gate);
|
||||
assert!(circuit.is_satisfied());
|
||||
@@ -165,10 +172,7 @@ mod test {
|
||||
fn test_mock_circuit_zkp_helper(
|
||||
nv: usize,
|
||||
gate: &CustomizedGates,
|
||||
pcs_srs: &(
|
||||
MultilinearUniversalParams<Bls12_381>,
|
||||
UnivariateUniversalParams<Bls12_381>,
|
||||
),
|
||||
pcs_srs: &MultilinearUniversalParams<Bls12_381>,
|
||||
) -> Result<(), HyperPlonkErrors> {
|
||||
let circuit = MockCircuit::<Fr>::new(1 << nv, gate);
|
||||
assert!(circuit.is_satisfied());
|
||||
@@ -201,18 +205,19 @@ mod test {
|
||||
#[test]
|
||||
fn test_mock_circuit_zkp() -> Result<(), HyperPlonkErrors> {
|
||||
let mut rng = test_rng();
|
||||
let pcs_srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 16)?;
|
||||
for nv in 1..10 {
|
||||
let pcs_srs =
|
||||
MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
|
||||
for nv in MIN_NUM_VARS..MAX_NUM_VARS {
|
||||
let vanilla_gate = CustomizedGates::vanilla_plonk_gate();
|
||||
test_mock_circuit_zkp_helper(nv, &vanilla_gate, &pcs_srs)?;
|
||||
}
|
||||
for nv in 1..10 {
|
||||
for nv in MIN_NUM_VARS..MAX_NUM_VARS {
|
||||
let tubro_gate = CustomizedGates::jellyfish_turbo_plonk_gate();
|
||||
test_mock_circuit_zkp_helper(nv, &tubro_gate, &pcs_srs)?;
|
||||
}
|
||||
let nv = 5;
|
||||
for num_witness in 2..10 {
|
||||
for degree in [1, 2, 4, 8, 16] {
|
||||
for degree in CUSTOM_DEGREE {
|
||||
let mock_gate = CustomizedGates::mock_gate(num_witness, degree);
|
||||
test_mock_circuit_zkp_helper(nv, &mock_gate, &pcs_srs)?;
|
||||
}
|
||||
@@ -224,12 +229,26 @@ mod test {
|
||||
#[test]
|
||||
fn test_mock_circuit_e2e() -> Result<(), HyperPlonkErrors> {
|
||||
let mut rng = test_rng();
|
||||
let pcs_srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 23)?;
|
||||
let nv = 18;
|
||||
let pcs_srs =
|
||||
MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
|
||||
let nv = MAX_NUM_VARS;
|
||||
|
||||
let vanilla_gate = CustomizedGates::vanilla_plonk_gate();
|
||||
test_mock_circuit_zkp_helper(nv, &vanilla_gate, &pcs_srs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mock_long_selector_e2e() -> Result<(), HyperPlonkErrors> {
|
||||
let mut rng = test_rng();
|
||||
let pcs_srs =
|
||||
MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, SUPPORTED_SIZE)?;
|
||||
let nv = MAX_NUM_VARS;
|
||||
|
||||
let long_selector_gate = CustomizedGates::super_long_selector_gate();
|
||||
test_mock_circuit_zkp_helper(nv, &long_selector_gate, &pcs_srs)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@ use crate::{
|
||||
witness::WitnessColumn,
|
||||
HyperPlonkSNARK,
|
||||
};
|
||||
use arithmetic::{
|
||||
evaluate_opt, gen_eval_point, identity_permutation_mle, merge_polynomials, VPAuxInfo,
|
||||
};
|
||||
use arithmetic::{evaluate_opt, identity_permutation_mle, merge_polynomials, VPAuxInfo};
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::{end_timer, log2, start_timer, One, Zero};
|
||||
use pcs::prelude::{compute_qx_degree, PolynomialCommitmentScheme};
|
||||
use poly_iop::{
|
||||
prelude::{PermutationCheck, ZeroCheck},
|
||||
PolyIOP,
|
||||
use std::{marker::PhantomData, rc::Rc};
|
||||
use subroutines::{
|
||||
pcs::prelude::{Commitment, PolynomialCommitmentScheme},
|
||||
poly_iop::{
|
||||
prelude::{PermutationCheck, ZeroCheck},
|
||||
PolyIOP,
|
||||
},
|
||||
BatchProof,
|
||||
};
|
||||
use std::{cmp::max, marker::PhantomData, rc::Rc};
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
impl<E, PCS> HyperPlonkSNARK<E, PCS> for PolyIOP<E::Fr>
|
||||
@@ -30,6 +31,8 @@ where
|
||||
Polynomial = Rc<DenseMultilinearExtension<E::Fr>>,
|
||||
Point = Vec<E::Fr>,
|
||||
Evaluation = E::Fr,
|
||||
Commitment = Commitment<E>,
|
||||
BatchProof = BatchProof<E, PCS>,
|
||||
>,
|
||||
{
|
||||
type Index = HyperPlonkIndex<E::Fr>;
|
||||
@@ -44,29 +47,16 @@ where
|
||||
let num_vars = index.num_variables();
|
||||
|
||||
let log_num_witness_polys = log2(index.num_witness_columns()) as usize;
|
||||
let log_num_selector_polys = log2(index.num_selector_columns()) as usize;
|
||||
|
||||
let witness_merged_nv = num_vars + log_num_witness_polys;
|
||||
let selector_merged_nv = num_vars + log_num_selector_polys;
|
||||
|
||||
let max_nv = max(witness_merged_nv + 1, selector_merged_nv);
|
||||
let max_points = max(
|
||||
// prod(x) has 5 points
|
||||
5,
|
||||
max(
|
||||
// selector points
|
||||
index.num_selector_columns(),
|
||||
// witness points + public input point + perm point
|
||||
index.num_witness_columns() + 2,
|
||||
),
|
||||
);
|
||||
let log_chunk_size = log_num_witness_polys + 1;
|
||||
let prod_x_nv = num_vars + log_chunk_size;
|
||||
|
||||
let supported_uni_degree = compute_qx_degree(max_nv, max_points);
|
||||
let supported_ml_degree = max_nv;
|
||||
let supported_ml_degree = prod_x_nv;
|
||||
|
||||
// extract PCS prover and verifier keys from SRS
|
||||
let (pcs_prover_param, pcs_verifier_param) =
|
||||
PCS::trim(pcs_srs, supported_uni_degree, Some(supported_ml_degree))?;
|
||||
PCS::trim(pcs_srs, None, Some(supported_ml_degree))?;
|
||||
|
||||
// build permutation oracles
|
||||
let permutation_oracle = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
|
||||
@@ -82,22 +72,27 @@ where
|
||||
.map(|s| Rc::new(DenseMultilinearExtension::from(s)))
|
||||
.collect();
|
||||
|
||||
let selector_merged = merge_polynomials(&selector_oracles)?;
|
||||
let selector_com = PCS::commit(&pcs_prover_param, &selector_merged)?;
|
||||
let selector_commitments = selector_oracles
|
||||
.iter()
|
||||
.map(|poly| PCS::commit(&pcs_prover_param, poly))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// let selector_merged = merge_polynomials(&selector_oracles)?;
|
||||
// let selector_com = PCS::commit(&pcs_prover_param, &selector_merged)?;
|
||||
|
||||
Ok((
|
||||
Self::ProvingKey {
|
||||
params: index.params.clone(),
|
||||
permutation_oracle: permutation_oracle.clone(),
|
||||
selector_oracles,
|
||||
selector_com: selector_com.clone(),
|
||||
selector_commitments: selector_commitments.clone(),
|
||||
pcs_param: pcs_prover_param,
|
||||
},
|
||||
Self::VerifyingKey {
|
||||
params: index.params.clone(),
|
||||
permutation_oracle,
|
||||
pcs_param: pcs_verifier_param,
|
||||
selector_com,
|
||||
selector_commitments,
|
||||
perm_com,
|
||||
},
|
||||
))
|
||||
@@ -163,9 +158,13 @@ where
|
||||
// witness assignment of length 2^n
|
||||
let num_vars = pk.params.num_variables();
|
||||
let log_num_witness_polys = log2(pk.params.num_witness_columns()) as usize;
|
||||
let log_num_selector_polys = log2(pk.params.num_selector_columns()) as usize;
|
||||
// number of variables in merged polynomial for Multilinear-KZG
|
||||
let merged_nv = num_vars + log_num_witness_polys;
|
||||
|
||||
// number of nv in prod(x) which is supposed to be the cap
|
||||
// so each chunk we we store maximum 1 << (prod_x_nv - num_var) selectors
|
||||
let log_chunk_size = log_num_witness_polys + 1;
|
||||
let prod_x_nv = num_vars + log_chunk_size;
|
||||
|
||||
// online public input of length 2^\ell
|
||||
let ell = log2(pk.params.num_pub_input) as usize;
|
||||
|
||||
@@ -176,23 +175,31 @@ where
|
||||
// - prod(x)
|
||||
// - selectors
|
||||
//
|
||||
// Accumulator for w_merged and its points
|
||||
let mut w_merged_pcs_acc = PcsAccumulator::<E, PCS>::new();
|
||||
// Accumulator for prod(x) and its points
|
||||
let mut prod_pcs_acc = PcsAccumulator::<E, PCS>::new();
|
||||
// Accumulator for prod(x) and its points
|
||||
let mut selector_pcs_acc = PcsAccumulator::<E, PCS>::new();
|
||||
|
||||
let witness_polys: Vec<Rc<DenseMultilinearExtension<E::Fr>>> = witnesses
|
||||
.iter()
|
||||
.map(|w| Rc::new(DenseMultilinearExtension::from(w)))
|
||||
.collect();
|
||||
// Accumulator's nv is bounded by prod(x) that means
|
||||
// we need to split the selectors into multiple chunks if
|
||||
// #selectors > chunk_size
|
||||
// let mut pcs_acc = PcsAccumulator::<E, PCS>::new(prod_x_nv);
|
||||
let mut prod_x_pcs_acc = PcsAccumulator::<E, PCS>::new(prod_x_nv);
|
||||
let mut witness_and_selector_x_pcs_acc = PcsAccumulator::<E, PCS>::new(num_vars);
|
||||
|
||||
// =======================================================================
|
||||
// 1. Commit Witness polynomials `w_i(x)` and append commitment to
|
||||
// transcript
|
||||
// =======================================================================
|
||||
let step = start_timer!(|| "commit witnesses");
|
||||
|
||||
let witness_polys: Vec<Rc<DenseMultilinearExtension<E::Fr>>> = witnesses
|
||||
.iter()
|
||||
.map(|w| Rc::new(DenseMultilinearExtension::from(w)))
|
||||
.collect();
|
||||
|
||||
let witness_commits = witness_polys
|
||||
.iter()
|
||||
.map(|x| PCS::commit(&pk.pcs_param, x).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// merge all witness into a single MLE - we will run perm check on it
|
||||
// to obtain prod(x)
|
||||
let w_merged = merge_polynomials(&witness_polys)?;
|
||||
if w_merged.num_vars != merged_nv {
|
||||
return Err(HyperPlonkErrors::InvalidParameters(format!(
|
||||
@@ -200,9 +207,11 @@ where
|
||||
w_merged.num_vars, merged_nv
|
||||
)));
|
||||
}
|
||||
let w_merged_com = PCS::commit(&pk.pcs_param, &w_merged)?;
|
||||
w_merged_pcs_acc.init_poly(w_merged.clone(), w_merged_com.clone())?;
|
||||
transcript.append_serializable_element(b"w", &w_merged_com)?;
|
||||
|
||||
// TODO: we'll remove one of witness_merged_commit and witness_commits later.
|
||||
let witness_merged_commit = PCS::commit(&pk.pcs_param, &w_merged)?;
|
||||
transcript.append_serializable_element(b"w", &witness_merged_commit)?;
|
||||
|
||||
end_timer!(step);
|
||||
// =======================================================================
|
||||
// 2 Run ZeroCheck on
|
||||
@@ -266,7 +275,6 @@ where
|
||||
|
||||
// 4.1 (deferred) open prod(0,x), prod(1, x), prod(x, 0), prod(x, 1)
|
||||
// perm_check_point
|
||||
prod_pcs_acc.init_poly(prod_x, perm_check_proof.prod_x_comm.clone())?;
|
||||
// prod(0, x)
|
||||
let tmp_point1 = [perm_check_point.as_slice(), &[E::Fr::zero()]].concat();
|
||||
// prod(1, x)
|
||||
@@ -275,145 +283,77 @@ where
|
||||
let tmp_point3 = [&[E::Fr::zero()], perm_check_point.as_slice()].concat();
|
||||
// prod(x, 1)
|
||||
let tmp_point4 = [&[E::Fr::one()], perm_check_point.as_slice()].concat();
|
||||
// prod(1, ..., 1, 0)
|
||||
let tmp_point5 = [vec![E::Fr::zero()], vec![E::Fr::one(); merged_nv]].concat();
|
||||
// // prod(1, ..., 1, 0)
|
||||
// let tmp_point5 = [vec![E::Fr::zero()], vec![E::Fr::one();
|
||||
// merged_nv]].concat();
|
||||
|
||||
prod_pcs_acc.insert_point(&tmp_point1);
|
||||
prod_pcs_acc.insert_point(&tmp_point2);
|
||||
prod_pcs_acc.insert_point(&tmp_point3);
|
||||
prod_pcs_acc.insert_point(&tmp_point4);
|
||||
prod_pcs_acc.insert_point(&tmp_point5);
|
||||
prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point1);
|
||||
prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point2);
|
||||
prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point3);
|
||||
prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point4);
|
||||
|
||||
// 4.2 permutation check
|
||||
// - 4.2.1. (deferred) wi_poly(perm_check_point)
|
||||
w_merged_pcs_acc.insert_point(perm_check_point);
|
||||
|
||||
#[cfg(feature = "extensive_sanity_checks")]
|
||||
{
|
||||
// sanity check
|
||||
let eval = pk
|
||||
.permutation_oracle
|
||||
.evaluate(&perm_check_proof.zero_check_proof.point)
|
||||
.ok_or_else(|| {
|
||||
HyperPlonkErrors::InvalidParameters(
|
||||
"perm_oracle evaluation dimension does not match".to_string(),
|
||||
)
|
||||
})?;
|
||||
if eval != perm_oracle_eval {
|
||||
return Err(HyperPlonkErrors::InvalidProver(
|
||||
"perm_oracle evaluation is different from PCS opening".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// - 4.2.1. wi_poly(perm_check_point)
|
||||
let (perm_check_opening, perm_check_eval) =
|
||||
PCS::open(&pk.pcs_param, &w_merged, perm_check_point)?;
|
||||
|
||||
// - 4.3. zero check evaluations and proofs
|
||||
// - 4.3.1 (deferred) wi_poly(zero_check_point)
|
||||
for i in 0..witness_polys.len() {
|
||||
let tmp_point = gen_eval_point(i, log_num_witness_polys, &zero_check_proof.point);
|
||||
// Deferred opening zero check proof
|
||||
w_merged_pcs_acc.insert_point(&tmp_point);
|
||||
witness_and_selector_x_pcs_acc.insert_poly_and_points(
|
||||
&witness_polys[i],
|
||||
&witness_commits[i],
|
||||
&zero_check_proof.point,
|
||||
);
|
||||
}
|
||||
|
||||
// - 4.3.2. (deferred) selector_poly(zero_check_point)
|
||||
let selector_merged = merge_polynomials(&pk.selector_oracles)?;
|
||||
selector_pcs_acc.init_poly(selector_merged, pk.selector_com.clone())?;
|
||||
for i in 0..pk.selector_oracles.len() {
|
||||
let tmp_point = gen_eval_point(i, log_num_selector_polys, &zero_check_proof.point);
|
||||
// Deferred opening zero check proof
|
||||
selector_pcs_acc.insert_point(&tmp_point);
|
||||
}
|
||||
pk.selector_oracles
|
||||
.iter()
|
||||
.zip(pk.selector_commitments.iter())
|
||||
.for_each(|(poly, com)| {
|
||||
witness_and_selector_x_pcs_acc.insert_poly_and_points(
|
||||
poly,
|
||||
com,
|
||||
&zero_check_proof.point,
|
||||
)
|
||||
});
|
||||
|
||||
// - 4.4. public input consistency checks
|
||||
// - pi_poly(r_pi) where r_pi is sampled from transcript
|
||||
let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?;
|
||||
let tmp_point = [
|
||||
vec![E::Fr::zero(); num_vars - ell],
|
||||
r_pi,
|
||||
vec![E::Fr::zero(); log_num_witness_polys],
|
||||
]
|
||||
.concat();
|
||||
w_merged_pcs_acc.insert_point(&tmp_point);
|
||||
|
||||
#[cfg(feature = "extensive_sanity_checks")]
|
||||
{
|
||||
// sanity check
|
||||
let pi_poly = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
|
||||
ell, pub_input,
|
||||
));
|
||||
|
||||
let eval = pi_poly.evaluate(&r_pi).ok_or_else(|| {
|
||||
HyperPlonkErrors::InvalidParameters(
|
||||
"public input evaluation dimension does not match".to_string(),
|
||||
)
|
||||
})?;
|
||||
if eval != pi_eval {
|
||||
return Err(HyperPlonkErrors::InvalidProver(
|
||||
"public input evaluation is different from PCS opening".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
let tmp_point = [vec![E::Fr::zero(); num_vars - ell], r_pi].concat();
|
||||
witness_and_selector_x_pcs_acc.insert_poly_and_points(
|
||||
&witness_polys[0],
|
||||
&witness_commits[0],
|
||||
&tmp_point,
|
||||
);
|
||||
end_timer!(step);
|
||||
|
||||
// =======================================================================
|
||||
// 5. deferred batch opening
|
||||
// =======================================================================
|
||||
let step = start_timer!(|| "deferred batch openings");
|
||||
let sub_step = start_timer!(|| "open witness");
|
||||
let (w_merged_batch_opening, w_merged_batch_evals) =
|
||||
w_merged_pcs_acc.batch_open(&pk.pcs_param)?;
|
||||
end_timer!(sub_step);
|
||||
|
||||
let sub_step = start_timer!(|| "open prod(x)");
|
||||
let (prod_batch_openings, prod_batch_evals) = prod_pcs_acc.batch_open(&pk.pcs_param)?;
|
||||
end_timer!(sub_step);
|
||||
|
||||
let sub_step = start_timer!(|| "open selector");
|
||||
let (selector_batch_opening, selector_batch_evals) =
|
||||
selector_pcs_acc.batch_open(&pk.pcs_param)?;
|
||||
end_timer!(sub_step);
|
||||
let step = start_timer!(|| "deferred batch openings prod(x)");
|
||||
let batch_prod_x_openings = prod_x_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?;
|
||||
end_timer!(step);
|
||||
|
||||
let step = start_timer!(|| "deferred batch openings witness and selectors");
|
||||
let batch_witness_and_selector_openings =
|
||||
witness_and_selector_x_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?;
|
||||
end_timer!(step);
|
||||
|
||||
end_timer!(start);
|
||||
|
||||
Ok(HyperPlonkProof {
|
||||
// =======================================================================
|
||||
// witness related
|
||||
// =======================================================================
|
||||
/// PCS commit for witnesses
|
||||
w_merged_com,
|
||||
// Batch opening for witness commitment
|
||||
// - PermCheck eval: 1 point
|
||||
// - ZeroCheck evals: #witness points
|
||||
// - public input eval: 1 point
|
||||
w_merged_batch_opening,
|
||||
// Evaluations of Witness
|
||||
// - PermCheck eval: 1 point
|
||||
// - ZeroCheck evals: #witness points
|
||||
// - public input eval: 1 point
|
||||
w_merged_batch_evals,
|
||||
// =======================================================================
|
||||
// prod(x) related
|
||||
// =======================================================================
|
||||
// prod(x)'s openings
|
||||
// - prod(0, x),
|
||||
// - prod(1, x),
|
||||
// - prod(x, 0),
|
||||
// - prod(x, 1),
|
||||
// - prod(1, ..., 1,0)
|
||||
prod_batch_openings,
|
||||
// prod(x)'s evaluations
|
||||
// - prod(0, x),
|
||||
// - prod(1, x),
|
||||
// - prod(x, 0),
|
||||
// - prod(x, 1),
|
||||
// - prod(1, ..., 1,0)
|
||||
prod_batch_evals,
|
||||
// =======================================================================
|
||||
// selectors related
|
||||
// =======================================================================
|
||||
// PCS openings for selectors on zero check point
|
||||
selector_batch_opening,
|
||||
// Evaluates of selectors on zero check point
|
||||
selector_batch_evals,
|
||||
// PCS commit for witnesses
|
||||
witness_merged_commit,
|
||||
witness_commits,
|
||||
// batch_openings,
|
||||
batch_prod_x_openings,
|
||||
batch_witness_and_selector_openings,
|
||||
// perm check openings
|
||||
perm_check_opening,
|
||||
perm_check_eval,
|
||||
// =======================================================================
|
||||
// IOP proofs
|
||||
// =======================================================================
|
||||
@@ -460,15 +400,38 @@ where
|
||||
let start = start_timer!(|| "hyperplonk verification");
|
||||
|
||||
let mut transcript = IOPTranscript::<E::Fr>::new(b"hyperplonk");
|
||||
|
||||
let num_selectors = vk.params.num_selector_columns();
|
||||
let num_witnesses = vk.params.num_witness_columns();
|
||||
|
||||
// witness assignment of length 2^n
|
||||
let log_num_witness_polys = log2(num_witnesses) as usize;
|
||||
let num_vars = vk.params.num_variables();
|
||||
let log_num_witness_polys = log2(vk.params.num_witness_columns()) as usize;
|
||||
// number of variables in merged polynomial for Multilinear-KZG
|
||||
let merged_nv = num_vars + log_num_witness_polys;
|
||||
|
||||
// online public input of length 2^\ell
|
||||
let ell = log2(vk.params.num_pub_input) as usize;
|
||||
|
||||
// sequence:
|
||||
// - prod(x) at 5 points
|
||||
// - w_merged at perm check point
|
||||
// - w_merged at zero check points (#witness points)
|
||||
// - selector_merged at zero check points (#selector points)
|
||||
// - w[0] at r_pi
|
||||
let selector_evals = &proof
|
||||
.batch_witness_and_selector_openings
|
||||
.f_i_eval_at_point_i[num_witnesses..num_witnesses + num_selectors];
|
||||
let witness_evals = &proof
|
||||
.batch_witness_and_selector_openings
|
||||
.f_i_eval_at_point_i[..num_witnesses];
|
||||
let prod_evals = &proof.batch_prod_x_openings.f_i_eval_at_point_i[0..4];
|
||||
let pi_eval = proof
|
||||
.batch_witness_and_selector_openings
|
||||
.f_i_eval_at_point_i
|
||||
.last()
|
||||
.unwrap();
|
||||
|
||||
let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input);
|
||||
|
||||
// =======================================================================
|
||||
@@ -482,27 +445,6 @@ where
|
||||
1 << ell
|
||||
)));
|
||||
}
|
||||
if proof.selector_batch_evals.len() - 1 != vk.params.num_selector_columns() {
|
||||
return Err(HyperPlonkErrors::InvalidVerifier(format!(
|
||||
"Selector length is not correct: got {}, expect {}",
|
||||
proof.selector_batch_evals.len() - 1,
|
||||
1 << vk.params.num_selector_columns()
|
||||
)));
|
||||
}
|
||||
if proof.w_merged_batch_evals.len() != vk.params.num_witness_columns() + 3 {
|
||||
return Err(HyperPlonkErrors::InvalidVerifier(format!(
|
||||
"Witness length is not correct: got {}, expect {}",
|
||||
proof.w_merged_batch_evals.len() - 3,
|
||||
vk.params.num_witness_columns()
|
||||
)));
|
||||
}
|
||||
if proof.prod_batch_evals.len() - 1 != 5 {
|
||||
return Err(HyperPlonkErrors::InvalidVerifier(format!(
|
||||
"the number of product polynomial evaluations is not correct: got {}, expect {}",
|
||||
proof.prod_batch_evals.len() - 1,
|
||||
5
|
||||
)));
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// 1. Verify zero_check_proof on
|
||||
@@ -522,7 +464,7 @@ where
|
||||
phantom: PhantomData::default(),
|
||||
};
|
||||
// push witness to transcript
|
||||
transcript.append_serializable_element(b"w", &proof.w_merged_com)?;
|
||||
transcript.append_serializable_element(b"w", &proof.witness_merged_commit)?;
|
||||
|
||||
let zero_check_sub_claim = <Self as ZeroCheck<E::Fr>>::verify(
|
||||
&proof.zero_check_proof,
|
||||
@@ -533,11 +475,7 @@ where
|
||||
let zero_check_point = &zero_check_sub_claim.point;
|
||||
|
||||
// check zero check subclaim
|
||||
let f_eval = eval_f(
|
||||
&vk.params.gate_func,
|
||||
&proof.selector_batch_evals[..vk.params.num_selector_columns()],
|
||||
&proof.w_merged_batch_evals[1..],
|
||||
)?;
|
||||
let f_eval = eval_f(&vk.params.gate_func, selector_evals, witness_evals)?;
|
||||
if f_eval != zero_check_sub_claim.expected_evaluation {
|
||||
return Err(HyperPlonkErrors::InvalidProof(
|
||||
"zero check evaluation failed".to_string(),
|
||||
@@ -592,12 +530,10 @@ where
|
||||
let s_id_eval = evaluate_opt(&s_id, perm_check_point);
|
||||
let s_perm_eval = evaluate_opt(&vk.permutation_oracle, perm_check_point);
|
||||
|
||||
let q_x_rec = proof.prod_batch_evals[1]
|
||||
- proof.prod_batch_evals[2] * proof.prod_batch_evals[3]
|
||||
let q_x_rec = prod_evals[1] - prod_evals[2] * prod_evals[3]
|
||||
+ alpha
|
||||
* ((proof.w_merged_batch_evals[0] + beta * s_perm_eval + gamma)
|
||||
* proof.prod_batch_evals[0]
|
||||
- (proof.w_merged_batch_evals[0] + beta * s_id_eval + gamma));
|
||||
* ((prod_evals[0] + beta * s_perm_eval + gamma) * prod_evals[0]
|
||||
- (prod_evals[0] + beta * s_id_eval + gamma));
|
||||
|
||||
if q_x_rec
|
||||
!= perm_check_sub_claim
|
||||
@@ -619,84 +555,66 @@ where
|
||||
// =======================================================================
|
||||
// 3.1 open prod(x)' evaluations
|
||||
// =======================================================================
|
||||
let prod_final_query = perm_check_sub_claim.product_check_sub_claim.final_query;
|
||||
let points = [
|
||||
// TODO: Check prod(x) at (1,...,1,0)
|
||||
let _prod_final_query = perm_check_sub_claim.product_check_sub_claim.final_query;
|
||||
let prod_points = [
|
||||
[perm_check_point.as_slice(), &[E::Fr::zero()]].concat(),
|
||||
[perm_check_point.as_slice(), &[E::Fr::one()]].concat(),
|
||||
[&[E::Fr::zero()], perm_check_point.as_slice()].concat(),
|
||||
[&[E::Fr::one()], perm_check_point.as_slice()].concat(),
|
||||
prod_final_query.0,
|
||||
// prod_final_query.0,
|
||||
];
|
||||
|
||||
if !PCS::batch_verify_single_poly(
|
||||
&vk.pcs_param,
|
||||
&proof.perm_check_proof.prod_x_comm,
|
||||
&points,
|
||||
&proof.prod_batch_evals,
|
||||
&proof.prod_batch_openings,
|
||||
)? {
|
||||
return Err(HyperPlonkErrors::InvalidProof(
|
||||
"prod(0, x) pcs verification failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// 3.2 open selectors' evaluations
|
||||
// =======================================================================
|
||||
let log_num_selector_polys = log2(vk.params.num_selector_columns()) as usize;
|
||||
let mut points = vec![];
|
||||
for i in 0..vk.params.num_selector_columns() {
|
||||
let tmp_point =
|
||||
gen_eval_point(i, log_num_selector_polys, &proof.zero_check_proof.point);
|
||||
points.push(tmp_point);
|
||||
}
|
||||
|
||||
if !PCS::batch_verify_single_poly(
|
||||
&vk.pcs_param,
|
||||
&vk.selector_com,
|
||||
&points,
|
||||
&proof.selector_batch_evals,
|
||||
&proof.selector_batch_opening,
|
||||
)? {
|
||||
return Err(HyperPlonkErrors::InvalidProof(
|
||||
"selector pcs verification failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// 3.2 open witnesses' evaluations
|
||||
// =======================================================================
|
||||
let mut r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?;
|
||||
let pi_eval = evaluate_opt(&pi_poly, &r_pi);
|
||||
assert_eq!(
|
||||
pi_eval,
|
||||
proof.w_merged_batch_evals[proof.w_merged_batch_evals.len() - 2]
|
||||
);
|
||||
|
||||
r_pi = [
|
||||
vec![E::Fr::zero(); num_vars - ell],
|
||||
r_pi,
|
||||
vec![E::Fr::zero(); log_num_witness_polys],
|
||||
let res = PCS::batch_verify(
|
||||
&vk.pcs_param,
|
||||
[proof.perm_check_proof.prod_x_comm; 4].as_ref(),
|
||||
prod_points.as_ref(),
|
||||
&proof.batch_prod_x_openings,
|
||||
&mut transcript,
|
||||
)?;
|
||||
assert!(res);
|
||||
|
||||
// =======================================================================
|
||||
// 3.3 open witnesses' and selectors evaluations
|
||||
// =======================================================================
|
||||
|
||||
let res = PCS::verify(
|
||||
&vk.pcs_param,
|
||||
&proof.witness_merged_commit,
|
||||
perm_check_point,
|
||||
&proof.perm_check_eval,
|
||||
&proof.perm_check_opening,
|
||||
)?;
|
||||
assert!(res);
|
||||
|
||||
let pi_eval_rec = evaluate_opt(&pi_poly, &r_pi);
|
||||
assert_eq!(&pi_eval_rec, pi_eval);
|
||||
|
||||
r_pi = [vec![E::Fr::zero(); num_vars - ell], r_pi].concat();
|
||||
let commitments = [
|
||||
proof.witness_commits.as_slice(),
|
||||
vk.selector_commitments.as_slice(),
|
||||
&[proof.witness_commits[0]],
|
||||
]
|
||||
.concat();
|
||||
|
||||
let mut points = vec![perm_check_point.clone()];
|
||||
let points = [
|
||||
vec![zero_check_point.clone(); num_witnesses + num_selectors].as_slice(),
|
||||
&[r_pi],
|
||||
]
|
||||
.concat();
|
||||
|
||||
for i in 0..proof.w_merged_batch_evals.len() - 3 {
|
||||
points.push(gen_eval_point(i, log_num_witness_polys, zero_check_point))
|
||||
}
|
||||
points.push(r_pi);
|
||||
if !PCS::batch_verify_single_poly(
|
||||
let res = PCS::batch_verify(
|
||||
&vk.pcs_param,
|
||||
&proof.w_merged_com,
|
||||
&points,
|
||||
&proof.w_merged_batch_evals,
|
||||
&proof.w_merged_batch_opening,
|
||||
)? {
|
||||
return Err(HyperPlonkErrors::InvalidProof(
|
||||
"witness for permutation check pcs verification failed".to_string(),
|
||||
));
|
||||
}
|
||||
commitments.as_ref(),
|
||||
points.as_ref(),
|
||||
&proof.batch_witness_and_selector_openings,
|
||||
&mut transcript,
|
||||
)?;
|
||||
|
||||
assert!(res);
|
||||
|
||||
end_timer!(step);
|
||||
end_timer!(start);
|
||||
@@ -714,7 +632,7 @@ mod tests {
|
||||
use arithmetic::random_permutation_mle;
|
||||
use ark_bls12_381::Bls12_381;
|
||||
use ark_std::test_rng;
|
||||
use pcs::prelude::MultilinearKzgPCS;
|
||||
use subroutines::pcs::prelude::MultilinearKzgPCS;
|
||||
|
||||
#[test]
|
||||
fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> {
|
||||
|
||||
@@ -5,61 +5,31 @@ use ark_ec::PairingEngine;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::log2;
|
||||
use pcs::PolynomialCommitmentScheme;
|
||||
use poly_iop::prelude::{PermutationCheck, ZeroCheck};
|
||||
use std::rc::Rc;
|
||||
use subroutines::{
|
||||
pcs::PolynomialCommitmentScheme,
|
||||
poly_iop::prelude::{PermutationCheck, ZeroCheck},
|
||||
};
|
||||
|
||||
/// The proof for the HyperPlonk PolyIOP, consists of the following:
|
||||
/// - a batch commitment to all the witness MLEs
|
||||
/// - a batch opening to all the MLEs at certain index
|
||||
/// - the zero-check proof for checking custom gate-satisfiability
|
||||
/// - the permutation-check proof for checking the copy constraints
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HyperPlonkProof<E, PC, PCS>
|
||||
where
|
||||
E: PairingEngine,
|
||||
PC: PermutationCheck<E, PCS>,
|
||||
PCS: PolynomialCommitmentScheme<E>,
|
||||
{
|
||||
// =======================================================================
|
||||
// witness related
|
||||
// =======================================================================
|
||||
// PCS commit for witnesses
|
||||
pub w_merged_com: PCS::Commitment,
|
||||
// Batch opening for witness commitment
|
||||
// - PermCheck eval: 1 point
|
||||
// - ZeroCheck evals: #witness points
|
||||
// - public input eval: 1 point
|
||||
pub w_merged_batch_opening: PCS::BatchProof,
|
||||
// Evaluations of Witness
|
||||
// - PermCheck eval: 1 point
|
||||
// - ZeroCheck evals: #witness points
|
||||
// - public input eval: 1 point
|
||||
pub w_merged_batch_evals: Vec<E::Fr>,
|
||||
// =======================================================================
|
||||
// prod(x) related
|
||||
// =======================================================================
|
||||
// prod(x)'s openings
|
||||
// - prod(0, x),
|
||||
// - prod(1, x),
|
||||
// - prod(x, 0),
|
||||
// - prod(x, 1),
|
||||
// - prod(1, ..., 1,0)
|
||||
pub prod_batch_openings: PCS::BatchProof,
|
||||
// prod(x)'s evaluations
|
||||
// - prod(0, x),
|
||||
// - prod(1, x),
|
||||
// - prod(x, 0),
|
||||
// - prod(x, 1),
|
||||
// - prod(1, ..., 1,0)
|
||||
pub prod_batch_evals: Vec<E::Fr>,
|
||||
// =======================================================================
|
||||
// selectors related
|
||||
// =======================================================================
|
||||
// PCS openings for selectors on zero check point
|
||||
pub selector_batch_opening: PCS::BatchProof,
|
||||
// Evaluates of selectors on zero check point
|
||||
pub selector_batch_evals: Vec<E::Fr>,
|
||||
pub witness_merged_commit: PCS::Commitment,
|
||||
pub witness_commits: Vec<PCS::Commitment>,
|
||||
pub batch_prod_x_openings: PCS::BatchProof,
|
||||
pub batch_witness_and_selector_openings: PCS::BatchProof,
|
||||
pub perm_check_opening: PCS::Proof,
|
||||
pub perm_check_eval: PCS::Evaluation,
|
||||
// =======================================================================
|
||||
// IOP proofs
|
||||
// =======================================================================
|
||||
@@ -144,7 +114,7 @@ pub struct HyperPlonkProvingKey<E: PairingEngine, PCS: PolynomialCommitmentSchem
|
||||
/// The preprocessed selector polynomials
|
||||
pub selector_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>>,
|
||||
/// A commitment to the preprocessed selector polynomials
|
||||
pub selector_com: PCS::Commitment,
|
||||
pub selector_commitments: Vec<PCS::Commitment>,
|
||||
/// The parameters for PCS commitment
|
||||
pub pcs_param: PCS::ProverParam,
|
||||
}
|
||||
@@ -162,7 +132,7 @@ pub struct HyperPlonkVerifyingKey<E: PairingEngine, PCS: PolynomialCommitmentSch
|
||||
/// The parameters for PCS commitment
|
||||
pub pcs_param: PCS::VerifierParam,
|
||||
/// A commitment to the preprocessed selector polynomials
|
||||
pub selector_com: PCS::Commitment,
|
||||
pub selector_commitments: Vec<PCS::Commitment>,
|
||||
/// Permutation oracle's commitment
|
||||
pub perm_com: PCS::Commitment,
|
||||
}
|
||||
|
||||
@@ -2,83 +2,84 @@ use crate::{
|
||||
custom_gate::CustomizedGates, errors::HyperPlonkErrors, structs::HyperPlonkParams,
|
||||
witness::WitnessColumn,
|
||||
};
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use arithmetic::{evaluate_opt, VirtualPolynomial};
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use pcs::PolynomialCommitmentScheme;
|
||||
use std::{borrow::Borrow, rc::Rc};
|
||||
use subroutines::pcs::{prelude::Commitment, PolynomialCommitmentScheme};
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
/// An accumulator structure that holds a polynomial and
|
||||
/// its opening points
|
||||
#[derive(Debug)]
|
||||
pub(super) struct PcsAccumulator<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>> {
|
||||
pub(crate) polynomial: Option<PCS::Polynomial>,
|
||||
pub(crate) poly_commit: Option<PCS::Commitment>,
|
||||
// sequence:
|
||||
// - prod(x) at 5 points
|
||||
// - w_merged at perm check point
|
||||
// - w_merged at zero check points (#witness points)
|
||||
// - selector_merged at zero check points (#selector points)
|
||||
// - w[0] at r_pi
|
||||
pub(crate) num_var: usize,
|
||||
pub(crate) polynomials: Vec<PCS::Polynomial>,
|
||||
pub(crate) commitments: Vec<PCS::Commitment>,
|
||||
pub(crate) points: Vec<PCS::Point>,
|
||||
pub(crate) evals: Vec<PCS::Evaluation>,
|
||||
}
|
||||
|
||||
impl<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>> PcsAccumulator<E, PCS> {
|
||||
impl<E, PCS> PcsAccumulator<E, PCS>
|
||||
where
|
||||
E: PairingEngine,
|
||||
PCS: PolynomialCommitmentScheme<
|
||||
E,
|
||||
Polynomial = Rc<DenseMultilinearExtension<E::Fr>>,
|
||||
Point = Vec<E::Fr>,
|
||||
Evaluation = E::Fr,
|
||||
Commitment = Commitment<E>,
|
||||
>,
|
||||
{
|
||||
/// Create an empty accumulator.
|
||||
pub(super) fn new() -> Self {
|
||||
pub(super) fn new(num_var: usize) -> Self {
|
||||
Self {
|
||||
polynomial: None,
|
||||
poly_commit: None,
|
||||
num_var,
|
||||
polynomials: vec![],
|
||||
commitments: vec![],
|
||||
points: vec![],
|
||||
evals: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the polynomial; requires both the polynomial
|
||||
/// and its commitment.
|
||||
pub(super) fn init_poly(
|
||||
&mut self,
|
||||
polynomial: PCS::Polynomial,
|
||||
commitment: PCS::Commitment,
|
||||
) -> Result<(), HyperPlonkErrors> {
|
||||
if self.polynomial.is_some() || self.poly_commit.is_some() {
|
||||
return Err(HyperPlonkErrors::InvalidProver(
|
||||
"poly already set for accumulator".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.polynomial = Some(polynomial);
|
||||
self.poly_commit = Some(commitment);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Push a new evaluation point into the accumulator
|
||||
pub(super) fn insert_point(&mut self, point: &PCS::Point) {
|
||||
self.points.push(point.clone())
|
||||
pub(super) fn insert_poly_and_points(
|
||||
&mut self,
|
||||
poly: &PCS::Polynomial,
|
||||
commit: &PCS::Commitment,
|
||||
point: &PCS::Point,
|
||||
) {
|
||||
assert!(poly.num_vars == point.len());
|
||||
assert!(poly.num_vars == self.num_var);
|
||||
|
||||
let eval = evaluate_opt(poly, point);
|
||||
|
||||
self.evals.push(eval);
|
||||
self.polynomials.push(poly.clone());
|
||||
self.points.push(point.clone());
|
||||
self.commitments.push(*commit);
|
||||
}
|
||||
|
||||
/// Batch open all the points over a merged polynomial.
|
||||
/// A simple wrapper of PCS::multi_open
|
||||
pub(super) fn batch_open(
|
||||
pub(super) fn multi_open(
|
||||
&self,
|
||||
prover_param: impl Borrow<PCS::ProverParam>,
|
||||
) -> Result<(PCS::BatchProof, Vec<PCS::Evaluation>), HyperPlonkErrors> {
|
||||
let poly = match &self.polynomial {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return Err(HyperPlonkErrors::InvalidProver(
|
||||
"poly is set for accumulator".to_string(),
|
||||
))
|
||||
},
|
||||
};
|
||||
|
||||
let commitment = match &self.poly_commit {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
return Err(HyperPlonkErrors::InvalidProver(
|
||||
"poly is set for accumulator".to_string(),
|
||||
))
|
||||
},
|
||||
};
|
||||
Ok(PCS::multi_open_single_poly(
|
||||
transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<PCS::BatchProof, HyperPlonkErrors> {
|
||||
Ok(PCS::multi_open(
|
||||
prover_param.borrow(),
|
||||
commitment,
|
||||
poly,
|
||||
&self.points,
|
||||
self.polynomials.as_ref(),
|
||||
self.points.as_ref(),
|
||||
self.evals.as_ref(),
|
||||
transcript,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
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
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "poly-iop"
|
||||
name = "subroutines"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
@@ -17,36 +17,39 @@ ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "cur
|
||||
rand_chacha = { version = "0.3.0", default-features = false }
|
||||
displaydoc = { version = "0.2.3", default-features = false }
|
||||
rayon = { version = "1.5.2", default-features = false, optional = true }
|
||||
derivative = { version = "2", features = ["use_core"] }
|
||||
itertools = { version = "0.10.4", optional = true }
|
||||
|
||||
transcript = { path = "../transcript" }
|
||||
arithmetic = { path = "../arithmetic" }
|
||||
pcs = { path = "../pcs" }
|
||||
util = { path = "../util" }
|
||||
|
||||
[dev-dependencies]
|
||||
ark-ec = { version = "^0.3.0", default-features = false }
|
||||
# # Benchmarks
|
||||
# [[bench]]
|
||||
# name = "poly-iop-benches"
|
||||
# path = "benches/iop_bench.rs"
|
||||
# harness = false
|
||||
|
||||
# Benchmarks
|
||||
[[bench]]
|
||||
name = "poly-iop-benches"
|
||||
path = "benches/bench.rs"
|
||||
name = "pcs-benches"
|
||||
path = "benches/pcs_bench.rs"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
# default = [ "parallel", "print-trace" ]
|
||||
default = [ "parallel" ]
|
||||
# extensive sanity checks that are useful for debugging
|
||||
extensive_sanity_checks = [
|
||||
"pcs/extensive_sanity_checks",
|
||||
]
|
||||
extensive_sanity_checks = [ ]
|
||||
parallel = [
|
||||
"rayon",
|
||||
"arithmetic/parallel",
|
||||
"itertools",
|
||||
"ark-std/parallel",
|
||||
"ark-ff/parallel",
|
||||
"ark-poly/parallel",
|
||||
"pcs/parallel",
|
||||
"util/parallel"
|
||||
"ark-ec/parallel",
|
||||
"util/parallel",
|
||||
"arithmetic/parallel",
|
||||
]
|
||||
print-trace = [
|
||||
"arithmetic/print-trace",
|
||||
@@ -2,11 +2,13 @@ use arithmetic::{identity_permutation_mle, VPAuxInfo, VirtualPolynomial};
|
||||
use ark_bls12_381::{Bls12_381, Fr};
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::test_rng;
|
||||
use pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme};
|
||||
use poly_iop::prelude::{
|
||||
PermutationCheck, PolyIOP, PolyIOPErrors, ProductCheck, SumCheck, ZeroCheck,
|
||||
};
|
||||
use std::{marker::PhantomData, rc::Rc, time::Instant};
|
||||
use subroutines::{
|
||||
pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme},
|
||||
poly_iop::prelude::{
|
||||
PermutationCheck, PolyIOP, PolyIOPErrors, ProductCheck, SumCheck, ZeroCheck,
|
||||
},
|
||||
};
|
||||
|
||||
type KZG = MultilinearKzgPCS<Bls12_381>;
|
||||
|
||||
@@ -57,16 +59,12 @@ fn bench_sum_check() -> Result<(), PolyIOPErrors> {
|
||||
|
||||
for _ in 0..repetition {
|
||||
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
|
||||
let subclaim = <PolyIOP<Fr> as SumCheck<Fr>>::verify(
|
||||
let _subclaim = <PolyIOP<Fr> as SumCheck<Fr>>::verify(
|
||||
asserted_sum,
|
||||
&proof,
|
||||
&poly_info,
|
||||
&mut transcript,
|
||||
)?;
|
||||
assert!(
|
||||
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
|
||||
"wrong subclaim"
|
||||
);
|
||||
}
|
||||
println!(
|
||||
"sum check verification time for {} variables and {} degree: {} ns",
|
||||
@@ -115,12 +113,8 @@ fn bench_zero_check() -> Result<(), PolyIOPErrors> {
|
||||
let start = Instant::now();
|
||||
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
let zero_subclaim =
|
||||
let _zero_subclaim =
|
||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?;
|
||||
assert!(
|
||||
poly.evaluate(&zero_subclaim.point)? == zero_subclaim.expected_evaluation,
|
||||
"wrong subclaim"
|
||||
);
|
||||
println!(
|
||||
"zero check verification time for {} variables and {} degree: {} ns",
|
||||
nv,
|
||||
@@ -140,7 +134,7 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> {
|
||||
|
||||
for nv in 4..20 {
|
||||
let srs = KZG::gen_srs_for_testing(&mut rng, nv + 1)?;
|
||||
let (pcs_param, _) = KZG::trim(&srs, nv + 1, Some(nv + 1))?;
|
||||
let (pcs_param, _) = KZG::trim(&srs, None, Some(nv + 1))?;
|
||||
|
||||
let repetition = if nv < 10 {
|
||||
100
|
||||
@@ -211,7 +205,7 @@ fn bench_prod_check() -> Result<(), PolyIOPErrors> {
|
||||
|
||||
for nv in 4..20 {
|
||||
let srs = KZG::gen_srs_for_testing(&mut rng, nv + 1)?;
|
||||
let (pcs_param, _) = KZG::trim(&srs, nv + 1, Some(nv + 1))?;
|
||||
let (pcs_param, _) = KZG::trim(&srs, None, Some(nv + 1))?;
|
||||
|
||||
let repetition = if nv < 10 {
|
||||
100
|
||||
@@ -2,11 +2,11 @@ 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::{
|
||||
use std::time::Instant;
|
||||
use subroutines::pcs::{
|
||||
prelude::{MultilinearKzgPCS, PCSError, PolynomialCommitmentScheme},
|
||||
StructuredReferenceString,
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() -> Result<(), PCSError> {
|
||||
bench_pcs()
|
||||
@@ -16,22 +16,19 @@ fn bench_pcs() -> Result<(), PCSError> {
|
||||
let mut rng = test_rng();
|
||||
|
||||
// normal polynomials
|
||||
let uni_params = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 18)?;
|
||||
let uni_params = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, 24)?;
|
||||
|
||||
for nv in 4..19 {
|
||||
for nv in 4..25 {
|
||||
let repetition = if nv < 10 {
|
||||
100
|
||||
} else if nv < 20 {
|
||||
50
|
||||
} else {
|
||||
10
|
||||
} else if nv < 20 {
|
||||
5
|
||||
} else {
|
||||
2
|
||||
};
|
||||
|
||||
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 (ck, vk) = uni_params.trim(nv)?;
|
||||
|
||||
let point: Vec<_> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
5
subroutines/src/lib.rs
Normal file
5
subroutines/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod pcs;
|
||||
pub mod poly_iop;
|
||||
|
||||
pub use pcs::prelude::*;
|
||||
pub use poly_iop::prelude::*;
|
||||
@@ -11,6 +11,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
use ark_std::rand::{CryptoRng, RngCore};
|
||||
use errors::PCSError;
|
||||
use std::{borrow::Borrow, fmt::Debug, hash::Hash};
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
/// This trait defines APIs for polynomial commitment schemes.
|
||||
/// Note that for our usage of PCS, we do not require the hiding property.
|
||||
@@ -35,12 +36,10 @@ pub trait PolynomialCommitmentScheme<E: PairingEngine> {
|
||||
type Evaluation: Field;
|
||||
/// Commitments
|
||||
type Commitment: Clone + CanonicalSerialize + CanonicalDeserialize + Debug + PartialEq + Eq;
|
||||
/// Batch commitments
|
||||
type BatchCommitment: Clone + CanonicalSerialize + CanonicalDeserialize + Debug + PartialEq + Eq;
|
||||
/// Proofs
|
||||
type Proof: Clone + CanonicalSerialize + CanonicalDeserialize + Debug + PartialEq + Eq;
|
||||
/// Batch proofs
|
||||
type BatchProof: Clone + CanonicalSerialize + CanonicalDeserialize + Debug + PartialEq + Eq;
|
||||
type BatchProof;
|
||||
|
||||
/// Build SRS for testing.
|
||||
///
|
||||
@@ -67,7 +66,7 @@ pub trait PolynomialCommitmentScheme<E: PairingEngine> {
|
||||
/// ..)` etc.
|
||||
fn trim(
|
||||
srs: impl Borrow<Self::SRS>,
|
||||
supported_degree: usize,
|
||||
supported_degree: Option<usize>,
|
||||
supported_num_vars: Option<usize>,
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError>;
|
||||
|
||||
@@ -85,12 +84,6 @@ pub trait PolynomialCommitmentScheme<E: PairingEngine> {
|
||||
poly: &Self::Polynomial,
|
||||
) -> Result<Self::Commitment, PCSError>;
|
||||
|
||||
/// Generate a commitment for a list of polynomials
|
||||
fn multi_commit(
|
||||
prover_param: impl Borrow<Self::ProverParam>,
|
||||
polys: &[Self::Polynomial],
|
||||
) -> Result<Self::BatchCommitment, PCSError>;
|
||||
|
||||
/// On input a polynomial `p` and a point `point`, outputs a proof for the
|
||||
/// same.
|
||||
fn open(
|
||||
@@ -102,20 +95,16 @@ pub trait PolynomialCommitmentScheme<E: PairingEngine> {
|
||||
/// Input a list of multilinear extensions, and a same number of points, and
|
||||
/// a transcript, compute a multi-opening for all the polynomials.
|
||||
fn multi_open(
|
||||
prover_param: impl Borrow<Self::ProverParam>,
|
||||
multi_commitment: &Self::BatchCommitment,
|
||||
polynomials: &[Self::Polynomial],
|
||||
points: &[Self::Point],
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError>;
|
||||
|
||||
/// Input a multilinear extension, and a number of points, and
|
||||
/// a transcript, compute a multi-opening for all the polynomials.
|
||||
fn multi_open_single_poly(
|
||||
prover_param: impl Borrow<Self::ProverParam>,
|
||||
commitment: &Self::Commitment,
|
||||
polynomials: &Self::Polynomial,
|
||||
points: &[Self::Point],
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError>;
|
||||
_prover_param: impl Borrow<Self::ProverParam>,
|
||||
_polynomials: &[Self::Polynomial],
|
||||
_points: &[Self::Point],
|
||||
_evals: &[Self::Evaluation],
|
||||
_transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<Self::BatchProof, PCSError> {
|
||||
// the reason we use unimplemented!() is to enable developers to implement the
|
||||
// trait without always implementing the batching APIs.
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
||||
/// committed inside `comm`.
|
||||
@@ -129,24 +118,17 @@ pub trait PolynomialCommitmentScheme<E: PairingEngine> {
|
||||
|
||||
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
|
||||
/// `poly_i` committed inside `comm`.
|
||||
fn batch_verify<R: RngCore + CryptoRng>(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
multi_commitment: &Self::BatchCommitment,
|
||||
points: &[Self::Point],
|
||||
values: &[E::Fr],
|
||||
batch_proof: &Self::BatchProof,
|
||||
rng: &mut R,
|
||||
) -> Result<bool, PCSError>;
|
||||
|
||||
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
|
||||
/// `poly` committed inside `comm`.
|
||||
fn batch_verify_single_poly(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
commitment: &Self::Commitment,
|
||||
points: &[Self::Point],
|
||||
values: &[E::Fr],
|
||||
batch_proof: &Self::BatchProof,
|
||||
) -> Result<bool, PCSError>;
|
||||
fn batch_verify(
|
||||
_verifier_param: &Self::VerifierParam,
|
||||
_commitments: &[Self::Commitment],
|
||||
_points: &[Self::Point],
|
||||
_batch_proof: &Self::BatchProof,
|
||||
_transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<bool, PCSError> {
|
||||
// the reason we use unimplemented!() is to enable developers to implement the
|
||||
// trait without always implementing the batching APIs.
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
/// API definitions for structured reference string
|
||||
329
subroutines/src/pcs/multilinear_kzg/batching.rs
Normal file
329
subroutines/src/pcs/multilinear_kzg/batching.rs
Normal file
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,15 @@
|
||||
|
||||
//! Main module for multilinear KZG commitment scheme
|
||||
|
||||
mod batching;
|
||||
pub(crate) mod batching;
|
||||
pub(crate) mod srs;
|
||||
pub(crate) mod util;
|
||||
|
||||
use self::batching::{
|
||||
batch_verify_internal, batch_verify_same_poly_internal, multi_open_internal,
|
||||
multi_open_same_poly_internal,
|
||||
};
|
||||
use crate::{
|
||||
prelude::{
|
||||
Commitment, UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam,
|
||||
},
|
||||
univariate_kzg::UnivariateKzgProof,
|
||||
PCSError, PolynomialCommitmentScheme, StructuredReferenceString,
|
||||
pcs::{prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString},
|
||||
BatchProof,
|
||||
};
|
||||
use arithmetic::{evaluate_opt, merge_polynomials};
|
||||
use arithmetic::evaluate_opt;
|
||||
use ark_ec::{
|
||||
msm::{FixedBaseMSM, VariableBaseMSM},
|
||||
AffineCurve, PairingEngine, ProjectiveCurve,
|
||||
@@ -43,6 +36,9 @@ use ark_std::{
|
||||
};
|
||||
// use batching::{batch_verify_internal, multi_open_internal};
|
||||
use srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam};
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
use self::batching::{batch_verify_internal, multi_open_internal};
|
||||
|
||||
/// KZG Polynomial Commitment Scheme on multilinear polynomials.
|
||||
pub struct MultilinearKzgPCS<E: PairingEngine> {
|
||||
@@ -57,36 +53,19 @@ pub struct MultilinearKzgProof<E: PairingEngine> {
|
||||
pub proofs: Vec<E::G1Affine>,
|
||||
}
|
||||
|
||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
/// proof of batch opening
|
||||
pub struct MultilinearKzgBatchProof<E: PairingEngine> {
|
||||
/// The actual proof
|
||||
pub proof: MultilinearKzgProof<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<UnivariateKzgProof<E>>,
|
||||
}
|
||||
|
||||
impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
// Parameters
|
||||
type ProverParam = (
|
||||
MultilinearProverParam<E>,
|
||||
UnivariateProverParam<E::G1Affine>,
|
||||
);
|
||||
type VerifierParam = (MultilinearVerifierParam<E>, UnivariateVerifierParam<E>);
|
||||
type SRS = (MultilinearUniversalParams<E>, UnivariateUniversalParams<E>);
|
||||
type ProverParam = MultilinearProverParam<E>;
|
||||
type VerifierParam = MultilinearVerifierParam<E>;
|
||||
type SRS = MultilinearUniversalParams<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 = MultilinearKzgProof<E>;
|
||||
type BatchProof = MultilinearKzgBatchProof<E>;
|
||||
type BatchProof = BatchProof<E, Self>;
|
||||
|
||||
/// Build SRS for testing.
|
||||
///
|
||||
@@ -99,10 +78,7 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
rng: &mut R,
|
||||
log_size: usize,
|
||||
) -> Result<Self::SRS, PCSError> {
|
||||
Ok((
|
||||
MultilinearUniversalParams::<E>::gen_srs_for_testing(rng, log_size)?,
|
||||
UnivariateUniversalParams::<E>::gen_srs_for_testing(rng, 1 << log_size)?,
|
||||
))
|
||||
MultilinearUniversalParams::<E>::gen_srs_for_testing(rng, log_size)
|
||||
}
|
||||
|
||||
/// Trim the universal parameters to specialize the public parameters.
|
||||
@@ -110,9 +86,11 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
/// `supported_num_vars` for multilinear.
|
||||
fn trim(
|
||||
srs: impl Borrow<Self::SRS>,
|
||||
supported_degree: usize,
|
||||
supported_degree: Option<usize>,
|
||||
supported_num_vars: Option<usize>,
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> {
|
||||
assert!(supported_degree.is_none());
|
||||
|
||||
let supported_num_vars = match supported_num_vars {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
@@ -121,10 +99,9 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
))
|
||||
},
|
||||
};
|
||||
let (uni_ck, uni_vk) = srs.borrow().1.trim(supported_degree)?;
|
||||
let (ml_ck, ml_vk) = srs.borrow().0.trim(supported_num_vars)?;
|
||||
let (ml_ck, ml_vk) = srs.borrow().trim(supported_num_vars)?;
|
||||
|
||||
Ok(((ml_ck, uni_ck), (ml_vk, uni_vk)))
|
||||
Ok((ml_ck, ml_vk))
|
||||
}
|
||||
|
||||
/// Generate a commitment for a polynomial.
|
||||
@@ -137,48 +114,20 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
) -> Result<Self::Commitment, PCSError> {
|
||||
let prover_param = prover_param.borrow();
|
||||
let commit_timer = start_timer!(|| "commit");
|
||||
if prover_param.0.num_vars < poly.num_vars {
|
||||
if prover_param.num_vars < poly.num_vars {
|
||||
return Err(PCSError::InvalidParameters(format!(
|
||||
"MlE length ({}) exceeds param limit ({})",
|
||||
poly.num_vars, prover_param.0.num_vars
|
||||
poly.num_vars, prover_param.num_vars
|
||||
)));
|
||||
}
|
||||
let ignored = prover_param.0.num_vars - poly.num_vars;
|
||||
let ignored = prover_param.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: impl Borrow<Self::ProverParam>,
|
||||
polys: &[Self::Polynomial],
|
||||
) -> Result<Self::Commitment, PCSError> {
|
||||
let prover_param = prover_param.borrow();
|
||||
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,
|
||||
&prover_param.powers_of_g[ignored].evals,
|
||||
scalars.as_slice(),
|
||||
)
|
||||
.into_affine();
|
||||
@@ -201,69 +150,24 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
polynomial: &Self::Polynomial,
|
||||
point: &Self::Point,
|
||||
) -> Result<(Self::Proof, Self::Evaluation), PCSError> {
|
||||
open_internal(&prover_param.borrow().0, polynomial, point)
|
||||
open_internal(prover_param.borrow(), polynomial, point)
|
||||
}
|
||||
|
||||
/// Input
|
||||
/// - the prover parameters for univariate KZG,
|
||||
/// - the prover parameters for multilinear KZG,
|
||||
/// - a list of multilinear extensions (MLEs),
|
||||
/// - a commitment to all multilinear extensions,
|
||||
/// - 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)`
|
||||
/// Input a list of multilinear extensions, and a same number of points, and
|
||||
/// a transcript, compute a multi-opening for all the polynomials.
|
||||
fn multi_open(
|
||||
prover_param: impl Borrow<Self::ProverParam>,
|
||||
multi_commitment: &Self::BatchCommitment,
|
||||
polynomials: &[Self::Polynomial],
|
||||
points: &[Self::Point],
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
|
||||
multi_open_internal::<E>(
|
||||
&prover_param.borrow().1,
|
||||
&prover_param.borrow().0,
|
||||
evals: &[Self::Evaluation],
|
||||
transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<BatchProof<E, Self>, PCSError> {
|
||||
multi_open_internal(
|
||||
prover_param.borrow(),
|
||||
polynomials,
|
||||
multi_commitment,
|
||||
points,
|
||||
)
|
||||
}
|
||||
|
||||
/// Input a multilinear extension, and a number of points, and
|
||||
/// a transcript, compute a multi-opening for all the polynomials.
|
||||
fn multi_open_single_poly(
|
||||
prover_param: impl Borrow<Self::ProverParam>,
|
||||
commitment: &Self::Commitment,
|
||||
polynomial: &Self::Polynomial,
|
||||
points: &[Self::Point],
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
|
||||
multi_open_same_poly_internal::<E>(
|
||||
&prover_param.borrow().1,
|
||||
&prover_param.borrow().0,
|
||||
polynomial,
|
||||
commitment,
|
||||
points,
|
||||
evals,
|
||||
transcript,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -280,55 +184,19 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for MultilinearKzgPCS<E> {
|
||||
value: &E::Fr,
|
||||
proof: &Self::Proof,
|
||||
) -> Result<bool, PCSError> {
|
||||
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 + CryptoRng>(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
multi_commitment: &Self::BatchCommitment,
|
||||
points: &[Self::Point],
|
||||
values: &[E::Fr],
|
||||
batch_proof: &Self::BatchProof,
|
||||
_rng: &mut R,
|
||||
) -> Result<bool, PCSError> {
|
||||
batch_verify_internal(
|
||||
&verifier_param.1,
|
||||
&verifier_param.0,
|
||||
multi_commitment,
|
||||
points,
|
||||
values,
|
||||
batch_proof,
|
||||
)
|
||||
verify_internal(verifier_param, commitment, point, value, proof)
|
||||
}
|
||||
|
||||
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
|
||||
/// `poly` committed inside `comm`.
|
||||
fn batch_verify_single_poly(
|
||||
/// `poly_i` committed inside `comm`.
|
||||
fn batch_verify(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
commitment: &Self::Commitment,
|
||||
commitments: &[Self::Commitment],
|
||||
points: &[Self::Point],
|
||||
values: &[E::Fr],
|
||||
batch_proof: &Self::BatchProof,
|
||||
transcript: &mut IOPTranscript<E::Fr>,
|
||||
) -> Result<bool, PCSError> {
|
||||
batch_verify_same_poly_internal(
|
||||
&verifier_param.1,
|
||||
&verifier_param.0,
|
||||
commitment,
|
||||
points,
|
||||
values,
|
||||
batch_proof,
|
||||
)
|
||||
batch_verify_internal(verifier_param, commitments, points, batch_proof, transcript)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,25 +346,22 @@ fn verify_internal<E: PairingEngine>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::multilinear_kzg::util::compute_qx_degree;
|
||||
use arithmetic::get_batched_nv;
|
||||
use ark_bls12_381::Bls12_381;
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::{log2, rand::RngCore, test_rng, vec::Vec, UniformRand};
|
||||
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 + CryptoRng>(
|
||||
params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
|
||||
params: &MultilinearUniversalParams<E>,
|
||||
poly: &Rc<DenseMultilinearExtension<Fr>>,
|
||||
rng: &mut R,
|
||||
) -> Result<(), PCSError> {
|
||||
let nv = poly.num_vars();
|
||||
assert_ne!(nv, 0);
|
||||
let uni_degree = 1;
|
||||
let (ck, vk) = MultilinearKzgPCS::trim(params, uni_degree, Some(nv + 1))?;
|
||||
let (ck, vk) = MultilinearKzgPCS::trim(params, None, Some(nv + 1))?;
|
||||
let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
|
||||
let com = MultilinearKzgPCS::commit(&ck, poly)?;
|
||||
let (proof, value) = MultilinearKzgPCS::open(&ck, poly, &point)?;
|
||||
@@ -537,108 +402,4 @@ mod tests {
|
||||
// normal polynomials
|
||||
assert!(MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 0).is_err());
|
||||
}
|
||||
|
||||
fn test_multi_open_single_poly_helper<R: RngCore + CryptoRng>(
|
||||
params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
|
||||
poly: Rc<DenseMultilinearExtension<Fr>>,
|
||||
num_open: usize,
|
||||
rng: &mut R,
|
||||
) -> Result<(), PCSError> {
|
||||
let nv = poly.num_vars();
|
||||
assert_ne!(nv, 0);
|
||||
let uni_degree = 1024;
|
||||
let (ck, vk) = MultilinearKzgPCS::trim(params, uni_degree, Some(nv + 1))?;
|
||||
let mut points = vec![];
|
||||
for _ in 0..num_open {
|
||||
let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
|
||||
points.push(point)
|
||||
}
|
||||
let com = MultilinearKzgPCS::commit(&ck, &poly)?;
|
||||
let (proof, mut values) =
|
||||
MultilinearKzgPCS::multi_open_single_poly(&ck, &com, &poly, &points)?;
|
||||
for (a, b) in values.iter().zip(points.iter()) {
|
||||
let p = poly.evaluate(&b).unwrap();
|
||||
assert_eq!(*a, p);
|
||||
}
|
||||
|
||||
assert!(MultilinearKzgPCS::batch_verify_single_poly(
|
||||
&vk, &com, &points, &values, &proof
|
||||
)?);
|
||||
|
||||
values[0] = Fr::rand(rng);
|
||||
assert!(!MultilinearKzgPCS::batch_verify_single_poly(
|
||||
&vk, &com, &points, &values, &proof
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_open_single_poly() -> Result<(), PCSError> {
|
||||
let mut rng = test_rng();
|
||||
|
||||
let params = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 15)?;
|
||||
|
||||
for nv in 1..10 {
|
||||
for num_open in 2..10 {
|
||||
let poly1 = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
|
||||
test_multi_open_single_poly_helper(¶ms, poly1, num_open, &mut rng)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn test_multi_open_helper<R: RngCore + CryptoRng>(
|
||||
params: &(MultilinearUniversalParams<E>, UnivariateUniversalParams<E>),
|
||||
polys: &[Rc<DenseMultilinearExtension<Fr>>],
|
||||
num_open: usize,
|
||||
rng: &mut R,
|
||||
) -> Result<(), PCSError> {
|
||||
let nv = polys[0].num_vars();
|
||||
assert_ne!(nv, 0);
|
||||
let merged_nv = get_batched_nv(nv, polys.len());
|
||||
let qx_degree = compute_qx_degree(merged_nv, polys.len());
|
||||
let padded_qx_degree = 1usize << log2(qx_degree);
|
||||
|
||||
let (ck, vk) = MultilinearKzgPCS::trim(params, padded_qx_degree, Some(merged_nv))?;
|
||||
let mut points = vec![];
|
||||
for _ in 0..num_open {
|
||||
let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect();
|
||||
points.push(point)
|
||||
}
|
||||
let com = MultilinearKzgPCS::multi_commit(&ck, &polys)?;
|
||||
let (proof, mut values) = MultilinearKzgPCS::multi_open(&ck, &com, polys, &points)?;
|
||||
|
||||
assert!(MultilinearKzgPCS::batch_verify(
|
||||
&vk, &com, &points, &values, &proof, rng
|
||||
)?);
|
||||
|
||||
values[0] = Fr::rand(rng);
|
||||
assert!(!MultilinearKzgPCS::batch_verify_single_poly(
|
||||
&vk, &com, &points, &values, &proof
|
||||
)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_open() -> Result<(), PCSError> {
|
||||
let mut rng = test_rng();
|
||||
|
||||
let params = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 15)?;
|
||||
|
||||
// normal polynomials
|
||||
for nv in 1..10 {
|
||||
for num_open in 1..4 {
|
||||
let mut polys = vec![];
|
||||
for _ in 0..num_open {
|
||||
let poly = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
|
||||
polys.push(poly)
|
||||
}
|
||||
|
||||
test_multi_open_helper(¶ms, &polys, num_open, &mut rng)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@
|
||||
// along with the Jellyfish library. If not, see <https://mit-license.org/>.
|
||||
|
||||
//! Implementing Structured Reference Strings for multilinear polynomial KZG
|
||||
use crate::{prelude::PCSError, StructuredReferenceString};
|
||||
use crate::pcs::{
|
||||
multilinear_kzg::util::eq_extension, prelude::PCSError, StructuredReferenceString,
|
||||
};
|
||||
use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve};
|
||||
use ark_ff::{Field, PrimeField};
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
@@ -232,28 +234,6 @@ fn remove_dummy_variable<F: Field>(poly: &[F], pad: usize) -> Result<Vec<F>, PCS
|
||||
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::*;
|
||||
51
subroutines/src/pcs/multilinear_kzg/util.rs
Normal file
51
subroutines/src/pcs/multilinear_kzg/util.rs
Normal file
@@ -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)
|
||||
}
|
||||
@@ -5,12 +5,12 @@
|
||||
// along with the Jellyfish library. If not, see <https://mit-license.org/>.
|
||||
|
||||
//! Prelude
|
||||
pub use crate::{
|
||||
pub use crate::pcs::{
|
||||
errors::PCSError,
|
||||
multilinear_kzg::{
|
||||
batching::BatchProof,
|
||||
srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam},
|
||||
util::compute_qx_degree,
|
||||
MultilinearKzgBatchProof, MultilinearKzgPCS, MultilinearKzgProof,
|
||||
MultilinearKzgPCS, MultilinearKzgProof,
|
||||
},
|
||||
structs::Commitment,
|
||||
univariate_kzg::{
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
//! Main module for univariate KZG commitment scheme
|
||||
|
||||
use crate::{prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString};
|
||||
use crate::pcs::{
|
||||
prelude::Commitment, PCSError, PolynomialCommitmentScheme, StructuredReferenceString,
|
||||
};
|
||||
use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial};
|
||||
@@ -20,12 +22,9 @@ use ark_std::{
|
||||
string::ToString,
|
||||
vec,
|
||||
vec::Vec,
|
||||
One, UniformRand, Zero,
|
||||
One,
|
||||
};
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::prelude::*;
|
||||
use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam};
|
||||
use util::parallelizable_slice_iter;
|
||||
|
||||
pub(crate) mod srs;
|
||||
|
||||
@@ -55,9 +54,10 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for UnivariateKzgPCS<E> {
|
||||
type Evaluation = E::Fr;
|
||||
// Polynomial and its associated types
|
||||
type Commitment = Commitment<E>;
|
||||
type BatchCommitment = Vec<Self::Commitment>;
|
||||
type Proof = UnivariateKzgProof<E>;
|
||||
type BatchProof = UnivariateKzgBatchProof<E>;
|
||||
|
||||
// We do not implement batch univariate KZG at the current version.
|
||||
type BatchProof = ();
|
||||
|
||||
/// Build SRS for testing.
|
||||
///
|
||||
@@ -77,15 +77,16 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for UnivariateKzgPCS<E> {
|
||||
/// `supported_num_vars` must be None or an error is returned.
|
||||
fn trim(
|
||||
srs: impl Borrow<Self::SRS>,
|
||||
supported_degree: usize,
|
||||
supported_degree: Option<usize>,
|
||||
supported_num_vars: Option<usize>,
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), PCSError> {
|
||||
assert!(supported_num_vars.is_none());
|
||||
if supported_num_vars.is_some() {
|
||||
return Err(PCSError::InvalidParameters(
|
||||
"univariate should not receive a num_var param".to_string(),
|
||||
));
|
||||
}
|
||||
srs.borrow().trim(supported_degree)
|
||||
srs.borrow().trim(supported_degree.unwrap())
|
||||
}
|
||||
|
||||
/// Generate a commitment for a polynomial
|
||||
@@ -120,21 +121,6 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for UnivariateKzgPCS<E> {
|
||||
Ok(Commitment(commitment))
|
||||
}
|
||||
|
||||
/// Generate a commitment for a list of polynomials
|
||||
fn multi_commit(
|
||||
prover_param: impl Borrow<Self::ProverParam>,
|
||||
polys: &[Self::Polynomial],
|
||||
) -> Result<Self::BatchCommitment, PCSError> {
|
||||
let prover_param = prover_param.borrow();
|
||||
let commit_time = start_timer!(|| format!("batch commit {} polynomials", polys.len()));
|
||||
let res = parallelizable_slice_iter(polys)
|
||||
.map(|poly| Self::commit(prover_param, poly))
|
||||
.collect::<Result<Vec<Self::Commitment>, PCSError>>()?;
|
||||
|
||||
end_timer!(commit_time);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// On input a polynomial `p` and a point `point`, outputs a proof for the
|
||||
/// same.
|
||||
fn open(
|
||||
@@ -165,48 +151,6 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for UnivariateKzgPCS<E> {
|
||||
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: impl Borrow<Self::ProverParam>,
|
||||
_multi_commitment: &Self::BatchCommitment,
|
||||
polynomials: &[Self::Polynomial],
|
||||
points: &[Self::Point],
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
|
||||
let open_time = start_timer!(|| format!("batch opening {} polynomials", polynomials.len()));
|
||||
if polynomials.len() != points.len() {
|
||||
return Err(PCSError::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.borrow(), poly, point)?;
|
||||
batch_proof.push(proof);
|
||||
evals.push(eval);
|
||||
}
|
||||
|
||||
end_timer!(open_time);
|
||||
Ok((batch_proof, evals))
|
||||
}
|
||||
|
||||
/// Input a multilinear extension, and a number of points, and
|
||||
/// a transcript, compute a multi-opening for all the polynomials.
|
||||
fn multi_open_single_poly(
|
||||
_prover_param: impl Borrow<Self::ProverParam>,
|
||||
_commitment: &Self::Commitment,
|
||||
_polynomials: &Self::Polynomial,
|
||||
_points: &[Self::Point],
|
||||
) -> Result<(Self::BatchProof, Vec<Self::Evaluation>), PCSError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Verifies that `value` is the evaluation at `x` of the polynomial
|
||||
/// committed inside `comm`.
|
||||
fn verify(
|
||||
@@ -234,78 +178,6 @@ impl<E: PairingEngine> PolynomialCommitmentScheme<E> for UnivariateKzgPCS<E> {
|
||||
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 + CryptoRng>(
|
||||
verifier_param: &Self::VerifierParam,
|
||||
multi_commitment: &Self::BatchCommitment,
|
||||
points: &[Self::Point],
|
||||
values: &[E::Fr],
|
||||
batch_proof: &Self::BatchProof,
|
||||
rng: &mut R,
|
||||
) -> Result<bool, PCSError> {
|
||||
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.0);
|
||||
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)
|
||||
}
|
||||
|
||||
/// Verifies that `value_i` is the evaluation at `x_i` of the polynomial
|
||||
/// `poly` committed inside `comm`.
|
||||
fn batch_verify_single_poly(
|
||||
_verifier_param: &Self::VerifierParam,
|
||||
_commitment: &Self::Commitment,
|
||||
_points: &[Self::Point],
|
||||
_values: &[E::Fr],
|
||||
_batch_proof: &Self::BatchProof,
|
||||
) -> Result<bool, PCSError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_leading_zeros_and_convert_to_bigints<F: PrimeField, P: UVPolynomial<F>>(
|
||||
@@ -385,43 +257,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn batch_check_test_template<E>() -> Result<(), PCSError>
|
||||
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 pp = UnivariateKzgPCS::<E>::gen_srs_for_testing(rng, degree)?;
|
||||
let (ck, vk) = UnivariateKzgPCS::<E>::trim(&pp, 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 = UnivariateKzgPCS::<E>::commit(&ck, &p)?;
|
||||
let point = E::Fr::rand(rng);
|
||||
let (proof, value) = UnivariateKzgPCS::<E>::open(&ck, &p, &point)?;
|
||||
|
||||
assert!(UnivariateKzgPCS::<E>::verify(
|
||||
&vk, &comm, &point, &value, &proof
|
||||
)?);
|
||||
comms.push(comm);
|
||||
values.push(value);
|
||||
points.push(point);
|
||||
proofs.push(proof);
|
||||
}
|
||||
assert!(UnivariateKzgPCS::<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");
|
||||
@@ -431,8 +266,4 @@ mod tests {
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
//! Implementing Structured Reference Strings for univariate polynomial KZG
|
||||
|
||||
use crate::{PCSError, StructuredReferenceString};
|
||||
use crate::pcs::{PCSError, StructuredReferenceString};
|
||||
use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
||||
@@ -1,9 +1,9 @@
|
||||
//! Error module.
|
||||
|
||||
use crate::pcs::prelude::PCSError;
|
||||
use arithmetic::ArithErrors;
|
||||
use ark_std::string::String;
|
||||
use displaydoc::Display;
|
||||
use pcs::prelude::PCSError;
|
||||
use transcript::TranscriptError;
|
||||
|
||||
/// A `enum` specifying the possible failure modes of the PolyIOP.
|
||||
@@ -1,11 +1,13 @@
|
||||
//! Main module for the Permutation Check protocol
|
||||
|
||||
use self::util::computer_num_and_denom;
|
||||
use crate::{errors::PolyIOPErrors, prelude::ProductCheck, PolyIOP};
|
||||
use crate::{
|
||||
pcs::PolynomialCommitmentScheme,
|
||||
poly_iop::{errors::PolyIOPErrors, prelude::ProductCheck, PolyIOP},
|
||||
};
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::{end_timer, start_timer};
|
||||
use pcs::PolynomialCommitmentScheme;
|
||||
use std::rc::Rc;
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
@@ -153,13 +155,15 @@ where
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::PermutationCheck;
|
||||
use crate::{errors::PolyIOPErrors, PolyIOP};
|
||||
use crate::{
|
||||
pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme},
|
||||
poly_iop::{errors::PolyIOPErrors, PolyIOP},
|
||||
};
|
||||
use arithmetic::{evaluate_opt, identity_permutation_mle, random_permutation_mle, VPAuxInfo};
|
||||
use ark_bls12_381::Bls12_381;
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::test_rng;
|
||||
use pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme};
|
||||
use std::{marker::PhantomData, rc::Rc};
|
||||
|
||||
type KZG = MultilinearKzgPCS<Bls12_381>;
|
||||
@@ -217,7 +221,7 @@ mod test {
|
||||
let mut rng = test_rng();
|
||||
|
||||
let srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, nv + 1)?;
|
||||
let (pcs_param, _) = MultilinearKzgPCS::<Bls12_381>::trim(&srs, nv + 1, Some(nv + 1))?;
|
||||
let (pcs_param, _) = MultilinearKzgPCS::<Bls12_381>::trim(&srs, None, Some(nv + 1))?;
|
||||
|
||||
{
|
||||
// good path: w is a permutation of w itself under the identify map
|
||||
@@ -233,14 +237,10 @@ mod test {
|
||||
// s_perm is a random map
|
||||
let s_perm = random_permutation_mle(nv, &mut rng);
|
||||
|
||||
if nv == 1 {
|
||||
test_permutation_check_helper::<Bls12_381, KZG>(&pcs_param, &w, &w, &s_perm)?;
|
||||
} else {
|
||||
assert!(test_permutation_check_helper::<Bls12_381, KZG>(
|
||||
&pcs_param, &w, &w, &s_perm
|
||||
)
|
||||
.is_err());
|
||||
}
|
||||
assert!(
|
||||
test_permutation_check_helper::<Bls12_381, KZG>(&pcs_param, &w, &w, &s_perm)
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
//! 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 ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
4
subroutines/src/poly_iop/prelude.rs
Normal file
4
subroutines/src/poly_iop/prelude.rs
Normal file
@@ -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,17 +1,19 @@
|
||||
//! Main module for the Product Check protocol
|
||||
|
||||
use crate::{
|
||||
errors::PolyIOPErrors,
|
||||
prod_check::util::{compute_product_poly, prove_zero_check},
|
||||
zero_check::ZeroCheck,
|
||||
PolyIOP,
|
||||
pcs::PolynomialCommitmentScheme,
|
||||
poly_iop::{
|
||||
errors::PolyIOPErrors,
|
||||
prod_check::util::{compute_product_poly, prove_zero_check},
|
||||
zero_check::ZeroCheck,
|
||||
PolyIOP,
|
||||
},
|
||||
};
|
||||
use arithmetic::VPAuxInfo;
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_ff::{One, PrimeField, Zero};
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::{end_timer, start_timer};
|
||||
use pcs::PolynomialCommitmentScheme;
|
||||
use std::rc::Rc;
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
@@ -201,13 +203,15 @@ where
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ProductCheck;
|
||||
use crate::{errors::PolyIOPErrors, PolyIOP};
|
||||
use crate::{
|
||||
pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme},
|
||||
poly_iop::{errors::PolyIOPErrors, PolyIOP},
|
||||
};
|
||||
use arithmetic::VPAuxInfo;
|
||||
use ark_bls12_381::{Bls12_381, Fr};
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::test_rng;
|
||||
use pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme};
|
||||
use std::{marker::PhantomData, rc::Rc};
|
||||
|
||||
// f and g are guaranteed to have the same product
|
||||
@@ -282,7 +286,7 @@ mod test {
|
||||
g.evaluations.reverse();
|
||||
|
||||
let srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, nv + 1)?;
|
||||
let (pcs_param, _) = MultilinearKzgPCS::<Bls12_381>::trim(&srs, nv + 1, Some(nv + 1))?;
|
||||
let (pcs_param, _) = MultilinearKzgPCS::<Bls12_381>::trim(&srs, None, Some(nv + 1))?;
|
||||
|
||||
test_product_check_helper::<Bls12_381, MultilinearKzgPCS<Bls12_381>>(&f, &g, &pcs_param)?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! 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 ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
@@ -1,6 +1,6 @@
|
||||
//! This module implements the sum check protocol.
|
||||
|
||||
use crate::{
|
||||
use crate::poly_iop::{
|
||||
errors::PolyIOPErrors,
|
||||
structs::{IOPProof, IOPProverState, IOPVerifierState},
|
||||
PolyIOP,
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Prover subroutines for a SumCheck protocol.
|
||||
|
||||
use super::SumCheckProver;
|
||||
use crate::{
|
||||
use crate::poly_iop::{
|
||||
errors::PolyIOPErrors,
|
||||
structs::{IOPProverMessage, IOPProverState},
|
||||
};
|
||||
@@ -88,7 +88,6 @@ impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
|
||||
flattened_ml_extensions
|
||||
.par_iter_mut()
|
||||
.for_each(|mle| *mle = fix_variables(mle, &[r]));
|
||||
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
flattened_ml_extensions
|
||||
.iter_mut()
|
||||
@@ -103,8 +102,7 @@ impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
|
||||
self.round += 1;
|
||||
|
||||
let products_list = self.poly.products.clone();
|
||||
let mut products_sum = Vec::with_capacity(self.poly.aux_info.max_degree + 1);
|
||||
products_sum.resize(self.poly.aux_info.max_degree + 1, F::zero());
|
||||
let mut products_sum = vec![F::zero(); self.poly.aux_info.max_degree + 1];
|
||||
|
||||
// let compute_sum = start_timer!(|| "compute sum");
|
||||
|
||||
@@ -112,30 +110,59 @@ impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
|
||||
// f(r_1, ... r_m,, x_{m+1}... x_n)
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
for (t, e) in products_sum.iter_mut().enumerate() {
|
||||
let t = F::from(t as u64);
|
||||
let one_minus_t = F::one() - t;
|
||||
let products = (0..1 << (self.poly.aux_info.num_variables - self.round))
|
||||
.into_par_iter()
|
||||
.map(|b| {
|
||||
// evaluate P_round(t)
|
||||
let mut tmp = F::zero();
|
||||
products_list.iter().for_each(|(coefficient, products)| {
|
||||
let num_mles = products.len();
|
||||
let mut product = *coefficient;
|
||||
for &f in products.iter().take(num_mles) {
|
||||
let table = &flattened_ml_extensions[f]; // f's range is checked in init
|
||||
product *= table[b << 1] * one_minus_t + table[(b << 1) + 1] * t;
|
||||
}
|
||||
tmp += product;
|
||||
});
|
||||
{
|
||||
let flag = (self.poly.aux_info.max_degree == 2)
|
||||
&& (products_list.len() == 1)
|
||||
&& (products_list[0].0 == F::one());
|
||||
if flag {
|
||||
for (t, e) in products_sum.iter_mut().enumerate() {
|
||||
let evals = (0..1 << (self.poly.aux_info.num_variables - self.round))
|
||||
.into_par_iter()
|
||||
.map(|b| {
|
||||
// evaluate P_round(t)
|
||||
let table0 = &flattened_ml_extensions[products_list[0].1[0]];
|
||||
let table1 = &flattened_ml_extensions[products_list[0].1[1]];
|
||||
if t == 0 {
|
||||
table0[b << 1] * table1[b << 1]
|
||||
} else if t == 1 {
|
||||
table0[(b << 1) + 1] * table1[(b << 1) + 1]
|
||||
} else {
|
||||
(table0[(b << 1) + 1] + table0[(b << 1) + 1] - table0[b << 1])
|
||||
* (table1[(b << 1) + 1] + table1[(b << 1) + 1] - table1[b << 1])
|
||||
}
|
||||
})
|
||||
.collect::<Vec<F>>();
|
||||
for val in evals.iter() {
|
||||
*e += val
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (t, e) in products_sum.iter_mut().enumerate() {
|
||||
let t = F::from(t as u64);
|
||||
let products = (0..1 << (self.poly.aux_info.num_variables - self.round))
|
||||
.into_par_iter()
|
||||
.map(|b| {
|
||||
// evaluate P_round(t)
|
||||
let mut tmp = F::zero();
|
||||
products_list.iter().for_each(|(coefficient, products)| {
|
||||
let num_mles = products.len();
|
||||
let mut product = *coefficient;
|
||||
for &f in products.iter().take(num_mles) {
|
||||
let table = &flattened_ml_extensions[f]; // f's range is checked in init
|
||||
product *=
|
||||
table[b << 1] + (table[(b << 1) + 1] - table[b << 1]) * t;
|
||||
}
|
||||
tmp += product;
|
||||
});
|
||||
|
||||
tmp
|
||||
})
|
||||
.collect::<Vec<F>>();
|
||||
tmp
|
||||
})
|
||||
.collect::<Vec<F>>();
|
||||
|
||||
for i in products.iter() {
|
||||
*e += i
|
||||
for i in products.iter() {
|
||||
*e += i
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +178,7 @@ impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
|
||||
let mut product = *coefficient;
|
||||
for &f in products.iter().take(num_mles) {
|
||||
let table = &flattened_ml_extensions[f]; // f's range is checked in init
|
||||
product *= table[b << 1] * one_minus_t + table[(b << 1) + 1] * t;
|
||||
product *= table[b << 1] + (table[(b << 1) + 1] - table[b << 1]) * t;
|
||||
}
|
||||
*e += product;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Verifier subroutines for a SumCheck protocol.
|
||||
|
||||
use super::{SumCheckSubClaim, SumCheckVerifier};
|
||||
use crate::{
|
||||
use crate::poly_iop::{
|
||||
errors::PolyIOPErrors,
|
||||
structs::{IOPProverMessage, IOPVerifierState},
|
||||
};
|
||||
@@ -311,7 +311,7 @@ fn u64_factorial(a: usize) -> u64 {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::interpolate_uni_poly;
|
||||
use crate::errors::PolyIOPErrors;
|
||||
use crate::poly_iop::errors::PolyIOPErrors;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial};
|
||||
use ark_std::{vec::Vec, UniformRand};
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{errors::PolyIOPErrors, sum_check::SumCheck, PolyIOP};
|
||||
use crate::poly_iop::{errors::PolyIOPErrors, sum_check::SumCheck, PolyIOP};
|
||||
use arithmetic::build_eq_x_r;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::MultilinearExtension;
|
||||
@@ -122,7 +122,7 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
mod test {
|
||||
|
||||
use super::ZeroCheck;
|
||||
use crate::{errors::PolyIOPErrors, PolyIOP};
|
||||
use crate::poly_iop::{errors::PolyIOPErrors, PolyIOP};
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_std::test_rng;
|
||||
Reference in New Issue
Block a user