From 5d891e029d3a1a536ab4b2e5cc7abc24b77a60aa Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 14:35:56 +0800 Subject: [PATCH 01/18] Add fixed_values to ProvingKey --- src/plonk.rs | 3 ++- src/plonk/circuit.rs | 6 ++---- src/plonk/keygen.rs | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index ae31166..e0accee 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -6,7 +6,7 @@ //! [plonk]: https://eprint.iacr.org/2019/953 use crate::arithmetic::CurveAffine; -use crate::poly::{multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, Polynomial}; +use crate::poly::{multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}; use crate::transcript::ChallengeScalar; mod circuit; @@ -37,6 +37,7 @@ pub struct ProvingKey { vk: VerifyingKey, // TODO: get rid of this? l0: Polynomial, + fixed_values: Vec>, fixed_polys: Vec>, fixed_cosets: Vec>, permutations: Vec>, diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 151ac67..8b0e045 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -432,13 +432,11 @@ impl ConstraintSystem { } fn query_any_index(&mut self, column: Column, at: i32) -> usize { - let index = match column.column_type() { + match column.column_type() { Any::Advice => self.query_advice_index(Column::::try_from(column).unwrap(), at), Any::Fixed => self.query_fixed_index(Column::::try_from(column).unwrap(), at), Any::Aux => self.query_aux_index(Column::::try_from(column).unwrap(), at), - }; - - index + } } /// Query an Any column at a relative position diff --git a/src/plonk/keygen.rs b/src/plonk/keygen.rs index 1b72625..5df667a 100644 --- a/src/plonk/keygen.rs +++ b/src/plonk/keygen.rs @@ -103,8 +103,8 @@ where let fixed_polys: Vec<_> = assembly .fixed - .into_iter() - .map(|poly| domain.lagrange_to_coeff(poly)) + .iter() + .map(|poly| domain.lagrange_to_coeff(poly.clone())) .collect(); let fixed_cosets = cs @@ -131,6 +131,7 @@ where cs, }, l0, + fixed_values: assembly.fixed, fixed_polys, fixed_cosets, permutations: permutation_pks, From 2ba44cff9f5b05796ffe80f2fd6805ea0af6479c Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 14:44:14 +0800 Subject: [PATCH 02/18] Add theta challenge --- src/plonk.rs | 4 ++++ src/plonk/prover.rs | 6 +++++- src/plonk/verifier.rs | 11 +++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index e0accee..244d205 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -91,6 +91,10 @@ impl VerifyingKey { } } +#[derive(Clone, Copy, Debug)] +struct Theta; +type ChallengeTheta = ChallengeScalar; + #[derive(Clone, Copy, Debug)] struct Beta; type ChallengeBeta = ChallengeScalar; diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index bce29c1..3c5830f 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,7 +3,8 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - permutation, ChallengeBeta, ChallengeGamma, ChallengeX, ChallengeY, Error, Proof, ProvingKey, + permutation, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, + Proof, ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ @@ -168,6 +169,9 @@ impl Proof { }) .collect(); + // Sample theta challenge for keeping lookup columns linearly independent + let theta = ChallengeTheta::::get(&mut transcript); + // Sample beta challenge let beta = ChallengeBeta::get(&mut transcript); diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 4925dbd..c005c9d 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -1,7 +1,10 @@ use ff::Field; use std::iter; -use super::{ChallengeBeta, ChallengeGamma, ChallengeX, ChallengeY, Error, Proof, VerifyingKey}; +use super::{ + ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, Proof, + VerifyingKey, +}; use crate::arithmetic::{CurveAffine, FieldExt}; use crate::poly::{ commitment::{Guard, Params, MSM}, @@ -45,6 +48,9 @@ impl<'a, C: CurveAffine> Proof { .map_err(|_| Error::TranscriptError)?; } + // Sample theta challenge for keeping lookup columns linearly independent + let theta = ChallengeTheta::get(&mut transcript); + // Sample beta challenge let beta = ChallengeBeta::get(&mut transcript); @@ -72,7 +78,7 @@ impl<'a, C: CurveAffine> Proof { // This check ensures the circuit is satisfied so long as the polynomial // commitments open to the correct values. - self.check_hx(params, vk, beta, gamma, y, x)?; + self.check_hx(params, vk, theta, beta, gamma, y, x)?; for eval in self .advice_evals @@ -189,6 +195,7 @@ impl<'a, C: CurveAffine> Proof { &self, params: &'a Params, vk: &VerifyingKey, + theta: ChallengeTheta, beta: ChallengeBeta, gamma: ChallengeGamma, y: ChallengeY, From 02344eb7118f8bde0a3355540eabf248c2bd38c2 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 14:53:20 +0800 Subject: [PATCH 03/18] Add lookup mod and structs --- src/plonk.rs | 1 + src/plonk/lookup.rs | 33 ++++++++++++++++++++++ src/plonk/lookup/prover.rs | 53 ++++++++++++++++++++++++++++++++++++ src/plonk/lookup/verifier.rs | 4 +++ 4 files changed, 91 insertions(+) create mode 100644 src/plonk/lookup.rs create mode 100644 src/plonk/lookup/prover.rs create mode 100644 src/plonk/lookup/verifier.rs diff --git a/src/plonk.rs b/src/plonk.rs index 244d205..376fc91 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -11,6 +11,7 @@ use crate::transcript::ChallengeScalar; mod circuit; mod keygen; +mod lookup; mod permutation; mod prover; mod verifier; diff --git a/src/plonk/lookup.rs b/src/plonk/lookup.rs new file mode 100644 index 0000000..640faef --- /dev/null +++ b/src/plonk/lookup.rs @@ -0,0 +1,33 @@ +use super::circuit::{Any, Column}; +use crate::arithmetic::CurveAffine; + +mod prover; +mod verifier; + +#[derive(Clone, Debug)] +pub(crate) struct Argument { + pub input_columns: Vec>, + pub table_columns: Vec>, +} + +impl Argument { + pub fn new(input_columns: &[Column], table_columns: &[Column]) -> Self { + assert_eq!(input_columns.len(), table_columns.len()); + Argument { + input_columns: input_columns.to_vec(), + table_columns: table_columns.to_vec(), + } + } +} + +#[derive(Clone, Debug)] +pub(crate) struct Proof { + product_commitment: C, + product_eval: C::Scalar, + product_inv_eval: C::Scalar, + permuted_input_commitment: C, + permuted_table_commitment: C, + permuted_input_eval: C::Scalar, + permuted_input_inv_eval: C::Scalar, + permuted_table_eval: C::Scalar, +} diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs new file mode 100644 index 0000000..7fc098a --- /dev/null +++ b/src/plonk/lookup/prover.rs @@ -0,0 +1,53 @@ +use crate::arithmetic::CurveAffine; +use crate::poly::{commitment::Blind, Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}; + +#[derive(Clone, Debug)] +pub(crate) struct Permuted { + permuted_input_value: Polynomial, + permuted_input_poly: Polynomial, + permuted_input_coset: Polynomial, + permuted_input_inv_coset: Polynomial, + permuted_input_blind: Blind, + permuted_input_commitment: C, + permuted_table_value: Polynomial, + permuted_table_poly: Polynomial, + permuted_table_coset: Polynomial, + permuted_table_blind: Blind, + permuted_table_commitment: C, +} + +#[derive(Clone, Debug)] +pub(crate) struct Product { + product_poly: Polynomial, + product_coset: Polynomial, + product_inv_coset: Polynomial, + product_blind: Blind, + product_commitment: C, +} + +#[derive(Clone, Debug)] +pub(crate) struct Committed { + permuted: Permuted, + product: Product, +} + +pub(crate) struct Constructed { + permuted_input_poly: Polynomial, + permuted_input_blind: Blind, + permuted_input_commitment: C, + permuted_table_poly: Polynomial, + permuted_table_blind: Blind, + permuted_table_commitment: C, + product_poly: Polynomial, + product_blind: Blind, + product_commitment: C, +} + +pub(crate) struct Evaluated { + constructed: Constructed, + pub product_eval: C::Scalar, + pub product_inv_eval: C::Scalar, + pub permuted_input_eval: C::Scalar, + pub permuted_input_inv_eval: C::Scalar, + pub permuted_table_eval: C::Scalar, +} diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs new file mode 100644 index 0000000..594644e --- /dev/null +++ b/src/plonk/lookup/verifier.rs @@ -0,0 +1,4 @@ +use super::Proof; +use crate::arithmetic::CurveAffine; + +impl Proof {} From 46eed7be93e26e2e375b7eed3535e00c9e25af5b Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 15:22:11 +0800 Subject: [PATCH 04/18] Add commit_permuted() in lookup::prover --- src/plonk.rs | 4 +- src/plonk/lookup/prover.rs | 205 ++++++++++++++++++++++++++++++++++++- 2 files changed, 206 insertions(+), 3 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 376fc91..020c36e 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -6,7 +6,9 @@ //! [plonk]: https://eprint.iacr.org/2019/953 use crate::arithmetic::CurveAffine; -use crate::poly::{multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}; +use crate::poly::{ + multiopen, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, +}; use crate::transcript::ChallengeScalar; mod circuit; diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 7fc098a..260f030 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -1,5 +1,18 @@ -use crate::arithmetic::CurveAffine; -use crate::poly::{commitment::Blind, Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial}; +use super::super::{ + circuit::{Advice, Any, Aux, Column, Fixed}, + Error, ProvingKey, +}; +use super::Argument; +use crate::{ + arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, + poly::{ + commitment::{Blind, Params}, + Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, + }, + transcript::{Hasher, Transcript}, +}; +use ff::Field; +use std::collections::BTreeMap; #[derive(Clone, Debug)] pub(crate) struct Permuted { @@ -51,3 +64,191 @@ pub(crate) struct Evaluated { pub permuted_input_inv_eval: C::Scalar, pub permuted_table_eval: C::Scalar, } + +impl Argument { + /// Given a Lookup with input columns [A_0, A_1, ..., A_m] and table columns + /// [S_0, S_1, ..., S_m], this method + /// - constructs A_compressed = A_0 + theta A_1 + theta^2 A_2 + ... and + /// S_compressed = S_0 + theta S_1 + theta^2 S_2 + ..., + /// - permutes A_compressed and S_compressed using permute_column_pair() helper, + /// obtaining A' and S', and + /// - constructs Permuted struct using permuted_input_value = A', and + /// permuted_table_value = S'. + /// The Permuted struct is used to update the Lookup, and is then returned. + pub(in crate::plonk) fn commit_permuted< + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + >( + &self, + pk: &ProvingKey, + params: &Params, + domain: &EvaluationDomain, + theta: C::Scalar, + advice_values: &[Polynomial], + fixed_values: &[Polynomial], + aux_values: &[Polynomial], + transcript: &mut Transcript, + ) -> Result, Error> { + // Values of input columns involved in the lookup + let unpermuted_input_values: Vec> = self + .input_columns + .iter() + .map(|&input| match input.column_type() { + Any::Advice => advice_values[input.index()].clone(), + Any::Fixed => fixed_values[input.index()].clone(), + Any::Aux => aux_values[input.index()].clone(), + }) + .collect(); + + // Compressed version of input columns + let compressed_input_value = unpermuted_input_values + .iter() + .fold(domain.empty_lagrange(), |acc, input| acc * theta + input); + + // Values of table columns involved in the lookup + let unpermuted_table_values: Vec> = self + .table_columns + .iter() + .map(|&table| match table.column_type() { + Any::Advice => advice_values[table.index()].clone(), + Any::Fixed => fixed_values[table.index()].clone(), + Any::Aux => aux_values[table.index()].clone(), + }) + .collect(); + + // Compressed version of table columns + let compressed_table_value = unpermuted_table_values + .iter() + .fold(domain.empty_lagrange(), |acc, table| acc * theta + table); + + // Permute compressed (InputColumn, TableColumn) pair + let (permuted_input_value, permuted_table_value) = + permute_column_pair::(domain, &compressed_input_value, &compressed_table_value)?; + + // Construct Permuted struct + let permuted_input_poly = pk.vk.domain.lagrange_to_coeff(permuted_input_value.clone()); + let permuted_input_coset = pk + .vk + .domain + .coeff_to_extended(permuted_input_poly.clone(), Rotation::default()); + let permuted_input_inv_coset = pk + .vk + .domain + .coeff_to_extended(permuted_input_poly.clone(), Rotation(-1)); + + let permuted_input_blind = Blind(C::Scalar::rand()); + let permuted_input_commitment = params + .commit_lagrange(&permuted_input_value, permuted_input_blind) + .to_affine(); + + let permuted_table_poly = pk.vk.domain.lagrange_to_coeff(permuted_table_value.clone()); + let permuted_table_coset = pk + .vk + .domain + .coeff_to_extended(permuted_table_poly.clone(), Rotation::default()); + let permuted_table_blind = Blind(C::Scalar::rand()); + let permuted_table_commitment = params + .commit_lagrange(&permuted_table_value, permuted_table_blind) + .to_affine(); + + // Hash each permuted input commitment + transcript + .absorb_point(&permuted_input_commitment) + .map_err(|_| Error::TranscriptError)?; + + // Hash each permuted table commitment + transcript + .absorb_point(&permuted_table_commitment) + .map_err(|_| Error::TranscriptError)?; + + Ok(Permuted { + permuted_input_value, + permuted_input_poly, + permuted_input_coset, + permuted_input_inv_coset, + permuted_input_blind, + permuted_input_commitment, + permuted_table_value, + permuted_table_poly, + permuted_table_coset, + permuted_table_blind, + permuted_table_commitment, + }) + } +} + +/// Given a column of input values A and a column of table values S, +/// this method permutes A and S to produce A' and S', such that: +/// - like values in A' are vertically adjacent to each other; and +/// - the first row in a sequence of like values in A' is the row +/// that has the corresponding value in S'. +/// This method returns (A', S') if no errors are encountered. +fn permute_column_pair( + domain: &EvaluationDomain, + input_column: &Polynomial, + table_column: &Polynomial, +) -> Result< + ( + Polynomial, + Polynomial, + ), + Error, +> { + let mut permuted_input_column = input_column.clone(); + + // Sort input lookup column values + permuted_input_column.sort(); + + // A BTreeMap of each unique element in the table column and its count + let mut leftover_table_map: BTreeMap = + table_column.iter().fold(BTreeMap::new(), |mut acc, coeff| { + *acc.entry(*coeff).or_insert(0) += 1; + acc + }); + let mut repeated_input_rows = vec![]; + let mut permuted_table_coeffs = vec![C::Scalar::zero(); table_column.len()]; + + for row in 0..permuted_input_column.len() { + let input_value = permuted_input_column[row]; + + // If this is the first occurence of `input_value` in the input column + if row == 0 || input_value != permuted_input_column[row - 1] { + permuted_table_coeffs[row] = input_value; + // Remove one instance of input_value from leftover_table_map + if let Some(count) = leftover_table_map.get_mut(&input_value) { + assert!(*count > 0); + *count -= 1; + } else { + // Return error if input_value not found + return Err(Error::ConstraintSystemFailure); + } + // If input value is repeated + } else { + repeated_input_rows.push(row); + } + } + + // Populate permuted table at unfilled rows with leftover table elements + for (coeff, count) in leftover_table_map.iter() { + for _ in 0..*count { + permuted_table_coeffs[repeated_input_rows.pop().unwrap() as usize] = *coeff; + } + } + assert!(repeated_input_rows.is_empty()); + + let mut permuted_table_column = domain.empty_lagrange(); + parallelize( + &mut permuted_table_column, + |permuted_table_column, start| { + for (permuted_table_value, permuted_table_coeff) in permuted_table_column + .iter_mut() + .zip(permuted_table_coeffs[start..].iter()) + { + *permuted_table_value += permuted_table_coeff; + } + }, + ); + + Ok((permuted_input_column, permuted_table_column)) +} From 2d0f4a11e34b08de150b6d43612764f3c226146a Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 15:30:52 +0800 Subject: [PATCH 05/18] Add commit_product() to lookup::prover --- src/plonk/lookup/prover.rs | 194 +++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 260f030..25f807d 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -176,6 +176,200 @@ impl Argument { permuted_table_commitment, }) } + + /// Given a Lookup with input columns, table columns, and the permuted + /// input column and permuted table column, this method constructs the + /// grand product polynomial over the lookup. The grand product polynomial + /// is used to populate the Product struct. The Product struct is + /// added to the Lookup and finally returned by the method. + pub(in crate::plonk) fn commit_product< + C: CurveAffine, + HBase: Hasher, + HScalar: Hasher, + >( + &self, + permuted: &Permuted, + pk: &ProvingKey, + params: &Params, + theta: C::Scalar, + beta: C::Scalar, + gamma: C::Scalar, + advice_values: &[Polynomial], + fixed_values: &[Polynomial], + aux_values: &[Polynomial], + transcript: &mut Transcript, + ) -> Result, Error> { + let permuted = permuted.clone(); + let unpermuted_input_values: Vec> = self + .input_columns + .iter() + .map(|&input| match input.column_type() { + Any::Advice => advice_values[input.index()].clone(), + Any::Fixed => fixed_values[input.index()].clone(), + Any::Aux => aux_values[input.index()].clone(), + }) + .collect(); + + let unpermuted_table_values: Vec> = self + .table_columns + .iter() + .map(|&table| match table.column_type() { + Any::Advice => advice_values[table.index()].clone(), + Any::Fixed => fixed_values[table.index()].clone(), + Any::Aux => aux_values[table.index()].clone(), + }) + .collect(); + + // Goal is to compute the products of fractions + // + // (a_1(\omega^i) + \theta a_2(\omega^i) + ... + beta)(s_1(\omega^i) + \theta(\omega^i) + ... + \gamma) / + // (a'(\omega^i) + \beta)(s'(\omega^i) + \gamma) + // + // where a_j(X) is the jth input column in this lookup, + // where a'(X) is the compression of the permuted input columns, + // s_j(X) is the jth table column in this lookup, + // s'(X) is the compression of the permuted table columns, + // and i is the ith row of the column. + let mut lookup_product = vec![C::Scalar::one(); params.n as usize]; + + // Denominator uses the permuted input column and permuted table column + parallelize(&mut lookup_product, |lookup_product, start| { + for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product + .iter_mut() + .zip(permuted.permuted_input_value[start..].iter()) + .zip(permuted.permuted_table_value[start..].iter()) + { + *lookup_product *= &(beta + permuted_input_value); + *lookup_product *= &(gamma + permuted_table_value); + } + }); + + // Batch invert to obtain the denominators for the lookup product + // polynomials + lookup_product.iter_mut().batch_invert(); + + // Finish the computation of the entire fraction by computing the numerators + // (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // Compress unpermuted input columns + let mut input_term = vec![C::Scalar::zero(); params.n as usize]; + for unpermuted_input_value in unpermuted_input_values.iter() { + parallelize(&mut input_term, |input_term, start| { + for (input_term, input_value) in input_term + .iter_mut() + .zip(unpermuted_input_value[start..].iter()) + { + *input_term *= θ + *input_term += input_value; + } + }); + } + + // Compress unpermuted table columns + let mut table_term = vec![C::Scalar::zero(); params.n as usize]; + for unpermuted_table_value in unpermuted_table_values.iter() { + parallelize(&mut table_term, |table_term, start| { + for (table_term, fixed_value) in table_term + .iter_mut() + .zip(unpermuted_table_value[start..].iter()) + { + *table_term *= θ + *table_term += fixed_value; + } + }); + } + + // Add \beta and \gamma offsets + parallelize(&mut lookup_product, |product, start| { + for ((product, input_term), table_term) in product + .iter_mut() + .zip(input_term[start..].iter()) + .zip(table_term[start..].iter()) + { + *product *= &(*input_term + &beta); + *product *= &(*table_term + &gamma); + } + }); + + // The product vector is a vector of products of fractions of the form + // + // (a_1(\omega^i) + \theta a_2(\omega^i) + ... + \beta)(s_1(\omega^i) + \theta s_2(\omega^i) + ... + \gamma)/ + // (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma) + // + // where a_j(\omega^i) is the jth input column in this lookup, + // a'j(\omega^i) is the permuted input column, + // s_j(\omega^i) is the jth table column in this lookup, + // s'(\omega^i) is the permuted table column, + // and i is the ith row of the column. + + // Compute the evaluations of the lookup product polynomial + // over our domain, starting with z[0] = 1 + let mut z = vec![C::Scalar::one()]; + for row in 1..(params.n as usize) { + let mut tmp = z[row - 1]; + tmp *= &lookup_product[row]; + z.push(tmp); + } + let z = pk.vk.domain.lagrange_from_vec(z); + + #[cfg(feature = "sanity-checks")] + // This test works only with intermediate representations in this method. + // It can be used for debugging purposes. + { + // While in Lagrange basis, check that product is correctly constructed + let n = params.n as usize; + + // z'(X) (a'(X) + \beta) (s'(X) + \gamma) + // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + for i in 0..n { + let prev_idx = (n + i - 1) % n; + + let mut left = z[i]; + let permuted_input_value = &permuted.permuted_input_value[i]; + + let permuted_table_value = &permuted.permuted_table_value[i]; + + left *= &(*beta + permuted_input_value); + left *= &(*gamma + permuted_table_value); + + let mut right = z[prev_idx]; + let mut input_term = unpermuted_input_values + .iter() + .fold(C::Scalar::zero(), |acc, input| acc * &theta + &input[i]); + + let mut table_term = unpermuted_table_values + .iter() + .fold(C::Scalar::zero(), |acc, table| acc * &theta + &table[i]); + + input_term += &(*beta); + table_term += &(*gamma); + right *= &(input_term * &table_term); + + assert_eq!(left, right); + } + } + + 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::default()); + let product_inv_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation(-1)); + + // Hash each product commitment + transcript + .absorb_point(&product_commitment) + .map_err(|_| Error::TranscriptError)?; + + Ok(Product:: { + product_poly: z, + product_coset, + product_inv_coset, + product_commitment, + product_blind, + }) + } } /// Given a column of input values A and a column of table values S, From 39df4954b5585252033f80ba42663c0291dabd30 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 16:19:07 +0800 Subject: [PATCH 06/18] Add Committed::construct() to lookup::prover --- Cargo.toml | 3 + src/plonk/lookup/prover.rs | 167 ++++++++++++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8d00e9f..e8ccfdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,6 @@ ff = "0.8" metrics = "=0.13.0-alpha.11" num_cpus = "1.13" rand = "0.7" + +[features] +sanity-checks = [] diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 25f807d..5c32256 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -12,7 +12,7 @@ use crate::{ transcript::{Hasher, Transcript}, }; use ff::Field; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, convert::TryFrom, iter}; #[derive(Clone, Debug)] pub(crate) struct Permuted { @@ -372,6 +372,171 @@ impl Argument { } } +impl Committed { + /// Given a Lookup with input columns, table columns, permuted input + /// column, permuted table column, and grand product polynomial, this + /// method constructs constraints that must hold between these values. + /// This method returns the constraints as a vector of polynomials in + /// the extended evaluation domain. + pub(in crate::plonk) fn construct<'a>( + self, + pk: &'a ProvingKey, + theta: C::Scalar, + beta: C::Scalar, + gamma: C::Scalar, + argument: Argument, + advice_cosets: &'a [Polynomial], + fixed_cosets: &'a [Polynomial], + aux_cosets: &'a [Polynomial], + ) -> Result< + ( + Constructed, + impl Iterator> + 'a, + ), + Error, + > { + let permuted = self.permuted; + let product = self.product; + let unpermuted_input_cosets: Vec> = argument + .input_columns + .iter() + .map(|&input| match input.column_type() { + Any::Advice => advice_cosets[pk + .vk + .cs + .get_advice_query_index(Column::::try_from(input).unwrap(), 0)] + .clone(), + Any::Fixed => fixed_cosets[pk + .vk + .cs + .get_fixed_query_index(Column::::try_from(input).unwrap(), 0)] + .clone(), + Any::Aux => aux_cosets[pk + .vk + .cs + .get_aux_query_index(Column::::try_from(input).unwrap(), 0)] + .clone(), + }) + .collect(); + + let unpermuted_table_cosets: Vec> = argument + .table_columns + .iter() + .map(|&table| match table.column_type() { + Any::Advice => advice_cosets[pk + .vk + .cs + .get_advice_query_index(Column::::try_from(table).unwrap(), 0)] + .clone(), + Any::Fixed => fixed_cosets[pk + .vk + .cs + .get_fixed_query_index(Column::::try_from(table).unwrap(), 0)] + .clone(), + Any::Aux => aux_cosets[pk + .vk + .cs + .get_aux_query_index(Column::::try_from(table).unwrap(), 0)] + .clone(), + }) + .collect(); + + let expressions = iter::empty() + // l_0(X) * (1 - z'(X)) = 0 + .chain(Some( + Polynomial::one_minus(product.product_coset.clone()) * &pk.l0, + )) + // z'(X) (a'(X) + \beta) (s'(X) + \gamma) + // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + .chain({ + // z'(X) (a'(X) + \beta) (s'(X) + \gamma) + let mut left = product.product_coset.clone(); + parallelize(&mut left, |left, start| { + for ((left, permuted_input), permuted_table) in left + .iter_mut() + .zip(permuted.permuted_input_coset[start..].iter()) + .zip(permuted.permuted_table_coset[start..].iter()) + { + *left *= &(*permuted_input + &beta); + *left *= &(*permuted_table + &gamma); + } + }); + + // z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + let mut right = product.product_inv_coset; + let mut input_terms = pk.vk.domain.empty_extended(); + + // Compress the unpermuted input columns + for input in unpermuted_input_cosets.iter() { + // (a_1(X) + \theta a_2(X) + ...) + parallelize(&mut input_terms, |input_term, start| { + for (input_term, input) in input_term.iter_mut().zip(input[start..].iter()) + { + *input_term *= θ + *input_term += input; + } + }); + } + + let mut table_terms = pk.vk.domain.empty_extended(); + // Compress the unpermuted table columns + for table in unpermuted_table_cosets.iter() { + // (s_1(X) + \theta s_2(X) + ...) + parallelize(&mut table_terms, |table_term, start| { + for (table_term, table) in table_term.iter_mut().zip(table[start..].iter()) + { + *table_term *= θ + *table_term += table; + } + }); + } + + // Add \beta and \gamma offsets + parallelize(&mut right, |right, start| { + for ((right, input_term), table_term) in right + .iter_mut() + .zip(input_terms[start..].iter()) + .zip(table_terms[start..].iter()) + { + *right *= &(*input_term + &beta); + *right *= &(*table_term + &gamma); + } + }); + + Some(left - &right) + }) + // Check that the first values in the permuted input column and permuted + // fixed column are the same. + // l_0(X) * (a'(X) - s'(X)) = 0 + .chain(Some( + (permuted.permuted_input_coset.clone() - &permuted.permuted_table_coset) * &pk.l0, + )) + // Check that each value in the permuted lookup input column is either + // equal to the value above it, or the value at the same index in the + // permuted table column. + // (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), + )); + + Ok(( + Constructed { + permuted_input_poly: permuted.permuted_input_poly, + permuted_input_blind: permuted.permuted_input_blind, + permuted_input_commitment: permuted.permuted_input_commitment, + permuted_table_poly: permuted.permuted_table_poly, + permuted_table_blind: permuted.permuted_table_blind, + permuted_table_commitment: permuted.permuted_table_commitment, + product_poly: product.product_poly, + product_blind: product.product_blind, + product_commitment: product.product_commitment, + }, + expressions, + )) + } +} + /// Given a column of input values A and a column of table values S, /// this method permutes A and S to produce A' and S', such that: /// - like values in A' are vertically adjacent to each other; and From 6ccf58fc7cef31df9b6d08ff81dddcdc3213a658 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 16:33:23 +0800 Subject: [PATCH 07/18] Add Constructed::evaluate() to lookup::prover --- src/plonk/lookup/prover.rs | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 5c32256..a470b71 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -1,6 +1,6 @@ use super::super::{ circuit::{Advice, Any, Aux, Column, Fixed}, - Error, ProvingKey, + ChallengeX, Error, ProvingKey, }; use super::Argument; use crate::{ @@ -537,6 +537,44 @@ impl Committed { } } +impl Constructed { + pub(in crate::plonk) fn evaluate, HScalar: Hasher>( + self, + pk: &ProvingKey, + x: ChallengeX, + transcript: &mut Transcript, + ) -> Evaluated { + let domain = &pk.vk.domain; + let x_inv = domain.rotate_omega(*x, Rotation(-1)); + + let product_eval = eval_polynomial(&self.product_poly, *x); + let product_inv_eval = eval_polynomial(&self.product_poly, x_inv); + let permuted_input_eval = eval_polynomial(&self.permuted_input_poly, *x); + let permuted_input_inv_eval = eval_polynomial(&self.permuted_input_poly, x_inv); + let permuted_table_eval = eval_polynomial(&self.permuted_table_poly, *x); + + // Hash each advice evaluation + for eval in iter::empty() + .chain(Some(product_eval)) + .chain(Some(product_inv_eval)) + .chain(Some(permuted_input_eval)) + .chain(Some(permuted_input_inv_eval)) + .chain(Some(permuted_table_eval)) + { + transcript.absorb_scalar(eval); + } + + Evaluated { + constructed: self, + product_eval, + product_inv_eval, + permuted_input_eval, + permuted_input_inv_eval, + permuted_table_eval, + } + } +} + /// Given a column of input values A and a column of table values S, /// this method permutes A and S to produce A' and S', such that: /// - like values in A' are vertically adjacent to each other; and From c692311a12b69aa50ae18063ff2ba4b5109a9bb6 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 16:46:11 +0800 Subject: [PATCH 08/18] Add Evaluated::open() and Evaluated::build() to lookup::prover --- src/plonk/lookup/prover.rs | 63 +++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index a470b71..638bc32 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -2,11 +2,12 @@ use super::super::{ circuit::{Advice, Any, Aux, Column, Fixed}, ChallengeX, Error, ProvingKey, }; -use super::Argument; +use super::{Argument, Proof}; use crate::{ arithmetic::{eval_polynomial, parallelize, BatchInvert, Curve, CurveAffine, FieldExt}, poly::{ commitment::{Blind, Params}, + multiopen::ProverQuery, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation, }, transcript::{Hasher, Transcript}, @@ -575,6 +576,66 @@ impl Constructed { } } +impl Evaluated { + pub(in crate::plonk) fn open<'a>( + &'a self, + pk: &'a ProvingKey, + x: ChallengeX, + ) -> impl Iterator> + Clone { + let x_inv = pk.vk.domain.rotate_omega(*x, Rotation(-1)); + + iter::empty() + // Open lookup product commitments at x + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.product_poly, + blind: self.constructed.product_blind, + eval: self.product_eval, + })) + // Open lookup input commitments at x + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.permuted_input_poly, + blind: self.constructed.permuted_input_blind, + eval: self.permuted_input_eval, + })) + // Open lookup table commitments at x + .chain(Some(ProverQuery { + point: *x, + poly: &self.constructed.permuted_table_poly, + blind: self.constructed.permuted_table_blind, + eval: self.permuted_table_eval, + })) + // Open lookup input commitments at x_inv + .chain(Some(ProverQuery { + point: x_inv, + poly: &self.constructed.permuted_input_poly, + blind: self.constructed.permuted_input_blind, + eval: self.permuted_input_eval, + })) + // Open lookup product commitments at x_inv + .chain(Some(ProverQuery { + point: x_inv, + poly: &self.constructed.product_poly, + blind: self.constructed.product_blind, + eval: self.product_eval, + })) + } + + pub(crate) fn build(self) -> Proof { + Proof { + product_commitment: self.constructed.product_commitment, + product_eval: self.product_eval, + product_inv_eval: self.product_inv_eval, + permuted_input_commitment: self.constructed.permuted_input_commitment, + permuted_table_commitment: self.constructed.permuted_table_commitment, + permuted_input_eval: self.permuted_input_eval, + permuted_input_inv_eval: self.permuted_input_inv_eval, + permuted_table_eval: self.permuted_table_eval, + } + } +} + /// Given a column of input values A and a column of table values S, /// this method permutes A and S to produce A' and S', such that: /// - like values in A' are vertically adjacent to each other; and From 19c1b20063d1a5eaef78d263844acdfd58f12200 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Tue, 1 Dec 2020 17:11:47 +0800 Subject: [PATCH 09/18] Add lookup::verifier methods --- src/plonk/lookup/verifier.rs | 159 ++++++++++++++++++++++++++++++++++- 1 file changed, 156 insertions(+), 3 deletions(-) diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index 594644e..c22ede1 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -1,4 +1,157 @@ -use super::Proof; -use crate::arithmetic::CurveAffine; +use std::iter; -impl Proof {} +use super::super::circuit::Any; +use super::{Argument, Proof}; +use crate::{ + arithmetic::CurveAffine, + plonk::{ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, VerifyingKey}, + poly::{multiopen::VerifierQuery, Rotation}, + transcript::{Hasher, Transcript}, +}; +use ff::Field; + +impl Proof { + pub(crate) fn absorb_permuted_commitments< + HBase: Hasher, + HScalar: Hasher, + >( + &self, + transcript: &mut Transcript, + ) -> Result<(), Error> { + transcript + .absorb_point(&self.permuted_input_commitment) + .map_err(|_| Error::TranscriptError)?; + transcript + .absorb_point(&self.permuted_table_commitment) + .map_err(|_| Error::TranscriptError)?; + Ok(()) + } + + pub(crate) fn absorb_product_commitment, HScalar: Hasher>( + &self, + transcript: &mut Transcript, + ) -> Result<(), Error> { + transcript + .absorb_point(&self.product_commitment) + .map_err(|_| Error::TranscriptError)?; + Ok(()) + } + + pub(in crate::plonk) fn expressions<'a>( + &'a self, + vk: &'a VerifyingKey, + l_0: C::Scalar, + argument: Argument, + theta: ChallengeTheta, + beta: ChallengeBeta, + gamma: ChallengeGamma, + advice_evals: &[C::Scalar], + fixed_evals: &[C::Scalar], + aux_evals: &[C::Scalar], + ) -> impl Iterator + 'a { + let product_expression = || { + // z'(X) (a'(X) + \beta) (s'(X) + \gamma) + // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + let left = self.product_eval + * &(self.permuted_input_eval + &beta) + * &(self.permuted_table_eval + &gamma); + + let mut right = self.product_inv_eval; + let mut input_term = C::Scalar::zero(); + for &input in argument.input_columns.iter() { + let index = vk.cs.get_any_query_index(input, 0); + let eval = match input.column_type() { + Any::Advice => advice_evals[index], + Any::Fixed => fixed_evals[index], + Any::Aux => aux_evals[index], + }; + input_term *= θ + input_term += &eval; + } + input_term += β + + let mut table_term = C::Scalar::zero(); + for &table in argument.table_columns.iter() { + let index = vk.cs.get_any_query_index(table, 0); + let eval = match table.column_type() { + Any::Advice => advice_evals[index], + Any::Fixed => fixed_evals[index], + Any::Aux => aux_evals[index], + }; + table_term *= θ + table_term += &eval; + } + table_term += γ + + right *= &(input_term * &table_term); + left - &right + }; + + std::iter::empty() + .chain( + // l_0(X) * (1 - z'(X)) = 0 + Some(l_0 * &(C::Scalar::one() - &self.product_eval)), + ) + .chain( + // z'(X) (a'(X) + \beta) (s'(X) + \gamma) + // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + Some(product_expression()), + ) + .chain(Some( + l_0 * &(self.permuted_input_eval - &self.permuted_table_eval), + )) + .chain(Some( + (self.permuted_input_eval - &self.permuted_table_eval) + * &(self.permuted_input_eval - &self.permuted_input_inv_eval), + )) + } + + pub(crate) fn evals(&self) -> impl Iterator { + iter::empty() + .chain(Some(self.product_eval)) + .chain(Some(self.product_inv_eval)) + .chain(Some(self.permuted_input_eval)) + .chain(Some(self.permuted_input_inv_eval)) + .chain(Some(self.permuted_table_eval)) + } + + pub(in crate::plonk) fn queries<'a>( + &'a self, + vk: &'a VerifyingKey, + x: ChallengeX, + ) -> impl Iterator> + Clone { + let x_inv = vk.domain.rotate_omega(*x, Rotation(-1)); + + iter::empty() + // Open lookup product commitments at x + .chain(Some(VerifierQuery { + point: *x, + commitment: &self.product_commitment, + eval: self.product_eval, + })) + // Open lookup input commitments at x + .chain(Some(VerifierQuery { + point: *x, + commitment: &self.permuted_input_commitment, + eval: self.permuted_input_eval, + })) + // Open lookup table commitments at x + .chain(Some(VerifierQuery { + point: *x, + commitment: &self.permuted_table_commitment, + eval: self.permuted_table_eval, + })) + // Open lookup input commitments at \omega^{-1} x + .chain(Some(VerifierQuery { + point: x_inv, + commitment: &self.permuted_input_commitment, + eval: self.permuted_input_inv_eval, + })) + // Open lookup product commitments at \omega^{-1} x + .chain(Some(VerifierQuery { + point: x_inv, + commitment: &self.product_commitment, + eval: self.product_inv_eval, + })) + } +} From 0c81e9adabcf60a394dcded72e462a4850b0f5c6 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 2 Dec 2020 03:00:59 +0800 Subject: [PATCH 10/18] Use lookup mod in plonk::prover and plonk::verifier --- src/plonk.rs | 1 + src/plonk/circuit.rs | 7 ++- src/plonk/lookup.rs | 2 +- src/plonk/lookup/prover.rs | 42 +++++++------- src/plonk/lookup/verifier.rs | 14 ++--- src/plonk/prover.rs | 104 ++++++++++++++++++++++++++++++++--- src/plonk/verifier.rs | 57 ++++++++++++++++--- 7 files changed, 181 insertions(+), 46 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 020c36e..5df7d86 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -53,6 +53,7 @@ pub struct Proof { advice_commitments: Vec, h_commitments: Vec, permutations: Option>, + lookups: Vec>, advice_evals: Vec, aux_evals: Vec, fixed_evals: Vec, diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 8b0e045..9db8f42 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -4,7 +4,7 @@ use ff::Field; use std::collections::BTreeMap; use std::convert::TryFrom; -use super::{permutation, Error}; +use super::{lookup, permutation, Error}; use crate::poly::Rotation; /// A column type @@ -313,6 +313,10 @@ pub struct ConstraintSystem { // Vector of permutation arguments, where each corresponds to a sequence of columns // that are involved in a permutation argument. pub(crate) permutations: Vec, + + // Vector of lookup arguments, where each corresponds to a sequence of + // input columns and a sequence of table columns involved in the lookup. + pub(crate) lookups: Vec, } impl Default for ConstraintSystem { @@ -330,6 +334,7 @@ impl Default for ConstraintSystem { aux_queries: Vec::new(), rotations, permutations: Vec::new(), + lookups: Vec::new(), } } } diff --git a/src/plonk/lookup.rs b/src/plonk/lookup.rs index 640faef..e24e2b3 100644 --- a/src/plonk/lookup.rs +++ b/src/plonk/lookup.rs @@ -1,7 +1,7 @@ use super::circuit::{Any, Column}; use crate::arithmetic::CurveAffine; -mod prover; +pub(crate) mod prover; mod verifier; #[derive(Clone, Debug)] diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 638bc32..87cdf18 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -1,6 +1,6 @@ use super::super::{ circuit::{Advice, Any, Aux, Column, Fixed}, - ChallengeX, Error, ProvingKey, + ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, ProvingKey, }; use super::{Argument, Proof}; use crate::{ @@ -41,8 +41,8 @@ pub(crate) struct Product { #[derive(Clone, Debug)] pub(crate) struct Committed { - permuted: Permuted, - product: Product, + pub permuted: Permuted, + pub product: Product, } pub(crate) struct Constructed { @@ -85,7 +85,7 @@ impl Argument { pk: &ProvingKey, params: &Params, domain: &EvaluationDomain, - theta: C::Scalar, + theta: ChallengeTheta, advice_values: &[Polynomial], fixed_values: &[Polynomial], aux_values: &[Polynomial], @@ -105,7 +105,7 @@ impl Argument { // Compressed version of input columns let compressed_input_value = unpermuted_input_values .iter() - .fold(domain.empty_lagrange(), |acc, input| acc * theta + input); + .fold(domain.empty_lagrange(), |acc, input| acc * *theta + input); // Values of table columns involved in the lookup let unpermuted_table_values: Vec> = self @@ -121,7 +121,7 @@ impl Argument { // Compressed version of table columns let compressed_table_value = unpermuted_table_values .iter() - .fold(domain.empty_lagrange(), |acc, table| acc * theta + table); + .fold(domain.empty_lagrange(), |acc, table| acc * *theta + table); // Permute compressed (InputColumn, TableColumn) pair let (permuted_input_value, permuted_table_value) = @@ -192,9 +192,9 @@ impl Argument { permuted: &Permuted, pk: &ProvingKey, params: &Params, - theta: C::Scalar, - beta: C::Scalar, - gamma: C::Scalar, + theta: ChallengeTheta, + beta: ChallengeBeta, + gamma: ChallengeGamma, advice_values: &[Polynomial], fixed_values: &[Polynomial], aux_values: &[Polynomial], @@ -240,8 +240,8 @@ impl Argument { .zip(permuted.permuted_input_value[start..].iter()) .zip(permuted.permuted_table_value[start..].iter()) { - *lookup_product *= &(beta + permuted_input_value); - *lookup_product *= &(gamma + permuted_table_value); + *lookup_product *= &(*beta + permuted_input_value); + *lookup_product *= &(*gamma + permuted_table_value); } }); @@ -382,10 +382,10 @@ impl Committed { pub(in crate::plonk) fn construct<'a>( self, pk: &'a ProvingKey, - theta: C::Scalar, - beta: C::Scalar, - gamma: C::Scalar, - argument: Argument, + theta: ChallengeTheta, + beta: ChallengeBeta, + gamma: ChallengeGamma, + argument: &'a Argument, advice_cosets: &'a [Polynomial], fixed_cosets: &'a [Polynomial], aux_cosets: &'a [Polynomial], @@ -458,8 +458,8 @@ impl Committed { .zip(permuted.permuted_input_coset[start..].iter()) .zip(permuted.permuted_table_coset[start..].iter()) { - *left *= &(*permuted_input + &beta); - *left *= &(*permuted_table + &gamma); + *left *= &(*permuted_input + &(*beta)); + *left *= &(*permuted_table + &(*gamma)); } }); @@ -473,7 +473,7 @@ impl Committed { parallelize(&mut input_terms, |input_term, start| { for (input_term, input) in input_term.iter_mut().zip(input[start..].iter()) { - *input_term *= θ + *input_term *= &(*theta); *input_term += input; } }); @@ -486,7 +486,7 @@ impl Committed { parallelize(&mut table_terms, |table_term, start| { for (table_term, table) in table_term.iter_mut().zip(table[start..].iter()) { - *table_term *= θ + *table_term *= &(*theta); *table_term += table; } }); @@ -499,8 +499,8 @@ impl Committed { .zip(input_terms[start..].iter()) .zip(table_terms[start..].iter()) { - *right *= &(*input_term + &beta); - *right *= &(*table_term + &gamma); + *right *= &(*input_term + &(*beta)); + *right *= &(*table_term + &(*gamma)); } }); diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index c22ede1..df7b6d6 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -41,7 +41,7 @@ impl Proof { &'a self, vk: &'a VerifyingKey, l_0: C::Scalar, - argument: Argument, + argument: &'a Argument, theta: ChallengeTheta, beta: ChallengeBeta, gamma: ChallengeGamma, @@ -106,13 +106,13 @@ impl Proof { )) } - pub(crate) fn evals(&self) -> impl Iterator { + pub(crate) fn evals(&self) -> impl Iterator { iter::empty() - .chain(Some(self.product_eval)) - .chain(Some(self.product_inv_eval)) - .chain(Some(self.permuted_input_eval)) - .chain(Some(self.permuted_input_inv_eval)) - .chain(Some(self.permuted_table_eval)) + .chain(Some(&self.product_eval)) + .chain(Some(&self.product_inv_eval)) + .chain(Some(&self.permuted_input_eval)) + .chain(Some(&self.permuted_input_inv_eval)) + .chain(Some(&self.permuted_table_eval)) } pub(in crate::plonk) fn queries<'a>( diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 3c5830f..ea826cf 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,8 +3,8 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - permutation, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, - Proof, ProvingKey, + lookup, permutation, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, + Error, Proof, ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ @@ -172,6 +172,28 @@ impl Proof { // Sample theta challenge for keeping lookup columns linearly independent let theta = ChallengeTheta::::get(&mut transcript); + // Construct permuted values for each lookup + let lookups_permuted = pk + .vk + .cs + .lookups + .iter() + .map(|lookup| { + lookup + .commit_permuted( + &pk, + ¶ms, + &domain, + theta, + &witness.advice, + &pk.fixed_values, + &aux, + &mut transcript, + ) + .unwrap() + }) + .collect::>(); + // Sample beta challenge let beta = ChallengeBeta::get(&mut transcript); @@ -192,6 +214,40 @@ impl Proof { None }; + // Construct products for each lookup + let lookups_products = pk + .vk + .cs + .lookups + .iter() + .zip(lookups_permuted.iter()) + .map(|(lookup, permuted)| { + lookup + .commit_product( + permuted, + &pk, + ¶ms, + theta, + beta, + gamma, + &witness.advice, + &pk.fixed_values, + &aux, + &mut transcript, + ) + .unwrap() + }) + .collect::>(); + + let lookups = lookups_permuted + .iter() + .zip(lookups_products.iter()) + .map(|(permuted, product)| lookup::prover::Committed { + permuted: permuted.clone(), + product: product.clone(), + }) + .collect::>(); + // Obtain challenge for keeping all separate gates linearly independent let y = ChallengeY::::get(&mut transcript); @@ -202,6 +258,25 @@ impl Proof { .map(|(p, expressions)| (Some(p), Some(expressions))) .unwrap_or_default(); + // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. + let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = lookups + .into_iter() + .zip(pk.vk.cs.lookups.iter()) + .map(|(p, argument)| { + p.construct( + pk, + theta, + beta, + gamma, + argument, + &advice_cosets, + &pk.fixed_cosets, + &aux_cosets, + ) + .unwrap() + }) + .unzip(); + // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided let h_poly = iter::empty() // Custom constraints @@ -217,6 +292,8 @@ impl Proof { })) // Permutation constraints, if any. .chain(permutation_expressions.into_iter().flatten()) + // Lookup constraints, if any. + .chain(lookup_expressions.into_iter().flatten()) .fold(domain.empty_extended(), |h_poly, v| h_poly * *y + &v); // Divide by t(X) = X^{params.n} - 1. @@ -296,6 +373,12 @@ impl Proof { // Evaluate the permutations, if any, at omega^i x. let permutations = permutations.map(|p| p.evaluate(pk, x, &mut transcript)); + // Evaluate the lookups, if any, at omega^i x. + let lookups = lookups + .into_iter() + .map(|p| p.evaluate(pk, x, &mut transcript)) + .collect::>(); + let instances = iter::empty() .chain(pk.vk.cs.advice_queries.iter().enumerate().map( @@ -339,13 +422,15 @@ impl Proof { let multiopening = multiopen::Proof::create( params, &mut transcript, - instances.chain( - permutations - .as_ref() - .map(|p| p.open(pk, x)) - .into_iter() - .flatten(), - ), + instances + .chain( + permutations + .as_ref() + .map(|p| p.open(pk, x)) + .into_iter() + .flatten(), + ) + .chain(lookups.iter().map(|p| p.open(pk, x)).into_iter().flatten()), ) .map_err(|_| Error::OpeningError)?; @@ -353,6 +438,7 @@ impl Proof { advice_commitments, h_commitments, permutations: permutations.map(|p| p.build()), + lookups: lookups.into_iter().map(|p| p.build()).collect::>(), advice_evals, fixed_evals, aux_evals, diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index c005c9d..5db3310 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -51,6 +51,11 @@ impl<'a, C: CurveAffine> Proof { // Sample theta challenge for keeping lookup columns linearly independent let theta = ChallengeTheta::get(&mut transcript); + // Hash each lookup permuted commitment + for lookup in &self.lookups { + lookup.absorb_permuted_commitments(&mut transcript)?; + } + // Sample beta challenge let beta = ChallengeBeta::get(&mut transcript); @@ -62,6 +67,11 @@ impl<'a, C: CurveAffine> Proof { p.absorb_commitments(&mut transcript)?; } + // Hash each lookup product commitment + for lookup in &self.lookups { + lookup.absorb_product_commitment(&mut transcript)?; + } + // Sample y challenge, which keeps the gates linearly independent. let y = ChallengeY::get(&mut transcript); @@ -93,6 +103,7 @@ impl<'a, C: CurveAffine> Proof { .into_iter() .flatten(), ) + .chain(self.lookups.iter().map(|p| p.evals()).into_iter().flatten()) { transcript.absorb_scalar(*eval); } @@ -142,13 +153,21 @@ impl<'a, C: CurveAffine> Proof { .verify( params, &mut transcript, - queries.chain( - self.permutations - .as_ref() - .map(|p| p.queries(vk, x)) - .into_iter() - .flatten(), - ), + queries + .chain( + self.permutations + .as_ref() + .map(|p| p.queries(vk, x)) + .into_iter() + .flatten(), + ) + .chain( + self.lookups + .iter() + .map(|p| p.queries(vk, x)) + .into_iter() + .flatten(), + ), msm, ) .map_err(|_| Error::OpeningError) @@ -180,6 +199,10 @@ impl<'a, C: CurveAffine> Proof { .map(|p| p.check_lengths(vk)) .transpose()?; + if self.lookups.len() != vk.cs.lookups.len() { + return Err(Error::IncompatibleParams); + } + // TODO: check h_commitments if self.advice_commitments.len() != vk.cs.num_advice_columns { @@ -230,6 +253,26 @@ impl<'a, C: CurveAffine> Proof { .into_iter() .flatten(), ) + .chain( + self.lookups + .iter() + .zip(vk.cs.lookups.iter()) + .map(|(p, argument)| { + p.expressions( + vk, + l_0, + argument, + theta, + beta, + gamma, + &self.advice_evals, + &self.fixed_evals, + &self.aux_evals, + ) + }) + .into_iter() + .flatten(), + ) .fold(C::Scalar::zero(), |h_eval, v| h_eval * &y + &v); // Compute h(x) from the prover From 0a85e9371425d63e4597d5906af9c6b364011030 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Wed, 2 Dec 2020 03:48:19 +0800 Subject: [PATCH 11/18] Add lookup to circuit and test --- src/plonk.rs | 65 ++++++++++++++++++++++++++++++++++++++++---- src/plonk/circuit.rs | 25 +++++++++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index 5df7d86..c6a8be1 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -121,7 +121,7 @@ fn test_proving() { use crate::poly::commitment::{Blind, Params}; use crate::transcript::DummyHash; use crate::tweedle::{EqAffine, Fp, Fq}; - use circuit::{Advice, Column, Fixed}; + use circuit::{Advice, Any, Column, Fixed}; use std::marker::PhantomData; const K: u32 = 5; @@ -144,6 +144,8 @@ fn test_proving() { sc: Column, sm: Column, sp: Column, + sl: Column, + sl2: Column, perm: usize, perm2: usize, @@ -160,10 +162,12 @@ fn test_proving() { fn public_input(&mut self, f: F) -> Result where F: FnOnce() -> Result; + fn lookup_table(&mut self, values: &[Vec]) -> Result<(), Error>; } struct MyCircuit { a: Option, + lookup_tables: Vec>, } struct StandardPLONK<'a, F: FieldExt, CS: Assignment + 'a> { @@ -297,6 +301,18 @@ fn test_proving() { Ok(Variable(self.config.a, index)) } + fn lookup_table(&mut self, values: &[Vec]) -> Result<(), Error> { + for (&value_0, &value_1) in values[0].iter().zip(values[1].iter()) { + let index = self.current_gate; + + self.current_gate += 1; + self.cs + .assign_fixed(self.config.sl, index, || Ok(value_0))?; + self.cs + .assign_fixed(self.config.sl2, index, || Ok(value_1))?; + } + Ok(()) + } } impl Circuit for MyCircuit { @@ -319,6 +335,30 @@ fn test_proving() { let sb = meta.fixed_column(); let sc = meta.fixed_column(); let sp = meta.fixed_column(); + let sl = meta.fixed_column(); + let sl2 = meta.fixed_column(); + + /* + * A B ... sl sl2 + * [ + * aux 0 ... 0 0 + * a a ... 0 0 + * a a^2 ... 0 0 + * a a ... 0 0 + * a a^2 ... 0 0 + * ... ... ... ... ... + * ... ... ... aux 0 + * ... ... ... a a + * ... ... ... a a^2 + * ... ... ... 0 0 + * + * ] + */ + meta.lookup(&[Column::::from(a)], &[Column::::from(sl)]); + meta.lookup( + &[Column::::from(a), Column::::from(b)], + &[Column::::from(sl), Column::::from(sl2)], + ); meta.create_gate(|meta| { let d = meta.query_advice(d, 1); @@ -355,6 +395,8 @@ fn test_proving() { sc, sm, sp, + sl, + sl2, perm, perm2, } @@ -391,22 +433,33 @@ fn test_proving() { cs.copy(b1, c0)?; } + cs.lookup_table(&self.lookup_tables)?; + Ok(()) } } - let circuit: MyCircuit = MyCircuit { - a: Some(Fp::rand()), + let a = Fp::rand(); + let a_squared = a * &a; + let aux = Fp::one() + Fp::one(); + let lookup_table = vec![aux, a, a, Fp::zero()]; + let lookup_table_2 = vec![Fp::zero(), a, a_squared, Fp::zero()]; + + let empty_circuit: MyCircuit = MyCircuit { + a: None, + lookup_tables: vec![lookup_table.clone(), lookup_table_2.clone()], }; - let empty_circuit: MyCircuit = MyCircuit { a: None }; + let circuit: MyCircuit = MyCircuit { + a: Some(a), + lookup_tables: vec![lookup_table, lookup_table_2], + }; // Initialize the proving key let pk = keygen(¶ms, &empty_circuit).expect("keygen should not fail"); let mut pubinputs = pk.get_vk().get_domain().empty_lagrange(); - pubinputs[0] = Fp::one(); - pubinputs[0] += Fp::one(); + pubinputs[0] = aux; let pubinput = params .commit_lagrange(&pubinputs, Blind::default()) .to_affine(); diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 9db8f42..3b1cd45 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -358,6 +358,31 @@ impl ConstraintSystem { index } + /// Add a lookup argument for some input columns and table columns. + pub fn lookup( + &mut self, + input_columns: &[Column], + table_columns: &[Column], + ) -> usize { + let index = self.lookups.len(); + if self.lookups.is_empty() { + let at = Rotation(-1); + let len = self.rotations.len(); + self.rotations.entry(at).or_insert(PointIndex(len)); + } + + for input in input_columns { + self.query_any_index(*input, 0); + } + for table in table_columns { + self.query_any_index(*table, 0); + } + self.lookups + .push(lookup::Argument::new(input_columns, table_columns)); + + index + } + fn query_fixed_index(&mut self, column: Column, at: i32) -> usize { let at = Rotation(at); { From e51ab7eaa752d175791420d321cc9a16f3dda047 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Dec 2020 11:56:22 +0800 Subject: [PATCH 12/18] Linearise state transition from Argument -> Permuted -> Committed --- src/plonk/lookup/prover.rs | 215 ++++++++++++++++--------------------- src/plonk/prover.rs | 60 +++-------- 2 files changed, 105 insertions(+), 170 deletions(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 87cdf18..e2f5728 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -15,14 +15,18 @@ use crate::{ use ff::Field; use std::{collections::BTreeMap, convert::TryFrom, iter}; -#[derive(Clone, Debug)] -pub(crate) struct Permuted { +#[derive(Debug)] +pub(crate) struct Permuted<'a, C: CurveAffine> { + unpermuted_input_values: Vec<&'a Polynomial>, + unpermuted_input_cosets: Vec<&'a Polynomial>, permuted_input_value: Polynomial, permuted_input_poly: Polynomial, permuted_input_coset: Polynomial, permuted_input_inv_coset: Polynomial, permuted_input_blind: Blind, permuted_input_commitment: C, + unpermuted_table_values: Vec<&'a Polynomial>, + unpermuted_table_cosets: Vec<&'a Polynomial>, permuted_table_value: Polynomial, permuted_table_poly: Polynomial, permuted_table_coset: Polynomial, @@ -30,8 +34,9 @@ pub(crate) struct Permuted { permuted_table_commitment: C, } -#[derive(Clone, Debug)] -pub(crate) struct Product { +#[derive(Debug)] +pub(crate) struct Committed<'a, C: CurveAffine> { + permuted: Permuted<'a, C>, product_poly: Polynomial, product_coset: Polynomial, product_inv_coset: Polynomial, @@ -39,12 +44,6 @@ pub(crate) struct Product { product_commitment: C, } -#[derive(Clone, Debug)] -pub(crate) struct Committed { - pub permuted: Permuted, - pub product: Product, -} - pub(crate) struct Constructed { permuted_input_poly: Polynomial, permuted_input_blind: Blind, @@ -77,6 +76,7 @@ impl Argument { /// permuted_table_value = S'. /// The Permuted struct is used to update the Lookup, and is then returned. pub(in crate::plonk) fn commit_permuted< + 'a, C: CurveAffine, HBase: Hasher, HScalar: Hasher, @@ -86,21 +86,42 @@ impl Argument { params: &Params, domain: &EvaluationDomain, theta: ChallengeTheta, - advice_values: &[Polynomial], - fixed_values: &[Polynomial], - aux_values: &[Polynomial], + advice_values: &'a [Polynomial], + fixed_values: &'a [Polynomial], + aux_values: &'a [Polynomial], + advice_cosets: &'a [Polynomial], + fixed_cosets: &'a [Polynomial], + aux_cosets: &'a [Polynomial], transcript: &mut Transcript, - ) -> Result, Error> { + ) -> Result, Error> { // Values of input columns involved in the lookup - let unpermuted_input_values: Vec> = self + let (unpermuted_input_values, unpermuted_input_cosets): (Vec<_>, Vec<_>) = self .input_columns .iter() .map(|&input| match input.column_type() { - Any::Advice => advice_values[input.index()].clone(), - Any::Fixed => fixed_values[input.index()].clone(), - Any::Aux => aux_values[input.index()].clone(), + Any::Advice => ( + &advice_values[input.index()], + &advice_cosets[pk + .vk + .cs + .get_advice_query_index(Column::::try_from(input).unwrap(), 0)], + ), + Any::Fixed => ( + &fixed_values[input.index()], + &fixed_cosets[pk + .vk + .cs + .get_fixed_query_index(Column::::try_from(input).unwrap(), 0)], + ), + Any::Aux => ( + &aux_values[input.index()], + &aux_cosets[pk + .vk + .cs + .get_aux_query_index(Column::::try_from(input).unwrap(), 0)], + ), }) - .collect(); + .unzip(); // Compressed version of input columns let compressed_input_value = unpermuted_input_values @@ -108,15 +129,33 @@ impl Argument { .fold(domain.empty_lagrange(), |acc, input| acc * *theta + input); // Values of table columns involved in the lookup - let unpermuted_table_values: Vec> = self + let (unpermuted_table_values, unpermuted_table_cosets): (Vec<_>, Vec<_>) = self .table_columns .iter() .map(|&table| match table.column_type() { - Any::Advice => advice_values[table.index()].clone(), - Any::Fixed => fixed_values[table.index()].clone(), - Any::Aux => aux_values[table.index()].clone(), + Any::Advice => ( + &advice_values[table.index()], + &advice_cosets[pk + .vk + .cs + .get_advice_query_index(Column::::try_from(table).unwrap(), 0)], + ), + Any::Fixed => ( + &fixed_values[table.index()], + &fixed_cosets[pk + .vk + .cs + .get_fixed_query_index(Column::::try_from(table).unwrap(), 0)], + ), + Any::Aux => ( + &aux_values[table.index()], + &aux_cosets[pk + .vk + .cs + .get_aux_query_index(Column::::try_from(table).unwrap(), 0)], + ), }) - .collect(); + .unzip(); // Compressed version of table columns let compressed_table_value = unpermuted_table_values @@ -164,12 +203,16 @@ impl Argument { .map_err(|_| Error::TranscriptError)?; Ok(Permuted { + unpermuted_input_values, + unpermuted_input_cosets, permuted_input_value, permuted_input_poly, permuted_input_coset, permuted_input_inv_coset, permuted_input_blind, permuted_input_commitment, + unpermuted_table_values, + unpermuted_table_cosets, permuted_table_value, permuted_table_poly, permuted_table_coset, @@ -177,50 +220,23 @@ impl Argument { permuted_table_commitment, }) } +} +impl<'a, C: CurveAffine> Permuted<'a, C> { /// Given a Lookup with input columns, table columns, and the permuted /// input column and permuted table column, this method constructs the /// grand product polynomial over the lookup. The grand product polynomial /// is used to populate the Product struct. The Product struct is /// added to the Lookup and finally returned by the method. - pub(in crate::plonk) fn commit_product< - C: CurveAffine, - HBase: Hasher, - HScalar: Hasher, - >( - &self, - permuted: &Permuted, + pub(in crate::plonk) fn commit_product, HScalar: Hasher>( + self, pk: &ProvingKey, params: &Params, theta: ChallengeTheta, beta: ChallengeBeta, gamma: ChallengeGamma, - advice_values: &[Polynomial], - fixed_values: &[Polynomial], - aux_values: &[Polynomial], transcript: &mut Transcript, - ) -> Result, Error> { - let permuted = permuted.clone(); - let unpermuted_input_values: Vec> = self - .input_columns - .iter() - .map(|&input| match input.column_type() { - Any::Advice => advice_values[input.index()].clone(), - Any::Fixed => fixed_values[input.index()].clone(), - Any::Aux => aux_values[input.index()].clone(), - }) - .collect(); - - let unpermuted_table_values: Vec> = self - .table_columns - .iter() - .map(|&table| match table.column_type() { - Any::Advice => advice_values[table.index()].clone(), - Any::Fixed => fixed_values[table.index()].clone(), - Any::Aux => aux_values[table.index()].clone(), - }) - .collect(); - + ) -> Result, Error> { // Goal is to compute the products of fractions // // (a_1(\omega^i) + \theta a_2(\omega^i) + ... + beta)(s_1(\omega^i) + \theta(\omega^i) + ... + \gamma) / @@ -237,8 +253,8 @@ impl Argument { parallelize(&mut lookup_product, |lookup_product, start| { for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product .iter_mut() - .zip(permuted.permuted_input_value[start..].iter()) - .zip(permuted.permuted_table_value[start..].iter()) + .zip(self.permuted_input_value[start..].iter()) + .zip(self.permuted_table_value[start..].iter()) { *lookup_product *= &(*beta + permuted_input_value); *lookup_product *= &(*gamma + permuted_table_value); @@ -253,7 +269,7 @@ impl Argument { // (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) // Compress unpermuted input columns let mut input_term = vec![C::Scalar::zero(); params.n as usize]; - for unpermuted_input_value in unpermuted_input_values.iter() { + for unpermuted_input_value in self.unpermuted_input_values.iter() { parallelize(&mut input_term, |input_term, start| { for (input_term, input_value) in input_term .iter_mut() @@ -267,7 +283,7 @@ impl Argument { // Compress unpermuted table columns let mut table_term = vec![C::Scalar::zero(); params.n as usize]; - for unpermuted_table_value in unpermuted_table_values.iter() { + for unpermuted_table_value in self.unpermuted_table_values.iter() { parallelize(&mut table_term, |table_term, start| { for (table_term, fixed_value) in table_term .iter_mut() @@ -325,19 +341,19 @@ impl Argument { let prev_idx = (n + i - 1) % n; let mut left = z[i]; - let permuted_input_value = &permuted.permuted_input_value[i]; + let permuted_input_value = &self.permuted_input_value[i]; - let permuted_table_value = &permuted.permuted_table_value[i]; + let permuted_table_value = &self.permuted_table_value[i]; left *= &(*beta + permuted_input_value); left *= &(*gamma + permuted_table_value); let mut right = z[prev_idx]; - let mut input_term = unpermuted_input_values + let mut input_term = self.unpermuted_input_values .iter() .fold(C::Scalar::zero(), |acc, input| acc * &theta + &input[i]); - let mut table_term = unpermuted_table_values + let mut table_term = self.unpermuted_table_values .iter() .fold(C::Scalar::zero(), |acc, table| acc * &theta + &table[i]); @@ -363,7 +379,8 @@ impl Argument { .absorb_point(&product_commitment) .map_err(|_| Error::TranscriptError)?; - Ok(Product:: { + Ok(Committed::<'a, C> { + permuted: self, product_poly: z, product_coset, product_inv_coset, @@ -373,22 +390,18 @@ impl Argument { } } -impl Committed { +impl<'a, C: CurveAffine> Committed<'a, C> { /// Given a Lookup with input columns, table columns, permuted input /// column, permuted table column, and grand product polynomial, this /// method constructs constraints that must hold between these values. /// This method returns the constraints as a vector of polynomials in /// the extended evaluation domain. - pub(in crate::plonk) fn construct<'a>( + pub(in crate::plonk) fn construct( self, pk: &'a ProvingKey, theta: ChallengeTheta, beta: ChallengeBeta, gamma: ChallengeGamma, - argument: &'a Argument, - advice_cosets: &'a [Polynomial], - fixed_cosets: &'a [Polynomial], - aux_cosets: &'a [Polynomial], ) -> Result< ( Constructed, @@ -397,61 +410,17 @@ impl Committed { Error, > { let permuted = self.permuted; - let product = self.product; - let unpermuted_input_cosets: Vec> = argument - .input_columns - .iter() - .map(|&input| match input.column_type() { - Any::Advice => advice_cosets[pk - .vk - .cs - .get_advice_query_index(Column::::try_from(input).unwrap(), 0)] - .clone(), - Any::Fixed => fixed_cosets[pk - .vk - .cs - .get_fixed_query_index(Column::::try_from(input).unwrap(), 0)] - .clone(), - Any::Aux => aux_cosets[pk - .vk - .cs - .get_aux_query_index(Column::::try_from(input).unwrap(), 0)] - .clone(), - }) - .collect(); - - let unpermuted_table_cosets: Vec> = argument - .table_columns - .iter() - .map(|&table| match table.column_type() { - Any::Advice => advice_cosets[pk - .vk - .cs - .get_advice_query_index(Column::::try_from(table).unwrap(), 0)] - .clone(), - Any::Fixed => fixed_cosets[pk - .vk - .cs - .get_fixed_query_index(Column::::try_from(table).unwrap(), 0)] - .clone(), - Any::Aux => aux_cosets[pk - .vk - .cs - .get_aux_query_index(Column::::try_from(table).unwrap(), 0)] - .clone(), - }) - .collect(); let expressions = iter::empty() // l_0(X) * (1 - z'(X)) = 0 .chain(Some( - Polynomial::one_minus(product.product_coset.clone()) * &pk.l0, + Polynomial::one_minus(self.product_coset.clone()) * &pk.l0, )) // z'(X) (a'(X) + \beta) (s'(X) + \gamma) // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) .chain({ // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - let mut left = product.product_coset.clone(); + let mut left = self.product_coset.clone(); parallelize(&mut left, |left, start| { for ((left, permuted_input), permuted_table) in left .iter_mut() @@ -464,11 +433,11 @@ impl Committed { }); // z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) - let mut right = product.product_inv_coset; + let mut right = self.product_inv_coset; let mut input_terms = pk.vk.domain.empty_extended(); // Compress the unpermuted input columns - for input in unpermuted_input_cosets.iter() { + for input in permuted.unpermuted_input_cosets.iter() { // (a_1(X) + \theta a_2(X) + ...) parallelize(&mut input_terms, |input_term, start| { for (input_term, input) in input_term.iter_mut().zip(input[start..].iter()) @@ -481,7 +450,7 @@ impl Committed { let mut table_terms = pk.vk.domain.empty_extended(); // Compress the unpermuted table columns - for table in unpermuted_table_cosets.iter() { + for table in permuted.unpermuted_table_cosets.iter() { // (s_1(X) + \theta s_2(X) + ...) parallelize(&mut table_terms, |table_term, start| { for (table_term, table) in table_term.iter_mut().zip(table[start..].iter()) @@ -529,9 +498,9 @@ impl Committed { permuted_table_poly: permuted.permuted_table_poly, permuted_table_blind: permuted.permuted_table_blind, permuted_table_commitment: permuted.permuted_table_commitment, - product_poly: product.product_poly, - product_blind: product.product_blind, - product_commitment: product.product_commitment, + product_poly: self.product_poly, + product_blind: self.product_blind, + product_commitment: self.product_commitment, }, expressions, )) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index ea826cf..5f3b13e 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -3,8 +3,8 @@ use std::iter; use super::{ circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed}, - lookup, permutation, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, - Error, Proof, ProvingKey, + permutation, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error, + Proof, ProvingKey, }; use crate::arithmetic::{eval_polynomial, Curve, CurveAffine, FieldExt}; use crate::poly::{ @@ -172,8 +172,8 @@ impl Proof { // Sample theta challenge for keeping lookup columns linearly independent let theta = ChallengeTheta::::get(&mut transcript); - // Construct permuted values for each lookup - let lookups_permuted = pk + // Construct and commit to permuted values for each lookup + let lookups = pk .vk .cs .lookups @@ -188,6 +188,9 @@ impl Proof { &witness.advice, &pk.fixed_values, &aux, + &advice_cosets, + &pk.fixed_cosets, + &aux_cosets, &mut transcript, ) .unwrap() @@ -214,40 +217,16 @@ impl Proof { None }; - // Construct products for each lookup - let lookups_products = pk - .vk - .cs - .lookups - .iter() - .zip(lookups_permuted.iter()) - .map(|(lookup, permuted)| { + // Construct and commit to products for each lookup + let lookups = lookups + .into_iter() + .map(|lookup| { lookup - .commit_product( - permuted, - &pk, - ¶ms, - theta, - beta, - gamma, - &witness.advice, - &pk.fixed_values, - &aux, - &mut transcript, - ) + .commit_product(&pk, ¶ms, theta, beta, gamma, &mut transcript) .unwrap() }) .collect::>(); - let lookups = lookups_permuted - .iter() - .zip(lookups_products.iter()) - .map(|(permuted, product)| lookup::prover::Committed { - permuted: permuted.clone(), - product: product.clone(), - }) - .collect::>(); - // Obtain challenge for keeping all separate gates linearly independent let y = ChallengeY::::get(&mut transcript); @@ -261,20 +240,7 @@ impl Proof { // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = lookups .into_iter() - .zip(pk.vk.cs.lookups.iter()) - .map(|(p, argument)| { - p.construct( - pk, - theta, - beta, - gamma, - argument, - &advice_cosets, - &pk.fixed_cosets, - &aux_cosets, - ) - .unwrap() - }) + .map(|p| p.construct(pk, theta, beta, gamma).unwrap()) .unzip(); // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided From 9a3d1b1d05ee0d2f6555e2813b1260623534cdfd Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Dec 2020 12:39:44 +0800 Subject: [PATCH 13/18] Optimisations and documentation updates --- src/plonk/circuit.rs | 3 ++ src/plonk/lookup/prover.rs | 86 ++++++++++++++++++------------------ src/plonk/lookup/verifier.rs | 12 ++--- src/plonk/prover.rs | 50 ++++++++++----------- 4 files changed, 77 insertions(+), 74 deletions(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 3b1cd45..822ea14 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -364,6 +364,9 @@ impl ConstraintSystem { input_columns: &[Column], table_columns: &[Column], ) -> usize { + // The function will panic if the number of input columns and table columns are not the same. + assert_eq!(input_columns.len(), table_columns.len()); + let index = self.lookups.len(); if self.lookups.is_empty() { let at = Rotation(-1); diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index e2f5728..4fe0e44 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -192,12 +192,12 @@ impl Argument { .commit_lagrange(&permuted_table_value, permuted_table_blind) .to_affine(); - // Hash each permuted input commitment + // Hash permuted input commitment transcript .absorb_point(&permuted_input_commitment) .map_err(|_| Error::TranscriptError)?; - // Hash each permuted table commitment + // Hash permuted table commitment transcript .absorb_point(&permuted_table_commitment) .map_err(|_| Error::TranscriptError)?; @@ -239,16 +239,15 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { ) -> Result, Error> { // Goal is to compute the products of fractions // - // (a_1(\omega^i) + \theta a_2(\omega^i) + ... + beta)(s_1(\omega^i) + \theta(\omega^i) + ... + \gamma) / - // (a'(\omega^i) + \beta)(s'(\omega^i) + \gamma) + // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + s_m(\omega^i) + \gamma)/ + // (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma) // // where a_j(X) is the jth input column in this lookup, // where a'(X) is the compression of the permuted input columns, // s_j(X) is the jth table column in this lookup, // s'(X) is the compression of the permuted table columns, // and i is the ith row of the column. - let mut lookup_product = vec![C::Scalar::one(); params.n as usize]; - + let mut lookup_product = vec![C::Scalar::zero(); params.n as usize]; // Denominator uses the permuted input column and permuted table column parallelize(&mut lookup_product, |lookup_product, start| { for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product @@ -256,8 +255,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { .zip(self.permuted_input_value[start..].iter()) .zip(self.permuted_table_value[start..].iter()) { - *lookup_product *= &(*beta + permuted_input_value); - *lookup_product *= &(*gamma + permuted_table_value); + *lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value); } }); @@ -266,7 +264,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { lookup_product.iter_mut().batch_invert(); // Finish the computation of the entire fraction by computing the numerators - // (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) // Compress unpermuted input columns let mut input_term = vec![C::Scalar::zero(); params.n as usize]; for unpermuted_input_value in self.unpermuted_input_values.iter() { @@ -309,10 +307,10 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { // The product vector is a vector of products of fractions of the form // - // (a_1(\omega^i) + \theta a_2(\omega^i) + ... + \beta)(s_1(\omega^i) + \theta s_2(\omega^i) + ... + \gamma)/ - // (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma) + // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) // - // where a_j(\omega^i) is the jth input column in this lookup, + // where there are m input columns and m table columns, + // a_j(\omega^i) is the jth input column in this lookup, // a'j(\omega^i) is the permuted input column, // s_j(\omega^i) is the jth table column in this lookup, // s'(\omega^i) is the permuted table column, @@ -320,12 +318,13 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { // Compute the evaluations of the lookup product polynomial // over our domain, starting with z[0] = 1 - let mut z = vec![C::Scalar::one()]; - for row in 1..(params.n as usize) { - let mut tmp = z[row - 1]; - tmp *= &lookup_product[row]; - z.push(tmp); - } + let z = iter::once(C::Scalar::one()) + .chain(lookup_product.into_iter().skip(1)) + .scan(C::Scalar::one(), |state, cur| { + *state *= &cur; + Some(*state) + }) + .collect::>(); let z = pk.vk.domain.lagrange_from_vec(z); #[cfg(feature = "sanity-checks")] @@ -336,7 +335,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { let n = params.n as usize; // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) for i in 0..n { let prev_idx = (n + i - 1) % n; @@ -374,7 +373,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { .coeff_to_extended(z.clone(), Rotation::default()); let product_inv_coset = pk.vk.domain.coeff_to_extended(z.clone(), Rotation(-1)); - // Hash each product commitment + // Hash product commitment transcript .absorb_point(&product_commitment) .map_err(|_| Error::TranscriptError)?; @@ -417,7 +416,7 @@ impl<'a, C: CurveAffine> Committed<'a, C> { Polynomial::one_minus(self.product_coset.clone()) * &pk.l0, )) // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) .chain({ // z'(X) (a'(X) + \beta) (s'(X) + \gamma) let mut left = self.product_coset.clone(); @@ -432,13 +431,13 @@ impl<'a, C: CurveAffine> Committed<'a, C> { } }); - // z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) let mut right = self.product_inv_coset; let mut input_terms = pk.vk.domain.empty_extended(); // Compress the unpermuted input columns for input in permuted.unpermuted_input_cosets.iter() { - // (a_1(X) + \theta a_2(X) + ...) + // \theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) parallelize(&mut input_terms, |input_term, start| { for (input_term, input) in input_term.iter_mut().zip(input[start..].iter()) { @@ -451,7 +450,7 @@ impl<'a, C: CurveAffine> Committed<'a, C> { let mut table_terms = pk.vk.domain.empty_extended(); // Compress the unpermuted table columns for table in permuted.unpermuted_table_cosets.iter() { - // (s_1(X) + \theta s_2(X) + ...) + // \theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) parallelize(&mut table_terms, |table_term, start| { for (table_term, table) in table_term.iter_mut().zip(table[start..].iter()) { @@ -633,28 +632,31 @@ fn permute_column_pair( *acc.entry(*coeff).or_insert(0) += 1; acc }); - let mut repeated_input_rows = vec![]; let mut permuted_table_coeffs = vec![C::Scalar::zero(); table_column.len()]; - for row in 0..permuted_input_column.len() { - let input_value = permuted_input_column[row]; - - // If this is the first occurence of `input_value` in the input column - if row == 0 || input_value != permuted_input_column[row - 1] { - permuted_table_coeffs[row] = input_value; - // Remove one instance of input_value from leftover_table_map - if let Some(count) = leftover_table_map.get_mut(&input_value) { - assert!(*count > 0); - *count -= 1; + let mut repeated_input_rows = permuted_input_column + .iter() + .zip(permuted_table_coeffs.iter_mut()) + .enumerate() + .filter_map(|(row, (input_value, table_value))| { + // If this is the first occurence of `input_value` in the input column + if row == 0 || *input_value != permuted_input_column[row - 1] { + *table_value = *input_value; + // Remove one instance of input_value from leftover_table_map + if let Some(count) = leftover_table_map.get_mut(&input_value) { + assert!(*count > 0); + *count -= 1; + None + } else { + // Return error if input_value not found + Some(Err(Error::ConstraintSystemFailure)) + } + // If input value is repeated } else { - // Return error if input_value not found - return Err(Error::ConstraintSystemFailure); + Some(Ok(row)) } - // If input value is repeated - } else { - repeated_input_rows.push(row); - } - } + }) + .collect::, _>>()?; // Populate permuted table at unfilled rows with leftover table elements for (coeff, count) in leftover_table_map.iter() { diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index df7b6d6..8bce162 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -23,8 +23,7 @@ impl Proof { .map_err(|_| Error::TranscriptError)?; transcript .absorb_point(&self.permuted_table_commitment) - .map_err(|_| Error::TranscriptError)?; - Ok(()) + .map_err(|_| Error::TranscriptError) } pub(crate) fn absorb_product_commitment, HScalar: Hasher>( @@ -33,8 +32,7 @@ impl Proof { ) -> Result<(), Error> { transcript .absorb_point(&self.product_commitment) - .map_err(|_| Error::TranscriptError)?; - Ok(()) + .map_err(|_| Error::TranscriptError) } pub(in crate::plonk) fn expressions<'a>( @@ -51,7 +49,7 @@ impl Proof { ) -> impl Iterator + 'a { let product_expression = || { // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) let left = self.product_eval * &(self.permuted_input_eval + &beta) * &(self.permuted_table_eval + &gamma); @@ -94,13 +92,15 @@ impl Proof { ) .chain( // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (a_1(X) + \theta a_2(X) + ... + \beta) (s_1(X) + \theta s_2(X) + ... + \gamma) + // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) Some(product_expression()), ) .chain(Some( + // l_0(X) * (a'(X) - s'(X)) = 0 l_0 * &(self.permuted_input_eval - &self.permuted_table_eval), )) .chain(Some( + // (a′(X)−s′(X))⋅(a′(X)−a′(\omega{-1} X)) = 0 (self.permuted_input_eval - &self.permuted_table_eval) * &(self.permuted_input_eval - &self.permuted_input_inv_eval), )) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 5f3b13e..964b3fb 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -179,23 +179,21 @@ impl Proof { .lookups .iter() .map(|lookup| { - lookup - .commit_permuted( - &pk, - ¶ms, - &domain, - theta, - &witness.advice, - &pk.fixed_values, - &aux, - &advice_cosets, - &pk.fixed_cosets, - &aux_cosets, - &mut transcript, - ) - .unwrap() + lookup.commit_permuted( + &pk, + ¶ms, + &domain, + theta, + &witness.advice, + &pk.fixed_values, + &aux, + &advice_cosets, + &pk.fixed_cosets, + &aux_cosets, + &mut transcript, + ) }) - .collect::>(); + .collect::, _>>()?; // Sample beta challenge let beta = ChallengeBeta::get(&mut transcript); @@ -220,12 +218,8 @@ impl Proof { // Construct and commit to products for each lookup let lookups = lookups .into_iter() - .map(|lookup| { - lookup - .commit_product(&pk, ¶ms, theta, beta, gamma, &mut transcript) - .unwrap() - }) - .collect::>(); + .map(|lookup| lookup.commit_product(&pk, ¶ms, theta, beta, gamma, &mut transcript)) + .collect::, _>>()?; // Obtain challenge for keeping all separate gates linearly independent let y = ChallengeY::::get(&mut transcript); @@ -238,10 +232,14 @@ impl Proof { .unwrap_or_default(); // Evaluate the h(X) polynomial's constraint system expressions for the lookup constraints, if any. - let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = lookups - .into_iter() - .map(|p| p.construct(pk, theta, beta, gamma).unwrap()) - .unzip(); + let (lookups, lookup_expressions): (Vec<_>, Vec<_>) = { + let tmp = lookups + .into_iter() + .map(|p| p.construct(pk, theta, beta, gamma)) + .collect::, _>>()?; + + tmp.into_iter().unzip() + }; // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided let h_poly = iter::empty() From 2284bbd0d8980d83aa13041f4e9c0e590d9d5ae9 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Thu, 3 Dec 2020 13:40:35 +0800 Subject: [PATCH 14/18] Deduplicate Argument::commit_permuted() and rename {input,table}_values -> {input,table}_columns --- src/plonk/lookup/prover.rs | 199 +++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 109 deletions(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 4fe0e44..c39294c 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -17,17 +17,17 @@ use std::{collections::BTreeMap, convert::TryFrom, iter}; #[derive(Debug)] pub(crate) struct Permuted<'a, C: CurveAffine> { - unpermuted_input_values: Vec<&'a Polynomial>, + unpermuted_input_columns: Vec<&'a Polynomial>, unpermuted_input_cosets: Vec<&'a Polynomial>, - permuted_input_value: Polynomial, + permuted_input_column: Polynomial, permuted_input_poly: Polynomial, permuted_input_coset: Polynomial, permuted_input_inv_coset: Polynomial, permuted_input_blind: Blind, permuted_input_commitment: C, - unpermuted_table_values: Vec<&'a Polynomial>, + unpermuted_table_columns: Vec<&'a Polynomial>, unpermuted_table_cosets: Vec<&'a Polynomial>, - permuted_table_value: Polynomial, + permuted_table_column: Polynomial, permuted_table_poly: Polynomial, permuted_table_coset: Polynomial, permuted_table_blind: Blind, @@ -73,7 +73,7 @@ impl Argument { /// - permutes A_compressed and S_compressed using permute_column_pair() helper, /// obtaining A' and S', and /// - constructs Permuted struct using permuted_input_value = A', and - /// permuted_table_value = S'. + /// permuted_table_column = S'. /// The Permuted struct is used to update the Lookup, and is then returned. pub(in crate::plonk) fn commit_permuted< 'a, @@ -94,103 +94,71 @@ impl Argument { aux_cosets: &'a [Polynomial], transcript: &mut Transcript, ) -> Result, Error> { - // Values of input columns involved in the lookup - let (unpermuted_input_values, unpermuted_input_cosets): (Vec<_>, Vec<_>) = self - .input_columns - .iter() - .map(|&input| match input.column_type() { - Any::Advice => ( - &advice_values[input.index()], - &advice_cosets[pk - .vk - .cs - .get_advice_query_index(Column::::try_from(input).unwrap(), 0)], - ), - Any::Fixed => ( - &fixed_values[input.index()], - &fixed_cosets[pk - .vk - .cs - .get_fixed_query_index(Column::::try_from(input).unwrap(), 0)], - ), - Any::Aux => ( - &aux_values[input.index()], - &aux_cosets[pk - .vk - .cs - .get_aux_query_index(Column::::try_from(input).unwrap(), 0)], - ), - }) - .unzip(); + // Closure to get values of columns and compress them + let compress_columns = |columns: &[Column]| { + // Values of input columns involved in the lookup + let (unpermuted_columns, unpermuted_cosets): (Vec<_>, Vec<_>) = columns + .iter() + .map(|&column| match column.column_type() { + Any::Advice => ( + &advice_values[column.index()], + &advice_cosets[pk.vk.cs.get_advice_query_index( + Column::::try_from(column).unwrap(), + 0, + )], + ), + Any::Fixed => ( + &fixed_values[column.index()], + &fixed_cosets[pk + .vk + .cs + .get_fixed_query_index(Column::::try_from(column).unwrap(), 0)], + ), + Any::Aux => ( + &aux_values[column.index()], + &aux_cosets[pk + .vk + .cs + .get_aux_query_index(Column::::try_from(column).unwrap(), 0)], + ), + }) + .unzip(); - // Compressed version of input columns - let compressed_input_value = unpermuted_input_values - .iter() - .fold(domain.empty_lagrange(), |acc, input| acc * *theta + input); + // Compressed version of columns + let compressed_column = unpermuted_columns + .iter() + .fold(domain.empty_lagrange(), |acc, column| acc * *theta + column); - // Values of table columns involved in the lookup - let (unpermuted_table_values, unpermuted_table_cosets): (Vec<_>, Vec<_>) = self - .table_columns - .iter() - .map(|&table| match table.column_type() { - Any::Advice => ( - &advice_values[table.index()], - &advice_cosets[pk - .vk - .cs - .get_advice_query_index(Column::::try_from(table).unwrap(), 0)], - ), - Any::Fixed => ( - &fixed_values[table.index()], - &fixed_cosets[pk - .vk - .cs - .get_fixed_query_index(Column::::try_from(table).unwrap(), 0)], - ), - Any::Aux => ( - &aux_values[table.index()], - &aux_cosets[pk - .vk - .cs - .get_aux_query_index(Column::::try_from(table).unwrap(), 0)], - ), - }) - .unzip(); + (unpermuted_columns, unpermuted_cosets, compressed_column) + }; - // Compressed version of table columns - let compressed_table_value = unpermuted_table_values - .iter() - .fold(domain.empty_lagrange(), |acc, table| acc * *theta + table); + // Closure to construct commitment to column of values + let commit_column = |column: &Polynomial| { + let poly = pk.vk.domain.lagrange_to_coeff(column.clone()); + let blind = Blind(C::Scalar::rand()); + let commitment = params.commit_lagrange(&column, blind).to_affine(); + (poly, blind, commitment) + }; + + // Get values of input columns involved in the lookup and compress them + let (unpermuted_input_columns, unpermuted_input_cosets, compressed_input_column) = + compress_columns(&self.input_columns); + + // Get values of table columns involved in the lookup and compress them + let (unpermuted_table_columns, unpermuted_table_cosets, compressed_table_column) = + compress_columns(&self.table_columns); // Permute compressed (InputColumn, TableColumn) pair - let (permuted_input_value, permuted_table_value) = - permute_column_pair::(domain, &compressed_input_value, &compressed_table_value)?; + let (permuted_input_column, permuted_table_column) = + permute_column_pair::(domain, &compressed_input_column, &compressed_table_column)?; - // Construct Permuted struct - let permuted_input_poly = pk.vk.domain.lagrange_to_coeff(permuted_input_value.clone()); - let permuted_input_coset = pk - .vk - .domain - .coeff_to_extended(permuted_input_poly.clone(), Rotation::default()); - let permuted_input_inv_coset = pk - .vk - .domain - .coeff_to_extended(permuted_input_poly.clone(), Rotation(-1)); + // Commit to permuted input column + let (permuted_input_poly, permuted_input_blind, permuted_input_commitment) = + commit_column(&permuted_input_column); - let permuted_input_blind = Blind(C::Scalar::rand()); - let permuted_input_commitment = params - .commit_lagrange(&permuted_input_value, permuted_input_blind) - .to_affine(); - - let permuted_table_poly = pk.vk.domain.lagrange_to_coeff(permuted_table_value.clone()); - let permuted_table_coset = pk - .vk - .domain - .coeff_to_extended(permuted_table_poly.clone(), Rotation::default()); - let permuted_table_blind = Blind(C::Scalar::rand()); - let permuted_table_commitment = params - .commit_lagrange(&permuted_table_value, permuted_table_blind) - .to_affine(); + // Commit to permuted table column + let (permuted_table_poly, permuted_table_blind, permuted_table_commitment) = + commit_column(&permuted_table_column); // Hash permuted input commitment transcript @@ -202,18 +170,31 @@ impl Argument { .absorb_point(&permuted_table_commitment) .map_err(|_| Error::TranscriptError)?; + let permuted_input_coset = pk + .vk + .domain + .coeff_to_extended(permuted_input_poly.clone(), Rotation::default()); + let permuted_input_inv_coset = pk + .vk + .domain + .coeff_to_extended(permuted_input_poly.clone(), Rotation(-1)); + let permuted_table_coset = pk + .vk + .domain + .coeff_to_extended(permuted_table_poly.clone(), Rotation::default()); + Ok(Permuted { - unpermuted_input_values, + unpermuted_input_columns, unpermuted_input_cosets, - permuted_input_value, + permuted_input_column, permuted_input_poly, permuted_input_coset, permuted_input_inv_coset, permuted_input_blind, permuted_input_commitment, - unpermuted_table_values, + unpermuted_table_columns, unpermuted_table_cosets, - permuted_table_value, + permuted_table_column, permuted_table_poly, permuted_table_coset, permuted_table_blind, @@ -252,8 +233,8 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { parallelize(&mut lookup_product, |lookup_product, start| { for ((lookup_product, permuted_input_value), permuted_table_value) in lookup_product .iter_mut() - .zip(self.permuted_input_value[start..].iter()) - .zip(self.permuted_table_value[start..].iter()) + .zip(self.permuted_input_column[start..].iter()) + .zip(self.permuted_table_column[start..].iter()) { *lookup_product = (*beta + permuted_input_value) * &(*gamma + permuted_table_value); } @@ -267,11 +248,11 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) // Compress unpermuted input columns let mut input_term = vec![C::Scalar::zero(); params.n as usize]; - for unpermuted_input_value in self.unpermuted_input_values.iter() { + for unpermuted_input_column in self.unpermuted_input_columns.iter() { parallelize(&mut input_term, |input_term, start| { for (input_term, input_value) in input_term .iter_mut() - .zip(unpermuted_input_value[start..].iter()) + .zip(unpermuted_input_column[start..].iter()) { *input_term *= θ *input_term += input_value; @@ -281,11 +262,11 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { // Compress unpermuted table columns let mut table_term = vec![C::Scalar::zero(); params.n as usize]; - for unpermuted_table_value in self.unpermuted_table_values.iter() { + for unpermuted_table_columns in self.unpermuted_table_columns.iter() { parallelize(&mut table_term, |table_term, start| { for (table_term, fixed_value) in table_term .iter_mut() - .zip(unpermuted_table_value[start..].iter()) + .zip(unpermuted_table_columns[start..].iter()) { *table_term *= θ *table_term += fixed_value; @@ -340,19 +321,19 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { let prev_idx = (n + i - 1) % n; let mut left = z[i]; - let permuted_input_value = &self.permuted_input_value[i]; + let permuted_input_value = &self.permuted_input_column[i]; - let permuted_table_value = &self.permuted_table_value[i]; + let permuted_table_value = &self.permuted_table_column[i]; left *= &(*beta + permuted_input_value); left *= &(*gamma + permuted_table_value); let mut right = z[prev_idx]; - let mut input_term = self.unpermuted_input_values + let mut input_term = self.unpermuted_input_columns .iter() .fold(C::Scalar::zero(), |acc, input| acc * &theta + &input[i]); - let mut table_term = self.unpermuted_table_values + let mut table_term = self.unpermuted_table_columns .iter() .fold(C::Scalar::zero(), |acc, table| acc * &theta + &table[i]); From ecc805fa354096559a1993cde1a3fc44dc20c0a5 Mon Sep 17 00:00:00 2001 From: ying tong Date: Fri, 4 Dec 2020 09:18:28 +0800 Subject: [PATCH 15/18] Correct privacy of lookup structs + minor cleanups Co-authored-by: str4d --- src/plonk/lookup/prover.rs | 54 ++++++++++++++---------------------- src/plonk/lookup/verifier.rs | 54 +++++++++++++++--------------------- src/plonk/prover.rs | 4 +-- 3 files changed, 46 insertions(+), 66 deletions(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index c39294c..31b6c1d 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -1,5 +1,5 @@ use super::super::{ - circuit::{Advice, Any, Aux, Column, Fixed}, + circuit::{Any, Column}, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, Error, ProvingKey, }; use super::{Argument, Proof}; @@ -13,10 +13,10 @@ use crate::{ transcript::{Hasher, Transcript}, }; use ff::Field; -use std::{collections::BTreeMap, convert::TryFrom, iter}; +use std::{collections::BTreeMap, iter}; #[derive(Debug)] -pub(crate) struct Permuted<'a, C: CurveAffine> { +pub(in crate::plonk) struct Permuted<'a, C: CurveAffine> { unpermuted_input_columns: Vec<&'a Polynomial>, unpermuted_input_cosets: Vec<&'a Polynomial>, permuted_input_column: Polynomial, @@ -35,7 +35,7 @@ pub(crate) struct Permuted<'a, C: CurveAffine> { } #[derive(Debug)] -pub(crate) struct Committed<'a, C: CurveAffine> { +pub(in crate::plonk) struct Committed<'a, C: CurveAffine> { permuted: Permuted<'a, C>, product_poly: Polynomial, product_coset: Polynomial, @@ -44,7 +44,7 @@ pub(crate) struct Committed<'a, C: CurveAffine> { product_commitment: C, } -pub(crate) struct Constructed { +pub(in crate::plonk) struct Constructed { permuted_input_poly: Polynomial, permuted_input_blind: Blind, permuted_input_commitment: C, @@ -56,13 +56,13 @@ pub(crate) struct Constructed { product_commitment: C, } -pub(crate) struct Evaluated { +pub(in crate::plonk) struct Evaluated { constructed: Constructed, - pub product_eval: C::Scalar, - pub product_inv_eval: C::Scalar, - pub permuted_input_eval: C::Scalar, - pub permuted_input_inv_eval: C::Scalar, - pub permuted_table_eval: C::Scalar, + product_eval: C::Scalar, + product_inv_eval: C::Scalar, + permuted_input_eval: C::Scalar, + permuted_input_inv_eval: C::Scalar, + permuted_table_eval: C::Scalar, } impl Argument { @@ -99,28 +99,16 @@ impl Argument { // Values of input columns involved in the lookup let (unpermuted_columns, unpermuted_cosets): (Vec<_>, Vec<_>) = columns .iter() - .map(|&column| match column.column_type() { - Any::Advice => ( - &advice_values[column.index()], - &advice_cosets[pk.vk.cs.get_advice_query_index( - Column::::try_from(column).unwrap(), - 0, - )], - ), - Any::Fixed => ( - &fixed_values[column.index()], - &fixed_cosets[pk - .vk - .cs - .get_fixed_query_index(Column::::try_from(column).unwrap(), 0)], - ), - Any::Aux => ( - &aux_values[column.index()], - &aux_cosets[pk - .vk - .cs - .get_aux_query_index(Column::::try_from(column).unwrap(), 0)], - ), + .map(|&column| { + let (values, cosets) = match column.column_type() { + Any::Advice => (advice_values, advice_cosets), + Any::Fixed => (fixed_values, fixed_cosets), + Any::Aux => (aux_values, aux_cosets), + }; + ( + &values[column.index()], + &cosets[pk.vk.cs.get_any_query_index(column, 0)], + ) }) .unzip(); diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index 8bce162..150c439 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -1,6 +1,6 @@ use std::iter; -use super::super::circuit::Any; +use super::super::circuit::{Any, Column}; use super::{Argument, Proof}; use crate::{ arithmetic::CurveAffine, @@ -11,7 +11,7 @@ use crate::{ use ff::Field; impl Proof { - pub(crate) fn absorb_permuted_commitments< + pub(in crate::plonk) fn absorb_permuted_commitments< HBase: Hasher, HScalar: Hasher, >( @@ -26,7 +26,10 @@ impl Proof { .map_err(|_| Error::TranscriptError) } - pub(crate) fn absorb_product_commitment, HScalar: Hasher>( + pub(in crate::plonk) fn absorb_product_commitment< + HBase: Hasher, + HScalar: Hasher, + >( &self, transcript: &mut Transcript, ) -> Result<(), Error> { @@ -54,34 +57,23 @@ impl Proof { * &(self.permuted_input_eval + &beta) * &(self.permuted_table_eval + &gamma); - let mut right = self.product_inv_eval; - let mut input_term = C::Scalar::zero(); - for &input in argument.input_columns.iter() { - let index = vk.cs.get_any_query_index(input, 0); - let eval = match input.column_type() { - Any::Advice => advice_evals[index], - Any::Fixed => fixed_evals[index], - Any::Aux => aux_evals[index], - }; - input_term *= θ - input_term += &eval; - } - input_term += β + let compress_columns = |columns: &[Column]| { + columns + .iter() + .map(|column| { + let index = vk.cs.get_any_query_index(*column, 0); + match column.column_type() { + Any::Advice => advice_evals[index], + Any::Fixed => fixed_evals[index], + Any::Aux => aux_evals[index], + } + }) + .fold(C::Scalar::zero(), |acc, eval| acc * &theta + &eval) + }; + let right = self.product_inv_eval + * &(compress_columns(&argument.input_columns) + &beta) + * &(compress_columns(&argument.table_columns) + &gamma); - let mut table_term = C::Scalar::zero(); - for &table in argument.table_columns.iter() { - let index = vk.cs.get_any_query_index(table, 0); - let eval = match table.column_type() { - Any::Advice => advice_evals[index], - Any::Fixed => fixed_evals[index], - Any::Aux => aux_evals[index], - }; - table_term *= θ - table_term += &eval; - } - table_term += γ - - right *= &(input_term * &table_term); left - &right }; @@ -106,7 +98,7 @@ impl Proof { )) } - pub(crate) fn evals(&self) -> impl Iterator { + pub(in crate::plonk) fn evals(&self) -> impl Iterator { iter::empty() .chain(Some(&self.product_eval)) .chain(Some(&self.product_inv_eval)) diff --git a/src/plonk/prover.rs b/src/plonk/prover.rs index 964b3fb..8b28990 100644 --- a/src/plonk/prover.rs +++ b/src/plonk/prover.rs @@ -170,7 +170,7 @@ impl Proof { .collect(); // Sample theta challenge for keeping lookup columns linearly independent - let theta = ChallengeTheta::::get(&mut transcript); + let theta = ChallengeTheta::get(&mut transcript); // Construct and commit to permuted values for each lookup let lookups = pk @@ -402,7 +402,7 @@ impl Proof { advice_commitments, h_commitments, permutations: permutations.map(|p| p.build()), - lookups: lookups.into_iter().map(|p| p.build()).collect::>(), + lookups: lookups.into_iter().map(|p| p.build()).collect(), advice_evals, fixed_evals, aux_evals, From 30c13d5a6a9c62a0b08765f1c2b57159f2a04741 Mon Sep 17 00:00:00 2001 From: ying tong Date: Sat, 5 Dec 2020 12:51:28 +0800 Subject: [PATCH 16/18] Further cleanups Co-authored-by: ebfull --- src/plonk.rs | 9 +-- src/plonk/circuit.rs | 3 +- src/plonk/lookup.rs | 2 +- src/plonk/lookup/prover.rs | 113 ++++++++++++++----------------------- 4 files changed, 47 insertions(+), 80 deletions(-) diff --git a/src/plonk.rs b/src/plonk.rs index c6a8be1..a3feeed 100644 --- a/src/plonk.rs +++ b/src/plonk.rs @@ -121,7 +121,7 @@ fn test_proving() { use crate::poly::commitment::{Blind, Params}; use crate::transcript::DummyHash; use crate::tweedle::{EqAffine, Fp, Fq}; - use circuit::{Advice, Any, Column, Fixed}; + use circuit::{Advice, Column, Fixed}; use std::marker::PhantomData; const K: u32 = 5; @@ -354,11 +354,8 @@ fn test_proving() { * * ] */ - meta.lookup(&[Column::::from(a)], &[Column::::from(sl)]); - meta.lookup( - &[Column::::from(a), Column::::from(b)], - &[Column::::from(sl), Column::::from(sl2)], - ); + meta.lookup(&[a.into()], &[sl.into()]); + meta.lookup(&[a.into(), b.into()], &[sl.into(), sl2.into()]); meta.create_gate(|meta| { let d = meta.query_advice(d, 1); diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index 822ea14..b217137 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -359,12 +359,13 @@ impl ConstraintSystem { } /// Add a lookup argument for some input columns and table columns. + /// The function will panic if the number of input columns and table + /// columns are not the same. pub fn lookup( &mut self, input_columns: &[Column], table_columns: &[Column], ) -> usize { - // The function will panic if the number of input columns and table columns are not the same. assert_eq!(input_columns.len(), table_columns.len()); let index = self.lookups.len(); diff --git a/src/plonk/lookup.rs b/src/plonk/lookup.rs index e24e2b3..640faef 100644 --- a/src/plonk/lookup.rs +++ b/src/plonk/lookup.rs @@ -1,7 +1,7 @@ use super::circuit::{Any, Column}; use crate::arithmetic::CurveAffine; -pub(crate) mod prover; +mod prover; mod verifier; #[derive(Clone, Debug)] diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 31b6c1d..80f743a 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -233,44 +233,28 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { lookup_product.iter_mut().batch_invert(); // Finish the computation of the entire fraction by computing the numerators - // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) - // Compress unpermuted input columns - let mut input_term = vec![C::Scalar::zero(); params.n as usize]; - for unpermuted_input_column in self.unpermuted_input_columns.iter() { - parallelize(&mut input_term, |input_term, start| { - for (input_term, input_value) in input_term - .iter_mut() - .zip(unpermuted_input_column[start..].iter()) - { - *input_term *= θ - *input_term += input_value; - } - }); - } - - // Compress unpermuted table columns - let mut table_term = vec![C::Scalar::zero(); params.n as usize]; - for unpermuted_table_columns in self.unpermuted_table_columns.iter() { - parallelize(&mut table_term, |table_term, start| { - for (table_term, fixed_value) in table_term - .iter_mut() - .zip(unpermuted_table_columns[start..].iter()) - { - *table_term *= θ - *table_term += fixed_value; - } - }); - } - - // Add \beta and \gamma offsets + // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta) + // * (\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) parallelize(&mut lookup_product, |product, start| { - for ((product, input_term), table_term) in product - .iter_mut() - .zip(input_term[start..].iter()) - .zip(table_term[start..].iter()) - { - *product *= &(*input_term + &beta); - *product *= &(*table_term + &gamma); + for (i, product) in product.iter_mut().enumerate() { + let i = i + start; + + // Compress unpermuted input columns + let mut input_term = C::Scalar::zero(); + for unpermuted_input_column in self.unpermuted_input_columns.iter() { + input_term *= θ + input_term += &unpermuted_input_column[i]; + } + + // Compress unpermuted table columns + let mut table_term = C::Scalar::zero(); + for unpermuted_table_column in self.unpermuted_table_columns.iter() { + table_term *= θ + table_term += &unpermuted_table_column[i]; + } + + *product *= &(input_term + &beta); + *product *= &(table_term + &gamma); } }); @@ -402,42 +386,27 @@ impl<'a, C: CurveAffine> Committed<'a, C> { // z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) let mut right = self.product_inv_coset; - let mut input_terms = pk.vk.domain.empty_extended(); - - // Compress the unpermuted input columns - for input in permuted.unpermuted_input_cosets.iter() { - // \theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) - parallelize(&mut input_terms, |input_term, start| { - for (input_term, input) in input_term.iter_mut().zip(input[start..].iter()) - { - *input_term *= &(*theta); - *input_term += input; - } - }); - } - - let mut table_terms = pk.vk.domain.empty_extended(); - // Compress the unpermuted table columns - for table in permuted.unpermuted_table_cosets.iter() { - // \theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) - parallelize(&mut table_terms, |table_term, start| { - for (table_term, table) in table_term.iter_mut().zip(table[start..].iter()) - { - *table_term *= &(*theta); - *table_term += table; - } - }); - } - - // Add \beta and \gamma offsets parallelize(&mut right, |right, start| { - for ((right, input_term), table_term) in right - .iter_mut() - .zip(input_terms[start..].iter()) - .zip(table_terms[start..].iter()) - { - *right *= &(*input_term + &(*beta)); - *right *= &(*table_term + &(*gamma)); + for (i, right) in right.iter_mut().enumerate() { + let i = i + start; + + // Compress the unpermuted input columns + let mut input_term = C::Scalar::zero(); + for input in permuted.unpermuted_input_cosets.iter() { + input_term *= θ + input_term += &input[i]; + } + + // Compress the unpermuted table columns + let mut table_term = C::Scalar::zero(); + for table in permuted.unpermuted_table_cosets.iter() { + table_term *= θ + table_term += &table[i]; + } + + // Add \beta and \gamma offsets + *right *= &(input_term + &beta); + *right *= &(table_term + &gamma); } }); From 4273bbb2ba84b4b64fbfcd570793a0baa9945547 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 6 Dec 2020 06:58:48 +0800 Subject: [PATCH 17/18] [Documentation] Consistently use zero-based numbering --- src/plonk/lookup/prover.rs | 27 +++++++++++++++------------ src/plonk/lookup/verifier.rs | 4 ++-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/plonk/lookup/prover.rs b/src/plonk/lookup/prover.rs index 80f743a..999db77 100644 --- a/src/plonk/lookup/prover.rs +++ b/src/plonk/lookup/prover.rs @@ -66,10 +66,10 @@ pub(in crate::plonk) struct Evaluated { } impl Argument { - /// Given a Lookup with input columns [A_0, A_1, ..., A_m] and table columns - /// [S_0, S_1, ..., S_m], this method - /// - constructs A_compressed = A_0 + theta A_1 + theta^2 A_2 + ... and - /// S_compressed = S_0 + theta S_1 + theta^2 S_2 + ..., + /// Given a Lookup with input columns [A_0, A_1, ..., A_{m-1}] and table columns + /// [S_0, S_1, ..., S_{m-1}], this method + /// - constructs A_compressed = \theta^{m-1} A_0 + theta^{m-2} A_1 + ... + \theta A_{m-2} + A_{m-1} + /// and S_compressed = \theta^{m-1} S_0 + theta^{m-2} S_1 + ... + \theta S_{m-2} + S_{m-1}, /// - permutes A_compressed and S_compressed using permute_column_pair() helper, /// obtaining A' and S', and /// - constructs Permuted struct using permuted_input_value = A', and @@ -208,8 +208,9 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { ) -> Result, Error> { // Goal is to compute the products of fractions // - // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + s_m(\omega^i) + \gamma)/ - // (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma) + // Numerator: (\theta^{m-1} a_0(\omega^i) + \theta^{m-2} a_1(\omega^i) + ... + \theta a_{m-2}(\omega^i) + a_{m-1}(\omega^i) + \beta) + // * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma) + // Denominator: (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma) // // where a_j(X) is the jth input column in this lookup, // where a'(X) is the compression of the permuted input columns, @@ -233,8 +234,8 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { lookup_product.iter_mut().batch_invert(); // Finish the computation of the entire fraction by computing the numerators - // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta) - // * (\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) + // (\theta^{m-1} a_0(\omega^i) + \theta^{m-2} a_1(\omega^i) + ... + \theta a_{m-2}(\omega^i) + a_{m-1}(\omega^i) + \beta) + // * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma) parallelize(&mut lookup_product, |product, start| { for (i, product) in product.iter_mut().enumerate() { let i = i + start; @@ -260,7 +261,9 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { // The product vector is a vector of products of fractions of the form // - // (\theta^{m-1} a_1(\omega^i) + \theta^{m-2} a_2(\omega^i) + ... + \theta a_{m-1}(\omega^i) + a_m(\omega^i) + \beta)(\theta^{m-1} s_1(\omega^i) + \theta^{m-2} s_2(\omega^i) + ... + \theta s_{m-1}(\omega^i) + s_m(\omega^i) + \gamma) + // Numerator: (\theta^{m-1} a_0(\omega^i) + \theta^{m-2} a_1(\omega^i) + ... + \theta a_{m-2}(\omega^i) + a_{m-1}(\omega^i) + \beta) + // * (\theta^{m-1} s_0(\omega^i) + \theta^{m-2} s_1(\omega^i) + ... + \theta s_{m-2}(\omega^i) + s_{m-1}(\omega^i) + \gamma) + // Denominator: (a'(\omega^i) + \beta) (s'(\omega^i) + \gamma) // // where there are m input columns and m table columns, // a_j(\omega^i) is the jth input column in this lookup, @@ -288,7 +291,7 @@ impl<'a, C: CurveAffine> Permuted<'a, C> { let n = params.n as usize; // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) + // - z'(\omega^{-1} X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) for i in 0..n { let prev_idx = (n + i - 1) % n; @@ -369,7 +372,7 @@ impl<'a, C: CurveAffine> Committed<'a, C> { Polynomial::one_minus(self.product_coset.clone()) * &pk.l0, )) // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) + // - z'(\omega^{-1} X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) .chain({ // z'(X) (a'(X) + \beta) (s'(X) + \gamma) let mut left = self.product_coset.clone(); @@ -384,7 +387,7 @@ impl<'a, C: CurveAffine> Committed<'a, C> { } }); - // z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) + // z'(\omega^{-1} X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) let mut right = self.product_inv_coset; parallelize(&mut right, |right, start| { for (i, right) in right.iter_mut().enumerate() { diff --git a/src/plonk/lookup/verifier.rs b/src/plonk/lookup/verifier.rs index 150c439..d7639e3 100644 --- a/src/plonk/lookup/verifier.rs +++ b/src/plonk/lookup/verifier.rs @@ -52,7 +52,7 @@ impl Proof { ) -> impl Iterator + 'a { let product_expression = || { // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) + // - z'(\omega^{-1} X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) let left = self.product_eval * &(self.permuted_input_eval + &beta) * &(self.permuted_table_eval + &gamma); @@ -84,7 +84,7 @@ impl Proof { ) .chain( // z'(X) (a'(X) + \beta) (s'(X) + \gamma) - // - z'(\omega^{-1} X) (\theta^m a_1(X) + \theta^{m-1} a_2(X) + ... + a_m(X) + \beta) (\theta^m s_1(X) + \theta^{m-1} s_2(X) + ... + s_m(X) + \gamma) + // - z'(\omega^{-1} X) (\theta^{m-1} a_0(X) + ... + a_{m-1}(X) + \beta) (\theta^{m-1} s_0(X) + ... + s_{m-1}(X) + \gamma) Some(product_expression()), ) .chain(Some( From e5f55a8576b939fd5622adb91f50ef9c14833736 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sun, 6 Dec 2020 07:19:44 +0800 Subject: [PATCH 18/18] Abstract add_rotation() helper in plonk::circuit --- src/plonk/circuit.rs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/plonk/circuit.rs b/src/plonk/circuit.rs index b217137..17673bf 100644 --- a/src/plonk/circuit.rs +++ b/src/plonk/circuit.rs @@ -344,9 +344,7 @@ impl ConstraintSystem { pub fn permutation(&mut self, columns: &[Column]) -> usize { let index = self.permutations.len(); if self.permutations.is_empty() { - let at = Rotation(-1); - let len = self.rotations.len(); - self.rotations.entry(at).or_insert(PointIndex(len)); + self.add_rotation(Rotation(-1)); } for column in columns { @@ -370,9 +368,7 @@ impl ConstraintSystem { let index = self.lookups.len(); if self.lookups.is_empty() { - let at = Rotation(-1); - let len = self.rotations.len(); - self.rotations.entry(at).or_insert(PointIndex(len)); + self.add_rotation(Rotation(-1)); } for input in input_columns { @@ -389,10 +385,7 @@ impl ConstraintSystem { fn query_fixed_index(&mut self, column: Column, at: i32) -> usize { let at = Rotation(at); - { - let len = self.rotations.len(); - self.rotations.entry(at).or_insert(PointIndex(len)); - } + self.add_rotation(at); // Return existing query, if it exists for (index, fixed_query) in self.fixed_queries.iter().enumerate() { @@ -415,10 +408,7 @@ impl ConstraintSystem { pub(crate) fn query_advice_index(&mut self, column: Column, at: i32) -> usize { let at = Rotation(at); - { - let len = self.rotations.len(); - self.rotations.entry(at).or_insert(PointIndex(len)); - } + self.add_rotation(at); // Return existing query, if it exists for (index, advice_query) in self.advice_queries.iter().enumerate() { @@ -441,10 +431,7 @@ impl ConstraintSystem { fn query_aux_index(&mut self, column: Column, at: i32) -> usize { let at = Rotation(at); - { - let len = self.rotations.len(); - self.rotations.entry(at).or_insert(PointIndex(len)); - } + self.add_rotation(at); // Return existing query, if it exists for (index, aux_query) in self.aux_queries.iter().enumerate() { @@ -568,4 +555,9 @@ impl ConstraintSystem { self.num_aux_columns += 1; tmp } + + fn add_rotation(&mut self, at: Rotation) { + let len = self.rotations.len(); + self.rotations.entry(at).or_insert(PointIndex(len)); + } }