From 35c4bd4dd96ea8325197436b6998d4c52d9ec1df Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 27 Aug 2020 13:27:24 -0600 Subject: [PATCH] Improve naming of offsets/indexes and mappings. --- src/plonk/circuit.rs | 30 +++++++---- src/plonk/domain.rs | 41 +++++++++++--- src/plonk/prover.rs | 121 +++++++++++++++++------------------------- src/plonk/verifier.rs | 76 ++++++++++++-------------- 4 files changed, 139 insertions(+), 129 deletions(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 5466260..d1923b6 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use super::Error; use crate::arithmetic::Field; +use super::domain::Rotation; /// This represents a wire which has a fixed (permanent) value #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct FixedWire(pub usize); @@ -135,6 +136,11 @@ impl Mul for Polynomial { } } +/// Represents an index into a vector where each entry corresponds to a distinct +/// point that polynomials are queried at. +#[derive(Copy, Clone, Debug)] +pub struct PointIndex(pub usize); + /// This is a description of the circuit environment, such as the gate, wire and /// permutation arrangements. #[derive(Debug, Clone)] @@ -143,15 +149,17 @@ pub struct MetaCircuit { pub(crate) num_advice_wires: usize, // permutations: Vec>, pub(crate) gates: Vec>, - pub(crate) advice_queries: Vec<(AdviceWire, i32)>, - pub(crate) fixed_queries: Vec<(FixedWire, i32)>, - pub(crate) query_rows: HashMap, + pub(crate) advice_queries: Vec<(AdviceWire, Rotation)>, + pub(crate) fixed_queries: Vec<(FixedWire, Rotation)>, + + // Mapping from a witness vector rotation to the index in the point vector. + pub(crate) rotations: HashMap, } impl Default for MetaCircuit { fn default() -> MetaCircuit { - let mut query_rows = HashMap::new(); - query_rows.insert(0, 0); + let mut rotations = HashMap::new(); + rotations.insert(Rotation::default(), PointIndex(0)); MetaCircuit { num_fixed_wires: 0, @@ -159,7 +167,7 @@ impl Default for MetaCircuit { gates: vec![], fixed_queries: Vec::new(), advice_queries: Vec::new(), - query_rows, + rotations, } } } @@ -167,9 +175,10 @@ impl Default for MetaCircuit { impl MetaCircuit { /// Query a fixed wire at a relative position pub fn query_fixed(&mut self, wire: FixedWire, at: i32) -> Polynomial { + let at = Rotation(at); { - let len = self.query_rows.len(); - self.query_rows.entry(at).or_insert(len); + let len = self.rotations.len(); + self.rotations.entry(at).or_insert(PointIndex(len)); } // TODO: check for existing query so we don't make redundant queries @@ -181,9 +190,10 @@ impl MetaCircuit { /// Query an advice wire at a relative position pub fn query_advice(&mut self, wire: AdviceWire, at: i32) -> Polynomial { + let at = Rotation(at); { - let len = self.query_rows.len(); - self.query_rows.entry(at).or_insert(len); + let len = self.rotations.len(); + self.rotations.entry(at).or_insert(PointIndex(len)); } // TODO: check for existing query so we don't make redundant queries diff --git a/src/plonk/domain.rs b/src/plonk/domain.rs index 07ac13a..6ebfb5c 100644 --- a/src/plonk/domain.rs +++ b/src/plonk/domain.rs @@ -1,5 +1,16 @@ use crate::arithmetic::{best_fft, parallelize, Field, Group}; +/// Describes a relative location in the evaluation domain; applying a rotation +/// by i will rotate the vector in the evaluation domain by i. +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct Rotation(pub i32); + +impl Default for Rotation { + fn default() -> Rotation { + Rotation(0) + } +} + /// This structure contains precomputed constants and other details needed for /// performing operations on an evaluation domain of size $2^k$ in the context /// of PLONK. @@ -119,22 +130,26 @@ impl EvaluationDomain { } /// This takes us from an n-length coefficient vector into the coset - /// evaluation domain. + /// evaluation domain, rotating by `rotation` if desired. /// /// This function will panic if the provided vector is not the correct /// length. - pub fn obtain_coset(&self, mut a: Vec, index: i32) -> Vec { + pub fn obtain_coset(&self, mut a: Vec, rotation: Rotation) -> Vec { assert_eq!(a.len(), 1 << self.k); - assert!(index != i32::MIN); - if index == 0 { + assert!(rotation.0 != i32::MIN); + if rotation.0 == 0 { + // In this special case, the powers of zeta repeat so we do not need + // to compute them. Self::distribute_powers_zeta(&mut a, self.g_coset); } else { let mut g = G::Scalar::ZETA; - if index > 0 { - g *= &self.omega.pow_vartime(&[index as u64, 0, 0, 0]); + if rotation.0 > 0 { + g *= &self.omega.pow_vartime(&[rotation.0 as u64, 0, 0, 0]); } else { - g *= &self.omega_inv.pow_vartime(&[index.abs() as u64, 0, 0, 0]); + g *= &self + .omega_inv + .pow_vartime(&[rotation.0.abs() as u64, 0, 0, 0]); } Self::distribute_powers(&mut a, g); } @@ -233,4 +248,16 @@ impl EvaluationDomain { pub fn get_omega_inv(&self) -> G::Scalar { self.omega_inv } + + pub fn rotate_omega(&self, constant: G::Scalar, rotation: Rotation) -> G::Scalar { + let mut point = constant; + if rotation.0 >= 0 { + point *= &self.get_omega().pow(&[rotation.0 as u64, 0, 0, 0]); + } else { + point *= &self + .get_omega_inv() + .pow(&[rotation.0.abs() as u64, 0, 0, 0]); + } + point + } } diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 95238e1..41310e3 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -1,5 +1,6 @@ use super::{ circuit::{AdviceWire, Circuit, ConstraintSystem, FixedWire, MetaCircuit}, + domain::Rotation, hash_point, Error, Proof, SRS, }; use crate::arithmetic::{ @@ -181,31 +182,13 @@ impl Proof { let advice_evals: Vec<_> = meta .advice_queries .iter() - .map(|&(wire, at)| { - let mut point = x_3; - if at >= 0 { - point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]); - } else { - point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]); - } - - eval_polynomial(&advice_polys[wire.0], point) - }) + .map(|&(wire, at)| eval_polynomial(&advice_polys[wire.0], domain.rotate_omega(x_3, at))) .collect(); let fixed_evals: Vec<_> = meta .fixed_queries .iter() - .map(|&(wire, at)| { - let mut point = x_3; - if at >= 0 { - point *= &domain.get_omega().pow(&[at as u64, 0, 0, 0]); - } else { - point *= &domain.get_omega_inv().pow(&[at.abs() as u64, 0, 0, 0]); - } - - eval_polynomial(&srs.fixed_polys[wire.0], point) - }) + .map(|&(wire, at)| eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at))) .collect(); let h_evals: Vec<_> = h_pieces @@ -240,49 +223,49 @@ impl Proof { // Collapse openings at same points together into single openings using // x_4 challenge. - let mut q_polys: Vec>> = vec![None; meta.query_rows.len()]; - let mut q_blinds = vec![C::Scalar::zero(); meta.query_rows.len()]; - let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.query_rows.len()]; + let mut q_polys: Vec>> = vec![None; meta.rotations.len()]; + let mut q_blinds = vec![C::Scalar::zero(); meta.rotations.len()]; + let mut q_evals: Vec<_> = vec![C::Scalar::zero(); meta.rotations.len()]; { - for (i, &(wire, ref at)) in meta.advice_queries.iter().enumerate() { - let query_row = *meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in meta.advice_queries.iter().enumerate() { + let point_index = (*meta.rotations.get(at).unwrap()).0; - if q_polys[query_row].is_none() { - q_polys[query_row] = Some(advice_polys[wire.0].clone()); - q_blinds[query_row] = advice_blinds[wire.0]; - q_evals[query_row] = advice_evals[i]; + if q_polys[point_index].is_none() { + q_polys[point_index] = Some(advice_polys[wire.0].clone()); + q_blinds[point_index] = advice_blinds[wire.0]; + q_evals[point_index] = advice_evals[query_index]; } else { - parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { + parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(advice_polys[wire.0][start..].iter()) { *q *= &x_4; *q += a; } }); - q_blinds[query_row] *= &x_4; - q_blinds[query_row] += &advice_blinds[wire.0]; - q_evals[query_row] *= &x_4; - q_evals[query_row] += &advice_evals[i]; + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += &advice_blinds[wire.0]; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &advice_evals[query_index]; } } - for (i, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() { - let query_row = *meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in meta.fixed_queries.iter().enumerate() { + let point_index = (*meta.rotations.get(at).unwrap()).0; - if q_polys[query_row].is_none() { - q_polys[query_row] = Some(srs.fixed_polys[wire.0].clone()); - q_blinds[query_row] = C::Scalar::one(); - q_evals[query_row] = fixed_evals[i]; + if q_polys[point_index].is_none() { + q_polys[point_index] = Some(srs.fixed_polys[wire.0].clone()); + q_blinds[point_index] = C::Scalar::one(); + q_evals[point_index] = fixed_evals[query_index]; } else { - parallelize(q_polys[query_row].as_mut().unwrap(), |q, start| { + parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(srs.fixed_polys[wire.0][start..].iter()) { *q *= &x_4; *q += a; } }); - q_blinds[query_row] *= &x_4; - q_blinds[query_row] += &C::Scalar::one(); - q_evals[query_row] *= &x_4; - q_evals[query_row] += &fixed_evals[i]; + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += &C::Scalar::one(); + q_evals[point_index] *= &x_4; + q_evals[point_index] += &fixed_evals[query_index]; } } @@ -292,23 +275,23 @@ impl Proof { .zip(h_evals.iter()) { // We query the h(X) polynomial at x_3 - let cur_row = *meta.query_rows.get(&0).unwrap(); + let point_index = (*meta.rotations.get(&Rotation::default()).unwrap()).0; - if q_polys[cur_row].is_none() { - q_polys[cur_row] = Some(h_poly); - q_blinds[cur_row] = *h_blind; - q_evals[cur_row] = *h_eval; + if q_polys[point_index].is_none() { + q_polys[point_index] = Some(h_poly); + q_blinds[point_index] = *h_blind; + q_evals[point_index] = *h_eval; } else { - parallelize(q_polys[cur_row].as_mut().unwrap(), |q, start| { + parallelize(q_polys[point_index].as_mut().unwrap(), |q, start| { for (q, a) in q.iter_mut().zip(h_poly[start..].iter()) { *q *= &x_4; *q += a; } }); - q_blinds[cur_row] *= &x_4; - q_blinds[cur_row] += h_blind; - q_evals[cur_row] *= &x_4; - q_evals[cur_row] += h_eval; + q_blinds[point_index] *= &x_4; + q_blinds[point_index] += h_blind; + q_evals[point_index] *= &x_4; + q_evals[point_index] += h_eval; } } } @@ -316,17 +299,10 @@ impl Proof { let x_5: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); let mut f_poly = None; - for (&row, &col) in meta.query_rows.iter() { - let mut poly = q_polys[col].as_ref().unwrap().clone(); - let mut point = x_3; - if row >= 0 { - point *= &domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]); - } else { - point *= &domain - .get_omega_inv() - .pow_vartime(&[row.abs() as u64, 0, 0, 0]); - } - poly[0] -= &q_evals[col]; + for (&row, &point_index) in meta.rotations.iter() { + let mut poly = q_polys[point_index.0].as_ref().unwrap().clone(); + let point = domain.rotate_omega(x_3, row); + poly[0] -= &q_evals[point_index.0]; let mut poly = kate_division(&poly, point); poly.push(C::Scalar::zero()); @@ -352,8 +328,11 @@ impl Proof { let mut q_evals = vec![]; - for (_, &col) in meta.query_rows.iter() { - q_evals.push(eval_polynomial(&q_polys[col].as_ref().unwrap(), x_6)); + for (_, &point_index) in meta.rotations.iter() { + q_evals.push(eval_polynomial( + &q_polys[point_index.0].as_ref().unwrap(), + x_6, + )); } for eval in q_evals.iter() { @@ -366,14 +345,14 @@ impl Proof { let x_7: C::Scalar = get_challenge_scalar(Challenge(transcript.squeeze().get_lower_128())); - for (_, &col) in meta.query_rows.iter() { + for (_, &point_index) in meta.rotations.iter() { f_blind *= &x_7; - f_blind += &q_blinds[col]; + f_blind += &q_blinds[point_index.0]; parallelize(&mut f_poly, |f, start| { for (f, a) in f .iter_mut() - .zip(q_polys[col].as_ref().unwrap()[start..].iter()) + .zip(q_polys[point_index.0].as_ref().unwrap()[start..].iter()) { *f *= &x_7; *f += a; diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index cc28d7d..fbffbc1 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,4 +1,4 @@ -use super::{hash_point, Proof, SRS}; +use super::{domain::Rotation, hash_point, Proof, SRS}; use crate::arithmetic::{get_challenge_scalar, Challenge, Curve, CurveAffine, Field}; use crate::polycommit::Params; use crate::transcript::Hasher; @@ -89,56 +89,57 @@ impl Proof { // Compress the commitments and expected evaluations at x_3 together // using the challenge x_4 - let mut q_commitments: Vec<_> = vec![None; srs.meta.query_rows.len()]; - let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.query_rows.len()]; + let mut q_commitments: Vec<_> = vec![None; srs.meta.rotations.len()]; + let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.meta.rotations.len()]; { - for (i, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { - let query_row = *srs.meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in srs.meta.advice_queries.iter().enumerate() { + let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - if q_commitments[query_row].is_none() { - q_commitments[query_row] = + if q_commitments[point_index].is_none() { + q_commitments[point_index] = Some(self.advice_commitments[wire.0].to_projective()); - q_evals[query_row] = self.advice_evals[i]; + q_evals[point_index] = self.advice_evals[query_index]; } else { - q_commitments[query_row].as_mut().map(|commitment| { + q_commitments[point_index].as_mut().map(|commitment| { *commitment *= x_4; *commitment += self.advice_commitments[wire.0]; }); - q_evals[query_row] *= &x_4; - q_evals[query_row] += &self.advice_evals[i]; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &self.advice_evals[query_index]; } } - for (i, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { - let query_row = *srs.meta.query_rows.get(at).unwrap(); + for (query_index, &(wire, ref at)) in srs.meta.fixed_queries.iter().enumerate() { + let point_index = (*srs.meta.rotations.get(at).unwrap()).0; - if q_commitments[query_row].is_none() { - q_commitments[query_row] = Some(srs.fixed_commitments[wire.0].to_projective()); - q_evals[query_row] = self.fixed_evals[i]; + if q_commitments[point_index].is_none() { + q_commitments[point_index] = + Some(srs.fixed_commitments[wire.0].to_projective()); + q_evals[point_index] = self.fixed_evals[query_index]; } else { - q_commitments[query_row].as_mut().map(|commitment| { + q_commitments[point_index].as_mut().map(|commitment| { *commitment *= x_4; *commitment += srs.fixed_commitments[wire.0]; }); - q_evals[query_row] *= &x_4; - q_evals[query_row] += &self.fixed_evals[i]; + q_evals[point_index] *= &x_4; + q_evals[point_index] += &self.fixed_evals[query_index]; } } for (h_commitment, h_eval) in self.h_commitments.iter().zip(self.h_evals.iter()) { // We query the h(X) polynomial at x_3 - let cur_row = *srs.meta.query_rows.get(&0).unwrap(); + let point_index = (*srs.meta.rotations.get(&Rotation::default()).unwrap()).0; - if q_commitments[cur_row].is_none() { - q_commitments[cur_row] = Some(h_commitment.to_projective()); - q_evals[cur_row] = *h_eval; + if q_commitments[point_index].is_none() { + q_commitments[point_index] = Some(h_commitment.to_projective()); + q_evals[point_index] = *h_eval; } else { - q_commitments[cur_row].as_mut().map(|commitment| { + q_commitments[point_index].as_mut().map(|commitment| { *commitment *= x_4; *commitment += *h_commitment; }); - q_evals[cur_row] *= &x_4; - q_evals[cur_row] += h_eval; + q_evals[point_index] *= &x_4; + q_evals[point_index] += h_eval; } } } @@ -166,18 +167,11 @@ impl Proof { // We can compute the expected f_eval at x_6 using the q_evals provided // by the prover and from x_5 let mut f_eval = C::Scalar::zero(); - for (&row, &col) in srs.meta.query_rows.iter() { - let mut eval: C::Scalar = self.q_evals[col].clone(); - let mut point = x_3; - if row >= 0 { - point *= &srs.domain.get_omega().pow_vartime(&[row as u64, 0, 0, 0]); - } else { - point *= &srs - .domain - .get_omega_inv() - .pow_vartime(&[row.abs() as u64, 0, 0, 0]); - } - eval = eval - &q_evals[col]; + for (&row, &point_index) in srs.meta.rotations.iter() { + let mut eval: C::Scalar = self.q_evals[point_index.0].clone(); + + let point = srs.domain.rotate_omega(x_3, row); + eval = eval - &q_evals[point_index.0]; eval = eval * &(x_6 - &point).invert().unwrap(); f_eval *= &x_5; @@ -190,11 +184,11 @@ impl Proof { // Compute the final commitment that has to be opened let mut f_commitment: C::Projective = self.f_commitment.to_projective(); - for (_, &col) in srs.meta.query_rows.iter() { + for (_, &point_index) in srs.meta.rotations.iter() { f_commitment *= x_7; - f_commitment = f_commitment + &q_commitments[col].as_ref().unwrap(); + f_commitment = f_commitment + &q_commitments[point_index.0].as_ref().unwrap(); f_eval *= &x_7; - f_eval += &self.q_evals[col]; + f_eval += &self.q_evals[point_index.0]; } // Verify the opening proof