From c44a020de75c0f4ab04c0727d5a27a27abbb85f6 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Sep 2020 00:45:03 +0800 Subject: [PATCH] Permutation checks in verifier --- src/plonk.rs | 3 +- src/plonk/circuit.rs | 14 ++++++++ src/plonk/prover.rs | 3 +- src/plonk/verifier.rs | 78 ++++++++++++++++++++++++++----------------- 4 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index adaf412e..86b173d7 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -47,8 +47,7 @@ pub struct Proof { permutation_product_commitments: Vec, permutation_product_evals: Vec, permutation_product_inv_evals: Vec, - permutation_evals: Vec, - advice_shifted_evals: Vec>>, + permutation_evals: Vec>, advice_evals: Vec, fixed_evals: Vec, h_evals: Vec, diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 26aca04e..dae4e070 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -169,6 +169,7 @@ pub struct MetaCircuit { // another permutation between wires (B, C, D) which allows the same with D // instead of A. pub(crate) permutations: Vec>, + pub(crate) permutation_queries: Vec>>, } impl Default for MetaCircuit { @@ -184,6 +185,7 @@ impl Default for MetaCircuit { advice_queries: Vec::new(), rotations, permutations: Vec::new(), + permutation_queries: Vec::new(), } } } @@ -192,7 +194,19 @@ impl MetaCircuit { /// Add a permutation argument for some advice wires pub fn permutation(&mut self, wires: &[AdviceWire]) -> usize { let index = self.permutations.len(); + if index == 0 { + // no permutations + let point_idx = self.rotations.len(); + self.rotations.insert(Rotation(-1), PointIndex(point_idx)); + } self.permutations.push(wires.to_vec()); + + let mut queries = vec![]; + for wire in wires { + queries.push(self.query_advice(*wire, 0)); + } + self.permutation_queries.push(queries); + index } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index dd1ba5c4..d15d567b 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -405,8 +405,7 @@ impl Proof { permutation_product_commitments: vec![C::default(); params.n as usize], permutation_product_evals: vec![C::Scalar::one(); params.n as usize], permutation_product_inv_evals: vec![C::Scalar::one(); params.n as usize], - permutation_evals: vec![C::Scalar::one(); params.n as usize], - advice_shifted_evals, + permutation_evals: vec![vec![C::Scalar::one(); params.n as usize]], advice_evals, fixed_evals, h_evals, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 635e180d..a8347b5e 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -36,36 +36,6 @@ impl Proof { } } - // For each permutation - for perm_idx in 0..srs.meta.permutations.len() { - // For each X in evaluation domain - for point_idx in 0..params.n as usize { - let point = omega_powers[point_idx]; - - let mut left_perm_eval = self.permutation_product_inv_evals[point_idx]; - let mut right_perm_eval = self.permutation_product_evals[point_idx]; - let mut cur_delta = C::Scalar::one(); - - for wire_idx in 0..srs.meta.permutations[perm_idx].len() { - // z(\omega^{-1} X) (a(X) + \beta X + \gamma) (b(X) + \delta \beta X + \gamma) (c(X) + \delta^2 \beta X + \gamma) - let left_tmp = &(self.advice_shifted_evals[perm_idx][wire_idx][point_idx] - + &(x_0 * &(cur_delta * &point))); - left_perm_eval *= &left_tmp; - - cur_delta *= &C::Scalar::DELTA; - - // z(X) (a(X) + \beta s_a(X) + \gamma) (b(X) + \beta s_b(X) + \gamma) (c(X) + \beta s_c(X) + \gamma) - let perm_eval = srs.permutation_polys[perm_idx][wire_idx][point_idx]; - let right_tmp = &(self.advice_shifted_evals[perm_idx][wire_idx][point_idx] - + &(x_0 * &perm_eval)); - right_perm_eval *= &right_tmp; - } - if left_perm_eval != right_perm_eval { - return false; - } - } - } - // Sample x_2 challenge, which keeps the gates linearly independent. let x_2: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); @@ -77,6 +47,7 @@ impl Proof { // Sample x_3 challenge, which is used to ensure the circuit is // satisfied with high probability. let x_3: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); + let xn = x_3.pow(&[params.n as u64, 0, 0, 0]); // Hash together all the openings provided by the prover into a new // transcript on the scalar field. @@ -110,7 +81,52 @@ impl Proof { h_eval += &evaluation; } - let xn = x_3.pow(&[params.n as u64, 0, 0, 0]); + + // Evaluate permutation polynomial at first point + // l_0(X) * (1 - z(X)) = 0 + for eval in self.permutation_product_evals.iter() { + h_eval *= &x_2; + + let mut l0_eval = (C::Scalar::from_u64(params.n) * &(xn * &x_3 - &C::Scalar::one())) + * &(x_3 - &C::Scalar::one()).invert().unwrap(); + l0_eval *= &(C::Scalar::one() - &eval); + + h_eval += &l0_eval; + } + + // Evaluate permutation polynomial at subsequent points + for (perm_idx, queries) in srs.meta.permutation_queries.iter().enumerate() { + h_eval *= &x_2; + + // queries is a vector of polynomials + let evals: Vec = queries + .iter() + .map(|poly| { + poly.evaluate( + &|index| self.fixed_evals[index], + &|index| self.advice_evals[index], + &|a, b| a + &b, + &|a, b| a * &b, + &|a, scalar| a * &scalar, + ) + }) + .collect(); + + let mut left = self.permutation_product_inv_evals[perm_idx]; + let mut cur_delta = x_0 * &x_3; + for eval in evals.iter() { + left *= &(*eval + &cur_delta + &x_1); + cur_delta *= &C::Scalar::DELTA; + } + + let mut right = self.permutation_product_evals[perm_idx]; + for (perm_eval, eval) in self.permutation_evals[perm_idx].iter().zip(evals.iter()) { + right *= &(*eval + &(x_0 * perm_eval) + &x_1); + } + + h_eval += &left; + h_eval -= &right; + } // Compute the expected h(x) value let mut expected_h_eval = C::Scalar::zero();