diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 51e47abb..31e25884 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -163,6 +163,7 @@ pub(crate) const NUM_MEMORY_VALUE_LIMBS: usize = 8; pub(crate) const CLOCK: usize = SIMPLE_LOGIC_DIFF_INV + 1; +// Uses_memop(i) is `F::ONE` iff this row includes a memory operation in its `i`th spot. const USES_MEMOP_START: usize = CLOCK + 1; pub const fn uses_memop(op: usize) -> usize { debug_assert!(op < NUM_MEMORY_OPS); diff --git a/evm/src/lib.rs b/evm/src/lib.rs index f60ea23c..422fae29 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -3,7 +3,6 @@ #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] #![feature(generic_const_exprs)] -#![allow(dead_code)] pub mod all_stark; pub mod config; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index f05a2749..a9ccec0a 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -17,6 +17,7 @@ use crate::memory::registers::{ SEGMENT_FIRST_CHANGE, SORTED_ADDR_CONTEXT, SORTED_ADDR_SEGMENT, SORTED_ADDR_VIRTUAL, SORTED_IS_READ, SORTED_TIMESTAMP, TIMESTAMP, VIRTUAL_FIRST_CHANGE, }; +use crate::permutation::PermutationPair; use crate::stark::Stark; use crate::util::{permuted_cols, trace_rows_to_poly_values}; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; @@ -28,7 +29,16 @@ pub struct MemoryStark { pub(crate) f: PhantomData, } -pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, F, F, [F; 8])> { +pub struct MemoryOp { + timestamp: F, + is_read: F, + context: F, + segment: F, + virt: F, + value: [F; 8], +} + +pub fn generate_random_memory_ops(num_ops: usize) -> Vec> { let mut memory_ops = Vec::new(); let mut rng = thread_rng(); @@ -61,7 +71,14 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, let timestamp = F::from_canonical_usize(i); - memory_ops.push((timestamp, is_read_field, context, segment, virt, vals)) + memory_ops.push(MemoryOp { + timestamp, + is_read: is_read_field, + context, + segment, + virt, + value: vals, + }); } memory_ops @@ -164,22 +181,27 @@ pub fn generate_range_check_value( impl, const D: usize> MemoryStark { pub(crate) fn generate_trace_rows( &self, - memory_ops: Vec<(F, F, F, F, F, [F; 8])>, + memory_ops: Vec>, ) -> Vec<[F; NUM_REGISTERS]> { let num_ops = memory_ops.len(); - let mut trace_cols: [Vec; NUM_REGISTERS] = vec![vec![F::ZERO; num_ops]; NUM_REGISTERS] - .try_into() - .unwrap(); + let mut trace_cols = [(); NUM_REGISTERS].map(|_| vec![F::ZERO; num_ops]); for i in 0..num_ops { - let (timestamp, is_read, context, segment, virt, values) = memory_ops[i]; + let MemoryOp { + timestamp, + is_read, + context, + segment, + virt, + value, + } = memory_ops[i]; trace_cols[TIMESTAMP][i] = timestamp; trace_cols[IS_READ][i] = is_read; trace_cols[ADDR_CONTEXT][i] = context; trace_cols[ADDR_SEGMENT][i] = segment; trace_cols[ADDR_VIRTUAL][i] = virt; for j in 0..8 { - trace_cols[value_limb(j)][i] = values[j]; + trace_cols[value_limb(j)][i] = value[j]; } } @@ -252,7 +274,7 @@ impl, const D: usize> MemoryStark { trace_cols[VIRTUAL_FIRST_CHANGE] = virtual_first_change; trace_cols[RANGE_CHECK] = range_check_value; - trace_cols[COUNTER] = (0..trace_cols[0].len()) + trace_cols[COUNTER] = (0..num_trace_rows) .map(|i| F::from_canonical_usize(i)) .collect(); @@ -262,13 +284,10 @@ impl, const D: usize> MemoryStark { trace_cols[COUNTER_PERMUTED] = permuted_table; } - pub fn generate_trace( - &self, - memory_ops: Vec<(F, F, F, F, F, [F; 8])>, - ) -> Vec> { + pub fn generate_trace(&self, memory_ops: Vec>) -> Vec> { let mut timing = TimingTree::new("generate trace", log::Level::Debug); - // Generate the witness, except for permuted columns in the lookup argument. + // Generate the witness. let trace_rows = timed!( &mut timing, "generate trace rows", @@ -363,7 +382,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { 3 } + + fn permutation_pairs(&self) -> Vec { + vec![ + PermutationPair::singletons(RANGE_CHECK, RANGE_CHECK_PERMUTED), + PermutationPair::singletons(COUNTER, COUNTER_PERMUTED), + ] + } } #[cfg(test)] diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index c81408c8..b89a9692 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -9,12 +9,14 @@ pub(crate) const ADDR_CONTEXT: usize = IS_READ + 1; pub(crate) const ADDR_SEGMENT: usize = ADDR_CONTEXT + 1; pub(crate) const ADDR_VIRTUAL: usize = ADDR_SEGMENT + 1; +// Eight limbs to hold up to a 256-bit value. const VALUE_START: usize = ADDR_VIRTUAL + 1; pub(crate) const fn value_limb(i: usize) -> usize { debug_assert!(i < NUM_MEMORY_VALUE_LIMBS); VALUE_START + i } +// Separate columns for the same memory operations, sorted by (addr, timestamp). pub(crate) const SORTED_TIMESTAMP: usize = VALUE_START + NUM_MEMORY_VALUE_LIMBS; pub(crate) const SORTED_IS_READ: usize = SORTED_TIMESTAMP + 1; pub(crate) const SORTED_ADDR_CONTEXT: usize = SORTED_IS_READ + 1; @@ -27,15 +29,24 @@ pub(crate) const fn sorted_value_limb(i: usize) -> usize { SORTED_VALUE_START + i } +// Flags to indicate whether this part of the address differs from the next row (in the sorted +// columns), and the previous parts do not differ. +// That is, e.g., `SEGMENT_FIRST_CHANGE` is `F::ONE` iff `SORTED_ADDR_CONTEXT` is the same in this +// row and the next, but `SORTED_ADDR_SEGMENT` is not. pub(crate) const CONTEXT_FIRST_CHANGE: usize = SORTED_VALUE_START + NUM_MEMORY_VALUE_LIMBS; pub(crate) const SEGMENT_FIRST_CHANGE: usize = CONTEXT_FIRST_CHANGE + 1; pub(crate) const VIRTUAL_FIRST_CHANGE: usize = SEGMENT_FIRST_CHANGE + 1; +// We use a range check to ensure sorting. pub(crate) const RANGE_CHECK: usize = VIRTUAL_FIRST_CHANGE + 1; +// The counter column (used for the range check) starts from 0 and increments. pub(crate) const COUNTER: usize = RANGE_CHECK + 1; +// Helper columns for the permutation argument used to enforce the range check. pub(crate) const RANGE_CHECK_PERMUTED: usize = COUNTER + 1; pub(crate) const COUNTER_PERMUTED: usize = RANGE_CHECK_PERMUTED + 1; +// Flags to indicate if this operation corresponds to the `i`th memory op in a certain row of the +// CPU table. const IS_MEMOP_START: usize = COUNTER_PERMUTED + 1; pub(crate) const fn is_memop(i: usize) -> usize { debug_assert!(i < NUM_MEMORY_OPS);