From 55424c8c2f574d935f06448d4aac96237486bb79 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 12 Jul 2021 11:44:09 -0600 Subject: [PATCH 1/7] Add `{mul,add,sub}_extended` and `rotate_extended` methods to `EvaluationDomain`. --- src/poly/domain.rs | 110 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 5 deletions(-) diff --git a/src/poly/domain.rs b/src/poly/domain.rs index 6c89f09a..61707384 100644 --- a/src/poly/domain.rs +++ b/src/poly/domain.rs @@ -247,7 +247,7 @@ impl EvaluationDomain { 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.values); + Self::distribute_powers_zeta(&mut a.values, true); } else { let mut g = self.g_coset; if rotation.0 > 0 { @@ -268,6 +268,102 @@ impl EvaluationDomain { } } + fn op_extended( + &self, + mut left: Polynomial, + right: &Polynomial, + rotation: Rotation, + op: impl Fn(&mut G, &G) + Send + Sync + 'static, + ) -> Polynomial + where + G: Field, + { + // Rotation while in the extended domain is simply exaggerated by how + // much larger the extended domain is compared to the original domain. + let rotation = (1 << (self.extended_k - self.k)) * rotation.0; + + parallelize(&mut left.values, |lhs, start| { + let start = ((((start + self.extended_len()) as i32) + rotation) as usize) + % self.extended_len(); + + for (lhs, rhs) in lhs + .iter_mut() + .zip(right.values[start..].iter().chain(right.values.iter())) + { + op(lhs, rhs); + } + }); + + left + } + + /// Multiply two polynomials in the extended domain, rotating the latter + /// polynomial over the original domain first. + pub fn mul_extended( + &self, + left: Polynomial, + right: &Polynomial, + rotation: Rotation, + ) -> Polynomial + where + G: Field, + { + self.op_extended(left, right, rotation, |lhs, rhs| { + *lhs *= *rhs; + }) + } + + /// Add two polynomials in the extended domain, rotating the latter + /// polynomial over the original domain first. + pub fn add_extended( + &self, + left: Polynomial, + right: &Polynomial, + rotation: Rotation, + ) -> Polynomial + where + G: Field, + { + self.op_extended(left, right, rotation, |lhs, rhs| { + *lhs += *rhs; + }) + } + + /// Subtract a polynomial from another in the extended domain, rotating the + /// former polynomial over the original domain first. + pub fn sub_extended( + &self, + left: Polynomial, + right: &Polynomial, + rotation: Rotation, + ) -> Polynomial + where + G: Field, + { + self.op_extended(left, right, rotation, |lhs, rhs| { + *lhs -= *rhs; + }) + } + + /// Rotate the extended domain polynomial over the original domain. + pub fn rotate_extended( + &self, + poly: &Polynomial, + rotation: Rotation, + ) -> Polynomial { + let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize; + + let mut poly = poly.clone(); + + if rotation.0 >= 0 { + poly.values.rotate_left(new_rotation); + } else { + poly.values.rotate_right(new_rotation); + } + + poly + } + /// This takes us from the extended evaluation domain and gets us the /// quotient polynomial coefficients. /// @@ -287,7 +383,7 @@ impl EvaluationDomain { // Distribute powers to move from coset; opposite from the // transformation we performed earlier. - Self::distribute_powers(&mut a.values, self.g_coset_inv); + Self::distribute_powers_zeta(&mut a.values, false); // Truncate it to match the size of the quotient polynomial; the // evaluation domain might be slightly larger than necessary because @@ -325,11 +421,15 @@ impl EvaluationDomain { // `[a_0, [zeta]a_1, [zeta^2]a_2, a_3, [zeta]a_4, [zeta^2]a_5, a_6, ...]`, // where zeta is a cube root of unity in the multiplicative subgroup with // order (p - 1), i.e. zeta^3 = 1. - fn distribute_powers_zeta(mut a: &mut [G]) { - let coset_powers = [G::Scalar::ZETA, G::Scalar::ZETA.square()]; + fn distribute_powers_zeta(mut a: &mut [G], direction: bool) { + let coset_powers = if direction { + [G::Scalar::ZETA, G::Scalar::ZETA.square()] + } else { + [G::Scalar::ZETA.square(), G::Scalar::ZETA] + }; parallelize(&mut a, |a, mut index| { for a in a { - // Distribute powers to move into coset + // Distribute powers to move into/from coset let i = index % (coset_powers.len() + 1); if i != 0 { a.group_scale(&coset_powers[i - 1]); From a091795aca158fca5194006d49bdc9ef8e4a232b Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 12 Jul 2021 12:53:12 -0600 Subject: [PATCH 2/7] Switch to computing only Rotation::cur() cosets until rotation is needed. --- src/dev.rs | 14 +-- src/plonk/circuit.rs | 97 ++++++++++----- src/plonk/keygen.rs | 9 +- src/plonk/lookup/prover.rs | 51 ++++---- src/plonk/lookup/verifier.rs | 6 +- src/plonk/permutation/prover.rs | 57 +++------ src/plonk/prover.rs | 38 +++--- src/plonk/verifier.rs | 6 +- tests/plonk_api.rs | 204 ++++++++++++++++++++++---------- 9 files changed, 289 insertions(+), 193 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index 7e64ce75..6ddc5d6e 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -500,8 +500,8 @@ impl MockProver { row: i32, queries: &'a [(Column, Rotation)], cells: &'a [Vec>], - ) -> impl Fn(usize) -> F + 'a { - move |index| { + ) -> impl Fn(usize, usize, Rotation) -> F + 'a { + move |index, _, _| { let (column, at) = &queries[index]; let resolved_row = (row + at.0) % n; cell_value(cells[column.index()][resolved_row as usize]) @@ -513,8 +513,8 @@ impl MockProver { row: i32, queries: &'a [(Column, Rotation)], cells: &'a [Vec], - ) -> impl Fn(usize) -> F + 'a { - move |index| { + ) -> impl Fn(usize, usize, Rotation) -> F + 'a { + move |index, _, _| { let (column, at) = &queries[index]; let resolved_row = (row + at.0) % n; cells[column.index()][resolved_row as usize] @@ -561,7 +561,7 @@ impl MockProver { let load = |expression: &Expression, row| { expression.evaluate( &|scalar| scalar, - &|index| { + &|index, _, _| { let query = self.cs.fixed_queries[index]; let column_index = query.0.index(); let rotation = query.1 .0; @@ -570,7 +570,7 @@ impl MockProver { [(row as i32 + n + rotation) as usize % n as usize], ) }, - &|index| { + &|index, _, _| { let query = self.cs.advice_queries[index]; let column_index = query.0.index(); let rotation = query.1 .0; @@ -579,7 +579,7 @@ impl MockProver { [(row as i32 + n + rotation) as usize % n as usize], ) }, - &|index| { + &|index, _, _| { let query = self.cs.instance_queries[index]; let column_index = query.0.index(); let rotation = query.1 .0; diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 3c6809e5..3dd1bba5 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -537,11 +537,32 @@ pub enum Expression { /// This is a constant polynomial Constant(F), /// This is a fixed column queried at a certain relative location - Fixed(usize), + Fixed { + /// Query index + query_index: usize, + /// Column index + column_index: usize, + /// Rotation of this query + rotation: Rotation, + }, /// This is an advice (witness) column queried at a certain relative location - Advice(usize), + Advice { + /// Query index + query_index: usize, + /// Column index + column_index: usize, + /// Rotation of this query + rotation: Rotation, + }, /// This is an instance (external) column queried at a certain relative location - Instance(usize), + Instance { + /// Query index + query_index: usize, + /// Column index + column_index: usize, + /// Rotation of this query + rotation: Rotation, + }, /// This is the sum of two polynomials Sum(Box>, Box>), /// This is the product of two polynomials @@ -556,18 +577,30 @@ impl Expression { pub fn evaluate( &self, constant: &impl Fn(F) -> T, - fixed_column: &impl Fn(usize) -> T, - advice_column: &impl Fn(usize) -> T, - instance_column: &impl Fn(usize) -> T, + fixed_column: &impl Fn(usize, usize, Rotation) -> T, + advice_column: &impl Fn(usize, usize, Rotation) -> T, + instance_column: &impl Fn(usize, usize, Rotation) -> T, sum: &impl Fn(T, T) -> T, product: &impl Fn(T, T) -> T, scaled: &impl Fn(T, F) -> T, ) -> T { match self { Expression::Constant(scalar) => constant(*scalar), - Expression::Fixed(index) => fixed_column(*index), - Expression::Advice(index) => advice_column(*index), - Expression::Instance(index) => instance_column(*index), + Expression::Fixed { + query_index, + column_index, + rotation, + } => fixed_column(*query_index, *column_index, *rotation), + Expression::Advice { + query_index, + column_index, + rotation, + } => advice_column(*query_index, *column_index, *rotation), + Expression::Instance { + query_index, + column_index, + rotation, + } => instance_column(*query_index, *column_index, *rotation), Expression::Sum(a, b) => { let a = a.evaluate( constant, @@ -629,9 +662,9 @@ impl Expression { pub fn degree(&self) -> usize { match self { Expression::Constant(_) => 0, - Expression::Fixed(_) => 1, - Expression::Advice(_) => 1, - Expression::Instance(_) => 1, + Expression::Fixed { .. } => 1, + Expression::Advice { .. } => 1, + Expression::Instance { .. } => 1, Expression::Sum(a, b) => max(a.degree(), b.degree()), Expression::Product(a, b) => a.degree() + b.degree(), Expression::Scaled(poly, _) => poly.degree(), @@ -1136,43 +1169,49 @@ impl<'a, F: Field> VirtualCells<'a, F> { pub fn query_selector(&mut self, selector: Selector) -> Expression { // Selectors are always queried at the current row. self.queried_selectors.push(selector); - Expression::Fixed(self.meta.query_fixed_index(selector.0, Rotation::cur())) + Expression::Fixed { + query_index: self.meta.query_fixed_index(selector.0, Rotation::cur()), + column_index: (selector.0).index, + rotation: Rotation::cur(), + } } /// Query a fixed column at a relative position pub fn query_fixed(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); - Expression::Fixed(self.meta.query_fixed_index(column, at)) + Expression::Fixed { + query_index: self.meta.query_fixed_index(column, at), + column_index: column.index, + rotation: at, + } } /// Query an advice column at a relative position pub fn query_advice(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); - Expression::Advice(self.meta.query_advice_index(column, at)) + Expression::Advice { + query_index: self.meta.query_advice_index(column, at), + column_index: column.index, + rotation: at, + } } /// Query an instance column at a relative position pub fn query_instance(&mut self, column: Column, at: Rotation) -> Expression { self.queried_cells.push((column, at).into()); - Expression::Instance(self.meta.query_instance_index(column, at)) + Expression::Instance { + query_index: self.meta.query_instance_index(column, at), + column_index: column.index, + rotation: at, + } } /// Query an Any column at a relative position pub fn query_any(&mut self, column: Column, at: Rotation) -> Expression { - self.queried_cells.push((column, at).into()); match column.column_type() { - Any::Advice => Expression::Advice( - self.meta - .query_advice_index(Column::::try_from(column).unwrap(), at), - ), - Any::Fixed => Expression::Fixed( - self.meta - .query_fixed_index(Column::::try_from(column).unwrap(), at), - ), - Any::Instance => Expression::Instance( - self.meta - .query_instance_index(Column::::try_from(column).unwrap(), at), - ), + Any::Advice => self.query_advice(Column::::try_from(column).unwrap(), at), + Any::Fixed => self.query_fixed(Column::::try_from(column).unwrap(), at), + Any::Instance => self.query_instance(Column::::try_from(column).unwrap(), at), } } } diff --git a/src/plonk/keygen.rs b/src/plonk/keygen.rs index ae2d1488..57445058 100644 --- a/src/plonk/keygen.rs +++ b/src/plonk/keygen.rs @@ -243,14 +243,9 @@ where .map(|poly| vk.domain.lagrange_to_coeff(poly.clone())) .collect(); - let fixed_cosets = vk - .cs - .fixed_queries + let fixed_cosets = fixed_polys .iter() - .map(|&(column, at)| { - let poly = fixed_polys[column.index()].clone(); - vk.domain.coeff_to_extended(poly, at) - }) + .map(|poly| vk.domain.coeff_to_extended(poly.clone(), Rotation::cur())) .collect(); let permutation_pk = assembly diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 5ef42032..5500b7f0 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -27,7 +27,6 @@ pub(in crate::plonk) struct Permuted { permuted_input_expression: Polynomial, permuted_input_poly: Polynomial, permuted_input_coset: Polynomial, - permuted_input_inv_coset: Polynomial, permuted_input_blind: Blind, permuted_input_commitment: C, unpermuted_table_expressions: Vec>, @@ -44,7 +43,6 @@ pub(in crate::plonk) struct Committed { permuted: Permuted, product_poly: Polynomial, product_coset: Polynomial, - product_next_coset: Polynomial, product_blind: Blind, product_commitment: C, } @@ -103,22 +101,13 @@ impl Argument { .map(|expression| { expression.evaluate( &|scalar| pk.vk.domain.constant_lagrange(scalar), - &|index| { - let query = pk.vk.cs.fixed_queries[index]; - let column_index = query.0.index(); - let rotation = query.1; + &|_, column_index, rotation| { fixed_values[column_index].clone().rotate(rotation) }, - &|index| { - let query = pk.vk.cs.advice_queries[index]; - let column_index = query.0.index(); - let rotation = query.1; + &|_, column_index, rotation| { advice_values[column_index].clone().rotate(rotation) }, - &|index| { - let query = pk.vk.cs.instance_queries[index]; - let column_index = query.0.index(); - let rotation = query.1; + &|_, column_index, rotation| { instance_values[column_index].clone().rotate(rotation) }, &|a, b| a + &b, @@ -145,9 +134,21 @@ impl Argument { .map(|expression| { expression.evaluate( &|scalar| pk.vk.domain.constant_extended(scalar), - &|index| fixed_cosets[index].clone(), - &|index| advice_cosets[index].clone(), - &|index| instance_cosets[index].clone(), + &|_, column_index, rotation| { + pk.vk + .domain + .rotate_extended(&fixed_cosets[column_index], rotation) + }, + &|_, column_index, rotation| { + pk.vk + .domain + .rotate_extended(&advice_cosets[column_index], rotation) + }, + &|_, column_index, rotation| { + pk.vk + .domain + .rotate_extended(&instance_cosets[column_index], rotation) + }, &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * scalar, @@ -216,10 +217,6 @@ impl Argument { .vk .domain .coeff_to_extended(permuted_input_poly.clone(), Rotation::cur()); - let permuted_input_inv_coset = pk - .vk - .domain - .coeff_to_extended(permuted_input_poly.clone(), Rotation(-1)); let permuted_table_coset = pk .vk .domain @@ -231,7 +228,6 @@ impl Argument { permuted_input_expression, permuted_input_poly, permuted_input_coset, - permuted_input_inv_coset, permuted_input_blind, permuted_input_commitment, unpermuted_table_expressions, @@ -396,7 +392,6 @@ impl Permuted { let product_commitment = params.commit_lagrange(&z, product_blind).to_affine(); let z = pk.vk.domain.lagrange_to_coeff(z); let product_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation::cur()); - let product_next_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation::next()); // Hash product commitment transcript @@ -407,7 +402,6 @@ impl Permuted { permuted: self, product_poly: z, product_coset, - product_next_coset, product_commitment, product_blind, }) @@ -430,6 +424,7 @@ impl<'a, C: CurveAffine> Committed { Constructed, impl Iterator> + 'a, ) { + let domain = &pk.vk.domain; let permuted = self.permuted; let active_rows = Polynomial::one_minus(pk.l_last.clone() + &pk.l_blind); @@ -450,7 +445,7 @@ impl<'a, C: CurveAffine> Committed { // ) = 0 .chain({ // z(\omega X) (a'(X) + \beta) (s'(X) + \gamma) - let mut left = self.product_next_coset.clone(); + let mut left = domain.rotate_extended(&self.product_coset, Rotation::next()); parallelize(&mut left, |left, start| { for ((left, permuted_input), permuted_table) in left .iter_mut() @@ -502,7 +497,11 @@ impl<'a, C: CurveAffine> Committed { // (1 - (l_last + l_blind)) * (a′(X) − s′(X))⋅(a′(X) − a′(\omega^{-1} X)) = 0 .chain(Some( (permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset) - * &(permuted.permuted_input_coset.clone() - &permuted.permuted_input_inv_coset) + * &(domain.sub_extended( + permuted.permuted_input_coset.clone(), + &permuted.permuted_input_coset, + Rotation::prev(), + )) * &active_rows, )); diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index de0de10d..5d7d77a9 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -134,9 +134,9 @@ impl Evaluated { .map(|expression| { expression.evaluate( &|scalar| scalar, - &|index| fixed_evals[index], - &|index| advice_evals[index], - &|index| instance_evals[index], + &|index, _, _| fixed_evals[index], + &|index, _, _| advice_evals[index], + &|index, _, _| instance_evals[index], &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * &scalar, diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index 340179a3..b8b0d602 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -18,8 +18,6 @@ use crate::{ pub struct CommittedSet { permutation_product_poly: Polynomial, permutation_product_coset: Polynomial, - permutation_product_coset_next: Polynomial, - permutation_product_coset_last: Option>, permutation_product_blind: Blind, } @@ -168,18 +166,7 @@ impl Argument { let z = domain.lagrange_to_coeff(z); let permutation_product_poly = z.clone(); - // We only keep these around if there's another set afterward. - let permutation_product_coset_last = if iter.len() > 0 { - // Keep the polynomial around, rotated to l_last. - Some( - domain.coeff_to_extended(z.clone(), Rotation(-((blinding_factors + 1) as i32))), - ) - } else { - None - }; - let permutation_product_coset = domain.coeff_to_extended(z.clone(), Rotation::cur()); - let permutation_product_coset_next = domain.coeff_to_extended(z, Rotation::next()); let permutation_product_commitment = permutation_product_commitment_projective.to_affine(); @@ -192,8 +179,6 @@ impl Argument { sets.push(CommittedSet { permutation_product_poly, permutation_product_coset, - permutation_product_coset_next, - permutation_product_coset_last, permutation_product_blind, }); } @@ -219,6 +204,8 @@ impl Committed { ) { let domain = &pk.vk.domain; let chunk_len = pk.vk.cs.degree() - 2; + let blinding_factors = pk.vk.cs.blinding_factors(); + let last_rotation = Rotation(-((blinding_factors + 1) as i32)); let constructed = Constructed { sets: self @@ -252,9 +239,11 @@ impl Committed { .skip(1) .zip(self.sets.iter()) .map(|(set, last_set)| { - (set.permutation_product_coset.clone() - - &last_set.permutation_product_coset_last.as_ref().unwrap()) - * &pk.l0 + domain.sub_extended( + set.permutation_product_coset.clone(), + &last_set.permutation_product_coset, + last_rotation, + ) * &pk.l0 }) .collect::>(), ) @@ -270,22 +259,14 @@ impl Committed { .zip(pkey.cosets.chunks(chunk_len)) .enumerate() .map(move |(chunk_index, ((set, columns), cosets))| { - let mut left = set.permutation_product_coset_next; + let mut left = domain + .rotate_extended(&set.permutation_product_coset, Rotation::next()); for (values, permutation) in columns .iter() .map(|&column| match column.column_type() { - Any::Advice => { - &advice_cosets - [pk.vk.cs.get_any_query_index(column, Rotation::cur())] - } - Any::Fixed => { - &fixed_cosets - [pk.vk.cs.get_any_query_index(column, Rotation::cur())] - } - Any::Instance => { - &instance_cosets - [pk.vk.cs.get_any_query_index(column, Rotation::cur())] - } + Any::Advice => &advice_cosets[column.index()], + Any::Fixed => &fixed_cosets[column.index()], + Any::Instance => &instance_cosets[column.index()], }) .zip(cosets.iter()) { @@ -306,17 +287,9 @@ impl Committed { * &(C::Scalar::DELTA.pow_vartime(&[(chunk_index * chunk_len) as u64])); let step = domain.get_extended_omega(); for values in columns.iter().map(|&column| match column.column_type() { - Any::Advice => { - &advice_cosets - [pk.vk.cs.get_any_query_index(column, Rotation::cur())] - } - Any::Fixed => { - &fixed_cosets[pk.vk.cs.get_any_query_index(column, Rotation::cur())] - } - Any::Instance => { - &instance_cosets - [pk.vk.cs.get_any_query_index(column, Rotation::cur())] - } + Any::Advice => &advice_cosets[column.index()], + Any::Fixed => &fixed_cosets[column.index()], + Any::Instance => &instance_cosets[column.index()], }) { parallelize(&mut right, move |right, start| { let mut beta_term = diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index d3e0ae3a..d77693ed 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -14,7 +14,7 @@ use super::{ use crate::poly::{ commitment::{Blind, Params}, multiopen::{self, ProverQuery}, - Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, + Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, }; use crate::{ arithmetic::{eval_polynomial, CurveAffine, FieldExt}, @@ -98,13 +98,9 @@ pub fn create_proof< }) .collect(); - let instance_cosets: Vec<_> = meta - .instance_queries + let instance_cosets: Vec<_> = instance_polys .iter() - .map(|&(column, at)| { - let poly = instance_polys[column.index()].clone(); - domain.coeff_to_extended(poly, at) - }) + .map(|poly| domain.coeff_to_extended(poly.clone(), Rotation::cur())) .collect(); Ok(InstanceSingle { @@ -295,13 +291,9 @@ pub fn create_proof< .map(|poly| domain.lagrange_to_coeff(poly)) .collect(); - let advice_cosets: Vec<_> = meta - .advice_queries + let advice_cosets: Vec<_> = advice_polys .iter() - .map(|&(column, at)| { - let poly = advice_polys[column.index()].clone(); - domain.coeff_to_extended(poly, at) - }) + .map(|poly| domain.coeff_to_extended(poly.clone(), Rotation::cur())) .collect(); Ok(AdviceSingle { @@ -429,9 +421,23 @@ pub fn create_proof< gate.polynomials().iter().map(move |poly| { poly.evaluate( &|scalar| pk.vk.domain.constant_extended(scalar), - &|index| pk.fixed_cosets[index].clone(), - &|index| advice.advice_cosets[index].clone(), - &|index| instance.instance_cosets[index].clone(), + &|_, column_index, rotation| { + pk.vk + .domain + .rotate_extended(&pk.fixed_cosets[column_index], rotation) + }, + &|_, column_index, rotation| { + pk.vk.domain.rotate_extended( + &advice.advice_cosets[column_index], + rotation, + ) + }, + &|_, column_index, rotation| { + pk.vk.domain.rotate_extended( + &instance.instance_cosets[column_index], + rotation, + ) + }, &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * scalar, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index bf6365c6..54536fdb 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -162,9 +162,9 @@ pub fn verify_proof<'params, C: CurveAffine, E: EncodedChallenge, T: Transcri gate.polynomials().iter().map(move |poly| { poly.evaluate( &|scalar| scalar, - &|index| fixed_evals[index], - &|index| advice_evals[index], - &|index| instance_evals[index], + &|index, _, _| fixed_evals[index], + &|index, _, _| advice_evals[index], + &|index, _, _| instance_evals[index], &|a, b| a + &b, &|a, b| a * &b, &|a, scalar| a * &scalar, diff --git a/tests/plonk_api.rs b/tests/plonk_api.rs index c94a0669..6db3dd1d 100644 --- a/tests/plonk_api.rs +++ b/tests/plonk_api.rs @@ -519,74 +519,134 @@ fn plonk_api() { Sum( Sum( Product( - Advice( - 0, - ), - Fixed( - 3, - ), + Advice { + query_index: 0, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 3, + column_index: 2, + rotation: Rotation( + 0, + ), + }, ), Product( - Advice( - 1, - ), - Fixed( - 4, - ), + Advice { + query_index: 1, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 4, + column_index: 3, + rotation: Rotation( + 0, + ), + }, ), ), Product( Product( - Advice( + Advice { + query_index: 0, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 2, + rotation: Rotation( + 0, + ), + }, + ), + Fixed { + query_index: 6, + column_index: 1, + rotation: Rotation( 0, ), - Advice( - 1, - ), - ), - Fixed( - 6, - ), + }, ), ), Scaled( Product( - Advice( - 2, - ), - Fixed( - 5, - ), + Advice { + query_index: 2, + column_index: 3, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 5, + column_index: 4, + rotation: Rotation( + 0, + ), + }, ), 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000, ), ), Product( - Fixed( - 2, - ), + Fixed { + query_index: 2, + column_index: 0, + rotation: Rotation( + 0, + ), + }, Product( - Advice( - 3, - ), - Advice( - 4, - ), + Advice { + query_index: 3, + column_index: 4, + rotation: Rotation( + 1, + ), + }, + Advice { + query_index: 4, + column_index: 0, + rotation: Rotation( + -1, + ), + }, ), ), ), Product( - Fixed( - 7, - ), - Sum( - Advice( + Fixed { + query_index: 7, + column_index: 5, + rotation: Rotation( 0, ), - Scaled( - Instance( + }, + Sum( + Advice { + query_index: 0, + column_index: 1, + rotation: Rotation( 0, ), + }, + Scaled( + Instance { + query_index: 0, + column_index: 0, + rotation: Rotation( + 0, + ), + }, 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000000, ), ), @@ -743,35 +803,59 @@ fn plonk_api() { lookups: [ Argument { input_expressions: [ - Advice( - 0, - ), + Advice { + query_index: 0, + column_index: 1, + rotation: Rotation( + 0, + ), + }, ], table_expressions: [ - Fixed( - 0, - ), + Fixed { + query_index: 0, + column_index: 6, + rotation: Rotation( + 0, + ), + }, ], }, Argument { input_expressions: [ Product( - Advice( - 0, - ), - Advice( - 1, - ), + Advice { + query_index: 0, + column_index: 1, + rotation: Rotation( + 0, + ), + }, + Advice { + query_index: 1, + column_index: 2, + rotation: Rotation( + 0, + ), + }, ), ], table_expressions: [ Product( - Fixed( - 0, - ), - Fixed( - 1, - ), + Fixed { + query_index: 0, + column_index: 6, + rotation: Rotation( + 0, + ), + }, + Fixed { + query_index: 1, + column_index: 7, + rotation: Rotation( + 0, + ), + }, ), ], }, From 827132302a15de8bc196b0305dce68e842e91ea4 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 12 Jul 2021 12:57:09 -0600 Subject: [PATCH 3/7] Remove the ability to rotate when switching to the extended domain. --- src/plonk/keygen.rs | 10 +++++----- src/plonk/lookup/prover.rs | 12 +++--------- src/plonk/permutation/keygen.rs | 4 ++-- src/plonk/permutation/prover.rs | 2 +- src/plonk/prover.rs | 6 +++--- src/poly/domain.rs | 30 +----------------------------- 6 files changed, 15 insertions(+), 49 deletions(-) diff --git a/src/plonk/keygen.rs b/src/plonk/keygen.rs index 57445058..c7fc1b58 100644 --- a/src/plonk/keygen.rs +++ b/src/plonk/keygen.rs @@ -14,7 +14,7 @@ use super::{ }; use crate::poly::{ commitment::{Blind, Params}, - EvaluationDomain, Rotation, + EvaluationDomain, }; use crate::{arithmetic::CurveAffine, poly::batch_invert_assigned}; @@ -245,7 +245,7 @@ where let fixed_cosets = fixed_polys .iter() - .map(|poly| vk.domain.coeff_to_extended(poly.clone(), Rotation::cur())) + .map(|poly| vk.domain.coeff_to_extended(poly.clone())) .collect(); let permutation_pk = assembly @@ -257,7 +257,7 @@ where let mut l0 = vk.domain.empty_lagrange(); l0[0] = C::Scalar::one(); let l0 = vk.domain.lagrange_to_coeff(l0); - let l0 = vk.domain.coeff_to_extended(l0, Rotation::cur()); + let l0 = vk.domain.coeff_to_extended(l0); // Compute l_blind(X) which evaluates to 1 for each blinding factor row // and 0 otherwise over the domain. @@ -266,14 +266,14 @@ where *evaluation = C::Scalar::one(); } let l_blind = vk.domain.lagrange_to_coeff(l_blind); - let l_blind = vk.domain.coeff_to_extended(l_blind, Rotation::cur()); + let l_blind = vk.domain.coeff_to_extended(l_blind); // Compute l_last(X) which evaluates to 1 on the first inactive row (just // before the blinding factors) and 0 otherwise over the domain let mut l_last = vk.domain.empty_lagrange(); l_last[params.n as usize - cs.blinding_factors() - 1] = C::Scalar::one(); let l_last = vk.domain.lagrange_to_coeff(l_last); - let l_last = vk.domain.coeff_to_extended(l_last, Rotation::cur()); + let l_last = vk.domain.coeff_to_extended(l_last); Ok(ProvingKey { vk, diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 5500b7f0..5a0a476e 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -213,14 +213,8 @@ impl Argument { .write_point(permuted_table_commitment) .map_err(|_| Error::TranscriptError)?; - let permuted_input_coset = pk - .vk - .domain - .coeff_to_extended(permuted_input_poly.clone(), Rotation::cur()); - let permuted_table_coset = pk - .vk - .domain - .coeff_to_extended(permuted_table_poly.clone(), Rotation::cur()); + let permuted_input_coset = pk.vk.domain.coeff_to_extended(permuted_input_poly.clone()); + let permuted_table_coset = pk.vk.domain.coeff_to_extended(permuted_table_poly.clone()); Ok(Permuted { unpermuted_input_expressions, @@ -391,7 +385,7 @@ impl Permuted { let product_blind = Blind(C::Scalar::rand()); let product_commitment = params.commit_lagrange(&z, product_blind).to_affine(); let z = pk.vk.domain.lagrange_to_coeff(z); - let product_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation::cur()); + let product_coset = pk.vk.domain.coeff_to_extended(z.clone()); // Hash product commitment transcript diff --git a/src/plonk/permutation/keygen.rs b/src/plonk/permutation/keygen.rs index 4a514058..1aa9c908 100644 --- a/src/plonk/permutation/keygen.rs +++ b/src/plonk/permutation/keygen.rs @@ -7,7 +7,7 @@ use crate::{ plonk::{Any, Column, Error}, poly::{ commitment::{Blind, Params}, - EvaluationDomain, Rotation, + EvaluationDomain, }, }; @@ -199,7 +199,7 @@ impl Assembly { permutations.push(permutation_poly.clone()); let poly = domain.lagrange_to_coeff(permutation_poly); polys.push(poly.clone()); - cosets.push(domain.coeff_to_extended(poly, Rotation::cur())); + cosets.push(domain.coeff_to_extended(poly)); } ProvingKey { permutations, diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index b8b0d602..f6b566e9 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -166,7 +166,7 @@ impl Argument { let z = domain.lagrange_to_coeff(z); let permutation_product_poly = z.clone(); - let permutation_product_coset = domain.coeff_to_extended(z.clone(), Rotation::cur()); + let permutation_product_coset = domain.coeff_to_extended(z.clone()); let permutation_product_commitment = permutation_product_commitment_projective.to_affine(); diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index d77693ed..abc4b56f 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -14,7 +14,7 @@ use super::{ use crate::poly::{ commitment::{Blind, Params}, multiopen::{self, ProverQuery}, - Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, + Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, }; use crate::{ arithmetic::{eval_polynomial, CurveAffine, FieldExt}, @@ -100,7 +100,7 @@ pub fn create_proof< let instance_cosets: Vec<_> = instance_polys .iter() - .map(|poly| domain.coeff_to_extended(poly.clone(), Rotation::cur())) + .map(|poly| domain.coeff_to_extended(poly.clone())) .collect(); Ok(InstanceSingle { @@ -293,7 +293,7 @@ pub fn create_proof< let advice_cosets: Vec<_> = advice_polys .iter() - .map(|poly| domain.coeff_to_extended(poly.clone(), Rotation::cur())) + .map(|poly| domain.coeff_to_extended(poly.clone())) .collect(); Ok(AdviceSingle { diff --git a/src/poly/domain.rs b/src/poly/domain.rs index 61707384..7a9d7022 100644 --- a/src/poly/domain.rs +++ b/src/poly/domain.rs @@ -239,26 +239,10 @@ impl EvaluationDomain { pub fn coeff_to_extended( &self, mut a: Polynomial, - rotation: Rotation, ) -> Polynomial { assert_eq!(a.values.len(), 1 << self.k); - 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.values, true); - } else { - let mut g = self.g_coset; - if rotation.0 > 0 { - g *= &self.omega.pow_vartime(&[rotation.0 as u64, 0, 0, 0]); - } else { - g *= &self - .omega_inv - .pow_vartime(&[rotation.0.abs() as u64, 0, 0, 0]); - } - Self::distribute_powers(&mut a.values, g); - } + Self::distribute_powers_zeta(&mut a.values, true); a.values.resize(self.extended_len(), G::group_zero()); best_fft(&mut a.values, self.extended_omega, self.extended_k); @@ -439,18 +423,6 @@ impl EvaluationDomain { }); } - // Given a length-`n` slice of group elements `a` and a scalar `g`, this - // returns `[a_0, [g]a_1, [g^2]a_2, [g^3]a_3, ..., [g^n-1] a_{n-1}]`. - fn distribute_powers(mut a: &mut [G], g: G::Scalar) { - parallelize(&mut a, |a, index| { - let mut cur = g.pow_vartime(&[index as u64, 0, 0, 0]); - for a in a { - a.group_scale(&cur); - cur *= &g; - } - }); - } - fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { best_fft(a, omega_inv, log_n); parallelize(a, |a, _| { From 574ce73d80d071666c5de4be3569193fa91a3ce5 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 13 Jul 2021 10:10:03 -0600 Subject: [PATCH 4/7] Minor changes to `distribute_powers_zeta`. --- src/poly/domain.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/poly/domain.rs b/src/poly/domain.rs index 7a9d7022..5e7f231d 100644 --- a/src/poly/domain.rs +++ b/src/poly/domain.rs @@ -242,7 +242,7 @@ impl EvaluationDomain { ) -> Polynomial { assert_eq!(a.values.len(), 1 << self.k); - Self::distribute_powers_zeta(&mut a.values, true); + self.distribute_powers_zeta(&mut a.values, true); a.values.resize(self.extended_len(), G::group_zero()); best_fft(&mut a.values, self.extended_omega, self.extended_k); @@ -367,7 +367,7 @@ impl EvaluationDomain { // Distribute powers to move from coset; opposite from the // transformation we performed earlier. - Self::distribute_powers_zeta(&mut a.values, false); + self.distribute_powers_zeta(&mut a.values, false); // Truncate it to match the size of the quotient polynomial; the // evaluation domain might be slightly larger than necessary because @@ -401,15 +401,15 @@ impl EvaluationDomain { } } - // Given a slice of group elements `[a_0, a_1, a_2, ...]`, this returns - // `[a_0, [zeta]a_1, [zeta^2]a_2, a_3, [zeta]a_4, [zeta^2]a_5, a_6, ...]`, - // where zeta is a cube root of unity in the multiplicative subgroup with - // order (p - 1), i.e. zeta^3 = 1. - fn distribute_powers_zeta(mut a: &mut [G], direction: bool) { - let coset_powers = if direction { - [G::Scalar::ZETA, G::Scalar::ZETA.square()] + /// Given a slice of group elements `[a_0, a_1, a_2, ...]`, this returns + /// `[a_0, [zeta]a_1, [zeta^2]a_2, a_3, [zeta]a_4, [zeta^2]a_5, a_6, ...]`, + /// where zeta is a cube root of unity in the multiplicative subgroup with + /// order (p - 1), i.e. zeta^3 = 1. + fn distribute_powers_zeta(&self, mut a: &mut [G], into_coset: bool) { + let coset_powers = if into_coset { + [self.g_coset, self.g_coset_inv] } else { - [G::Scalar::ZETA.square(), G::Scalar::ZETA] + [self.g_coset_inv, self.g_coset] }; parallelize(&mut a, |a, mut index| { for a in a { From f314f66f693c8f3b6489e246a9bf419e82e97f37 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 13 Jul 2021 10:11:38 -0600 Subject: [PATCH 5/7] Fix clippy lint --- src/plonk/permutation/prover.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plonk/permutation/prover.rs b/src/plonk/permutation/prover.rs index f6b566e9..aaaafbd2 100644 --- a/src/plonk/permutation/prover.rs +++ b/src/plonk/permutation/prover.rs @@ -73,12 +73,11 @@ impl Argument { let mut sets = vec![]; - let mut iter = self + for (columns, permutations) in self .columns .chunks(chunk_len) - .zip(pkey.permutations.chunks(chunk_len)); - - while let Some((columns, permutations)) = iter.next() { + .zip(pkey.permutations.chunks(chunk_len)) + { // Goal is to compute the products of fractions // // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / From 1e33570c7adbab950e5caa698344e125599b5ae0 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 19 Jul 2021 14:46:02 +0100 Subject: [PATCH 6/7] cargo fmt --- src/dev.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index bba8cc9d..5f0e8c1b 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -610,7 +610,8 @@ impl MockProver { row: i32, queries: &'a [(Column, Rotation)], cells: &'a [Vec], - ) -> impl Fn(usize, usize, Rotation) -> Value + 'a { + ) -> impl Fn(usize, usize, Rotation) -> Value + 'a + { move |index, _, _| { let (column, at) = &queries[index]; let resolved_row = (row + at.0) % n; @@ -623,7 +624,8 @@ impl MockProver { row: i32, queries: &'a [(Column, Rotation)], cells: &'a [Vec>], - ) -> impl Fn(usize, usize, Rotation) -> Value + 'a { + ) -> impl Fn(usize, usize, Rotation) -> Value + 'a + { move |index, _, _| { let (column, at) = &queries[index]; let resolved_row = (row + at.0) % n; From ce319c8da8a01e499eaa0525c5aa1f517f66d246 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Mon, 19 Jul 2021 09:08:24 -0600 Subject: [PATCH 7/7] Add clarifying comment to `distribute_powers_zeta`. --- src/poly/domain.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/poly/domain.rs b/src/poly/domain.rs index 731f33b8..257577a2 100644 --- a/src/poly/domain.rs +++ b/src/poly/domain.rs @@ -405,6 +405,9 @@ impl EvaluationDomain { /// `[a_0, [zeta]a_1, [zeta^2]a_2, a_3, [zeta]a_4, [zeta^2]a_5, a_6, ...]`, /// where zeta is a cube root of unity in the multiplicative subgroup with /// order (p - 1), i.e. zeta^3 = 1. + /// + /// `into_coset` should be set to `true` when moving into the coset, + /// and `false` when moving out. This toggles the choice of `zeta`. fn distribute_powers_zeta(&self, mut a: &mut [G], into_coset: bool) { let coset_powers = if into_coset { [self.g_coset, self.g_coset_inv]