mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 23:03:08 +00:00
Merge pull request #86 from mir-protocol/fix_z_check
Fix high degree `z` check by using partial products
This commit is contained in:
commit
c24fe65f44
@ -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;
|
||||
|
||||
@ -391,6 +392,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
/// Builds a "full circuit", with both prover and verifier data.
|
||||
pub fn build(mut self) -> CircuitData<F, D> {
|
||||
let quotient_degree_factor = 7; // TODO: add this as a parameter.
|
||||
let start = Instant::now();
|
||||
info!(
|
||||
"Degree before blinding & padding: {}",
|
||||
@ -402,6 +404,10 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
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_factor + 1,
|
||||
"Constraints are too high degree."
|
||||
);
|
||||
let prefixed_gates = PrefixedGate::from_tree(gate_tree);
|
||||
|
||||
let degree_bits = log2_strict(degree);
|
||||
@ -444,6 +450,9 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
.max()
|
||||
.expect("No gates?");
|
||||
|
||||
let num_partial_products =
|
||||
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 = [
|
||||
constants_sigmas_root.elements.to_vec(),
|
||||
@ -455,10 +464,11 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
config: self.config,
|
||||
degree_bits,
|
||||
gates: prefixed_gates,
|
||||
max_filtered_constraint_degree,
|
||||
quotient_degree_factor,
|
||||
num_gate_constraints,
|
||||
num_constants,
|
||||
k_is,
|
||||
num_partial_products,
|
||||
circuit_digest,
|
||||
};
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::ops::Range;
|
||||
use std::ops::{Range, RangeFrom};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@ -145,8 +145,8 @@ pub struct CommonCircuitData<F: Extendable<D>, const D: usize> {
|
||||
/// The types of gates used in this circuit, along with their prefixes.
|
||||
pub(crate) gates: Vec<PrefixedGate<F, D>>,
|
||||
|
||||
/// 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,
|
||||
@ -157,6 +157,10 @@ pub struct CommonCircuitData<F: Extendable<D>, const D: usize> {
|
||||
/// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument.
|
||||
pub(crate) k_is: Vec<F>,
|
||||
|
||||
/// 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),
|
||||
|
||||
/// 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<F>,
|
||||
@ -184,7 +188,7 @@ impl<F: Extendable<D>, const D: usize> CommonCircuitData<F, D> {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -201,6 +205,16 @@ impl<F: Extendable<D>, const D: usize> CommonCircuitData<F, D> {
|
||||
pub fn sigmas_range(&self) -> Range<usize> {
|
||||
self.num_constants..self.num_constants + self.config.num_routed_wires
|
||||
}
|
||||
|
||||
/// Range of the `z`s polynomials in the `zs_partial_products_commitment`.
|
||||
pub fn zs_range(&self) -> Range<usize> {
|
||||
0..self.config.num_challenges
|
||||
}
|
||||
|
||||
/// Range of the partial products polynomials in the `zs_partial_products_commitment`.
|
||||
pub fn partial_products_range(&self) -> RangeFrom<usize> {
|
||||
self.config.num_challenges..
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Target` version of `VerifierCircuitData`, for use inside recursive circuits. Note that this
|
||||
|
||||
@ -183,7 +183,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
|
||||
// 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::<Vec<_>>();
|
||||
|
||||
@ -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<F: Field + Extendable<D>, const D: usize>(
|
||||
initial_merkle_roots: &[Hash<F>],
|
||||
proof: &FriProof<F, D>,
|
||||
challenger: &mut Challenger<F>,
|
||||
config: &FriConfig,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Result<()> {
|
||||
let config = &common_data.config.fri_config;
|
||||
let total_arities = config.reduction_arity_bits.iter().sum::<usize>();
|
||||
ensure!(
|
||||
purported_degree_log
|
||||
@ -122,7 +124,7 @@ pub fn verify_fri_proof<F: Field + Extendable<D>, const D: usize>(
|
||||
n,
|
||||
&betas,
|
||||
round_proof,
|
||||
config,
|
||||
common_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
@ -147,8 +149,9 @@ fn fri_combine_initial<F: Field + Extendable<D>, const D: usize>(
|
||||
os: &OpeningSet<F, D>,
|
||||
zeta: F::Extension,
|
||||
subgroup_x: F,
|
||||
config: &FriConfig,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> 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<F: Field + Extendable<D>, 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)
|
||||
@ -185,9 +193,10 @@ fn fri_combine_initial<F: Field + Extendable<D>, 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));
|
||||
.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<F: Field + Extendable<D>, const D: usize>(
|
||||
n: usize,
|
||||
betas: &[F::Extension],
|
||||
round_proof: &FriQueryRound<F, D>,
|
||||
config: &FriConfig,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Result<()> {
|
||||
let config = &common_data.config.fri_config;
|
||||
let mut evaluations: Vec<Vec<F::Extension>> = Vec::new();
|
||||
let x = challenger.get_challenge();
|
||||
let mut domain_size = n;
|
||||
@ -262,7 +272,7 @@ fn fri_verifier_query_round<F: Field + Extendable<D>, const D: usize>(
|
||||
os,
|
||||
zeta,
|
||||
subgroup_x,
|
||||
config,
|
||||
common_data,
|
||||
)
|
||||
} else {
|
||||
let last_evals = &evaluations[i - 1];
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +79,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<const D: usize>(
|
||||
@ -116,6 +117,8 @@ mod tests {
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(PartialWitness::new());
|
||||
|
||||
verify(proof, &data.verifier_only, &data.common).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<const D: usize>(
|
||||
@ -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]
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ impl<F: Extendable<D>, const D: usize> Tree<GateRef<F, D>> {
|
||||
// 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;
|
||||
|
||||
@ -72,6 +72,7 @@ impl<F: Field> Challenger<F> {
|
||||
wires,
|
||||
plonk_zs,
|
||||
plonk_zs_right,
|
||||
partial_products,
|
||||
quotient_polys,
|
||||
} = os;
|
||||
for v in &[
|
||||
@ -80,6 +81,7 @@ impl<F: Field> Challenger<F> {
|
||||
wires,
|
||||
plonk_zs,
|
||||
plonk_zs_right,
|
||||
partial_products,
|
||||
quotient_polys,
|
||||
] {
|
||||
self.observe_extension_elements(v);
|
||||
|
||||
@ -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::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.
|
||||
@ -37,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,
|
||||
};
|
||||
@ -50,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."),
|
||||
}
|
||||
@ -64,41 +65,80 @@ pub(crate) fn eval_vanishing_poly<F: Extendable<D>, const D: usize>(
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
x: F::Extension,
|
||||
vars: EvaluationVars<F, D>,
|
||||
local_plonk_zs: &[F::Extension],
|
||||
next_plonk_zs: &[F::Extension],
|
||||
local_zs: &[F::Extension],
|
||||
next_zs: &[F::Extension],
|
||||
partial_products: &[F::Extension],
|
||||
s_sigmas: &[F::Extension],
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
alphas: &[F],
|
||||
) -> Vec<F::Extension> {
|
||||
let partial_products_degree = common_data.quotient_degree_factor;
|
||||
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);
|
||||
|
||||
// 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();
|
||||
}
|
||||
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 = x * k_i.into();
|
||||
wire_value + s_id * betas[i].into() + gammas[i].into()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
let quotient_values = (0..common_data.config.num_routed_wires)
|
||||
.map(|j| numerator_values[j] / denominator_values[j])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// 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,
|
||||
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(partial_products_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 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()
|
||||
.product();
|
||||
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,
|
||||
constraint_terms,
|
||||
]
|
||||
@ -114,42 +154,80 @@ pub(crate) fn eval_vanishing_poly_base<F: Extendable<D>, const D: usize>(
|
||||
index: usize,
|
||||
x: F,
|
||||
vars: EvaluationVarsBase<F>,
|
||||
local_plonk_zs: &[F],
|
||||
next_plonk_zs: &[F],
|
||||
local_zs: &[F],
|
||||
next_zs: &[F],
|
||||
partial_products: &[F],
|
||||
s_sigmas: &[F],
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
alphas: &[F],
|
||||
z_h_on_coset: &ZeroPolyOnCoset<F>,
|
||||
) -> Vec<F> {
|
||||
let partial_products_degree = common_data.quotient_degree_factor;
|
||||
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);
|
||||
|
||||
// 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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
let quotient_values = (0..common_data.config.num_routed_wires)
|
||||
.map(|j| numerator_values[j] / denominator_values[j])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// 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,
|
||||
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(partial_products_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 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()
|
||||
.product();
|
||||
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,
|
||||
constraint_terms,
|
||||
]
|
||||
|
||||
@ -147,6 +147,13 @@ impl<F: Field> ListPolynomialCommitment<F> {
|
||||
// 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::<Vec<_>>();
|
||||
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<F: Field> ListPolynomialCommitment<F> {
|
||||
]
|
||||
.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<F: Field> ListPolynomialCommitment<F> {
|
||||
alpha.reset();
|
||||
|
||||
// Zs polynomials are opened at `zeta` and `g*zeta`.
|
||||
let zs_polys = commitments[PlonkPolynomials::ZS.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<F: Field + Extendable<D>, const D: usize> OpeningProof<F, D> {
|
||||
os: &OpeningSet<F, D>,
|
||||
merkle_roots: &[Hash<F>],
|
||||
challenger: &mut Challenger<F>,
|
||||
fri_config: &FriConfig,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Result<()> {
|
||||
challenger.observe_opening_set(os);
|
||||
|
||||
@ -268,7 +272,7 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningProof<F, D> {
|
||||
merkle_roots,
|
||||
&self.fri_proof,
|
||||
challenger,
|
||||
fri_config,
|
||||
common_data,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -310,7 +314,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn check_batch_polynomial_commitment<F: Field + Extendable<D>, 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,
|
||||
@ -327,10 +331,11 @@ 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],
|
||||
num_partial_products: (0, 0),
|
||||
circuit_digest: Hash::from_partial(vec![]),
|
||||
};
|
||||
|
||||
@ -362,7 +367,7 @@ mod tests {
|
||||
lpcs[3].merkle_tree.root,
|
||||
],
|
||||
&mut Challenger::new(),
|
||||
&common_data.config.fri_config,
|
||||
&common_data,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
12
src/proof.rs
12
src/proof.rs
@ -154,6 +154,7 @@ pub struct OpeningSet<F: Field + Extendable<D>, const D: usize> {
|
||||
pub wires: Vec<F::Extension>,
|
||||
pub plonk_zs: Vec<F::Extension>,
|
||||
pub plonk_zs_right: Vec<F::Extension>,
|
||||
pub partial_products: Vec<F::Extension>,
|
||||
pub quotient_polys: Vec<F::Extension>,
|
||||
}
|
||||
|
||||
@ -163,7 +164,7 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||
g: F::Extension,
|
||||
constants_sigmas_commitment: &ListPolynomialCommitment<F>,
|
||||
wires_commitment: &ListPolynomialCommitment<F>,
|
||||
plonk_zs_commitment: &ListPolynomialCommitment<F>,
|
||||
zs_partial_products_commitment: &ListPolynomialCommitment<F>,
|
||||
quotient_polys_commitment: &ListPolynomialCommitment<F>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Self {
|
||||
@ -174,12 +175,17 @@ impl<F: Field + Extendable<D>, const D: usize> OpeningSet<F, D> {
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
157
src/prover.rs
157
src/prover.rs
@ -12,6 +12,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::{log2_ceil, transpose};
|
||||
use crate::vars::EvaluationVarsBase;
|
||||
use crate::witness::{PartialWitness, Witness};
|
||||
@ -78,21 +79,34 @@ pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
let betas = challenger.get_n_challenges(num_challenges);
|
||||
let gammas = challenger.get_n_challenges(num_challenges);
|
||||
|
||||
let plonk_z_vecs = timed!(
|
||||
compute_zs(&witness, &betas, &gammas, prover_data, common_data),
|
||||
"to compute Z's"
|
||||
assert!(
|
||||
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!(
|
||||
all_wires_permutation_partial_products(&witness, &betas, &gammas, prover_data, common_data),
|
||||
"to compute partial products"
|
||||
);
|
||||
|
||||
let plonk_zs_commitment = timed!(
|
||||
let plonk_z_vecs = timed!(compute_zs(&partial_products, common_data), "to compute Z's");
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
let zs_partial_products = [plonk_z_vecs, partial_products.concat()].concat();
|
||||
let zs_partial_products_commitment = timed!(
|
||||
ListPolynomialCommitment::new(
|
||||
plonk_z_vecs,
|
||||
zs_partial_products,
|
||||
fri_config.rate_bits,
|
||||
PlonkPolynomials::ZS.blinding
|
||||
PlonkPolynomials::ZS_PARTIAL_PRODUCTS.blinding
|
||||
),
|
||||
"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);
|
||||
|
||||
@ -101,7 +115,7 @@ pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
common_data,
|
||||
prover_data,
|
||||
&wires_commitment,
|
||||
&plonk_zs_commitment,
|
||||
&zs_partial_products_commitment,
|
||||
&betas,
|
||||
&gammas,
|
||||
&alphas,
|
||||
@ -116,7 +130,7 @@ pub(crate) fn prove<F: Extendable<D>, 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.
|
||||
@ -144,7 +158,7 @@ pub(crate) fn prove<F: Extendable<D>, const D: usize>(
|
||||
&[
|
||||
&prover_data.constants_sigmas_commitment,
|
||||
&wires_commitment,
|
||||
&plonk_zs_commitment,
|
||||
&zs_partial_products_commitment,
|
||||
"ient_polys_commitment,
|
||||
],
|
||||
zeta,
|
||||
@ -161,50 +175,103 @@ pub(crate) fn prove<F: Extendable<D>, 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,
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_zs<F: Extendable<D>, const D: usize>(
|
||||
/// Compute the partial products used in the `Z` polynomials.
|
||||
fn all_wires_permutation_partial_products<F: Extendable<D>, const D: usize>(
|
||||
witness: &Witness<F>,
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
) -> Vec<Vec<PolynomialValues<F>>> {
|
||||
(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<F: Extendable<D>, const D: usize>(
|
||||
/// Compute the partial products used in the `Z` polynomial.
|
||||
/// 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<F: Extendable<D>, const D: usize>(
|
||||
witness: &Witness<F>,
|
||||
beta: F,
|
||||
gamma: F,
|
||||
prover_data: &ProverOnlyCircuitData<F, D>,
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> PolynomialValues<F> {
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
let degree = common_data.quotient_degree_factor;
|
||||
let subgroup = &prover_data.subgroup;
|
||||
let mut plonk_z_points = vec![F::ONE];
|
||||
let k_is = &common_data.k_is;
|
||||
let values = subgroup
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.map(|(i, &x)| {
|
||||
let s_sigmas = &prover_data.sigmas[i];
|
||||
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;
|
||||
let s_sigma = s_sigmas[j];
|
||||
let numerator = wire_value + beta * s_id + gamma;
|
||||
let denominator = wire_value + beta * s_sigma + gamma;
|
||||
numerator / denominator
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let quotient_partials = partial_products("ient_values, degree);
|
||||
|
||||
// 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 quotient at the beginning of the vector to reuse them later in the computation of `Z`.
|
||||
[vec![quotient], quotient_partials].concat()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
transpose(&values)
|
||||
.into_par_iter()
|
||||
.map(PolynomialValues::new)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn compute_zs<F: Extendable<D>, const D: usize>(
|
||||
partial_products: &[Vec<PolynomialValues<F>>],
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Vec<PolynomialValues<F>> {
|
||||
(0..common_data.config.num_challenges)
|
||||
.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<F: Extendable<D>, const D: usize>(
|
||||
partial_products: &[PolynomialValues<F>],
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> PolynomialValues<F> {
|
||||
let mut plonk_z_points = vec![F::ONE];
|
||||
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 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()
|
||||
}
|
||||
@ -213,28 +280,27 @@ fn compute_quotient_polys<'a, F: Extendable<D>, const D: usize>(
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
prover_data: &'a ProverOnlyCircuitData<F, D>,
|
||||
wires_commitment: &'a ListPolynomialCommitment<F>,
|
||||
plonk_zs_commitment: &'a ListPolynomialCommitment<F>,
|
||||
zs_partial_products_commitment: &'a ListPolynomialCommitment<F>,
|
||||
betas: &[F],
|
||||
gammas: &[F],
|
||||
alphas: &[F],
|
||||
) -> Vec<PolynomialCoeffs<F>> {
|
||||
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`.
|
||||
@ -242,8 +308,7 @@ fn compute_quotient_polys<'a, F: Extendable<D>, 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<Vec<F>> = points
|
||||
.into_par_iter()
|
||||
@ -255,11 +320,14 @@ fn compute_quotient_polys<'a, F: Extendable<D>, 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 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,
|
||||
@ -270,8 +338,9 @@ fn compute_quotient_polys<'a, F: Extendable<D>, const D: usize>(
|
||||
i,
|
||||
shifted_x,
|
||||
vars,
|
||||
local_plonk_zs,
|
||||
next_plonk_zs,
|
||||
local_zs,
|
||||
next_zs,
|
||||
partial_products,
|
||||
s_sigmas,
|
||||
betas,
|
||||
gammas,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod partial_products;
|
||||
pub mod scaling;
|
||||
pub(crate) mod timing;
|
||||
|
||||
|
||||
95
src/util/partial_products.rs
Normal file
95
src/util/partial_products.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use std::iter::Product;
|
||||
use std::ops::Sub;
|
||||
|
||||
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.
|
||||
pub fn partial_products<T: Product + Copy>(v: &[T], max_degree: usize) -> Vec<T> {
|
||||
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::<Vec<_>>();
|
||||
res.extend_from_slice(&new_partials);
|
||||
remainder = new_partials;
|
||||
}
|
||||
|
||||
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) {
|
||||
debug_assert!(max_degree > 1);
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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<T: Product + Copy + Sub<Output = T>>(
|
||||
v: &[T],
|
||||
partials: &[T],
|
||||
max_degree: usize,
|
||||
) -> Vec<T> {
|
||||
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::<Vec<T>>();
|
||||
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() {
|
||||
let v = vec![1, 2, 3, 4, 5, 6];
|
||||
let p = partial_products(&v, 2);
|
||||
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)
|
||||
.iter()
|
||||
.all(|x| x.is_zero()));
|
||||
assert_eq!(
|
||||
v.into_iter().product::<i32>(),
|
||||
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]);
|
||||
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::<i32>(),
|
||||
p[p.len() - nums.1..].iter().copied().product(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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]
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -13,7 +14,6 @@ pub(crate) fn verify<F: Extendable<D>, const D: usize>(
|
||||
common_data: &CommonCircuitData<F, D>,
|
||||
) -> Result<()> {
|
||||
let config = &common_data.config;
|
||||
let fri_config = &config.fri_config;
|
||||
let num_challenges = config.num_challenges;
|
||||
|
||||
let mut challenger = Challenger::new();
|
||||
@ -37,17 +37,19 @@ pub(crate) fn verify<F: Extendable<D>, 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 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_zs,
|
||||
next_zs,
|
||||
partial_products,
|
||||
s_sigmas,
|
||||
&betas,
|
||||
&gammas,
|
||||
@ -56,9 +58,18 @@ pub(crate) fn verify<F: Extendable<D>, 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;
|
||||
// `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()
|
||||
{
|
||||
ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg));
|
||||
}
|
||||
|
||||
let evaluations = proof.openings.clone();
|
||||
@ -75,7 +86,7 @@ pub(crate) fn verify<F: Extendable<D>, const D: usize>(
|
||||
&evaluations,
|
||||
merkle_roots,
|
||||
&mut challenger,
|
||||
fri_config,
|
||||
common_data,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user