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()