diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 14b4efc1..a4fce3dd 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -221,11 +221,12 @@ mod tests { .to_canonical_u64() .try_into() .unwrap(); - let clock = mem_timestamp / NUM_MEMORY_OPS; - let op = mem_timestamp % NUM_MEMORY_OPS; + let clock = mem_timestamp; + let op = (0..4) + .filter(|&o| memory_trace[memory::registers::is_memop(o)].values[i] == F::ONE) + .collect_vec()[0]; cpu_trace_rows[i][cpu::columns::uses_memop(op)] = F::ONE; - memory_trace[memory::registers::is_memop(op)].values[i] = F::ONE; cpu_trace_rows[i][cpu::columns::CLOCK] = F::from_canonical_usize(clock); cpu_trace_rows[i][cpu::columns::memop_is_read(op)] = memory_trace[memory::registers::IS_READ].values[i]; diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 422fae29..9ded1431 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -12,6 +12,7 @@ pub mod cross_table_lookup; mod get_challenges; pub mod keccak; pub mod logic; +pub mod lookup; pub mod memory; pub mod permutation; pub mod proof; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index e7d63df2..7387653d 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -10,7 +10,9 @@ use plonky2::timed; use plonky2::util::timing::TimingTree; use rand::Rng; +use super::registers::is_memop; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::lookup::{eval_lookups, eval_lookups_circuit, permuted_cols}; use crate::memory::registers::{ sorted_value_limb, value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, COUNTER, COUNTER_PERMUTED, IS_READ, NUM_REGISTERS, RANGE_CHECK, RANGE_CHECK_PERMUTED, @@ -19,7 +21,7 @@ use crate::memory::registers::{ }; use crate::permutation::PermutationPair; use crate::stark::Stark; -use crate::util::{permuted_cols, trace_rows_to_poly_values}; +use crate::util::trace_rows_to_poly_values; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub(crate) const NUM_PUBLIC_INPUTS: usize = 0; @@ -30,6 +32,7 @@ pub struct MemoryStark { } pub struct MemoryOp { + channel_index: usize, timestamp: F, is_read: F, context: F, @@ -73,8 +76,10 @@ pub fn generate_random_memory_ops( }; let timestamp = F::from_canonical_usize(i); + let channel_index = rng.gen_range(0..4); memory_ops.push(MemoryOp { + channel_index, timestamp, is_read: is_read_field, context, @@ -191,6 +196,7 @@ impl, const D: usize> MemoryStark { let mut trace_cols = [(); NUM_REGISTERS].map(|_| vec![F::ZERO; num_ops]); for i in 0..num_ops { let MemoryOp { + channel_index, timestamp, is_read, context, @@ -198,6 +204,7 @@ impl, const D: usize> MemoryStark { virt, value, } = memory_ops[i]; + trace_cols[is_memop(channel_index)][i] = F::ONE; trace_cols[TIMESTAMP][i] = timestamp; trace_cols[IS_READ][i] = is_read; trace_cols[ADDR_CONTEXT][i] = context; @@ -382,22 +389,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { diff --git a/evm/src/util.rs b/evm/src/util.rs index 86eaa046..011a1add 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -1,7 +1,5 @@ -use std::cmp::Ordering; - use itertools::Itertools; -use plonky2::field::field_types::{Field, PrimeField64}; +use plonky2::field::field_types::Field; use plonky2::field::polynomial::PolynomialValues; use plonky2::util::transpose; @@ -16,71 +14,3 @@ pub fn trace_rows_to_poly_values( .map(|column| PolynomialValues::new(column)) .collect() } - -/// Given an input column and a table column, generate the permuted input and permuted table columns -/// used in the Halo2 permutation argument. -pub fn permuted_cols(inputs: &[F], table: &[F]) -> (Vec, Vec) { - let n = inputs.len(); - - // The permuted inputs do not have to be ordered, but we found that sorting was faster than - // hash-based grouping. We also sort the table, as this helps us identify "unused" table - // elements efficiently. - - // To compare elements, e.g. for sorting, we first need them in canonical form. It would be - // wasteful to canonicalize in each comparison, as a single element may be involved in many - // comparisons. So we will canonicalize once upfront, then use `to_noncanonical_u64` when - // comparing elements. - - let sorted_inputs = inputs - .iter() - .map(|x| x.to_canonical()) - .sorted_unstable_by_key(|x| x.to_noncanonical_u64()) - .collect_vec(); - let sorted_table = table - .iter() - .map(|x| x.to_canonical()) - .sorted_unstable_by_key(|x| x.to_noncanonical_u64()) - .collect_vec(); - - let mut unused_table_inds = Vec::with_capacity(n); - let mut unused_table_vals = Vec::with_capacity(n); - let mut permuted_table = vec![F::ZERO; n]; - let mut i = 0; - let mut j = 0; - while (j < n) && (i < n) { - let input_val = sorted_inputs[i].to_noncanonical_u64(); - let table_val = sorted_table[j].to_noncanonical_u64(); - match input_val.cmp(&table_val) { - Ordering::Greater => { - unused_table_vals.push(sorted_table[j]); - j += 1; - } - Ordering::Less => { - if let Some(x) = unused_table_vals.pop() { - permuted_table[i] = x; - } else { - unused_table_inds.push(i); - } - i += 1; - } - Ordering::Equal => { - permuted_table[i] = sorted_table[j]; - i += 1; - j += 1; - } - } - } - - #[allow(clippy::needless_range_loop)] // indexing is just more natural here - for jj in j..n { - unused_table_vals.push(sorted_table[jj]); - } - for ii in i..n { - unused_table_inds.push(ii); - } - for (ind, val) in unused_table_inds.into_iter().zip_eq(unused_table_vals) { - permuted_table[ind] = val; - } - - (sorted_inputs, permuted_table) -}