diff --git a/evm/src/arithmetic/arithmetic_stark.rs b/evm/src/arithmetic/arithmetic_stark.rs index 6b0c9fa5..52fc2f78 100644 --- a/evm/src/arithmetic/arithmetic_stark.rs +++ b/evm/src/arithmetic/arithmetic_stark.rs @@ -14,7 +14,7 @@ use static_assertions::const_assert; use super::columns::NUM_ARITH_COLUMNS; use super::shift; use crate::all_stark::Table; -use crate::arithmetic::columns::{RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; +use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS}; use crate::arithmetic::{addcy, byte, columns, divmod, modular, mul, Operation}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; @@ -290,11 +290,12 @@ impl, const D: usize> Stark for ArithmeticSta 3 } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: SHARED_COLS.collect(), - table_column: RANGE_COUNTER, - frequencies_column: RC_FREQUENCIES, + columns: Column::singles(SHARED_COLS).collect(), + table_column: Column::single(RANGE_COUNTER), + frequencies_column: Column::single(RC_FREQUENCIES), + filter_columns: vec![None; NUM_SHARED_COLS], }] } } diff --git a/evm/src/byte_packing/byte_packing_stark.rs b/evm/src/byte_packing/byte_packing_stark.rs index e5596b3e..0de8968a 100644 --- a/evm/src/byte_packing/byte_packing_stark.rs +++ b/evm/src/byte_packing/byte_packing_stark.rs @@ -391,11 +391,12 @@ impl, const D: usize> Stark for BytePackingSt 3 } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: (value_bytes(0)..value_bytes(0) + NUM_BYTES).collect(), - table_column: RANGE_COUNTER, - frequencies_column: RC_FREQUENCIES, + columns: Column::singles(value_bytes(0)..value_bytes(0) + NUM_BYTES).collect(), + table_column: Column::single(RANGE_COUNTER), + frequencies_column: Column::single(RC_FREQUENCIES), + filter_columns: vec![None; NUM_BYTES], }] } } diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index e8e9c1c2..65b27b13 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -259,6 +259,14 @@ impl Column { res } + /// Evaluates the column on all rows. + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } + /// Circuit version of `eval`: Given a row's targets, returns their linear combination. pub(crate) fn eval_circuit( &self, @@ -401,6 +409,14 @@ impl Filter { .map(|col| col.eval_table(table, row)) .sum() } + + pub(crate) fn eval_all_rows(&self, table: &[PolynomialValues]) -> Vec { + let length = table[0].len(); + + (0..length) + .map(|row| self.eval_table(table, row)) + .collect::>() + } } /// A `Table` with a linear combination of columns and a filter. diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 75e0ced3..2cfc3409 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -800,11 +800,12 @@ impl, const D: usize> Stark for KeccakSpongeS 3 } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: get_block_bytes_range().collect(), - table_column: RANGE_COUNTER, - frequencies_column: RC_FREQUENCIES, + columns: Column::singles(get_block_bytes_range()).collect(), + table_column: Column::single(RANGE_COUNTER), + frequencies_column: Column::single(RC_FREQUENCIES), + filter_columns: vec![None; KECCAK_RATE_BYTES], }] } } diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index a93e4fb3..7944d78b 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -1,6 +1,6 @@ use itertools::Itertools; use num_bigint::BigUint; -use plonky2::field::batch_util::batch_add_inplace; +use plonky2::field::batch_util::{batch_add_inplace, batch_multiply_inplace}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -12,22 +12,27 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2_util::ceil_div_usize; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookup::{Column, Filter}; use crate::evaluation_frame::StarkEvaluationFrame; use crate::stark::Stark; -pub struct Lookup { +pub struct Lookup { /// Columns whose values should be contained in the lookup table. /// These are the f_i(x) polynomials in the logUp paper. - pub(crate) columns: Vec, + pub(crate) columns: Vec>, /// Column containing the lookup table. /// This is the t(x) polynomial in the paper. - pub(crate) table_column: usize, + pub(crate) table_column: Column, /// Column containing the frequencies of `columns` in `table_column`. /// This is the m(x) polynomial in the paper. - pub(crate) frequencies_column: usize, + pub(crate) frequencies_column: Column, + + /// Columns to filter some elements. There is at most one filter + /// column per column to range-check. + pub(crate) filter_columns: Vec>>, } -impl Lookup { +impl Lookup { pub(crate) fn num_helper_columns(&self, constraint_degree: usize) -> usize { // One helper column for each column batch of size `constraint_degree-1`, // then one column for the inverse of `table + challenge` and one for the `Z` polynomial. @@ -41,7 +46,7 @@ impl Lookup { /// this computes the helper columns `h_i = 1/(x+f_2i) + 1/(x+f_2i+1)`, `g = 1/(x+t)`, /// and `Z(gx) = Z(x) + sum h_i(x) - m(x)g(x)` where `m` is the frequencies column. pub(crate) fn lookup_helper_columns( - lookup: &Lookup, + lookup: &Lookup, trace_poly_values: &[PolynomialValues], challenge: F, constraint_degree: usize, @@ -51,6 +56,8 @@ pub(crate) fn lookup_helper_columns( "TODO: Allow other constraint degrees." ); + assert_eq!(lookup.columns.len(), lookup.filter_columns.len()); + let num_total_logup_entries = trace_poly_values[0].values.len() * lookup.columns.len(); assert!(BigUint::from(num_total_logup_entries) < F::characteristic()); @@ -67,30 +74,43 @@ pub(crate) fn lookup_helper_columns( // h_k polynomials; instead there's a separate helper column for it (see below). // * Here, we use 1 instead of -1 as the numerator (and subtract later). // * Here, for now, the batch size (l) is always constraint_degree - 1 = 2. - for mut col_inds in &lookup.columns.iter().chunks(constraint_degree - 1) { - let first = *col_inds.next().unwrap(); - // TODO: The clone could probably be avoided by using a modified version of `batch_multiplicative_inverse` - // taking `challenge` as an additional argument. - let mut column = trace_poly_values[first].values.clone(); + for (i, mut col_inds) in (&lookup.columns.iter().chunks(constraint_degree - 1)) + .into_iter() + .enumerate() + { + let first = col_inds.next().unwrap(); + + let mut column = first.eval_all_rows(trace_poly_values); + let length = column.len(); + for x in column.iter_mut() { *x = challenge + *x; } let mut acc = F::batch_multiplicative_inverse(&column); - for &ind in col_inds { - let mut column = trace_poly_values[ind].values.clone(); + if let Some(filter) = &lookup.filter_columns[0] { + batch_multiply_inplace(&mut acc, &filter.eval_all_rows(trace_poly_values)); + } + + for (j, ind) in col_inds.enumerate() { + let mut column = ind.eval_all_rows(trace_poly_values); for x in column.iter_mut() { *x = challenge + *x; } column = F::batch_multiplicative_inverse(&column); + let filter_idx = (constraint_degree - 1) * i + j + 1; + if let Some(filter) = &lookup.filter_columns[filter_idx] { + batch_multiply_inplace(&mut column, &filter.eval_all_rows(trace_poly_values)); + } batch_add_inplace(&mut acc, &column); } + helper_columns.push(acc.into()); } // Add `1/(table+challenge)` to the helper columns. // This is 1/phi_0(x) = 1/(x + t(x)) from the paper. // Here, we don't include m(x) in the numerator, instead multiplying it with this column later. - let mut table = trace_poly_values[lookup.table_column].values.clone(); + let mut table = lookup.table_column.eval_all_rows(trace_poly_values); for x in table.iter_mut() { *x = challenge + *x; } @@ -100,7 +120,7 @@ pub(crate) fn lookup_helper_columns( // This enforces the check from the paper, that the sum of the h_k(x) polynomials is 0 over H. // In the paper, that sum includes m(x)/(x + t(x)) = frequencies(x)/g(x), because that was bundled // into the h_k(x) polynomials. - let frequencies = &trace_poly_values[lookup.frequencies_column].values; + let frequencies = &lookup.frequencies_column.eval_all_rows(trace_poly_values); let mut z = Vec::with_capacity(frequencies.len()); z.push(F::ZERO); for i in 0..frequencies.len() - 1 { @@ -130,7 +150,7 @@ where /// Constraints for the logUp lookup argument. pub(crate) fn eval_packed_lookups_generic( stark: &S, - lookups: &[Lookup], + lookups: &[Lookup], vars: &S::EvaluationFrame, lookup_vars: LookupCheckVars, yield_constr: &mut ConstraintConsumer

, @@ -140,6 +160,8 @@ pub(crate) fn eval_packed_lookups_generic, S: Stark, { + let local_values = vars.get_local_values(); + let next_values = vars.get_next_values(); let degree = stark.constraint_degree(); assert_eq!(degree, 3, "TODO: Allow other constraint degrees."); let mut start = 0; @@ -147,19 +169,34 @@ pub(crate) fn eval_packed_lookups_generic>(); + let last_filter_value = filters[0]; + for (val, f) in col_values.zip_eq(filters) { + x *= val + challenge; + y += (val + challenge) * f; } match chunk.len() { 2 => yield_constr.constraint(x - y), - 1 => yield_constr.constraint(x - P::ONES), + 1 => yield_constr.constraint(x - last_filter_value), _ => todo!("Allow other constraint degrees."), } } @@ -167,12 +204,12 @@ pub(crate) fn eval_packed_lookups_generic>(); + let filters = lookup.filter_columns + [(degree - 1) * j..(degree - 1) * j + chunk.len()] + .iter() + .map(|maybe_filter| { + if let Some(filter) = maybe_filter { + filter.eval_filter_circuit(builder, local_values, next_values) + } else { + one + } + }) + .rev() + .collect::>(); + let last_filter_value = filters[0]; + for (&val, f) in col_values.iter().zip_eq(filters) { + let tmp = builder.add_extension(val, challenge); x = builder.mul_extension(x, tmp); - y = builder.add_extension(y, tmp); + y = builder.mul_add_extension(f, tmp, y); } match chunk.len() { 2 => { @@ -220,7 +276,7 @@ pub(crate) fn eval_ext_lookups_circuit< yield_constr.constraint(builder, tmp) } 1 => { - let tmp = builder.sub_extension(x, one); + let tmp = builder.sub_extension(x, last_filter_value); yield_constr.constraint(builder, tmp) } _ => todo!("Allow other constraint degrees."), @@ -229,14 +285,19 @@ pub(crate) fn eval_ext_lookups_circuit< let z = lookup_vars.local_values[start + num_helper_columns - 1]; let next_z = lookup_vars.next_values[start + num_helper_columns - 1]; - let table_with_challenge = - builder.add_extension(vars.get_local_values()[lookup.table_column], challenge); + let table_column = lookup + .table_column + .eval_circuit(builder, vars.get_local_values()); + let table_with_challenge = builder.add_extension(table_column, challenge); let mut y = builder.add_many_extension( &lookup_vars.local_values[start..start + num_helper_columns - 1], ); + let frequencies_column = lookup + .frequencies_column + .eval_circuit(builder, vars.get_local_values()); y = builder.mul_extension(y, table_with_challenge); - y = builder.sub_extension(y, vars.get_local_values()[lookup.frequencies_column]); + y = builder.sub_extension(y, frequencies_column); let mut constraint = builder.sub_extension(next_z, z); constraint = builder.mul_extension(constraint, table_with_challenge); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 4dfdc913..666f4e11 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -528,11 +528,12 @@ impl, const D: usize> Stark for MemoryStark Vec { + fn lookups(&self) -> Vec> { vec![Lookup { - columns: vec![RANGE_CHECK], - table_column: COUNTER, - frequencies_column: FREQUENCIES, + columns: vec![Column::single(RANGE_CHECK)], + table_column: Column::single(COUNTER), + frequencies_column: Column::single(FREQUENCIES), + filter_columns: vec![None], }] } } diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 32989c8f..cc748f16 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -532,7 +532,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( trace_commitment: &'a PolynomialBatch, auxiliary_polys_commitment: &'a PolynomialBatch, lookup_challenges: Option<&'a Vec>, - lookups: &[Lookup], + lookups: &[Lookup], ctl_data: &CtlData, alphas: Vec, degree_bits: usize, @@ -688,7 +688,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( trace_commitment: &'a PolynomialBatch, auxiliary_commitment: &'a PolynomialBatch, lookup_challenges: Option<&'a Vec>, - lookups: &[Lookup], + lookups: &[Lookup], ctl_data: &CtlData, alphas: Vec, degree_bits: usize, diff --git a/evm/src/stark.rs b/evm/src/stark.rs index 51a3ec1b..6099abdd 100644 --- a/evm/src/stark.rs +++ b/evm/src/stark.rs @@ -207,7 +207,7 @@ pub trait Stark, const D: usize>: Sync { } } - fn lookups(&self) -> Vec { + fn lookups(&self) -> Vec> { vec![] } diff --git a/evm/src/vanishing_poly.rs b/evm/src/vanishing_poly.rs index 2e1adfc7..15444c7e 100644 --- a/evm/src/vanishing_poly.rs +++ b/evm/src/vanishing_poly.rs @@ -19,7 +19,7 @@ use crate::stark::Stark; pub(crate) fn eval_vanishing_poly( stark: &S, vars: &S::EvaluationFrame, - lookups: &[Lookup], + lookups: &[Lookup], lookup_vars: Option>, ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

,