diff --git a/plonk/src/circuit/basic.rs b/plonk/src/circuit/basic.rs index 41f454da9..2146da44d 100644 --- a/plonk/src/circuit/basic.rs +++ b/plonk/src/circuit/basic.rs @@ -29,6 +29,14 @@ use rayon::prelude::*; /// The wire type identifier for range gates. const RANGE_WIRE_ID: usize = 5; +/// The wire type identifier for the key index in a lookup gate +const LOOKUP_KEY_WIRE_ID: usize = 0; +/// The wire type identifiers for the searched pair values in a lookup gate +const LOOKUP_VAL_1_WIRE_ID: usize = 1; +const LOOKUP_VAL_2_WIRE_ID: usize = 2; +/// The wire type identifiers for the pair values in the lookup table +const TABLE_VAL_1_WIRE_ID: usize = 3; +const TABLE_VAL_2_WIRE_ID: usize = 4; /// Hardcoded parameters for Plonk systems. #[derive(Debug, Clone, Copy)] @@ -314,29 +322,38 @@ impl Circuit for PlonkCircuit { } // key-value map lookup gates let mut key_val_table = HashSet::new(); - key_val_table.insert((F::zero(), F::zero(), F::zero())); - let mut num_table_elems: u32 = 0; + key_val_table.insert((F::zero(), F::zero(), F::zero(), F::zero())); let q_lookup_vec = self.q_lookup(); - for (gate_id, &q_lookup) in q_lookup_vec.iter().enumerate() { + let q_dom_sep_vec = self.q_dom_sep(); + let table_key_vec = self.table_key_vec(); + let table_dom_sep_vec = self.table_dom_sep_vec(); + // insert table elements + for (gate_id, ((&q_lookup, &table_dom_sep), &table_key)) in q_lookup_vec + .iter() + .zip(table_dom_sep_vec.iter()) + .zip(table_key_vec.iter()) + .enumerate() + { if q_lookup != F::zero() { - let key = F::from(num_table_elems); - let val0 = self.witness(self.wire_variable(3, gate_id))?; - let val1 = self.witness(self.wire_variable(4, gate_id))?; - key_val_table.insert((key, val0, val1)); - num_table_elems += 1; + let val0 = self.witness(self.wire_variable(TABLE_VAL_1_WIRE_ID, gate_id))?; + let val1 = self.witness(self.wire_variable(TABLE_VAL_2_WIRE_ID, gate_id))?; + key_val_table.insert((table_dom_sep, table_key, val0, val1)); } } - for (gate_id, &q_lookup) in q_lookup_vec.iter().enumerate() { + // check lookups + for (gate_id, (&q_lookup, &q_dom_sep)) in + q_lookup_vec.iter().zip(q_dom_sep_vec.iter()).enumerate() + { if q_lookup != F::zero() { - let key = self.witness(self.wire_variable(0, gate_id))?; - let val0 = self.witness(self.wire_variable(1, gate_id))?; - let val1 = self.witness(self.wire_variable(2, gate_id))?; - if !key_val_table.contains(&(key, val0, val1)) { + let key = self.witness(self.wire_variable(LOOKUP_KEY_WIRE_ID, gate_id))?; + let val0 = self.witness(self.wire_variable(LOOKUP_VAL_1_WIRE_ID, gate_id))?; + let val1 = self.witness(self.wire_variable(LOOKUP_VAL_2_WIRE_ID, gate_id))?; + if !key_val_table.contains(&(q_dom_sep, key, val0, val1)) { return Err(GateCheckFailure( gate_id, format!( - "Lookup gate failed: ({}, {}, {}) not in the table", - key, val0, val1 + "Lookup gate failed: ({}, {}, {}, {}) not in the table", + q_dom_sep, key, val0, val1 ), ) .into()); @@ -788,6 +805,21 @@ impl PlonkCircuit { fn q_lookup(&self) -> Vec { self.gates.iter().map(|g| g.q_lookup()).collect() } + // getter for all lookup domain separation selector + #[inline] + fn q_dom_sep(&self) -> Vec { + self.gates.iter().map(|g| g.q_dom_sep()).collect() + } + // getter for the vector of table keys + #[inline] + fn table_key_vec(&self) -> Vec { + self.gates.iter().map(|g| g.table_key()).collect() + } + // getter for the vector of table domain separation ids + #[inline] + fn table_dom_sep_vec(&self) -> Vec { + self.gates.iter().map(|g| g.table_dom_sep()).collect() + } // TODO: (alex) try return reference instead of expensive clone // getter for all selectors in the following order: // q_lc, q_mul, q_hash, q_o, q_c, q_ecc, [q_lookup (if support lookup)] @@ -1171,16 +1203,36 @@ where } fn compute_key_table_polynomial(&self) -> Result, PlonkError> { - let key_table = self.compute_key_table()?; + self.check_plonk_type(PlonkType::UltraPlonk)?; + self.check_finalize_flag(true)?; + let domain = &self.eval_domain; + Ok(DensePolynomial::from_coefficients_vec( + domain.ifft(&self.table_key_vec()), + )) + } + + fn compute_table_dom_sep_polynomial(&self) -> Result, PlonkError> { + self.check_plonk_type(PlonkType::UltraPlonk)?; + self.check_finalize_flag(true)?; + let domain = &self.eval_domain; + Ok(DensePolynomial::from_coefficients_vec( + domain.ifft(&self.table_dom_sep_vec()), + )) + } + + fn compute_q_dom_sep_polynomial(&self) -> Result, PlonkError> { + self.check_plonk_type(PlonkType::UltraPlonk)?; + self.check_finalize_flag(true)?; let domain = &self.eval_domain; Ok(DensePolynomial::from_coefficients_vec( - domain.ifft(&key_table), + domain.ifft(&self.q_dom_sep()), )) } fn compute_merged_lookup_table(&self, tau: F) -> Result, PlonkError> { let range_table = self.compute_range_table()?; - let key_table = self.compute_key_table()?; + let table_key_vec = self.table_key_vec(); + let table_dom_sep_vec = self.table_dom_sep_vec(); let q_lookup_vec = self.q_lookup(); let mut merged_lookup_table = vec![]; @@ -1188,7 +1240,8 @@ where merged_lookup_table.push(self.merged_table_value( tau, &range_table, - &key_table, + &table_key_vec, + &table_dom_sep_vec, &q_lookup_vec, i, )?); @@ -1230,9 +1283,11 @@ where let beta_plus_one = F::one() + *beta; let gamma_mul_beta_plus_one = *gamma * beta_plus_one; let q_lookup_vec = self.q_lookup(); + let q_dom_sep_vec = self.q_dom_sep(); for j in 0..(n - 2) { // compute merged lookup witness value - let lookup_wire_val = self.merged_lookup_wire_value(*tau, j, &q_lookup_vec)?; + let lookup_wire_val = + self.merged_lookup_wire_value(*tau, j, &q_lookup_vec, &q_dom_sep_vec)?; let table_val = merged_lookup_table[j]; let table_next_val = merged_lookup_table[j + 1]; let h1_val = sorted_vec[j]; @@ -1281,8 +1336,9 @@ where // only the first n-1 variables are for lookup let mut lookup_map = HashMap::::new(); let q_lookup_vec = self.q_lookup(); + let q_dom_sep_vec = self.q_dom_sep(); for i in 0..(n - 1) { - let elem = self.merged_lookup_wire_value(tau, i, &q_lookup_vec)?; + let elem = self.merged_lookup_wire_value(tau, i, &q_lookup_vec, &q_dom_sep_vec)?; let n_lookups = lookup_map.entry(elem).or_insert(0); *n_lookups += 1; } @@ -1328,35 +1384,26 @@ impl PlonkCircuit { Ok(range_table) } - #[inline] - // TODO: generalize to arbitrary key sets. - fn compute_key_table(&self) -> Result, PlonkError> { - self.check_plonk_type(PlonkType::UltraPlonk)?; - self.check_finalize_flag(true)?; - let n = self.eval_domain_size()?; - let mut key_table = vec![F::zero(); n - 1 - self.num_table_elems]; - for i in 0..self.num_table_elems { - key_table.push(F::from(i as u32)); - } - key_table.push(F::zero()); - Ok(key_table) - } - #[inline] fn merged_table_value( &self, tau: F, range_table: &[F], - key_table: &[F], + table_key_vec: &[F], + table_dom_sep_vec: &[F], q_lookup_vec: &[F], i: usize, ) -> Result { let range_val = range_table[i]; - let key_val = key_table[i]; + let key_val = table_key_vec[i]; + let dom_sep_val = table_dom_sep_vec[i]; let q_lookup_val = q_lookup_vec[i]; - let w3_val = self.witness(self.wire_variable(3, i))?; - let w4_val = self.witness(self.wire_variable(4, i))?; - Ok(range_val + q_lookup_val * tau * (key_val + tau * (w3_val + tau * w4_val))) + let table_val_1 = self.witness(self.wire_variable(TABLE_VAL_1_WIRE_ID, i))?; + let table_val_2 = self.witness(self.wire_variable(TABLE_VAL_2_WIRE_ID, i))?; + Ok(range_val + + q_lookup_val + * tau + * (dom_sep_val + tau * (key_val + tau * (table_val_1 + tau * table_val_2)))) } #[inline] @@ -1365,13 +1412,18 @@ impl PlonkCircuit { tau: F, i: usize, q_lookup_vec: &[F], + q_dom_sep_vec: &[F], ) -> Result { let w_range_val = self.witness(self.wire_variable(RANGE_WIRE_ID, i))?; - let w_0_val = self.witness(self.wire_variable(0, i))?; - let w_1_val = self.witness(self.wire_variable(1, i))?; - let w_2_val = self.witness(self.wire_variable(2, i))?; + let lookup_key = self.witness(self.wire_variable(LOOKUP_KEY_WIRE_ID, i))?; + let lookup_val_1 = self.witness(self.wire_variable(LOOKUP_VAL_1_WIRE_ID, i))?; + let lookup_val_2 = self.witness(self.wire_variable(LOOKUP_VAL_2_WIRE_ID, i))?; let q_lookup_val = q_lookup_vec[i]; - Ok(w_range_val + q_lookup_val * tau * (w_0_val + tau * (w_1_val + tau * w_2_val))) + let q_dom_sep_val = q_dom_sep_vec[i]; + Ok(w_range_val + + q_lookup_val + * tau + * (q_dom_sep_val + tau * (lookup_key + tau * (lookup_val_1 + tau * lookup_val_2)))) } } @@ -1969,7 +2021,7 @@ pub(crate) mod test { // Check key table polynomial let key_table_poly = circuit.compute_key_table_polynomial()?; - let key_table = circuit.compute_key_table()?; + let key_table = circuit.table_key_vec(); check_polynomial(&key_table_poly, &key_table); // Check sorted vector polynomials @@ -2016,8 +2068,10 @@ pub(crate) mod test { let one_plus_beta = F::one() + beta; let gamma_mul_one_plus_beta = gamma * one_plus_beta; let q_lookup_vec = circuit.q_lookup(); + let q_dom_sep = circuit.q_dom_sep(); for j in 0..(n - 2) { - let lookup_wire_val = circuit.merged_lookup_wire_value(tau, j, &q_lookup_vec)?; + let lookup_wire_val = + circuit.merged_lookup_wire_value(tau, j, &q_lookup_vec, &q_dom_sep)?; let table_val = merged_lookup_table[j]; let table_next_val = merged_lookup_table[j + 1]; let h1_val = sorted_vec[j]; diff --git a/plonk/src/circuit/customized/ecc/msm.rs b/plonk/src/circuit/customized/ecc/msm.rs index a2bd425f2..0238237eb 100644 --- a/plonk/src/circuit/customized/ecc/msm.rs +++ b/plonk/src/circuit/customized/ecc/msm.rs @@ -335,9 +335,6 @@ where // create circuit let range_size = F::from((1 << c) as u32); - for &var in decomposed_scalar_vars.iter() { - circuit.range_gate(var, c)?; - } circuit.decompose_vars_gate(decomposed_scalar_vars.clone(), scalar_var, range_size)?; Ok(decomposed_scalar_vars) diff --git a/plonk/src/circuit/customized/gates.rs b/plonk/src/circuit/customized/gates.rs index f5db890b8..aec8b97c7 100644 --- a/plonk/src/circuit/customized/gates.rs +++ b/plonk/src/circuit/customized/gates.rs @@ -305,9 +305,13 @@ where /// An UltraPlonk lookup gate #[derive(Debug, Clone)] -pub struct LookupGate; +pub struct LookupGate { + pub(crate) q_dom_sep: F, + pub(crate) table_dom_sep: F, + pub(crate) table_key: F, +} -impl Gate for LookupGate +impl Gate for LookupGate where F: Field, { @@ -317,4 +321,13 @@ where fn q_lookup(&self) -> F { F::one() } + fn q_dom_sep(&self) -> F { + self.q_dom_sep + } + fn table_key(&self) -> F { + self.table_key + } + fn table_dom_sep(&self) -> F { + self.table_dom_sep + } } diff --git a/plonk/src/circuit/customized/ultraplonk/lookup_table.rs b/plonk/src/circuit/customized/ultraplonk/lookup_table.rs index b723c4f16..fe9b517ca 100644 --- a/plonk/src/circuit/customized/ultraplonk/lookup_table.rs +++ b/plonk/src/circuit/customized/ultraplonk/lookup_table.rs @@ -11,20 +11,14 @@ use crate::{ errors::PlonkError, }; use ark_ff::PrimeField; -use ark_std::{boxed::Box, cmp::max, vec::Vec}; +use ark_std::{boxed::Box, cmp::max}; impl PlonkCircuit { /// Create a table with keys/values - /// [table_id, ..., table_id + n - 1] and - /// [table_vars\[0\], ..., table_vars[n - 1]]; + /// [0, ..., n - 1] and + /// [table_vars\[0\], ..., table_vars\[n - 1\]]; /// and create a list of variable tuples to be looked up: - /// [lookup_vars\[0\], ..., lookup_vars[m - 1]]; - /// - /// **For each variable tuple `(lookup_var.0, lookup_var.1, lookup_var.2)` - /// to be looked up, the index variable `lookup_var.0` is required to be - /// in range [0, n) (either constrained by a range-check gate or other - /// circuits), so that one can't set it out of bounds and thus do a - /// lookup into one of the *other* tables. ** + /// [lookup_vars\[0\], ..., lookup_vars\[m - 1\]]; /// /// w.l.o.g we assume n = m as we can pad with dummy tuples when n != m pub fn create_table_and_lookup_variables( @@ -42,24 +36,38 @@ impl PlonkCircuit { self.check_var_bound(table_var.1)?; } let n = max(lookup_vars.len(), table_vars.len()); - // update lookup keys for domain separation. - let lookup_keys: Vec = lookup_vars - .iter() - .map(|&(key, ..)| self.add_constant(key, &F::from(self.num_table_elems() as u32))) - .collect::, _>>()?; let n_gate = self.num_gates(); (*self.table_gate_ids_mut()).push((n_gate, n)); + let table_ctr = F::from(self.table_gate_ids_mut().len() as u64); for i in 0..n { - let (key, val0, val1) = match i < lookup_vars.len() { - true => (lookup_keys[i], lookup_vars[i].1, lookup_vars[i].2), - false => (self.zero(), self.zero(), self.zero()), + let (q_dom_sep, key, val0, val1) = match i < lookup_vars.len() { + true => ( + table_ctr, + lookup_vars[i].0, + lookup_vars[i].1, + lookup_vars[i].2, + ), + false => (F::zero(), self.zero(), self.zero(), self.zero()), }; - let (table_val0, table_val1) = match i < table_vars.len() { - true => table_vars[i], - false => (self.zero(), self.zero()), + let (table_dom_sep, table_key, table_val0, table_val1) = match i < table_vars.len() { + true => ( + table_ctr, + F::from(i as u64), + table_vars[i].0, + table_vars[i].1, + ), + false => (F::zero(), F::zero(), self.zero(), self.zero()), }; let wire_vars = [key, val0, val1, table_val0, table_val1]; - self.insert_gate(&wire_vars, Box::new(LookupGate))?; + + self.insert_gate( + &wire_vars, + Box::new(LookupGate { + q_dom_sep, + table_dom_sep, + table_key, + }), + )?; } *self.num_table_elems_mut() += n; Ok(()) @@ -136,6 +144,27 @@ mod test { .create_table_and_lookup_variables(&lookup_vars, &bad_table_vars) .is_err()); + // A lookup over a separate table should not satisfy the circuit. + let mut circuit: PlonkCircuit = PlonkCircuit::new_ultra_plonk(4); + let mut rng = test_rng(); + + let val0 = circuit.create_variable(F::rand(&mut rng))?; + let val1 = circuit.create_variable(F::rand(&mut rng))?; + let table_vars_1 = vec![(val0, val1)]; + let val2 = circuit.create_variable(F::rand(&mut rng))?; + let val3 = circuit.create_variable(F::rand(&mut rng))?; + let table_vars_2 = vec![(val2, val3)]; + let val2 = circuit.witness(table_vars_2[0].0)?; + let val3 = circuit.witness(table_vars_2[0].1)?; + let val2_var = circuit.create_variable(val2)?; + let val3_var = circuit.create_variable(val3)?; + let lookup_vars_1 = vec![(circuit.zero(), val2_var, val3_var)]; + + circuit.create_table_and_lookup_variables(&lookup_vars_1, &table_vars_2)?; + assert!(circuit.check_circuit_satisfiability(&[]).is_ok()); + circuit.create_table_and_lookup_variables(&lookup_vars_1, &table_vars_1)?; + assert!(circuit.check_circuit_satisfiability(&[]).is_err()); + Ok(()) } } diff --git a/plonk/src/circuit/gates.rs b/plonk/src/circuit/gates.rs index 5378e85ef..627df1232 100644 --- a/plonk/src/circuit/gates.rs +++ b/plonk/src/circuit/gates.rs @@ -44,6 +44,18 @@ pub trait Gate: Downcast + GateClone { fn q_lookup(&self) -> F { F::zero() } + /// UltraPlonk lookup domain separation selector. + fn q_dom_sep(&self) -> F { + F::zero() + } + /// UltraPlonk table keys. + fn table_key(&self) -> F { + F::zero() + } + /// UltraPlonk table domain separation ids + fn table_dom_sep(&self) -> F { + F::zero() + } } impl_downcast!(Gate where F: Field); diff --git a/plonk/src/circuit/mod.rs b/plonk/src/circuit/mod.rs index e5d9b13c6..e32326efb 100644 --- a/plonk/src/circuit/mod.rs +++ b/plonk/src/circuit/mod.rs @@ -188,6 +188,20 @@ pub trait Arithmetization: Circuit { Err(LookupUnsupported.into()) } + /// Compute and return the polynomial that interpolates the table domain + /// sepration ids. Return an error if the circuit does not support + /// lookup or has not been finalized. + fn compute_table_dom_sep_polynomial(&self) -> Result, PlonkError> { + Err(LookupUnsupported.into()) + } + + /// Compute and return the polynomial that interpolates the lookup domain + /// sepration selectors for the lookup gates. Return an error if the + /// circuit does not support lookup or has not been finalized. + fn compute_q_dom_sep_polynomial(&self) -> Result, PlonkError> { + Err(LookupUnsupported.into()) + } + /// Compute and return the combined lookup table vector given random /// challenge `tau`. fn compute_merged_lookup_table(&self, _tau: F) -> Result, PlonkError> { diff --git a/plonk/src/proof_system/prover.rs b/plonk/src/proof_system/prover.rs index a96971b0f..43d59994d 100644 --- a/plonk/src/proof_system/prover.rs +++ b/plonk/src/proof_system/prover.rs @@ -236,10 +236,15 @@ impl Prover { let range_table_poly_ref = &pk.plookup_pk.as_ref().unwrap().range_table_poly; let key_table_poly_ref = &pk.plookup_pk.as_ref().unwrap().key_table_poly; + let table_dom_sep_poly_ref = &pk.plookup_pk.as_ref().unwrap().table_dom_sep_poly; + let q_dom_sep_poly_ref = &pk.plookup_pk.as_ref().unwrap().q_dom_sep_poly; + let range_table_eval = range_table_poly_ref.evaluate(&challenges.zeta); let key_table_eval = key_table_poly_ref.evaluate(&challenges.zeta); let h_1_eval = online_oracles.plookup_oracles.h_polys[0].evaluate(&challenges.zeta); let q_lookup_eval = pk.q_lookup_poly()?.evaluate(&challenges.zeta); + let table_dom_sep_eval = table_dom_sep_poly_ref.evaluate(&challenges.zeta); + let q_dom_sep_eval = q_dom_sep_poly_ref.evaluate(&challenges.zeta); let zeta_mul_g = challenges.zeta * self.domain.group_gen; let prod_next_eval = online_oracles @@ -253,6 +258,7 @@ impl Prover { let q_lookup_next_eval = pk.q_lookup_poly()?.evaluate(&zeta_mul_g); let w_3_next_eval = online_oracles.wire_polys[3].evaluate(&zeta_mul_g); let w_4_next_eval = online_oracles.wire_polys[4].evaluate(&zeta_mul_g); + let table_dom_sep_next_eval = table_dom_sep_poly_ref.evaluate(&zeta_mul_g); Ok(PlookupEvaluations { range_table_eval, @@ -260,6 +266,8 @@ impl Prover { h_1_eval, q_lookup_eval, prod_next_eval, + table_dom_sep_eval, + q_dom_sep_eval, range_table_next_eval, key_table_next_eval, h_1_next_eval, @@ -267,6 +275,7 @@ impl Prover { q_lookup_next_eval, w_3_next_eval, w_4_next_eval, + table_dom_sep_next_eval, }) } @@ -405,6 +414,8 @@ impl Prover { &pk.plookup_pk.as_ref().unwrap().key_table_poly, &oracles.plookup_oracles.h_polys[0], pk.q_lookup_poly()?, + &pk.plookup_pk.as_ref().unwrap().table_dom_sep_poly, + &pk.plookup_pk.as_ref().unwrap().q_dom_sep_poly, ]) } @@ -424,6 +435,7 @@ impl Prover { pk.q_lookup_poly()?, &oracles.wire_polys[3], &oracles.wire_polys[4], + &pk.plookup_pk.as_ref().unwrap().table_dom_sep_poly, ]) } @@ -551,11 +563,19 @@ impl Prover { // Compute coset evaluations of Plookup online oracles. let ( + table_dom_sep_coset_fft, + q_dom_sep_coset_fft, range_table_coset_fft, key_table_coset_fft, h_coset_ffts, prod_lookup_poly_coset_fft, ) = if lookup_flag { + let table_dom_sep_coset_fft = self + .quot_domain + .coset_fft(pk.plookup_pk.as_ref().unwrap().table_dom_sep_poly.coeffs()); + let q_dom_sep_coset_fft = self + .quot_domain + .coset_fft(pk.plookup_pk.as_ref().unwrap().q_dom_sep_poly.coeffs()); let range_table_coset_fft = self .quot_domain .coset_fft(pk.plookup_pk.as_ref().unwrap().range_table_poly.coeffs()); // safe unwrap @@ -572,13 +592,15 @@ impl Prover { .quot_domain .coset_fft(oracles.plookup_oracles.prod_lookup_poly.coeffs()); ( + Some(table_dom_sep_coset_fft), + Some(q_dom_sep_coset_fft), Some(range_table_coset_fft), Some(key_table_coset_fft), Some(h_coset_ffts), Some(prod_lookup_poly_coset_fft), ) } else { - (None, None, None, None) + (None, None, None, None, None, None) }; // Compute coset evaluations of the quotient polynomial. @@ -624,6 +646,8 @@ impl Prover { range_table_coset_fft.as_ref().unwrap(), key_table_coset_fft.as_ref().unwrap(), selectors_coset_fft.last().unwrap(), // TODO: add a method to extract q_lookup_coset_fft + table_dom_sep_coset_fft.as_ref().unwrap(), + q_dom_sep_coset_fft.as_ref().unwrap(), challenges, ); t1 += t_lookup_1; @@ -765,6 +789,8 @@ impl Prover { range_table_coset_fft: &[E::Fr], key_table_coset_fft: &[E::Fr], q_lookup_coset_fft: &[E::Fr], + table_dom_sep_coset_fft: &[E::Fr], + q_dom_sep_coset_fft: &[E::Fr], challenges: &Challenges, ) -> (E::Fr, E::Fr) { assert!(pk.plookup_pk.is_some()); @@ -788,8 +814,12 @@ impl Prover { let p_xw = prod_lookup_coset_fft[(i + domain_size_ratio) % m]; let range_table_x = range_table_coset_fft[i]; let key_table_x = key_table_coset_fft[i]; + let table_dom_sep_x = table_dom_sep_coset_fft[i]; + let q_dom_sep_x = q_dom_sep_coset_fft[i]; + let range_table_xw = range_table_coset_fft[(i + domain_size_ratio) % m]; let key_table_xw = key_table_coset_fft[(i + domain_size_ratio) % m]; + let table_dom_sep_xw = table_dom_sep_coset_fft[(i + domain_size_ratio) % m]; let merged_table_x = eval_merged_table::( challenges.tau, range_table_x, @@ -797,6 +827,7 @@ impl Prover { q_lookup_coset_fft[i], w[3], w[4], + table_dom_sep_x, ); let merged_table_xw = eval_merged_table::( challenges.tau, @@ -805,6 +836,7 @@ impl Prover { q_lookup_coset_fft[(i + domain_size_ratio) % m], w_next[3], w_next[4], + table_dom_sep_xw, ); let merged_lookup_x = eval_merged_lookup_witness::( challenges.tau, @@ -813,6 +845,7 @@ impl Prover { w[1], w[2], q_lookup_coset_fft[i], + q_dom_sep_x, ); // The check that h1(X) - h2(wX) = 0 at point w^{n-1} @@ -993,6 +1026,7 @@ impl Prover { plookup_evals.q_lookup_eval, w_evals[3], w_evals[4], + plookup_evals.table_dom_sep_eval, ); let merged_table_next_eval = eval_merged_table::( challenges.tau, @@ -1001,6 +1035,7 @@ impl Prover { plookup_evals.q_lookup_next_eval, plookup_evals.w_3_next_eval, plookup_evals.w_4_next_eval, + plookup_evals.table_dom_sep_next_eval, ); let merged_lookup_eval = eval_merged_lookup_witness::( challenges.tau, @@ -1009,6 +1044,7 @@ impl Prover { w_evals[1], w_evals[2], plookup_evals.q_lookup_eval, + plookup_evals.q_dom_sep_eval, ); let beta_plus_one = one + challenges.beta; diff --git a/plonk/src/proof_system/snark.rs b/plonk/src/proof_system/snark.rs index 178046a6a..c18330658 100644 --- a/plonk/src/proof_system/snark.rs +++ b/plonk/src/proof_system/snark.rs @@ -87,9 +87,13 @@ where let plookup_pk = if circuit.support_lookup() { let range_table_poly = circuit.compute_range_table_polynomial()?; let key_table_poly = circuit.compute_key_table_polynomial()?; + let table_dom_sep_poly = circuit.compute_table_dom_sep_polynomial()?; + let q_dom_sep_poly = circuit.compute_q_dom_sep_polynomial()?; Some(PlookupProvingKey { range_table_poly, key_table_poly, + table_dom_sep_poly, + q_dom_sep_poly, }) } else { None @@ -133,6 +137,20 @@ where None, )? .0, + table_dom_sep_comm: KZG10::commit( + &commit_key, + &plookup_pk.as_ref().unwrap().table_dom_sep_poly, + None, + None, + )? + .0, + q_dom_sep_comm: KZG10::commit( + &commit_key, + &plookup_pk.as_ref().unwrap().q_dom_sep_poly, + None, + None, + )? + .0, }), }; @@ -1357,6 +1375,12 @@ pub mod test { // on the vanishing set excluding point w^{n-1} let beta_plus_one = E::Fr::one() + beta; let gamma_mul_beta_plus_one = gamma * beta_plus_one; + + let range_table_poly_ref = &pk.plookup_pk.as_ref().unwrap().range_table_poly; + let key_table_poly_ref = &pk.plookup_pk.as_ref().unwrap().key_table_poly; + let table_dom_sep_poly_ref = &pk.plookup_pk.as_ref().unwrap().table_dom_sep_poly; + let q_dom_sep_poly_ref = &pk.plookup_pk.as_ref().unwrap().q_dom_sep_poly; + for i in 0..domain.size() - 1 { let point = domain.element(i); let next_point = point * domain.group_gen; @@ -1367,9 +1391,8 @@ pub mod test { oracles.wire_polys[1].evaluate(&point), oracles.wire_polys[2].evaluate(&point), pk.q_lookup_poly()?.evaluate(&point), + q_dom_sep_poly_ref.evaluate(&point), ); - let range_table_poly_ref = &pk.plookup_pk.as_ref().unwrap().range_table_poly; - let key_table_poly_ref = &pk.plookup_pk.as_ref().unwrap().key_table_poly; let merged_table_eval = eval_merged_table::( challenges.tau, range_table_poly_ref.evaluate(&point), @@ -1377,6 +1400,7 @@ pub mod test { pk.q_lookup_poly()?.evaluate(&point), oracles.wire_polys[3].evaluate(&point), oracles.wire_polys[4].evaluate(&point), + table_dom_sep_poly_ref.evaluate(&point), ); let merged_table_next_eval = eval_merged_table::( challenges.tau, @@ -1385,6 +1409,7 @@ pub mod test { pk.q_lookup_poly()?.evaluate(&next_point), oracles.wire_polys[3].evaluate(&next_point), oracles.wire_polys[4].evaluate(&next_point), + table_dom_sep_poly_ref.evaluate(&next_point), ); let eval_1 = prod_poly.evaluate(&point) diff --git a/plonk/src/proof_system/structs.rs b/plonk/src/proof_system/structs.rs index a4f44dd96..3748cba4e 100644 --- a/plonk/src/proof_system/structs.rs +++ b/plonk/src/proof_system/structs.rs @@ -478,6 +478,12 @@ pub struct PlookupEvaluations { /// Key table polynomial evaluation at point `zeta`. pub(crate) key_table_eval: F, + /// Table domain separation polynomial evaluation at point `zeta`. + pub(crate) table_dom_sep_eval: F, + + /// Domain separation selector polynomial evaluation at point `zeta`. + pub(crate) q_dom_sep_eval: F, + /// The first sorted vector polynomial evaluation at point `zeta`. pub(crate) h_1_eval: F, @@ -493,6 +499,9 @@ pub struct PlookupEvaluations { /// Key table polynomial evaluation at point `zeta * g`. pub(crate) key_table_next_eval: F, + /// Table domain separation polynomial evaluation at point `zeta * g`. + pub(crate) table_dom_sep_next_eval: F, + /// The first sorted vector polynomial evaluation at point `zeta * g`. pub(crate) h_1_next_eval: F, @@ -517,6 +526,8 @@ impl PlookupEvaluations { self.key_table_eval, self.h_1_eval, self.q_lookup_eval, + self.table_dom_sep_eval, + self.q_dom_sep_eval, ] } @@ -531,6 +542,7 @@ impl PlookupEvaluations { self.q_lookup_next_eval, self.w_3_next_eval, self.w_4_next_eval, + self.table_dom_sep_next_eval, ] } } @@ -564,6 +576,12 @@ pub struct PlookupProvingKey { /// Key table polynomial. pub(crate) key_table_poly: DensePolynomial, + + /// Table domain separation polynomial. + pub(crate) table_dom_sep_poly: DensePolynomial, + + /// Lookup domain separation selector polynomial. + pub(crate) q_dom_sep_poly: DensePolynomial, } impl<'a, E: PairingEngine> ProvingKey<'a, E> { @@ -734,6 +752,14 @@ pub struct PlookupVerifyingKey { /// Key table polynomial commitment. The commitment is not hiding. pub(crate) key_table_comm: Commitment, + + /// Table domain separation polynomial commitment. The commitment is not + /// hiding. + pub(crate) table_dom_sep_comm: Commitment, + + /// Lookup domain separation selector polynomial commitment. The commitment + /// is not hiding. + pub(crate) q_dom_sep_comm: Commitment, } impl VerifyingKey { @@ -916,8 +942,12 @@ pub(crate) fn eval_merged_table( q_lookup_eval: E::Fr, w3_eval: E::Fr, w4_eval: E::Fr, + table_dom_sep_eval: E::Fr, ) -> E::Fr { - range_eval + q_lookup_eval * tau * (key_eval + tau * (w3_eval + tau * w4_eval)) + range_eval + + q_lookup_eval + * tau + * (table_dom_sep_eval + tau * (key_eval + tau * (w3_eval + tau * w4_eval))) } // Utility function for computing merged lookup witness evaluations. @@ -929,8 +959,12 @@ pub(crate) fn eval_merged_lookup_witness( w_1_eval: E::Fr, w_2_eval: E::Fr, q_lookup_eval: E::Fr, + q_dom_sep_eval: E::Fr, ) -> E::Fr { - w_range_eval + q_lookup_eval * tau * (w_0_eval + tau * (w_1_eval + tau * w_2_eval)) + w_range_eval + + q_lookup_eval + * tau + * (q_dom_sep_eval + tau * (w_0_eval + tau * (w_1_eval + tau * w_2_eval))) } #[cfg(test)] diff --git a/plonk/src/proof_system/verifier.rs b/plonk/src/proof_system/verifier.rs index aaf18eaa2..e076a5180 100644 --- a/plonk/src/proof_system/verifier.rs +++ b/plonk/src/proof_system/verifier.rs @@ -632,6 +632,7 @@ where w_evals[1], w_evals[2], lookup_evals.q_lookup_eval, + lookup_evals.q_dom_sep_eval, ); let merged_table_x = eval_merged_table::( challenges.tau, @@ -640,6 +641,7 @@ where lookup_evals.q_lookup_eval, w_evals[3], w_evals[4], + lookup_evals.table_dom_sep_eval, ); let merged_table_xw = eval_merged_table::( challenges.tau, @@ -648,6 +650,7 @@ where lookup_evals.q_lookup_next_eval, lookup_evals.w_3_next_eval, lookup_evals.w_4_next_eval, + lookup_evals.table_dom_sep_next_eval, ); // coefficient for prod_lookup_poly(X): @@ -833,6 +836,8 @@ where vk.plookup_vk.as_ref().unwrap().key_table_comm, proof.h_poly_comms[0], *vk.q_lookup_comm()?, + vk.plookup_vk.as_ref().unwrap().table_dom_sep_comm, + vk.plookup_vk.as_ref().unwrap().q_dom_sep_comm, ]) } @@ -853,6 +858,7 @@ where *vk.q_lookup_comm()?, wires_poly_comms[3], wires_poly_comms[4], + vk.plookup_vk.as_ref().unwrap().table_dom_sep_comm, ]) }