From a0298a61f494b16acaacebe3ad6143d220a7ac99 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 30 Jun 2021 15:05:40 +0200 Subject: [PATCH 01/23] Added partial products --- src/prover.rs | 107 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 18 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 67b5a185..76b69e10 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -1,5 +1,6 @@ use std::time::Instant; +use itertools::Itertools; use log::info; use rayon::prelude::*; @@ -12,7 +13,7 @@ use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; -use crate::util::{log2_ceil, transpose}; +use crate::util::{ceil_div_usize, log2_ceil, transpose}; use crate::vars::EvaluationVarsBase; use crate::witness::{PartialWitness, Witness}; @@ -78,14 +79,20 @@ pub(crate) fn prove, const D: usize>( let betas = challenger.get_n_challenges(num_challenges); let gammas = challenger.get_n_challenges(num_challenges); + let partial_products = timed!( + all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data), + "to compute partial products" + ); + let plonk_z_vecs = timed!( - compute_zs(&witness, &betas, &gammas, prover_data, common_data), + compute_zs(&partial_products, prover_data, common_data), "to compute Z's" ); + let zs_partial_products = [partial_products.concat(), plonk_z_vecs].concat(); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new( - plonk_z_vecs, + zs_partial_products, fri_config.rate_bits, PlonkPolynomials::ZS.blinding ), @@ -168,41 +175,105 @@ pub(crate) fn prove, const D: usize>( } } -fn compute_zs, const D: usize>( +fn all_wires_permutation_partial_products, const D: usize>( witness: &Witness, betas: &[F], gammas: &[F], prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, -) -> Vec> { +) -> Vec>> { (0..common_data.config.num_challenges) - .map(|i| compute_z(witness, betas[i], gammas[i], prover_data, common_data)) + .map(|i| { + wires_permutation_partial_products( + witness, + betas[i], + gammas[i], + prover_data, + common_data, + ) + }) .collect() } -fn compute_z, const D: usize>( +fn wires_permutation_partial_products, const D: usize>( witness: &Witness, beta: F, gamma: F, prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, +) -> Vec> { + let vanish_degree = common_data + .max_filtered_constraint_degree + .next_power_of_two(); + let num_polys = ceil_div_usize(common_data.config.num_routed_wires, vanish_degree); + assert!( + num_polys <= vanish_degree, + "Not supported yet. would need to add partial products of partial products for this." + ); + let subgroup = &prover_data.subgroup; + let mut values = vec![vec![F::ONE; 2 * num_polys]]; + let k_is = &common_data.k_is; + for i in 1..common_data.degree() { + let x = subgroup[i - 1]; + let s_sigmas = &prover_data.sigmas[i - 1]; + let mut partials_numerator = Vec::with_capacity(2 * num_polys); + let mut partials_denominator = Vec::with_capacity(num_polys); + for chunk in (0..common_data.config.num_routed_wires) + .collect::>() + .chunks(vanish_degree) + { + let (numerator, denominator) = chunk.iter().fold((F::ONE, F::ONE), |acc, &j| { + let wire_value = witness.get_wire(i - 1, j); + let k_i = k_is[j]; + let s_id = k_i * x; + let s_sigma = s_sigmas[j]; + ( + acc.0 * wire_value + beta * s_id + gamma, + acc.1 * wire_value + beta * s_sigma + gamma, + ) + }); + partials_numerator.push(numerator); + partials_denominator.push(denominator); + } + partials_numerator.append(&mut partials_denominator); + values.push(partials_numerator); + } + + transpose(&values) + .into_par_iter() + .map(PolynomialValues::new) + .collect() +} + +fn compute_zs, const D: usize>( + partial_products: &[Vec>], + prover_data: &ProverOnlyCircuitData, + common_data: &CommonCircuitData, +) -> Vec> { + (0..common_data.config.num_challenges) + .map(|i| compute_z(&partial_products[i], prover_data, common_data)) + .collect() +} + +fn compute_z, const D: usize>( + partial_products: &[PolynomialValues], + prover_data: &ProverOnlyCircuitData, + common_data: &CommonCircuitData, ) -> PolynomialValues { + let num_partials = partial_products.len() / 2; let subgroup = &prover_data.subgroup; let mut plonk_z_points = vec![F::ONE]; let k_is = &common_data.k_is; for i in 1..common_data.degree() { let x = subgroup[i - 1]; - let mut numerator = F::ONE; - let mut denominator = F::ONE; - let s_sigmas = &prover_data.sigmas[i - 1]; - for j in 0..common_data.config.num_routed_wires { - let wire_value = witness.get_wire(i - 1, j); - let k_i = k_is[j]; - let s_id = k_i * x; - let s_sigma = s_sigmas[j]; - numerator *= wire_value + beta * s_id + gamma; - denominator *= wire_value + beta * s_sigma + gamma; - } + let numerator = partial_products[..num_partials] + .iter() + .map(|vs| vs.values[i]) + .product(); + let denominator = partial_products[num_partials..] + .iter() + .map(|vs| vs.values[i]) + .product(); let last = *plonk_z_points.last().unwrap(); plonk_z_points.push(last * numerator / denominator); } From b8e764bb909a467422108d2a782725ce4cc9825a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 30 Jun 2021 16:48:41 +0200 Subject: [PATCH 02/23] Allow for `degree^2 < num_routed_wires` --- src/circuit_builder.rs | 84 ++++++++++++++++++------------------ src/prover.rs | 42 ++++++++---------- src/util/mod.rs | 1 + src/util/partial_products.rs | 30 +++++++++++++ 4 files changed, 92 insertions(+), 65 deletions(-) create mode 100644 src/util/partial_products.rs diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 1bf938a0..912fb146 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -280,48 +280,48 @@ impl, const D: usize> CircuitBuilder { } fn blind_and_pad(&mut self) { - let (regular_poly_openings, z_openings) = self.blinding_counts(); - - let num_routed_wires = self.config.num_routed_wires; - let num_wires = self.config.num_wires; - - // For each "regular" blinding factor, we simply add a no-op gate, and insert a random value - // for each wire. - for _ in 0..regular_poly_openings { - let gate = self.add_gate_no_constants(NoopGate::get()); - for w in 0..num_wires { - self.add_generator(RandomValueGenerator { - target: Target::Wire(Wire { gate, input: w }), - }); - } - } - - // For each z poly blinding factor, we add two new gates with the same random value, and - // enforce a copy constraint between them. - // See https://mirprotocol.org/blog/Adding-zero-knowledge-to-Plonk-Halo - for _ in 0..z_openings { - let gate_1 = self.add_gate_no_constants(NoopGate::get()); - let gate_2 = self.add_gate_no_constants(NoopGate::get()); - - for w in 0..num_routed_wires { - self.add_generator(RandomValueGenerator { - target: Target::Wire(Wire { - gate: gate_1, - input: w, - }), - }); - self.add_generator(CopyGenerator { - src: Target::Wire(Wire { - gate: gate_1, - input: w, - }), - dst: Target::Wire(Wire { - gate: gate_2, - input: w, - }), - }); - } - } + // let (regular_poly_openings, z_openings) = self.blinding_counts(); + // + // let num_routed_wires = self.config.num_routed_wires; + // let num_wires = self.config.num_wires; + // + // // For each "regular" blinding factor, we simply add a no-op gate, and insert a random value + // // for each wire. + // for _ in 0..regular_poly_openings { + // let gate = self.add_gate_no_constants(NoopGate::get()); + // for w in 0..num_wires { + // self.add_generator(RandomValueGenerator { + // target: Target::Wire(Wire { gate, input: w }), + // }); + // } + // } + // + // // For each z poly blinding factor, we add two new gates with the same random value, and + // // enforce a copy constraint between them. + // // See https://mirprotocol.org/blog/Adding-zero-knowledge-to-Plonk-Halo + // for _ in 0..z_openings { + // let gate_1 = self.add_gate_no_constants(NoopGate::get()); + // let gate_2 = self.add_gate_no_constants(NoopGate::get()); + // + // for w in 0..num_routed_wires { + // self.add_generator(RandomValueGenerator { + // target: Target::Wire(Wire { + // gate: gate_1, + // input: w, + // }), + // }); + // self.add_generator(CopyGenerator { + // src: Target::Wire(Wire { + // gate: gate_1, + // input: w, + // }), + // dst: Target::Wire(Wire { + // gate: gate_2, + // input: w, + // }), + // }); + // } + // } while !self.gate_instances.len().is_power_of_two() { self.add_gate_no_constants(NoopGate::get()); diff --git a/src/prover.rs b/src/prover.rs index 76b69e10..e909c923 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -13,6 +13,7 @@ use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; +use crate::util::partial_products::partial_products; use crate::util::{ceil_div_usize, log2_ceil, transpose}; use crate::vars::EvaluationVarsBase; use crate::witness::{PartialWitness, Witness}; @@ -89,10 +90,9 @@ pub(crate) fn prove, const D: usize>( "to compute Z's" ); - let zs_partial_products = [partial_products.concat(), plonk_z_vecs].concat(); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new( - zs_partial_products, + plonk_z_vecs, fri_config.rate_bits, PlonkPolynomials::ZS.blinding ), @@ -206,37 +206,33 @@ fn wires_permutation_partial_products, const D: usize>( .max_filtered_constraint_degree .next_power_of_two(); let num_polys = ceil_div_usize(common_data.config.num_routed_wires, vanish_degree); - assert!( - num_polys <= vanish_degree, - "Not supported yet. would need to add partial products of partial products for this." - ); let subgroup = &prover_data.subgroup; let mut values = vec![vec![F::ONE; 2 * num_polys]]; let k_is = &common_data.k_is; for i in 1..common_data.degree() { let x = subgroup[i - 1]; let s_sigmas = &prover_data.sigmas[i - 1]; - let mut partials_numerator = Vec::with_capacity(2 * num_polys); - let mut partials_denominator = Vec::with_capacity(num_polys); - for chunk in (0..common_data.config.num_routed_wires) - .collect::>() - .chunks(vanish_degree) - { - let (numerator, denominator) = chunk.iter().fold((F::ONE, F::ONE), |acc, &j| { + let numerator_values = (0..common_data.config.num_routed_wires) + .map(|j| { let wire_value = witness.get_wire(i - 1, j); let k_i = k_is[j]; let s_id = k_i * x; + wire_value + beta * s_id + gamma + }) + .collect::>(); + let denominator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = witness.get_wire(i - 1, j); let s_sigma = s_sigmas[j]; - ( - acc.0 * wire_value + beta * s_id + gamma, - acc.1 * wire_value + beta * s_sigma + gamma, - ) - }); - partials_numerator.push(numerator); - partials_denominator.push(denominator); - } - partials_numerator.append(&mut partials_denominator); - values.push(partials_numerator); + wire_value + beta * s_sigma + gamma + }) + .collect::>(); + let partials = [ + partial_products(numerator_values, vanish_degree), + partial_products(denominator_values, vanish_degree), + ] + .concat(); + values.push(partials); } transpose(&values) diff --git a/src/util/mod.rs b/src/util/mod.rs index f901b0af..3fb38c05 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,4 @@ +pub mod partial_products; pub mod scaling; pub(crate) mod timing; diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs new file mode 100644 index 00000000..0546d133 --- /dev/null +++ b/src/util/partial_products.rs @@ -0,0 +1,30 @@ +use std::iter::Product; + +pub fn partial_products(v: Vec, max_degree: usize) -> Vec { + let mut res = Vec::new(); + let mut remainder = v; + while remainder.len() > max_degree { + let new_partials = remainder + .chunks(max_degree) + .filter(|chunk| chunk.len() != 1) // Don't need to compute the product in this case. + .map(|chunk| chunk.iter().copied().product()) + .collect::>(); + res.extend_from_slice(&new_partials); + remainder = new_partials; + } + + res +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_partial_products() { + assert_eq!( + partial_products(vec![1, 2, 3, 4, 5, 6], 2), + vec![2, 12, 30, 24] + ); + } +} From f7c4a463fc8b9bac049f9f578992accafec61dc7 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 30 Jun 2021 18:54:28 +0200 Subject: [PATCH 03/23] Progress --- src/circuit_data.rs | 12 ++++++- src/plonk_common.rs | 69 ++++++++++++++++++++++++++++-------- src/prover.rs | 34 ++++++++++-------- src/util/partial_products.rs | 10 +++--- 4 files changed, 89 insertions(+), 36 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 6f352832..06a6700f 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -1,4 +1,4 @@ -use std::ops::Range; +use std::ops::{Range, RangeFrom}; use anyhow::Result; @@ -201,6 +201,16 @@ impl, const D: usize> CommonCircuitData { pub fn sigmas_range(&self) -> Range { self.num_constants..self.num_constants + self.config.num_routed_wires } + + /// Range of the constants polynomials in the `constants_sigmas_commitment`. + pub fn zs_range(&self) -> Range { + 0..self.config.num_challenges + } + + /// Range of the sigma polynomials in the `constants_sigmas_commitment`. + pub fn partial_products_range(&self) -> RangeFrom { + self.config.num_challenges.. + } } /// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this diff --git a/src/plonk_common.rs b/src/plonk_common.rs index db304c0a..3b271101 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -9,6 +9,7 @@ use crate::gates::gate::{GateRef, PrefixedGate}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; +use crate::util::partial_products::partial_products; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// Holds the Merkle tree index and blinding flag of a set of polynomials used in FRI. @@ -114,8 +115,9 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( index: usize, x: F, vars: EvaluationVarsBase, - local_plonk_zs: &[F], - next_plonk_zs: &[F], + local_zs: &[F], + next_zs: &[F], + local_partial_products: &[F], s_sigmas: &[F], betas: &[F], gammas: &[F], @@ -127,29 +129,66 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); + // The terms checking the partial products. + let mut vanishing_partial_products_terms = Vec::new(); // The Z(x) f'(x) - g'(x) Z(g x) terms. let mut vanishing_v_shift_terms = Vec::new(); for i in 0..common_data.config.num_challenges { - let z_x = local_plonk_zs[i]; - let z_gz = next_plonk_zs[i]; + let z_x = local_zs[i]; + let z_gz = next_zs[i]; vanishing_z_1_terms.push(z_h_on_coset.eval_l1(index, x) * (z_x - F::ONE)); - let mut f_prime = F::ONE; - let mut g_prime = F::ONE; - for j in 0..common_data.config.num_routed_wires { - let wire_value = vars.local_wires[j]; - let k_i = common_data.k_is[j]; - let s_id = k_i * x; - let s_sigma = s_sigmas[j]; - f_prime *= wire_value + betas[i] * s_id + gammas[i]; - g_prime *= wire_value + betas[i] * s_sigma + gammas[i]; - } - vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); + let numerator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = vars.local_wires[j]; + let k_i = common_data.k_is[j]; + let s_id = k_i * x; + wire_value + betas[i] * s_id + gammas[i] + }) + .collect::>(); + let denominator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = vars.local_wires[j]; + let s_sigma = s_sigmas[j]; + wire_value + betas[i] * s_sigma + gammas[i] + }) + .collect::>(); + let numerator_partial_products = + partial_products(numerator_values, common_data.max_filtered_constraint_degree); + let denominator_partial_products = partial_products( + denominator_values, + common_data.max_filtered_constraint_degree, + ); + + dbg!(numerator_partial_products + .clone() + .0 + .into_iter() + .chain(denominator_partial_products.clone().0) + .zip(local_partial_products) + .map(|(a, &b)| a - b) + .collect::>(),); + vanishing_partial_products_terms.append( + &mut numerator_partial_products + .0 + .into_iter() + .chain(denominator_partial_products.0) + .zip(local_partial_products) + .map(|(a, &b)| a - b) + .collect::>(), + ); + dbg!(&numerator_partial_products.1); + dbg!(&denominator_partial_products.1); + dbg!(common_data.max_filtered_constraint_degree); + let f_prime: F = numerator_partial_products.1.into_iter().product(); + let g_prime: F = denominator_partial_products.1.into_iter().product(); + // vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); } let vanishing_terms = [ vanishing_z_1_terms, + vanishing_partial_products_terms, vanishing_v_shift_terms, constraint_terms, ] diff --git a/src/prover.rs b/src/prover.rs index e909c923..de5c972c 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -90,9 +90,10 @@ pub(crate) fn prove, const D: usize>( "to compute Z's" ); + let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new( - plonk_z_vecs, + zs_partial_products, fri_config.rate_bits, PlonkPolynomials::ZS.blinding ), @@ -123,7 +124,7 @@ pub(crate) fn prove, const D: usize>( .flat_map(|mut quotient_poly| { quotient_poly.trim(); quotient_poly.pad(quotient_degree).expect( - "The quotient polynomial doesn't have the right degree.\ + "The quotient polynomial doesn't have the right degree. \ This may be because the `Z`s polynomials are still too high degree.", ); // Split t into degree-n chunks. @@ -202,12 +203,9 @@ fn wires_permutation_partial_products, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { - let vanish_degree = common_data - .max_filtered_constraint_degree - .next_power_of_two(); - let num_polys = ceil_div_usize(common_data.config.num_routed_wires, vanish_degree); + let degree = common_data.max_filtered_constraint_degree; let subgroup = &prover_data.subgroup; - let mut values = vec![vec![F::ONE; 2 * num_polys]]; + let mut values = Vec::new(); let k_is = &common_data.k_is; for i in 1..common_data.degree() { let x = subgroup[i - 1]; @@ -228,13 +226,14 @@ fn wires_permutation_partial_products, const D: usize>( }) .collect::>(); let partials = [ - partial_products(numerator_values, vanish_degree), - partial_products(denominator_values, vanish_degree), + partial_products(numerator_values, degree).0, + partial_products(denominator_values, degree).0, ] .concat(); values.push(partials); } + values.insert(0, vec![F::ONE; values[0].len()]); transpose(&values) .into_par_iter() .map(PolynomialValues::new) @@ -280,7 +279,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( common_data: &CommonCircuitData, prover_data: &'a ProverOnlyCircuitData, wires_commitment: &'a ListPolynomialCommitment, - plonk_zs_commitment: &'a ListPolynomialCommitment, + zs_partial_products_commitment: &'a ListPolynomialCommitment, betas: &[F], gammas: &[F], alphas: &[F], @@ -322,11 +321,15 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( let local_constants = &local_constants_sigmas[common_data.constants_range()]; let s_sigmas = &local_constants_sigmas[common_data.sigmas_range()]; let local_wires = get_at_index(wires_commitment, i); - let local_plonk_zs = get_at_index(plonk_zs_commitment, i); - let next_plonk_zs = get_at_index(plonk_zs_commitment, i_next); + let local_zs_partial_products = get_at_index(zs_partial_products_commitment, i); + let local_zs = &local_zs_partial_products[common_data.zs_range()]; + let next_zs = + &get_at_index(zs_partial_products_commitment, i_next)[common_data.zs_range()]; + let local_partial_products = + &local_zs_partial_products[common_data.partial_products_range()]; debug_assert_eq!(local_wires.len(), common_data.config.num_wires); - debug_assert_eq!(local_plonk_zs.len(), num_challenges); + debug_assert_eq!(local_zs.len(), num_challenges); let vars = EvaluationVarsBase { local_constants, @@ -337,8 +340,9 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( i, shifted_x, vars, - local_plonk_zs, - next_plonk_zs, + local_zs, + next_zs, + local_partial_products, s_sigmas, betas, gammas, diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 0546d133..c31fbff6 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -1,19 +1,19 @@ use std::iter::Product; -pub fn partial_products(v: Vec, max_degree: usize) -> Vec { +pub fn partial_products(v: Vec, max_degree: usize) -> (Vec, Vec) { let mut res = Vec::new(); let mut remainder = v; - while remainder.len() > max_degree { + while remainder.len() >= max_degree { let new_partials = remainder .chunks(max_degree) - .filter(|chunk| chunk.len() != 1) // Don't need to compute the product in this case. + // TODO: If `chunk.len()=1`, there's some redundant data. .map(|chunk| chunk.iter().copied().product()) .collect::>(); res.extend_from_slice(&new_partials); remainder = new_partials; } - res + (res, remainder) } #[cfg(test)] @@ -24,7 +24,7 @@ mod tests { fn test_partial_products() { assert_eq!( partial_products(vec![1, 2, 3, 4, 5, 6], 2), - vec![2, 12, 30, 24] + (vec![2, 12, 30, 24, 30], vec![24, 30]) ); } } From c83382aaaabddddb0b0ff137388c79696a5baca6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 15:20:16 +0200 Subject: [PATCH 04/23] Working partial products --- src/circuit_data.rs | 2 +- src/gadgets/arithmetic_extension.rs | 4 +- src/plonk_common.rs | 78 +++++++++++++++---------- src/prover.rs | 91 ++++++++++++++++------------- src/util/partial_products.rs | 46 ++++++++++++--- 5 files changed, 143 insertions(+), 78 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 06a6700f..9741bdc4 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -202,7 +202,7 @@ impl, const D: usize> CommonCircuitData { self.num_constants..self.num_constants + self.config.num_routed_wires } - /// Range of the constants polynomials in the `constants_sigmas_commitment`. + /// Range of the `z`s polynomials in the ``. pub fn zs_range(&self) -> Range { 0..self.config.num_challenges } diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index ce53c59f..24f20778 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -446,7 +446,9 @@ mod tests { type FF = QuarticCrandallField; const D: usize = 4; - let config = CircuitConfig::large_config(); + let mut config = CircuitConfig::large_config(); + config.rate_bits = 2; + config.fri_config.rate_bits = 2; let mut builder = CircuitBuilder::::new(config); diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 3b271101..b1910175 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -9,7 +9,7 @@ use crate::gates::gate::{GateRef, PrefixedGate}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; -use crate::util::partial_products::partial_products; +use crate::util::partial_products::{check_partial_products, partial_products}; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// Holds the Merkle tree index and blinding flag of a set of polynomials used in FRI. @@ -124,6 +124,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( alphas: &[F], z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { + let max_degree = common_data.max_filtered_constraint_degree; let constraint_terms = evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); @@ -154,36 +155,50 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( wire_value + betas[i] * s_sigma + gammas[i] }) .collect::>(); - let numerator_partial_products = - partial_products(numerator_values, common_data.max_filtered_constraint_degree); - let denominator_partial_products = partial_products( - denominator_values, - common_data.max_filtered_constraint_degree, - ); + let numerator_partial_products = partial_products(&numerator_values, max_degree); + let denominator_partial_products = partial_products(&denominator_values, max_degree); - dbg!(numerator_partial_products - .clone() - .0 - .into_iter() - .chain(denominator_partial_products.clone().0) - .zip(local_partial_products) - .map(|(a, &b)| a - b) - .collect::>(),); - vanishing_partial_products_terms.append( - &mut numerator_partial_products - .0 - .into_iter() - .chain(denominator_partial_products.0) - .zip(local_partial_products) - .map(|(a, &b)| a - b) - .collect::>(), - ); - dbg!(&numerator_partial_products.1); - dbg!(&denominator_partial_products.1); - dbg!(common_data.max_filtered_constraint_degree); - let f_prime: F = numerator_partial_products.1.into_iter().product(); - let g_prime: F = denominator_partial_products.1.into_iter().product(); - // vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); + let num_prods = numerator_partial_products.0.len(); + // dbg!(numerator_partial_products + // .0 + // .iter() + // .chain(&denominator_partial_products.0) + // .zip(&local_partial_products[i * num_prods..(i + 1) * num_prods]) + // .map(|(&a, &b)| a - b) + // .collect::>(),); + // vanishing_partial_products_terms.append( + // &mut numerator_partial_products + // .0 + // .into_iter() + // .chain(denominator_partial_products.0) + // .zip(&local_partial_products[i * num_prods..(i + 1) * num_prods]) + // .map(|(a, &b)| a - b) + // .collect::>(), + // ); + vanishing_partial_products_terms.extend(check_partial_products( + &numerator_values, + &local_partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], + max_degree, + )); + vanishing_partial_products_terms.extend(check_partial_products( + &denominator_values, + &local_partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], + max_degree, + )); + // dbg!(common_data.max_filtered_constraint_degree); + // dbg!(numerator_partial_products.1.len()); + // dbg!(denominator_partial_products.1.len()); + let f_prime: F = local_partial_products + [(2 * i + 1) * num_prods - numerator_partial_products.1..(2 * i + 1) * num_prods] + .iter() + .copied() + .product(); + let g_prime: F = local_partial_products + [(2 * i + 2) * num_prods - numerator_partial_products.1..(2 * i + 2) * num_prods] + .iter() + .copied() + .product(); + vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); } let vanishing_terms = [ @@ -193,6 +208,9 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( constraint_terms, ] .concat(); + // if index % 4 == 0 { + // println!("{}", vanishing_terms.iter().all(|x| x.is_zero())); + // } reduce_with_powers_multi(&vanishing_terms, alphas) } diff --git a/src/prover.rs b/src/prover.rs index de5c972c..fd0a093d 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -80,7 +80,7 @@ pub(crate) fn prove, const D: usize>( let betas = challenger.get_n_challenges(num_challenges); let gammas = challenger.get_n_challenges(num_challenges); - let partial_products = timed!( + let mut partial_products = timed!( all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data), "to compute partial products" ); @@ -90,6 +90,10 @@ pub(crate) fn prove, const D: usize>( "to compute Z's" ); + partial_products.iter_mut().for_each(|part| { + part.drain(0..2); + }); + let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new( @@ -205,35 +209,50 @@ fn wires_permutation_partial_products, const D: usize>( ) -> Vec> { let degree = common_data.max_filtered_constraint_degree; let subgroup = &prover_data.subgroup; - let mut values = Vec::new(); let k_is = &common_data.k_is; - for i in 1..common_data.degree() { - let x = subgroup[i - 1]; - let s_sigmas = &prover_data.sigmas[i - 1]; - let numerator_values = (0..common_data.config.num_routed_wires) - .map(|j| { - let wire_value = witness.get_wire(i - 1, j); - let k_i = k_is[j]; - let s_id = k_i * x; - wire_value + beta * s_id + gamma - }) - .collect::>(); - let denominator_values = (0..common_data.config.num_routed_wires) - .map(|j| { - let wire_value = witness.get_wire(i - 1, j); - let s_sigma = s_sigmas[j]; - wire_value + beta * s_sigma + gamma - }) - .collect::>(); - let partials = [ - partial_products(numerator_values, degree).0, - partial_products(denominator_values, degree).0, - ] - .concat(); - values.push(partials); - } + let values = subgroup + .iter() + .enumerate() + .map(|(i, &x)| { + let s_sigmas = &prover_data.sigmas[i]; + let numerator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = witness.get_wire(i, j); + let k_i = k_is[j]; + let s_id = k_i * x; + wire_value + beta * s_id + gamma + }) + .collect::>(); + let denominator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = witness.get_wire(i, j); + let s_sigma = s_sigmas[j]; + wire_value + beta * s_sigma + gamma + }) + .collect::>(); + let numerator_partials = partial_products(&numerator_values, degree); + let denominator_partials = partial_products(&denominator_values, degree); + let numerator = numerator_partials.0 + [numerator_partials.0.len() - numerator_partials.1..] + .iter() + .copied() + .product(); + let denominator = denominator_partials.0 + [denominator_partials.0.len() - denominator_partials.1..] + .iter() + .copied() + .product(); + + [ + vec![numerator], + vec![denominator], + numerator_partials.0, + denominator_partials.0, + ] + .concat() + }) + .collect::>(); - values.insert(0, vec![F::ONE; values[0].len()]); transpose(&values) .into_par_iter() .map(PolynomialValues::new) @@ -255,20 +274,12 @@ fn compute_z, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> PolynomialValues { - let num_partials = partial_products.len() / 2; let subgroup = &prover_data.subgroup; let mut plonk_z_points = vec![F::ONE]; - let k_is = &common_data.k_is; for i in 1..common_data.degree() { let x = subgroup[i - 1]; - let numerator = partial_products[..num_partials] - .iter() - .map(|vs| vs.values[i]) - .product(); - let denominator = partial_products[num_partials..] - .iter() - .map(|vs| vs.values[i]) - .product(); + let numerator = partial_products[0].values[i - 1]; + let denominator = partial_products[1].values[i - 1]; let last = *plonk_z_points.last().unwrap(); plonk_z_points.push(last * numerator / denominator); } @@ -312,7 +323,8 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( ZeroPolyOnCoset::new(common_data.degree_bits, max_filtered_constraint_degree_bits); let quotient_values: Vec> = points - .into_par_iter() + // .into_par_iter() + .into_iter() .enumerate() .map(|(i, x)| { let shifted_x = F::coset_shift() * x; @@ -335,6 +347,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( local_constants, local_wires, }; + dbg!(i); let mut quotient_values = eval_vanishing_poly_base( common_data, i, diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index c31fbff6..c5426eea 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -1,8 +1,9 @@ use std::iter::Product; +use std::ops::Sub; -pub fn partial_products(v: Vec, max_degree: usize) -> (Vec, Vec) { +pub fn partial_products(v: &[T], max_degree: usize) -> (Vec, usize) { let mut res = Vec::new(); - let mut remainder = v; + let mut remainder = v.to_vec(); while remainder.len() >= max_degree { let new_partials = remainder .chunks(max_degree) @@ -13,18 +14,49 @@ pub fn partial_products(v: Vec, max_degree: usize) -> (Vec remainder = new_partials; } - (res, remainder) + (res, remainder.len()) +} + +pub fn check_partial_products>( + v: &[T], + partials: &[T], + max_degree: usize, +) -> Vec { + let mut res = Vec::new(); + let mut remainder = v.to_vec(); + let mut partials = partials.to_vec(); + while remainder.len() >= max_degree { + let products = remainder + .chunks(max_degree) + .map(|chunk| chunk.iter().copied().product()) + .collect::>(); + res.extend(products.iter().zip(&partials).map(|(&a, &b)| a - b)); + remainder = partials.drain(..products.len()).collect(); + } + + res } #[cfg(test)] mod tests { + use num::Zero; + use super::*; #[test] fn test_partial_products() { - assert_eq!( - partial_products(vec![1, 2, 3, 4, 5, 6], 2), - (vec![2, 12, 30, 24, 30], vec![24, 30]) - ); + let v = vec![1, 2, 3, 4, 5, 6]; + let p = partial_products(&v, 2); + assert_eq!(p, (vec![2, 12, 30, 24, 30, 720], 1)); + assert!(check_partial_products(&v, &p.0, 2) + .iter() + .all(|x| x.is_zero())); + + let v = vec![1, 2, 3, 4, 5, 6]; + let p = partial_products(&v, 3); + assert_eq!(p, (vec![6, 120], 2)); + assert!(check_partial_products(&v, &p.0, 3) + .iter() + .all(|x| x.is_zero())); } } From 59410447bfa61c07cf0c6045ef7601cf16ff09fe Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 15:41:01 +0200 Subject: [PATCH 05/23] Add lengths to `CommonData` --- src/circuit_builder.rs | 5 +++++ src/circuit_data.rs | 4 ++++ src/plonk_common.rs | 28 ++++------------------------ src/polynomial/commitment.rs | 1 + src/prover.rs | 12 ++++++------ src/util/partial_products.rs | 28 ++++++++++++++++++++++------ 6 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 912fb146..748d937c 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -21,6 +21,7 @@ use crate::plonk_common::PlonkPolynomials; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; +use crate::util::partial_products::num_partial_products; use crate::util::{log2_ceil, log2_strict, transpose, transpose_poly_values}; use crate::wire::Wire; @@ -434,6 +435,9 @@ impl, const D: usize> CircuitBuilder { .max() .expect("No gates?"); + let num_partial_products = + num_partial_products(self.config.num_routed_wires, max_filtered_constraint_degree); + // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ constants_sigmas_root.elements.to_vec(), @@ -449,6 +453,7 @@ impl, const D: usize> CircuitBuilder { num_gate_constraints, num_constants, k_is, + num_partial_products, circuit_digest, }; diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 9741bdc4..0e48b171 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -157,6 +157,10 @@ pub struct CommonCircuitData, const D: usize> { /// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument. pub(crate) k_is: Vec, + /// The number of partial products needed to compute the `Z` polynomials, as well as the number + /// of partial products needed to compute the last product. + pub(crate) num_partial_products: (usize, usize), + /// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to /// seed Fiat-Shamir. pub(crate) circuit_digest: Hash, diff --git a/src/plonk_common.rs b/src/plonk_common.rs index b1910175..4b593b75 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -155,26 +155,8 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( wire_value + betas[i] * s_sigma + gammas[i] }) .collect::>(); - let numerator_partial_products = partial_products(&numerator_values, max_degree); - let denominator_partial_products = partial_products(&denominator_values, max_degree); - let num_prods = numerator_partial_products.0.len(); - // dbg!(numerator_partial_products - // .0 - // .iter() - // .chain(&denominator_partial_products.0) - // .zip(&local_partial_products[i * num_prods..(i + 1) * num_prods]) - // .map(|(&a, &b)| a - b) - // .collect::>(),); - // vanishing_partial_products_terms.append( - // &mut numerator_partial_products - // .0 - // .into_iter() - // .chain(denominator_partial_products.0) - // .zip(&local_partial_products[i * num_prods..(i + 1) * num_prods]) - // .map(|(a, &b)| a - b) - // .collect::>(), - // ); + let (num_prods, final_num_prod) = common_data.num_partial_products; vanishing_partial_products_terms.extend(check_partial_products( &numerator_values, &local_partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], @@ -185,16 +167,14 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( &local_partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], max_degree, )); - // dbg!(common_data.max_filtered_constraint_degree); - // dbg!(numerator_partial_products.1.len()); - // dbg!(denominator_partial_products.1.len()); + let f_prime: F = local_partial_products - [(2 * i + 1) * num_prods - numerator_partial_products.1..(2 * i + 1) * num_prods] + [(2 * i + 1) * num_prods - final_num_prod..(2 * i + 1) * num_prods] .iter() .copied() .product(); let g_prime: F = local_partial_products - [(2 * i + 2) * num_prods - numerator_partial_products.1..(2 * i + 2) * num_prods] + [(2 * i + 2) * num_prods - final_num_prod..(2 * i + 2) * num_prods] .iter() .copied() .product(); diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 9bd3905b..0b9de2a5 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -331,6 +331,7 @@ mod tests { num_gate_constraints: 0, num_constants: 4, k_is: vec![F::ONE; 6], + num_partial_products: (0, 0), circuit_digest: Hash::from_partial(vec![]), }; diff --git a/src/prover.rs b/src/prover.rs index fd0a093d..8aa1d979 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -232,13 +232,13 @@ fn wires_permutation_partial_products, const D: usize>( .collect::>(); let numerator_partials = partial_products(&numerator_values, degree); let denominator_partials = partial_products(&denominator_values, degree); - let numerator = numerator_partials.0 - [numerator_partials.0.len() - numerator_partials.1..] + let numerator = numerator_partials + [common_data.num_partial_products.0 - common_data.num_partial_products.1..] .iter() .copied() .product(); - let denominator = denominator_partials.0 - [denominator_partials.0.len() - denominator_partials.1..] + let denominator = denominator_partials + [common_data.num_partial_products.0 - common_data.num_partial_products.1..] .iter() .copied() .product(); @@ -246,8 +246,8 @@ fn wires_permutation_partial_products, const D: usize>( [ vec![numerator], vec![denominator], - numerator_partials.0, - denominator_partials.0, + numerator_partials, + denominator_partials, ] .concat() }) diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index c5426eea..2d9d33e2 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -1,7 +1,9 @@ use std::iter::Product; use std::ops::Sub; -pub fn partial_products(v: &[T], max_degree: usize) -> (Vec, usize) { +use crate::util::ceil_div_usize; + +pub fn partial_products(v: &[T], max_degree: usize) -> Vec { let mut res = Vec::new(); let mut remainder = v.to_vec(); while remainder.len() >= max_degree { @@ -14,7 +16,19 @@ pub fn partial_products(v: &[T], max_degree: usize) -> (Vec (usize, usize) { + let mut res = 0; + let mut remainder = n; + while remainder >= max_degree { + let new_partials_len = ceil_div_usize(remainder, max_degree); + res += new_partials_len; + remainder = new_partials_len; + } + + (res, remainder) } pub fn check_partial_products>( @@ -47,15 +61,17 @@ mod tests { fn test_partial_products() { let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 2); - assert_eq!(p, (vec![2, 12, 30, 24, 30, 720], 1)); - assert!(check_partial_products(&v, &p.0, 2) + assert_eq!(p, vec![2, 12, 30, 24, 30, 720]); + assert_eq!(p.len(), num_partial_products(v.len(), 2).0); + assert!(check_partial_products(&v, &p, 2) .iter() .all(|x| x.is_zero())); let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 3); - assert_eq!(p, (vec![6, 120], 2)); - assert!(check_partial_products(&v, &p.0, 3) + assert_eq!(p, vec![6, 120]); + assert_eq!(p.len(), num_partial_products(v.len(), 3).0); + assert!(check_partial_products(&v, &p, 3) .iter() .all(|x| x.is_zero())); } From 1858a869a71039bb5b5301c96997ec7b6f22b92e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 15:57:55 +0200 Subject: [PATCH 06/23] Optimize products of 1 element --- src/util/partial_products.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 2d9d33e2..f22ba01e 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -9,11 +9,17 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec while remainder.len() >= max_degree { let new_partials = remainder .chunks(max_degree) - // TODO: If `chunk.len()=1`, there's some redundant data. + .filter(|chunk| chunk.len() != 1) .map(|chunk| chunk.iter().copied().product()) .collect::>(); res.extend_from_slice(&new_partials); + let addendum = if remainder.len() % max_degree == 1 { + vec![*remainder.last().unwrap()] + } else { + vec![] + }; remainder = new_partials; + remainder.extend(addendum); } res @@ -24,7 +30,8 @@ pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { let mut remainder = n; while remainder >= max_degree { let new_partials_len = ceil_div_usize(remainder, max_degree); - res += new_partials_len; + let addendum = if remainder % max_degree == 1 { 1 } else { 0 }; + res += new_partials_len - addendum; remainder = new_partials_len; } @@ -42,10 +49,17 @@ pub fn check_partial_products>( while remainder.len() >= max_degree { let products = remainder .chunks(max_degree) + .filter(|chunk| chunk.len() != 1) .map(|chunk| chunk.iter().copied().product()) .collect::>(); res.extend(products.iter().zip(&partials).map(|(&a, &b)| a - b)); + let addendum = if remainder.len() % max_degree == 1 { + vec![*remainder.last().unwrap()] + } else { + vec![] + }; remainder = partials.drain(..products.len()).collect(); + remainder.extend(addendum) } res @@ -61,7 +75,7 @@ mod tests { fn test_partial_products() { let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 2); - assert_eq!(p, vec![2, 12, 30, 24, 30, 720]); + assert_eq!(p, vec![2, 12, 30, 24, 720]); assert_eq!(p.len(), num_partial_products(v.len(), 2).0); assert!(check_partial_products(&v, &p, 2) .iter() From 20e6d6540f0e90d0bc0136e3647fde5ce83ef958 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 17:28:30 +0200 Subject: [PATCH 07/23] Add comments --- src/circuit_data.rs | 2 +- src/prover.rs | 34 ++++++++++++++++++++-------------- src/util/partial_products.rs | 10 ++++++++++ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 0e48b171..22ef8ead 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -158,7 +158,7 @@ pub struct CommonCircuitData, const D: usize> { pub(crate) k_is: Vec, /// The number of partial products needed to compute the `Z` polynomials, as well as the number - /// of partial products needed to compute the last product. + /// of partial products needed to compute the final product. pub(crate) num_partial_products: (usize, usize), /// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to diff --git a/src/prover.rs b/src/prover.rs index 8aa1d979..0a1c7886 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -85,17 +85,16 @@ pub(crate) fn prove, const D: usize>( "to compute partial products" ); - let plonk_z_vecs = timed!( - compute_zs(&partial_products, prover_data, common_data), - "to compute Z's" - ); + let plonk_z_vecs = timed!(compute_zs(&partial_products, common_data), "to compute Z's"); + // The first two polynomials in `partial_products` represent the final products used in the + // computation of `Z`. They aren't needed anymore so we discard them. partial_products.iter_mut().for_each(|part| { part.drain(0..2); }); let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); - let plonk_zs_commitment = timed!( + let zs_partial_products_commitment = timed!( ListPolynomialCommitment::new( zs_partial_products, fri_config.rate_bits, @@ -104,7 +103,7 @@ pub(crate) fn prove, const D: usize>( "to commit to Z's" ); - challenger.observe_hash(&plonk_zs_commitment.merkle_tree.root); + challenger.observe_hash(&zs_partial_products_commitment.merkle_tree.root); let alphas = challenger.get_n_challenges(num_challenges); @@ -113,7 +112,7 @@ pub(crate) fn prove, const D: usize>( common_data, prover_data, &wires_commitment, - &plonk_zs_commitment, + &zs_partial_products_commitment, &betas, &gammas, &alphas, @@ -156,7 +155,7 @@ pub(crate) fn prove, const D: usize>( &[ &prover_data.constants_sigmas_commitment, &wires_commitment, - &plonk_zs_commitment, + &zs_partial_products_commitment, "ient_polys_commitment, ], zeta, @@ -173,13 +172,14 @@ pub(crate) fn prove, const D: usize>( Proof { wires_root: wires_commitment.merkle_tree.root, - plonk_zs_root: plonk_zs_commitment.merkle_tree.root, + plonk_zs_root: zs_partial_products_commitment.merkle_tree.root, quotient_polys_root: quotient_polys_commitment.merkle_tree.root, openings, opening_proof, } } +/// Compute the partial products used in the `Z` polynomials. fn all_wires_permutation_partial_products, const D: usize>( witness: &Witness, betas: &[F], @@ -200,6 +200,9 @@ fn all_wires_permutation_partial_products, const D: usize>( .collect() } +/// Compute the partial products used in the `Z` polynomial. +/// Returns the polynomials interpolating `partial_products(f) + partial_products(g)` +/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = n / d`. fn wires_permutation_partial_products, const D: usize>( witness: &Witness, beta: F, @@ -230,19 +233,25 @@ fn wires_permutation_partial_products, const D: usize>( wire_value + beta * s_sigma + gamma }) .collect::>(); + let numerator_partials = partial_products(&numerator_values, degree); let denominator_partials = partial_products(&denominator_values, degree); + + // This is the final product for the numerator. let numerator = numerator_partials [common_data.num_partial_products.0 - common_data.num_partial_products.1..] .iter() .copied() .product(); + // This is the final product for the denominator. let denominator = denominator_partials [common_data.num_partial_products.0 - common_data.num_partial_products.1..] .iter() .copied() .product(); + // We add the numerator and denominator at the beginning of the vector to reuse them + // later in the computation of `Z`. [ vec![numerator], vec![denominator], @@ -261,23 +270,20 @@ fn wires_permutation_partial_products, const D: usize>( fn compute_zs, const D: usize>( partial_products: &[Vec>], - prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { (0..common_data.config.num_challenges) - .map(|i| compute_z(&partial_products[i], prover_data, common_data)) + .map(|i| compute_z(&partial_products[i], common_data)) .collect() } +/// Compute the `Z` polynomial by reusing the computations done in `wires_permutation_partial_products`. fn compute_z, const D: usize>( partial_products: &[PolynomialValues], - prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> PolynomialValues { - let subgroup = &prover_data.subgroup; let mut plonk_z_points = vec![F::ONE]; for i in 1..common_data.degree() { - let x = subgroup[i - 1]; let numerator = partial_products[0].values[i - 1]; let denominator = partial_products[1].values[i - 1]; let last = *plonk_z_points.last().unwrap(); diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index f22ba01e..2eefdd1d 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -3,12 +3,17 @@ use std::ops::Sub; use crate::util::ceil_div_usize; +/// Compute partial products of the original vector `v` such that no products are of `max_degree` or +/// less elements. This is done until we've computed the product `P` of all elements in the vector. +/// The final product resulting in `P` has `max_degree-1` elements at most since `P` is multiplied +/// by the `Z` polynomial in the Plonk check. pub fn partial_products(v: &[T], max_degree: usize) -> Vec { let mut res = Vec::new(); let mut remainder = v.to_vec(); while remainder.len() >= max_degree { let new_partials = remainder .chunks(max_degree) + // No need to compute the product if the chunk has size 1. .filter(|chunk| chunk.len() != 1) .map(|chunk| chunk.iter().copied().product()) .collect::>(); @@ -19,12 +24,15 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec vec![] }; remainder = new_partials; + // If there were a chunk of size 1, add it back to the remainder. remainder.extend(addendum); } res } +/// Returns a tuple `(a,b)`, where `a` is the length of the output of `partial_products()` on a +/// vector of length `n`, and `b` is the number of elements needed to compute the final product. pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { let mut res = 0; let mut remainder = n; @@ -38,6 +46,8 @@ pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { (res, remainder) } +/// Checks that the partial products of `v` are coherent with those in `partials` by only computing +/// products of size `max_degree` or less. pub fn check_partial_products>( v: &[T], partials: &[T], From cc3c278a92dec1bd002c245983b31058fc87f4a3 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 17:34:00 +0200 Subject: [PATCH 08/23] Some renaming --- src/fri/recursive_verifier.rs | 2 +- src/fri/verifier.rs | 2 +- src/plonk_challenger.rs | 2 ++ src/plonk_common.rs | 4 ++-- src/polynomial/commitment.rs | 2 +- src/proof.rs | 12 +++++++++--- src/prover.rs | 2 +- 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/fri/recursive_verifier.rs b/src/fri/recursive_verifier.rs index fc320f31..31ab0a25 100644 --- a/src/fri/recursive_verifier.rs +++ b/src/fri/recursive_verifier.rs @@ -183,7 +183,7 @@ impl, const D: usize> CircuitBuilder { // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof - .unsalted_evals(PlonkPolynomials::ZS) + .unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS) .iter() .map(|&e| self.convert_to_ext(e)) .collect::>(); diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 3db4f6f5..62bfbbd4 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -185,7 +185,7 @@ fn fri_combine_initial, const D: usize>( // Polynomials opened at `x` and `g x`, i.e., the Zs polynomials. let zs_evals = proof - .unsalted_evals(PlonkPolynomials::ZS) + .unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS) .iter() .map(|&e| F::Extension::from_basefield(e)); let zs_composition_eval = alpha.clone().reduce(zs_evals); diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 9af5e590..287eaa07 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -72,6 +72,7 @@ impl Challenger { wires, plonk_zs, plonk_zs_right, + partial_products, quotient_polys, } = os; for v in &[ @@ -80,6 +81,7 @@ impl Challenger { wires, plonk_zs, plonk_zs_right, + partial_products, quotient_polys, ] { self.observe_extension_elements(v); diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 4b593b75..b10b1ee6 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -38,7 +38,7 @@ impl PlonkPolynomials { index: 1, blinding: true, }; - pub const ZS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { + pub const ZS_PARTIAL_PRODUCTS: PolynomialsIndexBlinding = PolynomialsIndexBlinding { index: 2, blinding: true, }; @@ -51,7 +51,7 @@ impl PlonkPolynomials { match i { 0 => Self::CONSTANTS_SIGMAS, 1 => Self::WIRES, - 2 => Self::ZS, + 2 => Self::ZS_PARTIAL_PRODUCTS, 3 => Self::QUOTIENT, _ => panic!("There are only 4 sets of polynomials in Plonk."), } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 0b9de2a5..b88d9a65 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -162,7 +162,7 @@ impl ListPolynomialCommitment { alpha.reset(); // Zs polynomials are opened at `zeta` and `g*zeta`. - let zs_polys = commitments[PlonkPolynomials::ZS.index] + let zs_polys = commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index] .polynomials .iter() .map(|p| p.to_extension()); diff --git a/src/proof.rs b/src/proof.rs index 47b67c9c..f712da40 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -154,6 +154,7 @@ pub struct OpeningSet, const D: usize> { pub wires: Vec, pub plonk_zs: Vec, pub plonk_zs_right: Vec, + pub partial_products: Vec, pub quotient_polys: Vec, } @@ -163,7 +164,7 @@ impl, const D: usize> OpeningSet { g: F::Extension, constants_sigmas_commitment: &ListPolynomialCommitment, wires_commitment: &ListPolynomialCommitment, - plonk_zs_commitment: &ListPolynomialCommitment, + zs_partial_products_commitment: &ListPolynomialCommitment, quotient_polys_commitment: &ListPolynomialCommitment, common_data: &CommonCircuitData, ) -> Self { @@ -174,12 +175,17 @@ impl, const D: usize> OpeningSet { .collect::>() }; let constants_sigmas_eval = eval_commitment(z, constants_sigmas_commitment); + let zs_partial_products_eval = eval_commitment(z, zs_partial_products_commitment); Self { constants: constants_sigmas_eval[common_data.constants_range()].to_vec(), plonk_s_sigmas: constants_sigmas_eval[common_data.sigmas_range()].to_vec(), wires: eval_commitment(z, wires_commitment), - plonk_zs: eval_commitment(z, plonk_zs_commitment), - plonk_zs_right: eval_commitment(g * z, plonk_zs_commitment), + plonk_zs: zs_partial_products_eval[common_data.zs_range()].to_vec(), + plonk_zs_right: eval_commitment(g * z, zs_partial_products_commitment) + [common_data.zs_range()] + .to_vec(), + partial_products: zs_partial_products_eval[common_data.partial_products_range()] + .to_vec(), quotient_polys: eval_commitment(z, quotient_polys_commitment), } } diff --git a/src/prover.rs b/src/prover.rs index 0a1c7886..6246ad54 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -98,7 +98,7 @@ pub(crate) fn prove, const D: usize>( ListPolynomialCommitment::new( zs_partial_products, fri_config.rate_bits, - PlonkPolynomials::ZS.blinding + PlonkPolynomials::ZS_PARTIAL_PRODUCTS.blinding ), "to commit to Z's" ); From b86e60a397fed1cd143052ac90c0e860e0068e4e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 18:24:49 +0200 Subject: [PATCH 09/23] Open the partial products polynomials only at `zeta` --- src/fri/verifier.rs | 24 +++++++++++++++++------- src/polynomial/commitment.rs | 24 ++++++++++++++---------- src/verifier.rs | 2 +- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 62bfbbd4..e0716cde 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -1,5 +1,6 @@ use anyhow::{ensure, Result}; +use crate::circuit_data::CommonCircuitData; use crate::field::extension_field::{flatten, Extendable, FieldExtension, Frobenius}; use crate::field::field::Field; use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2}; @@ -75,8 +76,9 @@ pub fn verify_fri_proof, const D: usize>( initial_merkle_roots: &[Hash], proof: &FriProof, challenger: &mut Challenger, - config: &FriConfig, + common_data: &CommonCircuitData, ) -> Result<()> { + let config = &common_data.config.fri_config; let total_arities = config.reduction_arity_bits.iter().sum::(); ensure!( purported_degree_log @@ -122,7 +124,7 @@ pub fn verify_fri_proof, const D: usize>( n, &betas, round_proof, - config, + common_data, )?; } @@ -147,8 +149,9 @@ fn fri_combine_initial, const D: usize>( os: &OpeningSet, zeta: F::Extension, subgroup_x: F, - config: &FriConfig, + common_data: &CommonCircuitData, ) -> F::Extension { + let config = &common_data.config.fri_config; assert!(D > 1, "Not implemented for D=1."); let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; let subgroup_x = F::Extension::from_basefield(subgroup_x); @@ -167,12 +170,17 @@ fn fri_combine_initial, const D: usize>( ] .iter() .flat_map(|&p| proof.unsalted_evals(p)) + .chain( + &proof.unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS) + [common_data.partial_products_range()], + ) .map(|&e| F::Extension::from_basefield(e)); let single_openings = os .constants .iter() .chain(&os.plonk_s_sigmas) - .chain(&os.quotient_polys); + .chain(&os.quotient_polys) + .chain(&os.partial_products); let single_diffs = single_evals .into_iter() .zip(single_openings) @@ -187,7 +195,8 @@ fn fri_combine_initial, const D: usize>( let zs_evals = proof .unsalted_evals(PlonkPolynomials::ZS_PARTIAL_PRODUCTS) .iter() - .map(|&e| F::Extension::from_basefield(e)); + .map(|&e| F::Extension::from_basefield(e)) + .take(common_data.zs_range().end); let zs_composition_eval = alpha.clone().reduce(zs_evals); let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; let zs_interpol = interpolate2( @@ -236,8 +245,9 @@ fn fri_verifier_query_round, const D: usize>( n: usize, betas: &[F::Extension], round_proof: &FriQueryRound, - config: &FriConfig, + common_data: &CommonCircuitData, ) -> Result<()> { + let config = &common_data.config.fri_config; let mut evaluations: Vec> = Vec::new(); let x = challenger.get_challenge(); let mut domain_size = n; @@ -262,7 +272,7 @@ fn fri_verifier_query_round, const D: usize>( os, zeta, subgroup_x, - config, + common_data, ) } else { let last_evals = &evaluations[i - 1]; diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index b88d9a65..b68e8cc2 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -147,6 +147,13 @@ impl ListPolynomialCommitment { // Final low-degree polynomial that goes into FRI. let mut final_poly = PolynomialCoeffs::empty(); + let mut zs_polys = commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index] + .polynomials + .iter() + .map(|p| p.to_extension()) + .collect::>(); + let partial_products_polys = zs_polys.split_off(common_data.zs_range().end); + // Polynomials opened at a single point. let single_polys = [ PlonkPolynomials::CONSTANTS_SIGMAS, @@ -154,7 +161,8 @@ impl ListPolynomialCommitment { ] .iter() .flat_map(|&p| &commitments[p.index].polynomials) - .map(|p| p.to_extension()); + .map(|p| p.to_extension()) + .chain(partial_products_polys); let single_composition_poly = alpha.reduce_polys(single_polys); let single_quotient = Self::compute_quotient([zeta], single_composition_poly); @@ -162,11 +170,7 @@ impl ListPolynomialCommitment { alpha.reset(); // Zs polynomials are opened at `zeta` and `g*zeta`. - let zs_polys = commitments[PlonkPolynomials::ZS_PARTIAL_PRODUCTS.index] - .polynomials - .iter() - .map(|p| p.to_extension()); - let zs_composition_poly = alpha.reduce_polys(zs_polys); + let zs_composition_poly = alpha.reduce_polys(zs_polys.into_iter()); let zs_quotient = Self::compute_quotient([zeta, g * zeta], zs_composition_poly); alpha.shift_poly(&mut final_poly); @@ -254,7 +258,7 @@ impl, const D: usize> OpeningProof { os: &OpeningSet, merkle_roots: &[Hash], challenger: &mut Challenger, - fri_config: &FriConfig, + common_data: &CommonCircuitData, ) -> Result<()> { challenger.observe_opening_set(os); @@ -268,7 +272,7 @@ impl, const D: usize> OpeningProof { merkle_roots, &self.fri_proof, challenger, - fri_config, + common_data, ) } } @@ -310,7 +314,7 @@ mod tests { } fn check_batch_polynomial_commitment, const D: usize>() -> Result<()> { - let ks = [10, 2, 3, 8]; + let ks = [10, 2, 10, 8]; let degree_log = 11; let fri_config = FriConfig { proof_of_work_bits: 2, @@ -363,7 +367,7 @@ mod tests { lpcs[3].merkle_tree.root, ], &mut Challenger::new(), - &common_data.config.fri_config, + &common_data, ) } diff --git a/src/verifier.rs b/src/verifier.rs index 57bad7cf..96361bb4 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -75,7 +75,7 @@ pub(crate) fn verify, const D: usize>( &evaluations, merkle_roots, &mut challenger, - fri_config, + common_data, )?; Ok(()) From ba06dc88979a3f4ab89e1b3c0cc4e00f9ffaa0e9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 1 Jul 2021 21:21:28 +0200 Subject: [PATCH 10/23] Make changes in `eval_vanish_poly` --- src/plonk_common.rs | 64 +++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index b10b1ee6..90a97fe1 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -65,41 +65,74 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( common_data: &CommonCircuitData, x: F::Extension, vars: EvaluationVars, - local_plonk_zs: &[F::Extension], - next_plonk_zs: &[F::Extension], + local_zs: &[F::Extension], + next_zs: &[F::Extension], + local_partial_products: &[F::Extension], s_sigmas: &[F::Extension], betas: &[F], gammas: &[F], alphas: &[F], ) -> Vec { + let max_degree = common_data.max_filtered_constraint_degree; let constraint_terms = evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); // The L_1(x) (Z(x) - 1) vanishing terms. let mut vanishing_z_1_terms = Vec::new(); + // The terms checking the partial products. + let mut vanishing_partial_products_terms = Vec::new(); // The Z(x) f'(x) - g'(x) Z(g x) terms. let mut vanishing_v_shift_terms = Vec::new(); for i in 0..common_data.config.num_challenges { - let z_x = local_plonk_zs[i]; - let z_gz = next_plonk_zs[i]; + let z_x = local_zs[i]; + let z_gz = next_zs[i]; vanishing_z_1_terms.push(eval_l_1(common_data.degree(), x) * (z_x - F::Extension::ONE)); - let mut f_prime = F::Extension::ONE; - let mut g_prime = F::Extension::ONE; - for j in 0..common_data.config.num_routed_wires { - let wire_value = vars.local_wires[j]; - let k_i = common_data.k_is[j]; - let s_id = x * k_i.into(); - let s_sigma = s_sigmas[j]; - f_prime *= wire_value + s_id * betas[i].into() + gammas[i].into(); - g_prime *= wire_value + s_sigma * betas[i].into() + gammas[i].into(); - } + let numerator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = vars.local_wires[j]; + let k_i = common_data.k_is[j]; + let s_id = x * k_i.into(); + wire_value + s_id * betas[i].into() + gammas[i].into() + }) + .collect::>(); + let denominator_values = (0..common_data.config.num_routed_wires) + .map(|j| { + let wire_value = vars.local_wires[j]; + let s_sigma = s_sigmas[j]; + wire_value + s_sigma * betas[i].into() + gammas[i].into() + }) + .collect::>(); + + let (num_prods, final_num_prod) = common_data.num_partial_products; + vanishing_partial_products_terms.extend(check_partial_products( + &numerator_values, + &local_partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], + max_degree, + )); + vanishing_partial_products_terms.extend(check_partial_products( + &denominator_values, + &local_partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], + max_degree, + )); + + let f_prime: F = local_partial_products + [(2 * i + 1) * num_prods - final_num_prod..(2 * i + 1) * num_prods] + .iter() + .copied() + .product(); + let g_prime: F = local_partial_products + [(2 * i + 2) * num_prods - final_num_prod..(2 * i + 2) * num_prods] + .iter() + .copied() + .product(); vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); } let vanishing_terms = [ vanishing_z_1_terms, + vanishing_partial_products_terms, vanishing_v_shift_terms, constraint_terms, ] @@ -188,9 +221,6 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( constraint_terms, ] .concat(); - // if index % 4 == 0 { - // println!("{}", vanishing_terms.iter().all(|x| x.is_zero())); - // } reduce_with_powers_multi(&vanishing_terms, alphas) } From 90bdb5796caf969c50cc1aa71fabce711a9cba94 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 2 Jul 2021 09:55:28 +0200 Subject: [PATCH 11/23] Fix product bug --- src/plonk_common.rs | 20 ++++++++++---------- src/prover.rs | 5 ++--- src/verifier.rs | 2 ++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 90a97fe1..ad4ccb9a 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -67,7 +67,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( vars: EvaluationVars, local_zs: &[F::Extension], next_zs: &[F::Extension], - local_partial_products: &[F::Extension], + partial_products: &[F::Extension], s_sigmas: &[F::Extension], betas: &[F], gammas: &[F], @@ -108,21 +108,21 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( let (num_prods, final_num_prod) = common_data.num_partial_products; vanishing_partial_products_terms.extend(check_partial_products( &numerator_values, - &local_partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], + &partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], max_degree, )); vanishing_partial_products_terms.extend(check_partial_products( &denominator_values, - &local_partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], + &partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], max_degree, )); - let f_prime: F = local_partial_products + let f_prime: F::Extension = partial_products [(2 * i + 1) * num_prods - final_num_prod..(2 * i + 1) * num_prods] .iter() .copied() .product(); - let g_prime: F = local_partial_products + let g_prime: F::Extension = partial_products [(2 * i + 2) * num_prods - final_num_prod..(2 * i + 2) * num_prods] .iter() .copied() @@ -150,7 +150,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( vars: EvaluationVarsBase, local_zs: &[F], next_zs: &[F], - local_partial_products: &[F], + partial_products: &[F], s_sigmas: &[F], betas: &[F], gammas: &[F], @@ -192,21 +192,21 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( let (num_prods, final_num_prod) = common_data.num_partial_products; vanishing_partial_products_terms.extend(check_partial_products( &numerator_values, - &local_partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], + &partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], max_degree, )); vanishing_partial_products_terms.extend(check_partial_products( &denominator_values, - &local_partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], + &partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], max_degree, )); - let f_prime: F = local_partial_products + let f_prime: F = partial_products [(2 * i + 1) * num_prods - final_num_prod..(2 * i + 1) * num_prods] .iter() .copied() .product(); - let g_prime: F = local_partial_products + let g_prime: F = partial_products [(2 * i + 2) * num_prods - final_num_prod..(2 * i + 2) * num_prods] .iter() .copied() diff --git a/src/prover.rs b/src/prover.rs index 6246ad54..4f788fc5 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -343,8 +343,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( let local_zs = &local_zs_partial_products[common_data.zs_range()]; let next_zs = &get_at_index(zs_partial_products_commitment, i_next)[common_data.zs_range()]; - let local_partial_products = - &local_zs_partial_products[common_data.partial_products_range()]; + let partial_products = &local_zs_partial_products[common_data.partial_products_range()]; debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_zs.len(), num_challenges); @@ -361,7 +360,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( vars, local_zs, next_zs, - local_partial_products, + partial_products, s_sigmas, betas, gammas, diff --git a/src/verifier.rs b/src/verifier.rs index 96361bb4..7284fbc5 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -40,6 +40,7 @@ pub(crate) fn verify, const D: usize>( let local_plonk_zs = &proof.openings.plonk_zs; let next_plonk_zs = &proof.openings.plonk_zs_right; let s_sigmas = &proof.openings.plonk_s_sigmas; + let local_partial_products = &proof.openings.partial_products; // Evaluate the vanishing polynomial at our challenge point, zeta. let vanishing_polys_zeta = eval_vanishing_poly( @@ -48,6 +49,7 @@ pub(crate) fn verify, const D: usize>( vars, local_plonk_zs, next_plonk_zs, + local_partial_products, s_sigmas, &betas, &gammas, From a3f473a073eebcefbc1b36103a5b25c07ab64c97 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 2 Jul 2021 10:12:13 +0200 Subject: [PATCH 12/23] Comments and minor refactor in `eval_vanish*` --- src/plonk_common.rs | 48 ++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index ad4ccb9a..3cb27246 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -74,6 +74,8 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( alphas: &[F], ) -> Vec { let max_degree = common_data.max_filtered_constraint_degree; + let (num_prods, final_num_prod) = common_data.num_partial_products; + let constraint_terms = evaluate_gate_constraints(&common_data.gates, common_data.num_gate_constraints, vars); @@ -105,25 +107,33 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( }) .collect::>(); - let (num_prods, final_num_prod) = common_data.num_partial_products; + // The partial products considered for this iteration of `i`. + let current_partial_products = + &partial_products[2 * i * num_prods..(2 * i + 2) * num_prods]; + // The partial products for the numerator are in the first `num_prods` elements. + let numerator_partial_products = ¤t_partial_products[..num_prods]; + // The partial products for the denominator are in the last `num_prods` elements. + let denominator_partial_products = ¤t_partial_products[num_prods..]; + // Check the numerator partial products. vanishing_partial_products_terms.extend(check_partial_products( &numerator_values, - &partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], + numerator_partial_products, max_degree, )); + // Check the denominator partial products. vanishing_partial_products_terms.extend(check_partial_products( &denominator_values, - &partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], + denominator_partial_products, max_degree, )); - let f_prime: F::Extension = partial_products - [(2 * i + 1) * num_prods - final_num_prod..(2 * i + 1) * num_prods] + // The numerator final product is the product of the last `final_num_prod` elements. + let f_prime: F::Extension = numerator_partial_products[num_prods - final_num_prod..] .iter() .copied() .product(); - let g_prime: F::Extension = partial_products - [(2 * i + 2) * num_prods - final_num_prod..(2 * i + 2) * num_prods] + // The denominator final product is the product of the last `final_num_prod` elements. + let g_prime: F::Extension = denominator_partial_products[num_prods - final_num_prod..] .iter() .copied() .product(); @@ -158,6 +168,8 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { let max_degree = common_data.max_filtered_constraint_degree; + let (num_prods, final_num_prod) = common_data.num_partial_products; + let constraint_terms = evaluate_gate_constraints_base(&common_data.gates, common_data.num_gate_constraints, vars); @@ -189,25 +201,33 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( }) .collect::>(); - let (num_prods, final_num_prod) = common_data.num_partial_products; + // The partial products considered for this iteration of `i`. + let current_partial_products = + &partial_products[2 * i * num_prods..(2 * i + 2) * num_prods]; + // The partial products for the numerator are in the first `num_prods` elements. + let numerator_partial_products = ¤t_partial_products[..num_prods]; + // The partial products for the denominator are in the last `num_prods` elements. + let denominator_partial_products = ¤t_partial_products[num_prods..]; + // Check the numerator partial products. vanishing_partial_products_terms.extend(check_partial_products( &numerator_values, - &partial_products[2 * i * num_prods..(2 * i + 1) * num_prods], + numerator_partial_products, max_degree, )); + // Check the denominator partial products. vanishing_partial_products_terms.extend(check_partial_products( &denominator_values, - &partial_products[(2 * i + 1) * num_prods..(2 * i + 2) * num_prods], + denominator_partial_products, max_degree, )); - let f_prime: F = partial_products - [(2 * i + 1) * num_prods - final_num_prod..(2 * i + 1) * num_prods] + // The numerator final product is the product of the last `final_num_prod` elements. + let f_prime: F = numerator_partial_products[num_prods - final_num_prod..] .iter() .copied() .product(); - let g_prime: F = partial_products - [(2 * i + 2) * num_prods - final_num_prod..(2 * i + 2) * num_prods] + // The denominator final product is the product of the last `final_num_prod` elements. + let g_prime: F = denominator_partial_products[num_prods - final_num_prod..] .iter() .copied() .product(); From d456efbc3ffa814122a42ccecaa0cf7eb623b317 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 2 Jul 2021 10:20:44 +0200 Subject: [PATCH 13/23] Minor addition to partial product test --- src/circuit_data.rs | 2 +- src/util/partial_products.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index e7f08dcb..6591a6df 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -157,7 +157,7 @@ pub struct CommonCircuitData, const D: usize> { /// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument. pub(crate) k_is: Vec, - /// The number of partial products needed to compute the `Z` polynomials, as well as the number + /// The number of partial products needed to compute the `Z` polynomials and the number /// of partial products needed to compute the final product. pub(crate) num_partial_products: (usize, usize), diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 2eefdd1d..33fe737f 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -86,17 +86,27 @@ mod tests { let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 2); assert_eq!(p, vec![2, 12, 30, 24, 720]); - assert_eq!(p.len(), num_partial_products(v.len(), 2).0); + let nums = num_partial_products(v.len(), 2); + assert_eq!(p.len(), nums.0); assert!(check_partial_products(&v, &p, 2) .iter() .all(|x| x.is_zero())); + assert_eq!( + v.into_iter().product::(), + p[p.len() - nums.1..].iter().copied().product(), + ); let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 3); assert_eq!(p, vec![6, 120]); - assert_eq!(p.len(), num_partial_products(v.len(), 3).0); + let nums = num_partial_products(v.len(), 3); + assert_eq!(p.len(), nums.0); assert!(check_partial_products(&v, &p, 3) .iter() .all(|x| x.is_zero())); + assert_eq!( + v.into_iter().product::(), + p[p.len() - nums.1..].iter().copied().product(), + ); } } From 1915ef9b27340316d19305054f64739c1a17944b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 2 Jul 2021 10:58:59 +0200 Subject: [PATCH 14/23] Minor improvements --- src/circuit_data.rs | 4 ++-- src/gadgets/arithmetic_extension.rs | 4 +--- src/prover.rs | 11 ++++++----- src/util/partial_products.rs | 6 +++--- src/verifier.rs | 12 ++++++------ 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 6591a6df..4cc60c5d 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -206,12 +206,12 @@ impl, const D: usize> CommonCircuitData { self.num_constants..self.num_constants + self.config.num_routed_wires } - /// Range of the `z`s polynomials in the ``. + /// Range of the `z`s polynomials in the `zs_partial_products_commitment`. pub fn zs_range(&self) -> Range { 0..self.config.num_challenges } - /// Range of the sigma polynomials in the `constants_sigmas_commitment`. + /// Range of the partial products polynomials in the `zs_partial_products_commitment`. pub fn partial_products_range(&self) -> RangeFrom { self.config.num_challenges.. } diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index 221662d1..4f7b1e14 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -446,9 +446,7 @@ mod tests { type FF = QuarticCrandallField; const D: usize = 4; - let mut config = CircuitConfig::large_config(); - config.rate_bits = 2; - config.fri_config.rate_bits = 2; + let config = CircuitConfig::large_config(); let mut builder = CircuitBuilder::::new(config); diff --git a/src/prover.rs b/src/prover.rs index 4f788fc5..ecb4cb50 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -1,6 +1,5 @@ use std::time::Instant; -use itertools::Itertools; use log::info; use rayon::prelude::*; @@ -80,6 +79,10 @@ pub(crate) fn prove, const D: usize>( let betas = challenger.get_n_challenges(num_challenges); let gammas = challenger.get_n_challenges(num_challenges); + assert!( + common_data.max_filtered_constraint_degree<=common_data.config.num_routed_wires, + "When the number of routed wires is smaller that the degree, we should change the logic to avoid computing partial products." + ); let mut partial_products = timed!( all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data), "to compute partial products" @@ -214,7 +217,7 @@ fn wires_permutation_partial_products, const D: usize>( let subgroup = &prover_data.subgroup; let k_is = &common_data.k_is; let values = subgroup - .iter() + .par_iter() .enumerate() .map(|(i, &x)| { let s_sigmas = &prover_data.sigmas[i]; @@ -329,8 +332,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( ZeroPolyOnCoset::new(common_data.degree_bits, max_filtered_constraint_degree_bits); let quotient_values: Vec> = points - // .into_par_iter() - .into_iter() + .into_par_iter() .enumerate() .map(|(i, x)| { let shifted_x = F::coset_shift() * x; @@ -352,7 +354,6 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( local_constants, local_wires, }; - dbg!(i); let mut quotient_values = eval_vanishing_poly_base( common_data, i, diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 33fe737f..5c215fde 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -3,9 +3,9 @@ use std::ops::Sub; use crate::util::ceil_div_usize; -/// Compute partial products of the original vector `v` such that no products are of `max_degree` or -/// less elements. This is done until we've computed the product `P` of all elements in the vector. -/// The final product resulting in `P` has `max_degree-1` elements at most since `P` is multiplied +/// Compute partial products of the original vector `v` such that all products consist of `max_degree` +/// or less elements. This is done until we've computed the product `P` of all elements in the vector. +/// The final product resulting in `P` consists of at most `max_degree-1` elements since `P` is multiplied /// by the `Z` polynomial in the Plonk check. pub fn partial_products(v: &[T], max_degree: usize) -> Vec { let mut res = Vec::new(); diff --git a/src/verifier.rs b/src/verifier.rs index 7284fbc5..0d85647e 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -37,19 +37,19 @@ pub(crate) fn verify, const D: usize>( local_constants, local_wires, }; - let local_plonk_zs = &proof.openings.plonk_zs; - let next_plonk_zs = &proof.openings.plonk_zs_right; + let local_zs = &proof.openings.plonk_zs; + let next_zs = &proof.openings.plonk_zs_right; let s_sigmas = &proof.openings.plonk_s_sigmas; - let local_partial_products = &proof.openings.partial_products; + let partial_products = &proof.openings.partial_products; // Evaluate the vanishing polynomial at our challenge point, zeta. let vanishing_polys_zeta = eval_vanishing_poly( common_data, zeta, vars, - local_plonk_zs, - next_plonk_zs, - local_partial_products, + local_zs, + next_zs, + partial_products, s_sigmas, &betas, &gammas, From b5b2ef9f3e6db3bc698c0cce26c0006897cf7130 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 2 Jul 2021 11:01:20 +0200 Subject: [PATCH 15/23] Clippy --- src/gates/gate_tree.rs | 2 +- src/plonk_common.rs | 2 +- src/prover.rs | 2 +- src/verifier.rs | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gates/gate_tree.rs b/src/gates/gate_tree.rs index 5c2e084e..0c10ed70 100644 --- a/src/gates/gate_tree.rs +++ b/src/gates/gate_tree.rs @@ -74,7 +74,7 @@ impl, const D: usize> Tree> { // Iterate backwards from `max_degree` to try to find a tree with a lower degree // but the same number of constants. 'optdegree: for degree in (0..max_degree).rev() { - if let Some(mut tree) = Self::find_tree(&gates, degree, max_constants) { + if let Some(tree) = Self::find_tree(&gates, degree, max_constants) { let num_constants = tree.num_constants(); if num_constants > best_num_constants { break 'optdegree; diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 3cb27246..baaf8fc5 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -9,7 +9,7 @@ use crate::gates::gate::{GateRef, PrefixedGate}; use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; -use crate::util::partial_products::{check_partial_products, partial_products}; +use crate::util::partial_products::check_partial_products; use crate::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase}; /// Holds the Merkle tree index and blinding flag of a set of polynomials used in FRI. diff --git a/src/prover.rs b/src/prover.rs index ecb4cb50..b19f0360 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -13,7 +13,7 @@ use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::Proof; use crate::timed; use crate::util::partial_products::partial_products; -use crate::util::{ceil_div_usize, log2_ceil, transpose}; +use crate::util::{log2_ceil, transpose}; use crate::vars::EvaluationVarsBase; use crate::witness::{PartialWitness, Witness}; diff --git a/src/verifier.rs b/src/verifier.rs index 0d85647e..3bfe2dc6 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -13,7 +13,6 @@ pub(crate) fn verify, const D: usize>( common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config; - let fri_config = &config.fri_config; let num_challenges = config.num_challenges; let mut challenger = Challenger::new(); From 50cafca70543c645dcaf0fa7475c3bb622953430 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 6 Jul 2021 10:51:32 +0200 Subject: [PATCH 16/23] Partial products of quotient --- src/circuit_builder.rs | 6 ++- src/plonk_common.rs | 90 +++++++++++++++--------------------- src/prover.rs | 52 +++++++-------------- src/util/partial_products.rs | 21 ++++++--- 4 files changed, 74 insertions(+), 95 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index eca0f205..a28d5121 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -439,8 +439,10 @@ impl, const D: usize> CircuitBuilder { .max() .expect("No gates?"); - let num_partial_products = - num_partial_products(self.config.num_routed_wires, max_filtered_constraint_degree); + let num_partial_products = num_partial_products( + self.config.num_routed_wires, + max_filtered_constraint_degree - 1, + ); // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ diff --git a/src/plonk_common.rs b/src/plonk_common.rs index baaf8fc5..2da30fcd 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -73,7 +73,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let max_degree = common_data.max_filtered_constraint_degree; + let max_degree = common_data.max_filtered_constraint_degree - 1; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -106,38 +106,29 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( wire_value + s_sigma * betas[i].into() + gammas[i].into() }) .collect::>(); + let quotient_values = (0..common_data.config.num_routed_wires) + .map(|j| numerator_values[j] / denominator_values[j]) + .collect::>(); // The partial products considered for this iteration of `i`. - let current_partial_products = - &partial_products[2 * i * num_prods..(2 * i + 2) * num_prods]; - // The partial products for the numerator are in the first `num_prods` elements. - let numerator_partial_products = ¤t_partial_products[..num_prods]; - // The partial products for the denominator are in the last `num_prods` elements. - let denominator_partial_products = ¤t_partial_products[num_prods..]; + let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the numerator partial products. - vanishing_partial_products_terms.extend(check_partial_products( - &numerator_values, - numerator_partial_products, - max_degree, - )); - // Check the denominator partial products. - vanishing_partial_products_terms.extend(check_partial_products( - &denominator_values, - denominator_partial_products, - max_degree, - )); + let mut partial_product_check = + check_partial_products("ient_values, current_partial_products, max_degree); + denominator_values + .chunks(max_degree - 1) + .zip(partial_product_check.iter_mut()) + .for_each(|(d, q)| { + *q *= d.iter().copied().product(); + }); + vanishing_partial_products_terms.extend(partial_product_check); // The numerator final product is the product of the last `final_num_prod` elements. - let f_prime: F::Extension = numerator_partial_products[num_prods - final_num_prod..] + let quotient: F::Extension = current_partial_products[num_prods - final_num_prod..] .iter() .copied() .product(); - // The denominator final product is the product of the last `final_num_prod` elements. - let g_prime: F::Extension = denominator_partial_products[num_prods - final_num_prod..] - .iter() - .copied() - .product(); - vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); + vanishing_v_shift_terms.push(quotient * z_x - z_gz); } let vanishing_terms = [ @@ -167,7 +158,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( alphas: &[F], z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { - let max_degree = common_data.max_filtered_constraint_degree; + let max_degree = common_data.max_filtered_constraint_degree - 1; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -200,44 +191,39 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( wire_value + betas[i] * s_sigma + gammas[i] }) .collect::>(); + let quotient_values = (0..common_data.config.num_routed_wires) + .map(|j| numerator_values[j] / denominator_values[j]) + .collect::>(); // The partial products considered for this iteration of `i`. - let current_partial_products = - &partial_products[2 * i * num_prods..(2 * i + 2) * num_prods]; - // The partial products for the numerator are in the first `num_prods` elements. - let numerator_partial_products = ¤t_partial_products[..num_prods]; - // The partial products for the denominator are in the last `num_prods` elements. - let denominator_partial_products = ¤t_partial_products[num_prods..]; + let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the numerator partial products. - vanishing_partial_products_terms.extend(check_partial_products( - &numerator_values, - numerator_partial_products, - max_degree, - )); - // Check the denominator partial products. - vanishing_partial_products_terms.extend(check_partial_products( - &denominator_values, - denominator_partial_products, - max_degree, - )); + let mut partial_product_check = + check_partial_products("ient_values, current_partial_products, max_degree); + denominator_values + .chunks(max_degree) + .zip(partial_product_check.iter_mut()) + .for_each(|(d, q)| { + *q *= d.iter().copied().product(); + }); + dbg!( + quotient_values[27], + current_partial_products.last().unwrap() + ); + partial_product_check.pop(); + vanishing_partial_products_terms.extend(partial_product_check); // The numerator final product is the product of the last `final_num_prod` elements. - let f_prime: F = numerator_partial_products[num_prods - final_num_prod..] + let quotient: F = current_partial_products[num_prods - final_num_prod..] .iter() .copied() .product(); - // The denominator final product is the product of the last `final_num_prod` elements. - let g_prime: F = denominator_partial_products[num_prods - final_num_prod..] - .iter() - .copied() - .product(); - vanishing_v_shift_terms.push(f_prime * z_x - g_prime * z_gz); + vanishing_v_shift_terms.push(quotient * z_x - z_gz); } - let vanishing_terms = [ vanishing_z_1_terms, vanishing_partial_products_terms, - vanishing_v_shift_terms, + // vanishing_v_shift_terms, constraint_terms, ] .concat(); diff --git a/src/prover.rs b/src/prover.rs index b19f0360..88cd2b4b 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -93,7 +93,7 @@ pub(crate) fn prove, const D: usize>( // The first two polynomials in `partial_products` represent the final products used in the // computation of `Z`. They aren't needed anymore so we discard them. partial_products.iter_mut().for_each(|part| { - part.drain(0..2); + part.remove(0); }); let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat(); @@ -204,8 +204,8 @@ fn all_wires_permutation_partial_products, const D: usize>( } /// Compute the partial products used in the `Z` polynomial. -/// Returns the polynomials interpolating `partial_products(f) + partial_products(g)` -/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = n / d`. +/// Returns the polynomials interpolating `partial_products(f / g)` +/// where `f, g` are the products in the definition of `Z`: `Z(g^i) = f / g`. fn wires_permutation_partial_products, const D: usize>( witness: &Witness, beta: F, @@ -213,7 +213,7 @@ fn wires_permutation_partial_products, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { - let degree = common_data.max_filtered_constraint_degree; + let degree = common_data.max_filtered_constraint_degree - 1; let subgroup = &prover_data.subgroup; let k_is = &common_data.k_is; let values = subgroup @@ -221,47 +221,29 @@ fn wires_permutation_partial_products, const D: usize>( .enumerate() .map(|(i, &x)| { let s_sigmas = &prover_data.sigmas[i]; - let numerator_values = (0..common_data.config.num_routed_wires) + let quotient_values = (0..common_data.config.num_routed_wires) .map(|j| { let wire_value = witness.get_wire(i, j); let k_i = k_is[j]; let s_id = k_i * x; - wire_value + beta * s_id + gamma - }) - .collect::>(); - let denominator_values = (0..common_data.config.num_routed_wires) - .map(|j| { - let wire_value = witness.get_wire(i, j); let s_sigma = s_sigmas[j]; - wire_value + beta * s_sigma + gamma + let numerator = wire_value + beta * s_id + gamma; + let denominator = wire_value + beta * s_sigma + gamma; + numerator / denominator }) .collect::>(); - let numerator_partials = partial_products(&numerator_values, degree); - let denominator_partials = partial_products(&denominator_values, degree); + let quotient_partials = partial_products("ient_values, degree); - // This is the final product for the numerator. - let numerator = numerator_partials - [common_data.num_partial_products.0 - common_data.num_partial_products.1..] - .iter() - .copied() - .product(); - // This is the final product for the denominator. - let denominator = denominator_partials + // This is the final product for the quotient. + let quotient = quotient_partials [common_data.num_partial_products.0 - common_data.num_partial_products.1..] .iter() .copied() .product(); - // We add the numerator and denominator at the beginning of the vector to reuse them - // later in the computation of `Z`. - [ - vec![numerator], - vec![denominator], - numerator_partials, - denominator_partials, - ] - .concat() + // We add the quotient at the beginning of the vector to reuse them later in the computation of `Z`. + [vec![quotient], quotient_partials].concat() }) .collect::>(); @@ -287,10 +269,9 @@ fn compute_z, const D: usize>( ) -> PolynomialValues { let mut plonk_z_points = vec![F::ONE]; for i in 1..common_data.degree() { - let numerator = partial_products[0].values[i - 1]; - let denominator = partial_products[1].values[i - 1]; + let quotient = partial_products[0].values[i - 1]; let last = *plonk_z_points.last().unwrap(); - plonk_z_points.push(last * numerator / denominator); + plonk_z_points.push(last * quotient); } plonk_z_points.into() } @@ -332,7 +313,8 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( ZeroPolyOnCoset::new(common_data.degree_bits, max_filtered_constraint_degree_bits); let quotient_values: Vec> = points - .into_par_iter() + // .into_par_iter() + .into_iter() .enumerate() .map(|(i, x)| { let shifted_x = F::coset_shift() * x; diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index 5c215fde..e9edde2b 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -10,7 +10,7 @@ use crate::util::ceil_div_usize; pub fn partial_products(v: &[T], max_degree: usize) -> Vec { let mut res = Vec::new(); let mut remainder = v.to_vec(); - while remainder.len() >= max_degree { + while remainder.len() > max_degree { let new_partials = remainder .chunks(max_degree) // No need to compute the product if the chunk has size 1. @@ -25,7 +25,10 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec }; remainder = new_partials; // If there were a chunk of size 1, add it back to the remainder. - remainder.extend(addendum); + remainder.extend_from_slice(&addendum); + if remainder.len() <= max_degree { + res.extend(addendum); + } } res @@ -36,11 +39,14 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { let mut res = 0; let mut remainder = n; - while remainder >= max_degree { + while remainder > max_degree { let new_partials_len = ceil_div_usize(remainder, max_degree); let addendum = if remainder % max_degree == 1 { 1 } else { 0 }; res += new_partials_len - addendum; remainder = new_partials_len; + if remainder <= max_degree { + res += addendum; + } } (res, remainder) @@ -56,7 +62,7 @@ pub fn check_partial_products>( let mut res = Vec::new(); let mut remainder = v.to_vec(); let mut partials = partials.to_vec(); - while remainder.len() >= max_degree { + while remainder.len() > max_degree { let products = remainder .chunks(max_degree) .filter(|chunk| chunk.len() != 1) @@ -69,7 +75,10 @@ pub fn check_partial_products>( vec![] }; remainder = partials.drain(..products.len()).collect(); - remainder.extend(addendum) + remainder.extend_from_slice(&addendum); + if remainder.len() <= max_degree { + res.extend(addendum.into_iter().map(|a| a - *partials.last().unwrap())); + } } res @@ -85,7 +94,7 @@ mod tests { fn test_partial_products() { let v = vec![1, 2, 3, 4, 5, 6]; let p = partial_products(&v, 2); - assert_eq!(p, vec![2, 12, 30, 24, 720]); + assert_eq!(p, vec![2, 12, 30, 24, 30]); let nums = num_partial_products(v.len(), 2); assert_eq!(p.len(), nums.0); assert!(check_partial_products(&v, &p, 2) From 151d1ad07b449788364525006af0c6255e7bc2ad Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 6 Jul 2021 11:00:05 +0200 Subject: [PATCH 17/23] Remove addendum --- src/plonk_common.rs | 5 ----- src/util/partial_products.rs | 28 +--------------------------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 2da30fcd..99ba585e 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -206,11 +206,6 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( .for_each(|(d, q)| { *q *= d.iter().copied().product(); }); - dbg!( - quotient_values[27], - current_partial_products.last().unwrap() - ); - partial_product_check.pop(); vanishing_partial_products_terms.extend(partial_product_check); // The numerator final product is the product of the last `final_num_prod` elements. diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index e9edde2b..e7c9069b 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -13,22 +13,10 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec while remainder.len() > max_degree { let new_partials = remainder .chunks(max_degree) - // No need to compute the product if the chunk has size 1. - .filter(|chunk| chunk.len() != 1) .map(|chunk| chunk.iter().copied().product()) .collect::>(); res.extend_from_slice(&new_partials); - let addendum = if remainder.len() % max_degree == 1 { - vec![*remainder.last().unwrap()] - } else { - vec![] - }; remainder = new_partials; - // If there were a chunk of size 1, add it back to the remainder. - remainder.extend_from_slice(&addendum); - if remainder.len() <= max_degree { - res.extend(addendum); - } } res @@ -41,12 +29,8 @@ pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { let mut remainder = n; while remainder > max_degree { let new_partials_len = ceil_div_usize(remainder, max_degree); - let addendum = if remainder % max_degree == 1 { 1 } else { 0 }; - res += new_partials_len - addendum; + res += new_partials_len; remainder = new_partials_len; - if remainder <= max_degree { - res += addendum; - } } (res, remainder) @@ -65,20 +49,10 @@ pub fn check_partial_products>( while remainder.len() > max_degree { let products = remainder .chunks(max_degree) - .filter(|chunk| chunk.len() != 1) .map(|chunk| chunk.iter().copied().product()) .collect::>(); res.extend(products.iter().zip(&partials).map(|(&a, &b)| a - b)); - let addendum = if remainder.len() % max_degree == 1 { - vec![*remainder.last().unwrap()] - } else { - vec![] - }; remainder = partials.drain(..products.len()).collect(); - remainder.extend_from_slice(&addendum); - if remainder.len() <= max_degree { - res.extend(addendum.into_iter().map(|a| a - *partials.last().unwrap())); - } } res From 4a27a67bab071ee773ccf8515e98bead437ee55d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 6 Jul 2021 11:10:08 +0200 Subject: [PATCH 18/23] Constraint degree should be at least 3 --- src/circuit_builder.rs | 1 + src/util/partial_products.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 93b440ab..f1db3ae3 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -403,6 +403,7 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let (gate_tree, max_filtered_constraint_degree, num_constants) = Tree::from_gates(gates); + let max_filtered_constraint_degree = max_filtered_constraint_degree.max(3); let prefixed_gates = PrefixedGate::from_tree(gate_tree); let degree_bits = log2_strict(degree); diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index e7c9069b..c3e5c2cd 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -25,6 +25,7 @@ pub fn partial_products(v: &[T], max_degree: usize) -> Vec /// Returns a tuple `(a,b)`, where `a` is the length of the output of `partial_products()` on a /// vector of length `n`, and `b` is the number of elements needed to compute the final product. pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) { + debug_assert!(max_degree > 1); let mut res = 0; let mut remainder = n; while remainder > max_degree { From 274ec48f5e47319948422bc8f8ac172f391e55b8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 6 Jul 2021 11:19:58 +0200 Subject: [PATCH 19/23] Update comments --- src/plonk_common.rs | 14 +++++++++----- src/prover.rs | 7 +++---- src/util/partial_products.rs | 3 +-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 99ba585e..2ae5ec01 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -112,18 +112,20 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; - // Check the numerator partial products. + // Check the quotient partial products. let mut partial_product_check = check_partial_products("ient_values, current_partial_products, max_degree); + // The first checks are of the form `q - n/d` which is a rational function not a polynomial. + // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. denominator_values - .chunks(max_degree - 1) + .chunks(max_degree) .zip(partial_product_check.iter_mut()) .for_each(|(d, q)| { *q *= d.iter().copied().product(); }); vanishing_partial_products_terms.extend(partial_product_check); - // The numerator final product is the product of the last `final_num_prod` elements. + // The quotient final product is the product of the last `final_num_prod` elements. let quotient: F::Extension = current_partial_products[num_prods - final_num_prod..] .iter() .copied() @@ -200,6 +202,8 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( // Check the numerator partial products. let mut partial_product_check = check_partial_products("ient_values, current_partial_products, max_degree); + // The first checks are of the form `q - n/d` which is a rational function not a polynomial. + // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. denominator_values .chunks(max_degree) .zip(partial_product_check.iter_mut()) @@ -208,7 +212,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( }); vanishing_partial_products_terms.extend(partial_product_check); - // The numerator final product is the product of the last `final_num_prod` elements. + // The quotient final product is the product of the last `final_num_prod` elements. let quotient: F = current_partial_products[num_prods - final_num_prod..] .iter() .copied() @@ -218,7 +222,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( let vanishing_terms = [ vanishing_z_1_terms, vanishing_partial_products_terms, - // vanishing_v_shift_terms, + vanishing_v_shift_terms, constraint_terms, ] .concat(); diff --git a/src/prover.rs b/src/prover.rs index 88cd2b4b..a6f2ce4d 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -90,8 +90,8 @@ pub(crate) fn prove, const D: usize>( let plonk_z_vecs = timed!(compute_zs(&partial_products, common_data), "to compute Z's"); - // The first two polynomials in `partial_products` represent the final products used in the - // computation of `Z`. They aren't needed anymore so we discard them. + // The first polynomial in `partial_products` represent the final product used in the + // computation of `Z`. It isn't needed anymore so we discard it. partial_products.iter_mut().for_each(|part| { part.remove(0); }); @@ -313,8 +313,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( ZeroPolyOnCoset::new(common_data.degree_bits, max_filtered_constraint_degree_bits); let quotient_values: Vec> = points - // .into_par_iter() - .into_iter() + .into_par_iter() .enumerate() .map(|(i, x)| { let shifted_x = F::coset_shift() * x; diff --git a/src/util/partial_products.rs b/src/util/partial_products.rs index c3e5c2cd..1d169b8d 100644 --- a/src/util/partial_products.rs +++ b/src/util/partial_products.rs @@ -5,14 +5,13 @@ use crate::util::ceil_div_usize; /// Compute partial products of the original vector `v` such that all products consist of `max_degree` /// or less elements. This is done until we've computed the product `P` of all elements in the vector. -/// The final product resulting in `P` consists of at most `max_degree-1` elements since `P` is multiplied -/// by the `Z` polynomial in the Plonk check. pub fn partial_products(v: &[T], max_degree: usize) -> Vec { let mut res = Vec::new(); let mut remainder = v.to_vec(); while remainder.len() > max_degree { let new_partials = remainder .chunks(max_degree) + // TODO: can filter out chunks of length 1. .map(|chunk| chunk.iter().copied().product()) .collect::>(); res.extend_from_slice(&new_partials); From 805ebb1b0dd9857f02e5ec34e8547462a650a3f2 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 8 Jul 2021 15:13:29 +0200 Subject: [PATCH 20/23] Working verifier --- src/circuit_builder.rs | 14 ++++++++------ src/circuit_data.rs | 6 +++--- src/gadgets/arithmetic_extension.rs | 3 +++ src/gadgets/insert.rs | 3 +++ src/gadgets/interpolation.rs | 5 +++++ src/gadgets/rotate.rs | 3 +++ src/gadgets/split_base.rs | 3 +++ src/plonk_common.rs | 4 ++-- src/polynomial/commitment.rs | 2 +- src/prover.rs | 18 ++++++++---------- src/verifier.rs | 13 +++++++++---- 11 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index f1db3ae3..84973cd6 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -392,6 +392,7 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { + let quotient_degree = 7; // TODO: add this as a parameter. let start = Instant::now(); info!( "Degree before blinding & padding: {}", @@ -403,7 +404,10 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let (gate_tree, max_filtered_constraint_degree, num_constants) = Tree::from_gates(gates); - let max_filtered_constraint_degree = max_filtered_constraint_degree.max(3); + assert!( + max_filtered_constraint_degree <= quotient_degree + 1, + "Constraints are too high degree." + ); let prefixed_gates = PrefixedGate::from_tree(gate_tree); let degree_bits = log2_strict(degree); @@ -446,10 +450,8 @@ impl, const D: usize> CircuitBuilder { .max() .expect("No gates?"); - let num_partial_products = num_partial_products( - self.config.num_routed_wires, - max_filtered_constraint_degree - 1, - ); + let num_partial_products = + num_partial_products(self.config.num_routed_wires, quotient_degree); // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ @@ -462,7 +464,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates: prefixed_gates, - max_filtered_constraint_degree, + quotient_degree_factor: quotient_degree, num_gate_constraints, num_constants, k_is, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 4cc60c5d..10c5ffe0 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -145,8 +145,8 @@ pub struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit, along with their prefixes. pub(crate) gates: Vec>, - /// The maximum degree of a filter times a constraint by any gate. - pub(crate) max_filtered_constraint_degree: usize, + /// The degree of the PLONK quotient polynomial. + pub(crate) quotient_degree_factor: usize, /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -188,7 +188,7 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - (self.max_filtered_constraint_degree - 1) * self.degree() + self.quotient_degree_factor * self.degree() } pub fn total_constraints(&self) -> usize { diff --git a/src/gadgets/arithmetic_extension.rs b/src/gadgets/arithmetic_extension.rs index 4f7b1e14..3bf9c8a9 100644 --- a/src/gadgets/arithmetic_extension.rs +++ b/src/gadgets/arithmetic_extension.rs @@ -438,6 +438,7 @@ mod tests { use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; use crate::fri::FriConfig; + use crate::verifier::verify; use crate::witness::PartialWitness; #[test] @@ -461,5 +462,7 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } } diff --git a/src/gadgets/insert.rs b/src/gadgets/insert.rs index 1f69cb24..29e360fc 100644 --- a/src/gadgets/insert.rs +++ b/src/gadgets/insert.rs @@ -78,6 +78,7 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; + use crate::verifier::verify; use crate::witness::PartialWitness; fn real_insert( @@ -113,6 +114,8 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } #[test] diff --git a/src/gadgets/interpolation.rs b/src/gadgets/interpolation.rs index 37746685..8ece6063 100644 --- a/src/gadgets/interpolation.rs +++ b/src/gadgets/interpolation.rs @@ -66,6 +66,7 @@ mod tests { use crate::field::extension_field::FieldExtension; use crate::field::field::Field; use crate::field::interpolation::{interpolant, interpolate}; + use crate::verifier::verify; use crate::witness::PartialWitness; #[test] @@ -103,6 +104,8 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } #[test] @@ -135,5 +138,7 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } } diff --git a/src/gadgets/rotate.rs b/src/gadgets/rotate.rs index bd39a36b..61c1d2cc 100644 --- a/src/gadgets/rotate.rs +++ b/src/gadgets/rotate.rs @@ -118,6 +118,7 @@ mod tests { use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; use crate::field::field::Field; + use crate::verifier::verify; use crate::witness::PartialWitness; fn real_rotate( @@ -150,6 +151,8 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } #[test] diff --git a/src/gadgets/split_base.rs b/src/gadgets/split_base.rs index 37f88409..a3177a1f 100644 --- a/src/gadgets/split_base.rs +++ b/src/gadgets/split_base.rs @@ -41,6 +41,7 @@ mod tests { use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; use crate::field::field::Field; + use crate::verifier::verify; use crate::witness::PartialWitness; #[test] @@ -67,5 +68,7 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } } diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 2ae5ec01..5179b474 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -73,7 +73,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let max_degree = common_data.max_filtered_constraint_degree - 1; + let max_degree = common_data.quotient_degree_factor; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -160,7 +160,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( alphas: &[F], z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { - let max_degree = common_data.max_filtered_constraint_degree - 1; + let max_degree = common_data.quotient_degree_factor; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index b68e8cc2..7298331c 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -331,7 +331,7 @@ mod tests { }, degree_bits: 0, gates: vec![], - max_filtered_constraint_degree: 0, + quotient_degree_factor: 0, num_gate_constraints: 0, num_constants: 4, k_is: vec![F::ONE; 6], diff --git a/src/prover.rs b/src/prover.rs index a6f2ce4d..63c32995 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -80,7 +80,7 @@ pub(crate) fn prove, const D: usize>( let gammas = challenger.get_n_challenges(num_challenges); assert!( - common_data.max_filtered_constraint_degree<=common_data.config.num_routed_wires, + common_data.quotient_degree_factor + 1 <=common_data.config.num_routed_wires, "When the number of routed wires is smaller that the degree, we should change the logic to avoid computing partial products." ); let mut partial_products = timed!( @@ -213,7 +213,7 @@ fn wires_permutation_partial_products, const D: usize>( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, ) -> Vec> { - let degree = common_data.max_filtered_constraint_degree - 1; + let degree = common_data.quotient_degree_factor; let subgroup = &prover_data.subgroup; let k_is = &common_data.k_is; let values = subgroup @@ -286,22 +286,21 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( alphas: &[F], ) -> Vec> { let num_challenges = common_data.config.num_challenges; - let max_filtered_constraint_degree_bits = log2_ceil(common_data.max_filtered_constraint_degree); + let max_degree_bits = log2_ceil(common_data.quotient_degree_factor + 1); assert!( - max_filtered_constraint_degree_bits <= common_data.config.rate_bits, + max_degree_bits <= common_data.config.rate_bits, "Having constraints of degree higher than the rate is not supported yet. \ If we need this in the future, we can precompute the larger LDE before computing the `ListPolynomialCommitment`s." ); // We reuse the LDE computed in `ListPolynomialCommitment` and extract every `step` points to get // an LDE matching `max_filtered_constraint_degree`. - let step = 1 << (common_data.config.rate_bits - max_filtered_constraint_degree_bits); + let step = 1 << (common_data.config.rate_bits - max_degree_bits); // When opening the `Z`s polys at the "next" point in Plonk, need to look at the point `next_step` // steps away since we work on an LDE of degree `max_filtered_constraint_degree`. - let next_step = 1 << max_filtered_constraint_degree_bits; + let next_step = 1 << max_degree_bits; - let points = - F::two_adic_subgroup(common_data.degree_bits + max_filtered_constraint_degree_bits); + let points = F::two_adic_subgroup(common_data.degree_bits + max_degree_bits); let lde_size = points.len(); // Retrieve the LDE values at index `i`. @@ -309,8 +308,7 @@ fn compute_quotient_polys<'a, F: Extendable, const D: usize>( comm.get_lde_values(i * step) }; - let z_h_on_coset = - ZeroPolyOnCoset::new(common_data.degree_bits, max_filtered_constraint_degree_bits); + let z_h_on_coset = ZeroPolyOnCoset::new(common_data.degree_bits, max_degree_bits); let quotient_values: Vec> = points .into_par_iter() diff --git a/src/verifier.rs b/src/verifier.rs index 3bfe2dc6..64d4b371 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -2,8 +2,9 @@ use anyhow::{ensure, Result}; use crate::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use crate::field::extension_field::Extendable; +use crate::field::field::Field; use crate::plonk_challenger::Challenger; -use crate::plonk_common::{eval_vanishing_poly, eval_zero_poly}; +use crate::plonk_common::{eval_vanishing_poly, eval_zero_poly, reduce_with_powers}; use crate::proof::Proof; use crate::vars::EvaluationVars; @@ -57,9 +58,13 @@ pub(crate) fn verify, const D: usize>( // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; - let z_h_zeta = eval_zero_poly(common_data.degree(), zeta); - for i in 0..num_challenges { - ensure!(vanishing_polys_zeta[i] == z_h_zeta * quotient_polys_zeta[i]); + let zeta_pow_deg = zeta.exp_power_of_2(common_data.degree_bits); + let z_h_zeta = zeta_pow_deg - F::Extension::ONE; + for (i, chunk) in quotient_polys_zeta + .chunks(common_data.quotient_degree_factor) + .enumerate() + { + ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); } let evaluations = proof.openings.clone(); From 0512817d6825d60ea4d7e423b9f311041a12f4a9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 8 Jul 2021 15:16:05 +0200 Subject: [PATCH 21/23] quotient_degree -> quotient_degree_factor --- src/circuit_builder.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 84973cd6..c9f35212 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -392,7 +392,7 @@ impl, const D: usize> CircuitBuilder { /// Builds a "full circuit", with both prover and verifier data. pub fn build(mut self) -> CircuitData { - let quotient_degree = 7; // TODO: add this as a parameter. + let quotient_degree_factor = 7; // TODO: add this as a parameter. let start = Instant::now(); info!( "Degree before blinding & padding: {}", @@ -405,7 +405,7 @@ impl, const D: usize> CircuitBuilder { let gates = self.gates.iter().cloned().collect(); let (gate_tree, max_filtered_constraint_degree, num_constants) = Tree::from_gates(gates); assert!( - max_filtered_constraint_degree <= quotient_degree + 1, + max_filtered_constraint_degree <= quotient_degree_factor + 1, "Constraints are too high degree." ); let prefixed_gates = PrefixedGate::from_tree(gate_tree); @@ -451,7 +451,7 @@ impl, const D: usize> CircuitBuilder { .expect("No gates?"); let num_partial_products = - num_partial_products(self.config.num_routed_wires, quotient_degree); + num_partial_products(self.config.num_routed_wires, quotient_degree_factor); // TODO: This should also include an encoding of gate constraints. let circuit_digest_parts = [ @@ -464,7 +464,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates: prefixed_gates, - quotient_degree_factor: quotient_degree, + quotient_degree_factor, num_gate_constraints, num_constants, k_is, From 9cd5f82090efe755d77f92e096a4269a5b8d5aed Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 8 Jul 2021 15:21:47 +0200 Subject: [PATCH 22/23] Add verification to scaling gadget --- src/util/scaling.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/scaling.rs b/src/util/scaling.rs index 3449e0b9..6effc5db 100644 --- a/src/util/scaling.rs +++ b/src/util/scaling.rs @@ -174,6 +174,7 @@ mod tests { use crate::circuit_data::CircuitConfig; use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::QuarticCrandallField; + use crate::verifier::verify; use crate::witness::PartialWitness; fn test_reduce_gadget(n: usize) { @@ -205,6 +206,8 @@ mod tests { let data = builder.build(); let proof = data.prove(PartialWitness::new()); + + verify(proof, &data.verifier_only, &data.common).unwrap(); } #[test] From 02f0715040fb99e8b0650cad58a172e8e1c906af Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 14 Jul 2021 08:39:09 +0200 Subject: [PATCH 23/23] PR feedback --- src/plonk_common.rs | 24 +++++++++++++++--------- src/verifier.rs | 5 +++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 5179b474..b1f29803 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -73,7 +73,7 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec { - let max_degree = common_data.quotient_degree_factor; + let partial_products_degree = common_data.quotient_degree_factor; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -113,12 +113,15 @@ pub(crate) fn eval_vanishing_poly, const D: usize>( // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; // Check the quotient partial products. - let mut partial_product_check = - check_partial_products("ient_values, current_partial_products, max_degree); + let mut partial_product_check = check_partial_products( + "ient_values, + current_partial_products, + partial_products_degree, + ); // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. denominator_values - .chunks(max_degree) + .chunks(partial_products_degree) .zip(partial_product_check.iter_mut()) .for_each(|(d, q)| { *q *= d.iter().copied().product(); @@ -160,7 +163,7 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( alphas: &[F], z_h_on_coset: &ZeroPolyOnCoset, ) -> Vec { - let max_degree = common_data.quotient_degree_factor; + let partial_products_degree = common_data.quotient_degree_factor; let (num_prods, final_num_prod) = common_data.num_partial_products; let constraint_terms = @@ -199,13 +202,16 @@ pub(crate) fn eval_vanishing_poly_base, const D: usize>( // The partial products considered for this iteration of `i`. let current_partial_products = &partial_products[i * num_prods..(i + 1) * num_prods]; - // Check the numerator partial products. - let mut partial_product_check = - check_partial_products("ient_values, current_partial_products, max_degree); + // Check the quotient partial products. + let mut partial_product_check = check_partial_products( + "ient_values, + current_partial_products, + partial_products_degree, + ); // The first checks are of the form `q - n/d` which is a rational function not a polynomial. // We multiply them by `d` to get checks of the form `q*d - n` which low-degree polynomials. denominator_values - .chunks(max_degree) + .chunks(partial_products_degree) .zip(partial_product_check.iter_mut()) .for_each(|(d, q)| { *q *= d.iter().copied().product(); diff --git a/src/verifier.rs b/src/verifier.rs index 64d4b371..d7c96365 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -60,6 +60,11 @@ pub(crate) fn verify, const D: usize>( let quotient_polys_zeta = &proof.openings.quotient_polys; let zeta_pow_deg = zeta.exp_power_of_2(common_data.degree_bits); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; + // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. + // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` + // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. + // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each + // `quotient_degree_factor`-sized chunk of the original evaluations. for (i, chunk) in quotient_polys_zeta .chunks(common_data.quotient_degree_factor) .enumerate()