From 7c2cfebdee0506b2b62ec97a486d41fc8676d396 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 13 Jun 2022 14:37:29 -0700 Subject: [PATCH 01/46] memory --- evm/src/lib.rs | 1 + evm/src/memory/memory_stark.rs | 16 ++++++++++++++++ evm/src/memory/mod.rs | 2 ++ evm/src/memory/registers.rs | 3 +++ 4 files changed, 22 insertions(+) create mode 100644 evm/src/memory/memory_stark.rs create mode 100644 evm/src/memory/mod.rs create mode 100644 evm/src/memory/registers.rs diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 8e4d6468..422fae29 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 memory; pub mod permutation; pub mod proof; pub mod prover; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs new file mode 100644 index 00000000..0cc42d30 --- /dev/null +++ b/evm/src/memory/memory_stark.rs @@ -0,0 +1,16 @@ +#[derive(Default)] +pub struct TransactionMemory { + pub calls: Vec, +} + +/// A virtual memory space specific to the current contract call. +pub struct ContractMemory { + pub code: MemorySegment, + pub main: MemorySegment, + pub calldata: MemorySegment, + pub returndata: MemorySegment, +} + +pub struct MemorySegment { + pub content: Vec, +} diff --git a/evm/src/memory/mod.rs b/evm/src/memory/mod.rs new file mode 100644 index 00000000..53108262 --- /dev/null +++ b/evm/src/memory/mod.rs @@ -0,0 +1,2 @@ +pub mod memory_stark; +pub mod registers; \ No newline at end of file diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs new file mode 100644 index 00000000..1373d0d8 --- /dev/null +++ b/evm/src/memory/registers.rs @@ -0,0 +1,3 @@ +//! Memory unit. + +pub(super) const END: usize = super::START_MEMORY; From 24398e2f180296b3ca115c1542a9ad261fbf40b9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 7 Jun 2022 14:40:42 -0700 Subject: [PATCH 02/46] memory --- evm/src/memory/memory_stark.rs | 368 +++++++++++++++++++++++++++++++++ evm/src/memory/mod.rs | 2 +- evm/src/memory/registers.rs | 30 ++- 3 files changed, 398 insertions(+), 2 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 0cc42d30..af4981af 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,3 +1,19 @@ +use std::marker::PhantomData; + +use itertools::{izip, multiunzip}; +use plonky2::{field::{field_types::PrimeField64, extension_field::Extendable}, hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; + +use crate::memory::registers::{ + memory_value_limb, sorted_memory_value_limb, MEMORY_ADDR_CONTEXT, MEMORY_ADDR_SEGMENT, + MEMORY_ADDR_VIRTUAL, MEMORY_IS_READ, MEMORY_TIMESTAMP, SORTED_MEMORY_ADDR_CONTEXT, + SORTED_MEMORY_ADDR_SEGMENT, SORTED_MEMORY_ADDR_VIRTUAL, SORTED_MEMORY_TIMESTAMP, SORTED_MEMORY_IS_READ, + MEMORY_CONTEXT_FIRST_CHANGE, MEMORY_SEGMENT_FIRST_CHANGE, MEMORY_VIRTUAL_FIRST_CHANGE, +}; + +use crate::vars::{StarkEvaluationVars, StarkEvaluationTargets}; +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::stark::Stark; + #[derive(Default)] pub struct TransactionMemory { pub calls: Vec, @@ -14,3 +30,355 @@ pub struct ContractMemory { pub struct MemorySegment { pub content: Vec, } + + +#[derive(Copy, Clone)] +pub struct MemoryStark { + pub(crate) f: PhantomData, +} + +pub fn sort_memory_ops( + context: &[F], + segment: &[F], + virtuals: &[F], + values: &[Vec], + is_read: &[F], + timestamp: &[F], +) -> (Vec, Vec, Vec, Vec>, Vec, Vec) { + let mut ops: Vec<(F, F, F, Vec, F, F)> = izip!( + context.iter().cloned(), + segment.iter().cloned(), + virtuals.iter().cloned(), + values.iter().cloned(), + is_read.iter().cloned(), + timestamp.iter().cloned() + ) + .collect(); + + ops.sort_by(|&(c1, s1, v1, _, _, t1), &(c2, s2, v2, _, _, t2)| { + ( + c1.to_noncanonical_u64(), + s1.to_noncanonical_u64(), + v1.to_noncanonical_u64(), + t1.to_noncanonical_u64(), + ) + .cmp(&( + c2.to_noncanonical_u64(), + s2.to_noncanonical_u64(), + v2.to_noncanonical_u64(), + t2.to_noncanonical_u64(), + )) + }); + + multiunzip(ops) +} + +pub fn generate_first_change_flags( + context: &Vec, + segment: &Vec, + virtuals: &Vec, +) -> (Vec, Vec, Vec) { + let num_ops = context.len(); + let mut context_first_change = Vec::new(); + let mut segment_first_change = Vec::new(); + let mut virtual_first_change = Vec::new(); + for idx in 0..num_ops - 1 { + let this_context_first_change = if context[idx] != context[idx + 1] { + F::ONE + } else { + F::ZERO + }; + let this_segment_first_change = if segment[idx] != segment[idx + 1] { + F::ONE * (F::ONE - this_context_first_change) + } else { + F::ZERO + }; + let this_virtual_first_change = if virtuals[idx] != virtuals[idx + 1] { + F::ONE * (F::ONE - this_context_first_change) * (F::ONE - this_segment_first_change) + } else { + F::ZERO + }; + + context_first_change.push(this_context_first_change); + segment_first_change.push(this_segment_first_change); + virtual_first_change.push(this_virtual_first_change); + } + + context_first_change.push(F::ZERO); + segment_first_change.push(F::ZERO); + virtual_first_change.push(F::ZERO); + + ( + context_first_change, + segment_first_change, + virtual_first_change, + ) +} + +pub fn generate_range_check_value( + context: &Vec, + segment: &Vec, + virtuals: &Vec, + timestamp: &Vec, + context_first_change: &Vec, + segment_first_change: &Vec, + virtual_first_change: &Vec, +) -> Vec { + let num_ops = context.len(); + let mut range_check = Vec::new(); + + for idx in 0..num_ops - 1 { + let this_timestamp_first_change = F::ONE + - context_first_change[idx] + - segment_first_change[idx] + - virtual_first_change[idx]; + + range_check.push( + context_first_change[idx] * (context[idx + 1] - context[idx] - F::ONE) + + segment_first_change[idx] * (segment[idx + 1] - segment[idx] - F::ONE) + + virtual_first_change[idx] * (virtuals[idx + 1] - virtuals[idx] - F::ONE) + + this_timestamp_first_change * (timestamp[idx + 1] - timestamp[idx] - F::ONE), + ); + } + + range_check.push(F::ZERO); + + range_check +} + +impl, const D: usize> MemoryStark { + pub(crate) fn generate_memory(trace_cols: &mut [Vec]) { + let context = &trace_cols[MEMORY_ADDR_CONTEXT]; + let segment = &trace_cols[MEMORY_ADDR_SEGMENT]; + let virtuals = &trace_cols[MEMORY_ADDR_VIRTUAL]; + let values: Vec> = (0..8) + .map(|i| &trace_cols[memory_value_limb(i)]) + .cloned() + .collect(); + let is_read = &trace_cols[MEMORY_IS_READ]; + let timestamp = &trace_cols[MEMORY_TIMESTAMP]; + + let ( + sorted_context, + sorted_segment, + sorted_virtual, + sorted_values, + sorted_is_read, + sorted_timestamp, + ) = sort_memory_ops(context, segment, virtuals, &values, is_read, timestamp); + + let (context_first_change, segment_first_change, virtual_first_change) = + generate_first_change_flags(&sorted_context, &sorted_segment, &sorted_virtual); + + let range_check_value = generate_range_check_value( + &sorted_context, + &sorted_segment, + &sorted_virtual, + &sorted_timestamp, + &context_first_change, + &segment_first_change, + &virtual_first_change, + ); + + trace_cols[SORTED_MEMORY_ADDR_CONTEXT] = sorted_context; + trace_cols[SORTED_MEMORY_ADDR_SEGMENT] = sorted_segment; + trace_cols[SORTED_MEMORY_ADDR_VIRTUAL] = sorted_virtual; + for i in 0..8 { + trace_cols[sorted_memory_value_limb(i)] = sorted_values[i].clone(); + } + trace_cols[SORTED_MEMORY_IS_READ] = sorted_is_read; + trace_cols[SORTED_MEMORY_TIMESTAMP] = sorted_timestamp; + + trace_cols[MEMORY_CONTEXT_FIRST_CHANGE] = context_first_change; + trace_cols[MEMORY_SEGMENT_FIRST_CHANGE] = segment_first_change; + trace_cols[MEMORY_VIRTUAL_FIRST_CHANGE] = virtual_first_change; + + trace_cols[crate::registers::range_check_degree::col_rc_degree_input(0)] = range_check_value; + } +} + +impl, const D: usize> Stark for MemoryStark { + const COLUMNS: usize = NUM_REGISTERS; + const PUBLIC_INPUTS: usize = NUM_PUBLIC_INPUTS; + + fn eval_packed_generic( + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) { + let one = P::from(F::ONE); + + let addr_context = vars.local_values[SORTED_MEMORY_ADDR_CONTEXT]; + let addr_segment = vars.local_values[SORTED_MEMORY_ADDR_SEGMENT]; + let addr_virtual = vars.local_values[SORTED_MEMORY_ADDR_VIRTUAL]; + let values: Vec<_> = (0..8) + .map(|i| vars.local_values[sorted_memory_value_limb(i)]) + .collect(); + let timestamp = vars.local_values[SORTED_MEMORY_TIMESTAMP]; + + let next_addr_context = vars.next_values[SORTED_MEMORY_ADDR_CONTEXT]; + let next_addr_segment = vars.next_values[SORTED_MEMORY_ADDR_SEGMENT]; + let next_addr_virtual = vars.next_values[SORTED_MEMORY_ADDR_VIRTUAL]; + let next_values: Vec<_> = (0..8) + .map(|i| vars.next_values[sorted_memory_value_limb(i)]) + .collect(); + let next_is_read = vars.next_values[SORTED_MEMORY_IS_READ]; + let next_timestamp = vars.next_values[SORTED_MEMORY_TIMESTAMP]; + + let context_first_change = vars.local_values[MEMORY_CONTEXT_FIRST_CHANGE]; + let segment_first_change = vars.local_values[MEMORY_SEGMENT_FIRST_CHANGE]; + let virtual_first_change = vars.local_values[MEMORY_VIRTUAL_FIRST_CHANGE]; + let timestamp_first_change = + one - context_first_change - segment_first_change - virtual_first_change; + + let range_check = + vars.local_values[crate::registers::range_check_degree::col_rc_degree_input(0)]; + + let not_context_first_change = one - context_first_change; + let not_segment_first_change = one - segment_first_change; + let not_virtual_first_change = one - virtual_first_change; + let not_timestamp_first_change = one - timestamp_first_change; + + // First set of ordering constraint: first_change flags are boolean. + yield_constr.constraint(context_first_change * not_context_first_change); + yield_constr.constraint(segment_first_change * not_segment_first_change); + yield_constr.constraint(virtual_first_change * not_virtual_first_change); + yield_constr.constraint(timestamp_first_change * not_timestamp_first_change); + + // Second set of ordering constraints: no change before the column corresponding to the nonzero first_change flag. + yield_constr.constraint(segment_first_change * (next_addr_context - addr_context)); + yield_constr.constraint(virtual_first_change * (next_addr_context - addr_context)); + yield_constr.constraint(virtual_first_change * (next_addr_segment - addr_segment)); + yield_constr.constraint(timestamp_first_change * (next_addr_context - addr_context)); + yield_constr.constraint(timestamp_first_change * (next_addr_segment - addr_segment)); + yield_constr.constraint(timestamp_first_change * (next_addr_virtual - addr_virtual)); + + // Third set of ordering constraints: range-check difference in the column that should be increasing. + let range_check_value = context_first_change * (next_addr_context - addr_context - one) + + segment_first_change * (next_addr_segment - addr_segment - one) + + virtual_first_change * (next_addr_virtual - addr_virtual - one) + + timestamp_first_change * (next_timestamp - timestamp - one); + yield_constr.constraint(range_check - range_check_value); + + // Enumerate purportedly-ordered log. + for i in 0..8 { + yield_constr + .constraint(next_is_read * timestamp_first_change * (next_values[i] - values[i])); + } + } + + fn eval_memory_recursively( + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + let one = builder.one_extension(); + + let addr_context = vars.local_values[SORTED_MEMORY_ADDR_CONTEXT]; + let addr_segment = vars.local_values[SORTED_MEMORY_ADDR_SEGMENT]; + let addr_virtual = vars.local_values[SORTED_MEMORY_ADDR_VIRTUAL]; + let values: Vec<_> = (0..8) + .map(|i| vars.local_values[sorted_memory_value_limb(i)]) + .collect(); + let timestamp = vars.local_values[SORTED_MEMORY_TIMESTAMP]; + + let next_addr_context = vars.next_values[SORTED_MEMORY_ADDR_CONTEXT]; + let next_addr_segment = vars.next_values[SORTED_MEMORY_ADDR_SEGMENT]; + let next_addr_virtual = vars.next_values[SORTED_MEMORY_ADDR_VIRTUAL]; + let next_values: Vec<_> = (0..8) + .map(|i| vars.next_values[sorted_memory_value_limb(i)]) + .collect(); + let next_is_read = vars.next_values[SORTED_MEMORY_IS_READ]; + let next_timestamp = vars.next_values[SORTED_MEMORY_TIMESTAMP]; + + let context_first_change = vars.local_values[MEMORY_CONTEXT_FIRST_CHANGE]; + let segment_first_change = vars.local_values[MEMORY_SEGMENT_FIRST_CHANGE]; + let virtual_first_change = vars.local_values[MEMORY_VIRTUAL_FIRST_CHANGE]; + let timestamp_first_change = { + let mut cur = builder.sub_extension(one, context_first_change); + cur = builder.sub_extension(cur, segment_first_change); + builder.sub_extension(cur, virtual_first_change) + }; + + let range_check = + vars.local_values[crate::registers::range_check_degree::col_rc_degree_input(0)]; + + let not_context_first_change = builder.sub_extension(one, context_first_change); + let not_segment_first_change = builder.sub_extension(one, segment_first_change); + let not_virtual_first_change = builder.sub_extension(one, virtual_first_change); + let not_timestamp_first_change = builder.sub_extension(one, timestamp_first_change); + let addr_context_diff = builder.sub_extension(next_addr_context, addr_context); + let addr_segment_diff = builder.sub_extension(next_addr_segment, addr_segment); + let addr_virtual_diff = builder.sub_extension(next_addr_virtual, addr_virtual); + + // First set of ordering constraint: traces are boolean. + let context_first_change_bool = + builder.mul_extension(context_first_change, not_context_first_change); + yield_constr.constraint(builder, context_first_change_bool); + let segment_first_change_bool = + builder.mul_extension(segment_first_change, not_segment_first_change); + yield_constr.constraint(builder, segment_first_change_bool); + let virtual_first_change_bool = + builder.mul_extension(virtual_first_change, not_virtual_first_change); + yield_constr.constraint(builder, virtual_first_change_bool); + let timestamp_first_change_bool = + builder.mul_extension(timestamp_first_change, not_timestamp_first_change); + yield_constr.constraint(builder, timestamp_first_change_bool); + + // Second set of ordering constraints: no change before the column corresponding to the nonzero first_change flag. + let segment_first_change_check = builder.mul_extension(segment_first_change, addr_context_diff); + yield_constr.constraint(builder, segment_first_change_check); + let virtual_first_change_check_1 = + builder.mul_extension(virtual_first_change, addr_context_diff); + yield_constr.constraint(builder, virtual_first_change_check_1); + let virtual_first_change_check_2 = + builder.mul_extension(virtual_first_change, addr_segment_diff); + yield_constr.constraint(builder, virtual_first_change_check_2); + let timestamp_first_change_check_1 = + builder.mul_extension(timestamp_first_change, addr_context_diff); + yield_constr.constraint(builder, timestamp_first_change_check_1); + let timestamp_first_change_check_2 = + builder.mul_extension(timestamp_first_change, addr_segment_diff); + yield_constr.constraint(builder, timestamp_first_change_check_2); + let timestamp_first_change_check_3 = + builder.mul_extension(timestamp_first_change, addr_virtual_diff); + yield_constr.constraint(builder, timestamp_first_change_check_3); + + // Third set of ordering constraints: range-check difference in the column that should be increasing. + let context_diff = { + let diff = builder.sub_extension(next_addr_context, addr_context); + builder.sub_extension(diff, one) + }; + let context_range_check = builder.mul_extension(context_first_change, context_diff); + let segment_diff = { + let diff = builder.sub_extension(next_addr_segment, addr_segment); + builder.sub_extension(diff, one) + }; + let segment_range_check = builder.mul_extension(segment_first_change, segment_diff); + let virtual_diff = { + let diff = builder.sub_extension(next_addr_virtual, addr_virtual); + builder.sub_extension(diff, one) + }; + let virtual_range_check = builder.mul_extension(virtual_first_change, virtual_diff); + let timestamp_diff = { + let diff = builder.sub_extension(next_timestamp, timestamp); + builder.sub_extension(diff, one) + }; + let timestamp_range_check = builder.mul_extension(timestamp_first_change, timestamp_diff); + + let range_check_value = { + let mut sum = builder.add_extension(context_range_check, segment_range_check); + sum = builder.add_extension(sum, virtual_range_check); + builder.add_extension(sum, timestamp_range_check) + }; + let range_check_diff = builder.sub_extension(range_check, range_check_value); + yield_constr.constraint(builder, range_check_diff); + + // Enumerate purportedly-ordered log. + for i in 0..8 { + let value_diff = builder.sub_extension(next_values[i], values[i]); + let zero_if_read = builder.mul_extension(timestamp_first_change, value_diff); + let read_constraint = builder.mul_extension(next_is_read, zero_if_read); + yield_constr.constraint(builder, read_constraint); + } + } +} diff --git a/evm/src/memory/mod.rs b/evm/src/memory/mod.rs index 53108262..51adaf72 100644 --- a/evm/src/memory/mod.rs +++ b/evm/src/memory/mod.rs @@ -1,2 +1,2 @@ pub mod memory_stark; -pub mod registers; \ No newline at end of file +pub mod registers; diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index 1373d0d8..2ab4619f 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -1,3 +1,31 @@ //! Memory unit. -pub(super) const END: usize = super::START_MEMORY; +pub(crate) const MEMORY_ADDR_CONTEXT: usize = 0; +pub(crate) const MEMORY_ADDR_SEGMENT: usize = MEMORY_ADDR_CONTEXT + 1; +pub(crate) const MEMORY_ADDR_VIRTUAL: usize = MEMORY_ADDR_SEGMENT + 1; +pub(crate) const MEMORY_VALUE_START: usize = MEMORY_ADDR_VIRTUAL + 1; + +pub const fn memory_value_limb(i: usize) -> usize { + MEMORY_VALUE_START + i +} + +pub(crate) const MEMORY_IS_READ: usize = MEMORY_VALUE_START + 8; +pub(crate) const MEMORY_TIMESTAMP: usize = MEMORY_IS_READ + 1; + +pub(crate) const SORTED_MEMORY_ADDR_CONTEXT: usize = MEMORY_TIMESTAMP + 1; +pub(crate) const SORTED_MEMORY_ADDR_SEGMENT: usize = SORTED_MEMORY_ADDR_CONTEXT + 1; +pub(crate) const SORTED_MEMORY_ADDR_VIRTUAL: usize = SORTED_MEMORY_ADDR_SEGMENT + 1; +pub(crate) const SORTED_MEMORY_VALUE_START: usize = SORTED_MEMORY_ADDR_VIRTUAL + 1; + +pub const fn sorted_memory_value_limb(i: usize) -> usize { + SORTED_MEMORY_VALUE_START + i +} + +pub(crate) const SORTED_MEMORY_IS_READ: usize = SORTED_MEMORY_VALUE_START + 8; +pub(crate) const SORTED_MEMORY_TIMESTAMP: usize = SORTED_MEMORY_IS_READ + 1; + +pub(crate) const MEMORY_CONTEXT_FIRST_CHANGE: usize = SORTED_MEMORY_TIMESTAMP + 1; +pub(crate) const MEMORY_SEGMENT_FIRST_CHANGE: usize = MEMORY_CONTEXT_FIRST_CHANGE + 1; +pub(crate) const MEMORY_VIRTUAL_FIRST_CHANGE: usize = MEMORY_SEGMENT_FIRST_CHANGE + 1; + +pub(crate) const NUM_REGISTERS: usize = MEMORY_VIRTUAL_FIRST_CHANGE + 1; From 32d10a2f6955f8036a09b3543f85cf2b75d8da62 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 10:39:25 -0700 Subject: [PATCH 03/46] redoing columns --- evm/src/memory/memory_stark.rs | 64 ++++++++++++++++++++-------------- evm/src/memory/registers.rs | 5 ++- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index af4981af..b196a21d 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,18 +1,20 @@ use std::marker::PhantomData; use itertools::{izip, multiunzip}; -use plonky2::{field::{field_types::PrimeField64, extension_field::Extendable}, hash::hash_types::RichField, plonk::circuit_builder::CircuitBuilder}; +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::memory::registers::{ memory_value_limb, sorted_memory_value_limb, MEMORY_ADDR_CONTEXT, MEMORY_ADDR_SEGMENT, - MEMORY_ADDR_VIRTUAL, MEMORY_IS_READ, MEMORY_TIMESTAMP, SORTED_MEMORY_ADDR_CONTEXT, - SORTED_MEMORY_ADDR_SEGMENT, SORTED_MEMORY_ADDR_VIRTUAL, SORTED_MEMORY_TIMESTAMP, SORTED_MEMORY_IS_READ, - MEMORY_CONTEXT_FIRST_CHANGE, MEMORY_SEGMENT_FIRST_CHANGE, MEMORY_VIRTUAL_FIRST_CHANGE, + MEMORY_ADDR_VIRTUAL, MEMORY_CONTEXT_FIRST_CHANGE, MEMORY_COUNTER, MEMORY_IS_READ, + MEMORY_RANGE_CHECK, MEMORY_SEGMENT_FIRST_CHANGE, MEMORY_TIMESTAMP, MEMORY_VIRTUAL_FIRST_CHANGE, + NUM_REGISTERS, SORTED_MEMORY_ADDR_CONTEXT, SORTED_MEMORY_ADDR_SEGMENT, + SORTED_MEMORY_ADDR_VIRTUAL, SORTED_MEMORY_IS_READ, SORTED_MEMORY_TIMESTAMP, }; - -use crate::vars::{StarkEvaluationVars, StarkEvaluationTargets}; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::stark::Stark; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; #[derive(Default)] pub struct TransactionMemory { @@ -31,6 +33,7 @@ pub struct MemorySegment { pub content: Vec, } +pub(crate) const NUM_PUBLIC_INPUTS: usize = 0; #[derive(Copy, Clone)] pub struct MemoryStark { @@ -157,7 +160,7 @@ impl, const D: usize> MemoryStark { .collect(); let is_read = &trace_cols[MEMORY_IS_READ]; let timestamp = &trace_cols[MEMORY_TIMESTAMP]; - + let ( sorted_context, sorted_segment, @@ -166,10 +169,10 @@ impl, const D: usize> MemoryStark { sorted_is_read, sorted_timestamp, ) = sort_memory_ops(context, segment, virtuals, &values, is_read, timestamp); - + let (context_first_change, segment_first_change, virtual_first_change) = generate_first_change_flags(&sorted_context, &sorted_segment, &sorted_virtual); - + let range_check_value = generate_range_check_value( &sorted_context, &sorted_segment, @@ -179,7 +182,7 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); - + trace_cols[SORTED_MEMORY_ADDR_CONTEXT] = sorted_context; trace_cols[SORTED_MEMORY_ADDR_SEGMENT] = sorted_segment; trace_cols[SORTED_MEMORY_ADDR_VIRTUAL] = sorted_virtual; @@ -188,12 +191,13 @@ impl, const D: usize> MemoryStark { } trace_cols[SORTED_MEMORY_IS_READ] = sorted_is_read; trace_cols[SORTED_MEMORY_TIMESTAMP] = sorted_timestamp; - + trace_cols[MEMORY_CONTEXT_FIRST_CHANGE] = context_first_change; trace_cols[MEMORY_SEGMENT_FIRST_CHANGE] = segment_first_change; trace_cols[MEMORY_VIRTUAL_FIRST_CHANGE] = virtual_first_change; - - trace_cols[crate::registers::range_check_degree::col_rc_degree_input(0)] = range_check_value; + + trace_cols[MEMORY_RANGE_CHECK] = range_check_value; + trace_cols[MEMORY_COUNTER] = (0..trace_cols.len()).map(|i| F::from_canonical_usize(i)).collect(); } } @@ -201,11 +205,15 @@ impl, const D: usize> Stark for MemoryStark, + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, - ) { - let one = P::from(F::ONE); + ) where + FE: FieldExtension, + P: PackedField, + { + let one = P::from(FE::ONE); let addr_context = vars.local_values[SORTED_MEMORY_ADDR_CONTEXT]; let addr_segment = vars.local_values[SORTED_MEMORY_ADDR_SEGMENT]; @@ -230,8 +238,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, - vars: StarkEvaluationTargets, + fn eval_ext_circuit( + &self, + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + vars: StarkEvaluationTargets, yield_constr: &mut RecursiveConstraintConsumer, ) { let one = builder.one_extension(); @@ -299,8 +307,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { + 3 + } } diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index 2ab4619f..0a9f4f8e 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -28,4 +28,7 @@ pub(crate) const MEMORY_CONTEXT_FIRST_CHANGE: usize = SORTED_MEMORY_TIMESTAMP + pub(crate) const MEMORY_SEGMENT_FIRST_CHANGE: usize = MEMORY_CONTEXT_FIRST_CHANGE + 1; pub(crate) const MEMORY_VIRTUAL_FIRST_CHANGE: usize = MEMORY_SEGMENT_FIRST_CHANGE + 1; -pub(crate) const NUM_REGISTERS: usize = MEMORY_VIRTUAL_FIRST_CHANGE + 1; +pub(crate) const MEMORY_RANGE_CHECK: usize = MEMORY_VIRTUAL_FIRST_CHANGE + 1; +pub(crate) const MEMORY_COUNTER: usize = MEMORY_RANGE_CHECK + 1; + +pub(crate) const NUM_REGISTERS: usize = MEMORY_COUNTER + 1; From eabb10a34cf72d65e05a768e87176cbfc68d985d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 11:48:11 -0700 Subject: [PATCH 04/46] lookup argument for range check --- evm/src/memory/memory_stark.rs | 54 ++++++++++++++++++++++--- evm/src/memory/registers.rs | 4 +- evm/src/util.rs | 72 +++++++++++++++++++++++++++++++++- 3 files changed, 123 insertions(+), 7 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index b196a21d..c4422fde 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -8,12 +8,14 @@ use plonky2::hash::hash_types::RichField; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::memory::registers::{ memory_value_limb, sorted_memory_value_limb, MEMORY_ADDR_CONTEXT, MEMORY_ADDR_SEGMENT, - MEMORY_ADDR_VIRTUAL, MEMORY_CONTEXT_FIRST_CHANGE, MEMORY_COUNTER, MEMORY_IS_READ, - MEMORY_RANGE_CHECK, MEMORY_SEGMENT_FIRST_CHANGE, MEMORY_TIMESTAMP, MEMORY_VIRTUAL_FIRST_CHANGE, - NUM_REGISTERS, SORTED_MEMORY_ADDR_CONTEXT, SORTED_MEMORY_ADDR_SEGMENT, - SORTED_MEMORY_ADDR_VIRTUAL, SORTED_MEMORY_IS_READ, SORTED_MEMORY_TIMESTAMP, + MEMORY_ADDR_VIRTUAL, MEMORY_CONTEXT_FIRST_CHANGE, MEMORY_COUNTER, MEMORY_COUNTER_PERMUTED, + MEMORY_IS_READ, MEMORY_RANGE_CHECK, MEMORY_RANGE_CHECK_PERMUTED, MEMORY_SEGMENT_FIRST_CHANGE, + MEMORY_TIMESTAMP, MEMORY_VIRTUAL_FIRST_CHANGE, NUM_REGISTERS, SORTED_MEMORY_ADDR_CONTEXT, + SORTED_MEMORY_ADDR_SEGMENT, SORTED_MEMORY_ADDR_VIRTUAL, SORTED_MEMORY_IS_READ, + SORTED_MEMORY_TIMESTAMP, }; use crate::stark::Stark; +use crate::util::permuted_cols; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; #[derive(Default)] @@ -197,7 +199,14 @@ impl, const D: usize> MemoryStark { trace_cols[MEMORY_VIRTUAL_FIRST_CHANGE] = virtual_first_change; trace_cols[MEMORY_RANGE_CHECK] = range_check_value; - trace_cols[MEMORY_COUNTER] = (0..trace_cols.len()).map(|i| F::from_canonical_usize(i)).collect(); + trace_cols[MEMORY_COUNTER] = (0..trace_cols.len()) + .map(|i| F::from_canonical_usize(i)) + .collect(); + + let (permuted_inputs, permuted_table) = + permuted_cols(&trace_cols[MEMORY_RANGE_CHECK], &trace_cols[MEMORY_COUNTER]); + trace_cols[MEMORY_RANGE_CHECK_PERMUTED] = permuted_inputs; + trace_cols[MEMORY_COUNTER_PERMUTED] = permuted_table; } } @@ -271,6 +280,23 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index 0a9f4f8e..885fda19 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -30,5 +30,7 @@ pub(crate) const MEMORY_VIRTUAL_FIRST_CHANGE: usize = MEMORY_SEGMENT_FIRST_CHANG pub(crate) const MEMORY_RANGE_CHECK: usize = MEMORY_VIRTUAL_FIRST_CHANGE + 1; pub(crate) const MEMORY_COUNTER: usize = MEMORY_RANGE_CHECK + 1; +pub(crate) const MEMORY_RANGE_CHECK_PERMUTED: usize = MEMORY_COUNTER + 1; +pub(crate) const MEMORY_COUNTER_PERMUTED: usize = MEMORY_RANGE_CHECK_PERMUTED + 1; -pub(crate) const NUM_REGISTERS: usize = MEMORY_COUNTER + 1; +pub(crate) const NUM_REGISTERS: usize = MEMORY_COUNTER_PERMUTED + 1; diff --git a/evm/src/util.rs b/evm/src/util.rs index 011a1add..f8fba89f 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -1,5 +1,7 @@ +use std::cmp::Ordering; + use itertools::Itertools; -use plonky2::field::field_types::Field; +use plonky2::field::field_types::{Field, PrimeField64}; use plonky2::field::polynomial::PolynomialValues; use plonky2::util::transpose; @@ -14,3 +16,71 @@ 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) +} \ No newline at end of file From 40a0a2526bc0cec2e91233dbc22270616364c1ad Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 11:53:14 -0700 Subject: [PATCH 05/46] fmt --- evm/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/util.rs b/evm/src/util.rs index f8fba89f..86eaa046 100644 --- a/evm/src/util.rs +++ b/evm/src/util.rs @@ -83,4 +83,4 @@ pub fn permuted_cols(inputs: &[F], table: &[F]) -> (Vec, Vec } (sorted_inputs, permuted_table) -} \ No newline at end of file +} From 91dacf2e38ec359952590edfab7bf7b251fae60f Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 12:00:00 -0700 Subject: [PATCH 06/46] tests --- evm/src/memory/memory_stark.rs | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index c4422fde..f789f269 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -438,3 +438,40 @@ impl, const D: usize> Stark for MemoryStark Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = MemoryStark; + + let stark = S { + f: Default::default(), + }; + test_stark_low_degree(stark) + } + + #[test] + fn test_stark_circuit() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = MemoryStark; + + let stark = S { + f: Default::default(), + }; + test_stark_circuit_constraints::(stark) + } +} From 7ed2aa631595509f24cc9292afd22248bf3fa4b4 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 12:00:11 -0700 Subject: [PATCH 07/46] cleanup --- evm/src/memory/memory_stark.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index f789f269..b1ba0f1b 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -439,11 +439,9 @@ impl, const D: usize> Stark for MemoryStark Date: Fri, 17 Jun 2022 16:28:23 -0700 Subject: [PATCH 08/46] memory row generation --- evm/src/all_stark.rs | 4 + evm/src/memory/memory_stark.rs | 89 ++++++++++- plonky2/src/gadgets/curve_msm.rs | 71 +++++++++ starky/src/factorial_stark.rs | 244 +++++++++++++++++++++++++++++ starky/src/permutation_stark.rs | 253 +++++++++++++++++++++++++++++++ 5 files changed, 660 insertions(+), 1 deletion(-) create mode 100644 plonky2/src/gadgets/curve_msm.rs create mode 100644 starky/src/factorial_stark.rs create mode 100644 starky/src/permutation_stark.rs diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index a3c78b88..acb6c935 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -162,6 +162,10 @@ mod tests { .unwrap() }) .collect(); + let memory_trace = memory_stark.generate_trace(keccak_inputs); + let column_to_copy: Vec<_> = keccak_trace[keccak_looked_col].values[..].into(); + + let default = vec![F::ONE; 1]; let mut cpu_trace_rows = vec![]; for i in 0..num_keccak_perms { diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index b1ba0f1b..a92d3bd1 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -152,7 +152,34 @@ pub fn generate_range_check_value( } impl, const D: usize> MemoryStark { - pub(crate) fn generate_memory(trace_cols: &mut [Vec]) { + fn generate_trace_rows(&self, memory_ops: Vec<(F, F, F, [F; 8], F, F)>) -> 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(); + for i in 0..num_ops { + let (context, segment, virt, values, is_read, timestamp) = memory_ops[i]; + trace_cols[MEMORY_ADDR_CONTEXT][i] = context; + trace_cols[MEMORY_ADDR_SEGMENT][i] = segment; + trace_cols[MEMORY_ADDR_VIRTUAL][i] = virt; + for j in 0..8 { + trace_cols[memory_value_limb(j)][i] = values[j]; + } + trace_cols[MEMORY_IS_READ][i] = is_read; + trace_cols[MEMORY_TIMESTAMP][i] = timestamp; + } + + self.generate_memory(&mut trace_cols); + + let mut trace_rows = vec![[F::ZERO; NUM_REGISTERS]]; + for (i, col) in trace_cols.iter().enumerate() { + for (j, &val) in col.iter().enumerate() { + trace_rows[j][i] = val; + } + } + trace_rows + } + + fn generate_memory(&self, trace_cols: &mut [Vec]) { let context = &trace_cols[MEMORY_ADDR_CONTEXT]; let segment = &trace_cols[MEMORY_ADDR_SEGMENT]; let virtuals = &trace_cols[MEMORY_ADDR_VIRTUAL]; @@ -441,8 +468,13 @@ impl, const D: usize> Stark for MemoryStark(stark) } + + #[test] + fn test_memory_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = MemoryStark; + + let stark = S { + f: Default::default(), + }; + + const MAX_CONTEXT: usize = 256; + const MAX_SEGMENT: usize = 8; + const MAX_VIRTUAL: usize = 1 << 12; + + let mut rng = thread_rng(); + + let num_ops = 20; + let mut memory_ops = Vec::new(); + let current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); + let mut cur_timestamp = 0; + for i in 0..num_ops { + let is_read = if i == 0 { false } else { rng.gen() }; + let is_read_F = F::from_bool(is_read); + + let (context, segment, virt, vals) = if is_read { + let written: Vec<_> = current_memory_values.keys().collect(); + let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; + let &vals = current_memory_values.get(&(context, segment, virt)).unwrap(); + + (context, segment, virt, vals) + } else { + let context = F::from_canonical_usize(rng.gen_range(0..256)); + let segment = F::from_canonical_usize(rng.gen_range(0..8)); + let virt = F::from_canonical_usize(rng.gen_range(0..20)); + + let val: [u32; 8] = rng.gen(); + let vals: [F; 8] = val.iter().map(|&x| F::from_canonical_u32(x)).collect::>().try_into().unwrap(); + + current_memory_values.insert((context, segment, virt), vals); + + (context, segment, virt, vals) + }; + + let timestamp = F::from_canonical_usize(cur_timestamp); + cur_timestamp += 1; + + memory_ops.push((context, segment, virt, is_read_F, vals, timestamp)) + } + + let rows = stark.generate_trace_rows(memory_ops); + + Ok(()) + } } diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs new file mode 100644 index 00000000..60d1ddc4 --- /dev/null +++ b/plonky2/src/gadgets/curve_msm.rs @@ -0,0 +1,71 @@ +use plonky2_field::extension_field::Extendable; +use plonky2_field::field_types::Field; + +use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; +use crate::gadgets::curve::AffinePointTarget; +use crate::gadgets::nonnative::NonNativeTarget; +use crate::hash::hash_types::RichField; +use crate::plonk::circuit_builder::CircuitBuilder; + +const DIGITS_PER_CHUNK: usize = 80; + +const WINDOW_SIZE: usize = 4; + +pub struct MsmPrecomputationTarget { + /// For each generator (in the order they were passed to `msm_precompute`), contains a vector + /// of powers, i.e. [(2^w)^i] for i < DIGITS. + powers_per_generator: Vec>>, +} + +impl, const D: usize> CircuitBuilder { + pub fn precompute_single_generator( + &mut self, + g: AffinePointTarget, + ) -> Vec> { + let digits = (C::ScalarField::BITS + WINDOW_SIZE - 1) / WINDOW_SIZE; + let mut powers: Vec> = Vec::with_capacity(digits); + powers.push(g); + for i in 1..digits { + let mut power_i_proj = powers[i - 1].clone(); + for _j in 0..WINDOW_SIZE { + power_i_proj = self.curve_double(&power_i_proj); + } + powers.push(power_i_proj); + } + powers + } + + pub fn msm_precompute( + &mut self, + generators: &[AffinePointTarget], + ) -> MsmPrecomputationTarget { + MsmPrecomputationTarget { + powers_per_generator: generators + .into_iter() + .map(|g| self.precompute_single_generator(g.clone())) + .collect(), + w, + } + } + + pub fn msm_execute( + &mut self, + precomputation: &MsmPrecomputationTarget, + scalars: &[NonNativeTarget], + ) -> AffinePointTarget { + debug_assert_eq!(precomputation.powers_per_generator.len(), scalars.len()); + + let digits = (C::ScalarField::BITS + WINDOW_SIZE - 1) / WINDOW_SIZE; + let base = 1 << WINDOW_SIZE; + + for (i, scalar) in scalars.iter().enumerate() { + let digits = self.split_nonnative_to_4_bit_limbs(scalar); + } + + let digits: Vec<_> = (0..base).map(|i| self.constant(F::from_canonical_usize(i))).collect(); + let mut digit_acc: Vec> = Vec::new(); + for i in 0..base { + + } + } +} diff --git a/starky/src/factorial_stark.rs b/starky/src/factorial_stark.rs new file mode 100644 index 00000000..6650de25 --- /dev/null +++ b/starky/src/factorial_stark.rs @@ -0,0 +1,244 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::PermutationPair; +use crate::stark::Stark; +use crate::util::trace_rows_to_poly_values; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +/// Toy STARK system used for testing. +/// Computes a Factorial sequence with state `[fact, n]` using the state transition +/// `fact' <- fact * (n + 1), n' <- n + 1`. +#[derive(Copy, Clone)] +struct FactorialStark, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize> FactorialStark { + // The first public input is `x0`. + const PI_INDEX_X0: usize = 0; + // The second public input is the first element of the last row, which should be equal to + // `num_rows` factorial. + const PI_INDEX_RES: usize = 1; + + fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + /// Generate the trace using `x0, 1` as initial state values. + fn generate_trace(&self, x0: F) -> Vec> { + let mut trace_rows = (0..self.num_rows) + .scan([x0, F::ONE], |acc, _| { + let tmp = *acc; + acc[0] = tmp[0] * (tmp[1] + F::ONE); + acc[1] = tmp[1] + F::ONE; + Some(tmp) + }) + .collect::>(); + trace_rows_to_poly_values(trace_rows) + } +} + +impl, const D: usize> Stark for FactorialStark { + const COLUMNS: usize = 2; + const PUBLIC_INPUTS: usize = 2; + + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + // Check public inputs. + yield_constr + .constraint_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); + yield_constr + .constraint_last_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_RES]); + + // x0' <- x0 * (x1 + 1) + yield_constr.constraint_transition( + vars.next_values[0] - vars.local_values[0] * (vars.local_values[1] + FE::ONE), + ); + // x1' <- x1 + 1 + yield_constr.constraint_transition(vars.next_values[1] - vars.local_values[1] - FE::ONE); + } + + fn eval_ext_recursively( + &self, + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + // Check public inputs. + let pis_constraints = [ + builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]), + builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_RES]), + ]; + yield_constr.constraint_first_row(builder, pis_constraints[0]); + yield_constr.constraint_last_row(builder, pis_constraints[1]); + + let one = builder.one_extension(); + // x0' <- x0 * (x1 + 1) + let first_col_constraint = { + let tmp1 = builder.add_extension(vars.local_values[1], one); + let tmp2 = builder.mul_extension(vars.local_values[0], tmp1); + builder.sub_extension(vars.next_values[0], tmp2) + }; + yield_constr.constraint_transition(builder, first_col_constraint); + // x1' <- x1 + 1 + let second_col_constraint = { + let tmp = builder.add_extension(vars.local_values[1], one); + builder.sub_extension(vars.next_values[1], tmp) + }; + yield_constr.constraint_transition(builder, second_col_constraint); + } + + fn constraint_degree(&self) -> usize { + 2 + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::extension_field::Extendable; + use plonky2::field::field_types::Field; + use plonky2::hash::hash_types::RichField; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{ + AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, + }; + use plonky2::util::timing::TimingTree; + + use crate::config::StarkConfig; + use crate::factorial_stark::FactorialStark; + use crate::proof::StarkProofWithPublicInputs; + use crate::prover::prove; + use crate::recursive_verifier::{ + add_virtual_stark_proof_with_pis, recursively_verify_stark_proof, + set_stark_proof_with_pis_target, + }; + use crate::stark::Stark; + use crate::stark_testing::test_stark_low_degree; + use crate::verifier::verify_stark_proof; + + fn factorial(n: usize, x0: F) -> F { + (0..n) + .fold((x0, F::ONE), |x, _| (x.0 * (x.1 + F::ONE), x.1 + F::ONE)) + .0 + } + + #[test] + fn test_factorial_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FactorialStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 3; + let public_inputs = [F::ONE, factorial(num_rows - 1, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0]); + let proof = prove::( + stark, + &config, + trace, + public_inputs, + &mut TimingTree::default(), + )?; + + verify_stark_proof(stark, proof, &config) + } + + #[test] + fn test_factorial_stark_degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FactorialStark; + + let num_rows = 1 << 3; + let stark = S::new(num_rows); + test_stark_low_degree(stark) + } + + #[test] + fn test_recursive_stark_verifier() -> Result<()> { + init_logger(); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FactorialStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + let public_inputs = [F::ONE, factorial(num_rows - 1, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0]); + let proof = prove::( + stark, + &config, + trace, + public_inputs, + &mut TimingTree::default(), + )?; + verify_stark_proof(stark, proof.clone(), &config)?; + + recursive_proof::(stark, proof, &config, true) + } + + fn recursive_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark + Copy, + InnerC: GenericConfig, + const D: usize, + >( + stark: S, + inner_proof: StarkProofWithPublicInputs, + inner_config: &StarkConfig, + print_gate_counts: bool, + ) -> Result<()> + where + InnerC::Hasher: AlgebraicHasher, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); C::Hasher::HASH_SIZE]:, + { + let circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(circuit_config); + let mut pw = PartialWitness::new(); + let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); + + recursively_verify_stark_proof::(&mut builder, stark, pt, inner_config); + + if print_gate_counts { + builder.print_gate_counts(0); + } + + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof) + } + + fn init_logger() { + let _ = env_logger::builder().format_timestamp(None).try_init(); + } +} diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs new file mode 100644 index 00000000..b227847d --- /dev/null +++ b/starky/src/permutation_stark.rs @@ -0,0 +1,253 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::PermutationPair; +use crate::stark::Stark; +use crate::util::trace_rows_to_poly_values; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +/// Toy STARK system used for testing. +/// Proves that the last three public inputs are a sorted version of the first three. +/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. +/// Note: The `i, j` columns are only used to test the permutation argument. +#[derive(Copy, Clone)] +struct SortingStark, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize> SortingStark { + // The first public input is `a`. + const PI_INDEX_X0: usize = 0; + // The second public input is `b`. + const PI_INDEX_X1: usize = 1; + // The second public input is `c`. + const PI_INDEX_X1: usize = 2; + // The third public input is the second element of the last row, which should be equal to the + // `num_rows`-th Fibonacci number. + const PI_INDEX_RES: usize = 2; + + fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + /// Generate the trace using `x0, x1, 0, 1` as initial state values. + fn generate_trace(&self, x0: F, x1: F) -> Vec> { + let mut trace_rows = (0..self.num_rows) + .scan([x0, x1, F::ZERO, F::ONE], |acc, _| { + let tmp = *acc; + acc[0] = tmp[1]; + acc[1] = tmp[0] + tmp[1]; + acc[2] = tmp[2] + F::ONE; + acc[3] = tmp[3] + F::ONE; + Some(tmp) + }) + .collect::>(); + trace_rows[self.num_rows - 1][3] = F::ZERO; // So that column 2 and 3 are permutation of one another. + trace_rows_to_poly_values(trace_rows) + } +} + +impl, const D: usize> Stark for SortingStark { + const COLUMNS: usize = 4; + const PUBLIC_INPUTS: usize = 3; + + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + // Check public inputs. + yield_constr + .constraint_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); + yield_constr + .constraint_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); + yield_constr + .constraint_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); + + // x0' <- x1 + yield_constr.constraint_transition(vars.next_values[0] - vars.local_values[1]); + // x1' <- x0 + x1 + yield_constr.constraint_transition( + vars.next_values[1] - vars.local_values[0] - vars.local_values[1], + ); + } + + fn eval_ext_recursively( + &self, + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + // Check public inputs. + let pis_constraints = [ + builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]), + builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_X1]), + builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_RES]), + ]; + yield_constr.constraint_first_row(builder, pis_constraints[0]); + yield_constr.constraint_first_row(builder, pis_constraints[1]); + yield_constr.constraint_last_row(builder, pis_constraints[2]); + + // x0' <- x1 + let first_col_constraint = builder.sub_extension(vars.next_values[0], vars.local_values[1]); + yield_constr.constraint_transition(builder, first_col_constraint); + // x1' <- x0 + x1 + let second_col_constraint = { + let tmp = builder.sub_extension(vars.next_values[1], vars.local_values[0]); + builder.sub_extension(tmp, vars.local_values[1]) + }; + yield_constr.constraint_transition(builder, second_col_constraint); + } + + fn constraint_degree(&self) -> usize { + 2 + } + + fn permutation_pairs(&self) -> Vec { + vec![PermutationPair::singletons(2, 3)] + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::extension_field::Extendable; + use plonky2::field::field_types::Field; + use plonky2::hash::hash_types::RichField; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{ + AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, + }; + use plonky2::util::timing::TimingTree; + + use crate::config::StarkConfig; + use crate::fibonacci_stark::FibonacciStark; + use crate::proof::StarkProofWithPublicInputs; + use crate::prover::prove; + use crate::recursive_verifier::{ + add_virtual_stark_proof_with_pis, recursively_verify_stark_proof, + set_stark_proof_with_pis_target, + }; + use crate::stark::Stark; + use crate::stark_testing::test_stark_low_degree; + use crate::verifier::verify_stark_proof; + + fn fibonacci(n: usize, x0: F, x1: F) -> F { + (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 + } + + #[test] + fn test_fibonacci_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + let proof = prove::( + stark, + &config, + trace, + public_inputs, + &mut TimingTree::default(), + )?; + + verify_stark_proof(stark, proof, &config) + } + + #[test] + fn test_fibonacci_stark_degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + + let num_rows = 1 << 5; + let stark = S::new(num_rows); + test_stark_low_degree(stark) + } + + #[test] + fn test_recursive_stark_verifier() -> Result<()> { + init_logger(); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + let proof = prove::( + stark, + &config, + trace, + public_inputs, + &mut TimingTree::default(), + )?; + verify_stark_proof(stark, proof.clone(), &config)?; + + recursive_proof::(stark, proof, &config, true) + } + + fn recursive_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark + Copy, + InnerC: GenericConfig, + const D: usize, + >( + stark: S, + inner_proof: StarkProofWithPublicInputs, + inner_config: &StarkConfig, + print_gate_counts: bool, + ) -> Result<()> + where + InnerC::Hasher: AlgebraicHasher, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); C::Hasher::HASH_SIZE]:, + { + let circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(circuit_config); + let mut pw = PartialWitness::new(); + let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); + + recursively_verify_stark_proof::(&mut builder, stark, pt, inner_config); + + if print_gate_counts { + builder.print_gate_counts(0); + } + + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof) + } + + fn init_logger() { + let _ = env_logger::builder().format_timestamp(None).try_init(); + } +} From 0ad54233dea52809bb21f5f313223b22376517db Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 16:03:26 -0700 Subject: [PATCH 09/46] fmt --- evm/src/memory/memory_stark.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index a92d3bd1..403e1939 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -152,10 +152,15 @@ pub fn generate_range_check_value( } impl, const D: usize> MemoryStark { - fn generate_trace_rows(&self, memory_ops: Vec<(F, F, F, [F; 8], F, F)>) -> Vec<[F; NUM_REGISTERS]> { + fn generate_trace_rows( + &self, + memory_ops: Vec<(F, F, F, [F; 8], F, F)>, + ) -> 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: [Vec; NUM_REGISTERS] = vec![vec![F::ZERO; num_ops]; NUM_REGISTERS] + .try_into() + .unwrap(); for i in 0..num_ops { let (context, segment, virt, values, is_read, timestamp) = memory_ops[i]; trace_cols[MEMORY_ADDR_CONTEXT][i] = context; @@ -533,8 +538,10 @@ mod tests { let (context, segment, virt, vals) = if is_read { let written: Vec<_> = current_memory_values.keys().collect(); let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; - let &vals = current_memory_values.get(&(context, segment, virt)).unwrap(); - + let &vals = current_memory_values + .get(&(context, segment, virt)) + .unwrap(); + (context, segment, virt, vals) } else { let context = F::from_canonical_usize(rng.gen_range(0..256)); @@ -542,7 +549,12 @@ mod tests { let virt = F::from_canonical_usize(rng.gen_range(0..20)); let val: [u32; 8] = rng.gen(); - let vals: [F; 8] = val.iter().map(|&x| F::from_canonical_u32(x)).collect::>().try_into().unwrap(); + let vals: [F; 8] = val + .iter() + .map(|&x| F::from_canonical_u32(x)) + .collect::>() + .try_into() + .unwrap(); current_memory_values.insert((context, segment, virt), vals); From 31be2c8d490946c5f53f86ec12af7f40d08a81ea Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 9 Jun 2022 16:17:54 -0700 Subject: [PATCH 10/46] clippy and fix --- evm/src/memory/memory_stark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 403e1939..ad3d0b76 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -564,7 +564,7 @@ mod tests { let timestamp = F::from_canonical_usize(cur_timestamp); cur_timestamp += 1; - memory_ops.push((context, segment, virt, is_read_F, vals, timestamp)) + memory_ops.push((context, segment, virt, vals, is_read_F, timestamp)) } let rows = stark.generate_trace_rows(memory_ops); From 03112f898a222251980fdf832c9f1ade88ee7cb1 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 23 Jun 2022 13:59:57 -0700 Subject: [PATCH 11/46] updated all_stark framework to include memory stark (doesn't pass yet) --- evm/src/all_stark.rs | 81 ++++++++++++++++++-- evm/src/cpu/columns.rs | 10 +++ evm/src/memory/memory_stark.rs | 133 +++++++++++++++++++++------------ evm/src/prover.rs | 18 ++++- evm/src/recursive_verifier.rs | 26 +++++++ evm/src/verifier.rs | 11 +++ 6 files changed, 222 insertions(+), 57 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index acb6c935..ffc1e404 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -6,6 +6,7 @@ use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::CrossTableLookup; use crate::keccak::keccak_stark::KeccakStark; use crate::logic::LogicStark; +use crate::memory::memory_stark::MemoryStark; use crate::stark::Stark; #[derive(Clone)] @@ -13,6 +14,7 @@ pub struct AllStark, const D: usize> { pub cpu_stark: CpuStark, pub keccak_stark: KeccakStark, pub logic_stark: LogicStark, + pub memory_stark: MemoryStark, pub cross_table_lookups: Vec>, } @@ -43,11 +45,12 @@ pub enum Table { Cpu = 0, Keccak = 1, Logic = 2, + Memory = 3, } impl Table { pub(crate) fn num_tables() -> usize { - Table::Logic as usize + 1 + Table::Memory as usize + 1 } } @@ -68,11 +71,12 @@ mod tests { use crate::config::StarkConfig; use crate::cpu::columns::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS}; use crate::cpu::cpu_stark::{self as cpu_stark_mod, CpuStark}; - use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns}; use crate::keccak::keccak_stark::{ self as keccak_stark_mod, KeccakStark, NUM_INPUTS, NUM_ROUNDS, }; use crate::logic::{self, LogicStark}; + use crate::cross_table_lookup::{Column, CrossTableLookup, TableWithColumns}; + use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark}; use crate::proof::AllProof; use crate::prover::prove; use crate::recursive_verifier::{ @@ -137,6 +141,7 @@ mod tests { cpu_stark: &CpuStark, keccak_trace: &[PolynomialValues], logic_trace: &[PolynomialValues], + memory_trace: &[PolynomialValues], ) -> Vec> { let keccak_input_limbs: Vec<[F; 2 * NUM_INPUTS]> = (0..num_keccak_perms) .map(|i| { @@ -162,10 +167,6 @@ mod tests { .unwrap() }) .collect(); - let memory_trace = memory_stark.generate_trace(keccak_inputs); - let column_to_copy: Vec<_> = keccak_trace[keccak_looked_col].values[..].into(); - - let default = vec![F::ONE; 1]; let mut cpu_trace_rows = vec![]; for i in 0..num_keccak_perms { @@ -203,6 +204,30 @@ mod tests { cpu_stark.generate(&mut row); cpu_trace_rows.push(row); } + for i in 0..num_memory_ops { + let mem_timestamp: usize = memory_trace[memory::registers::TIMESTAMP].values[i] + .to_canonical_u64() + .try_into() + .unwrap(); + let clock = mem_timestamp / NUM_MEMORY_OPS; + let op = mem_timestamp % NUM_MEMORY_OPS; + + 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]; + cpu_trace_rows[i][cpu::columns::memop_addr_context(op)] = + memory_trace[memory::registers::ADDR_CONTEXT].values[i]; + cpu_trace_rows[i][cpu::columns::memop_addr_segment(op)] = + memory_trace[memory::registers::ADDR_SEGMENT].values[i]; + cpu_trace_rows[i][cpu::columns::memop_addr_virtual(op)] = + memory_trace[memory::registers::ADDR_VIRTUAL].values[i]; + for j in 0..8 { + cpu_trace_rows[i][cpu::columns::memop_value(op, j)] = + memory_trace[memory::registers::value_limb(j)].values[i]; + } + } trace_rows_to_poly_values(cpu_trace_rows) } @@ -220,6 +245,11 @@ mod tests { }; let num_logic_rows = 62; + let memory_stark = MemoryStark:: { + f: Default::default(), + }; + let num_memory_ops = 1 << 5; + let mut rng = thread_rng(); let num_keccak_perms = 2; @@ -228,11 +258,47 @@ mod tests { let cpu_trace = make_cpu_trace( num_keccak_perms, num_logic_rows, + num_memory_ops, &cpu_stark, &keccak_trace, &logic_trace, ); + let memory_ops = generate_random_memory_ops(num_memory_ops); + let memory_trace = memory_stark.generate_trace(memory_ops); + + let mut cpu_keccak_input_output = cpu::columns::KECCAK_INPUT_LIMBS.collect::>(); + cpu_keccak_input_output.extend(cpu::columns::KECCAK_OUTPUT_LIMBS); + let mut keccak_keccak_input_output = (0..2 * NUM_INPUTS) + .map(keccak::registers::reg_input_limb) + .collect::>(); + keccak_keccak_input_output.extend(Column::singles( + (0..2 * NUM_INPUTS).map(keccak::registers::reg_output_limb), + )); + + let cpu_logic_input_output = { + let mut res = vec![ + cpu::columns::IS_AND, + cpu::columns::IS_OR, + cpu::columns::IS_XOR, + ]; + res.extend(cpu::columns::LOGIC_INPUT0); + res.extend(cpu::columns::LOGIC_INPUT1); + res.extend(cpu::columns::LOGIC_OUTPUT); + res + }; + let logic_logic_input_output = { + let mut res = vec![ + logic::columns::IS_AND, + logic::columns::IS_OR, + logic::columns::IS_XOR, + ]; + res.extend(logic::columns::INPUT0_PACKED); + res.extend(logic::columns::INPUT1_PACKED); + res.extend(logic::columns::RESULT); + res + }; + let cross_table_lookups = vec![ CrossTableLookup::new( vec![TableWithColumns::new( @@ -262,13 +328,14 @@ mod tests { cpu_stark, keccak_stark, logic_stark, + memory_stark, cross_table_lookups, }; let proof = prove::( &all_stark, config, - vec![cpu_trace, keccak_trace, logic_trace], + vec![cpu_trace, keccak_trace, logic_trace, memory_trace], vec![vec![]; 3], &mut TimingTree::default(), )?; diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 5e5f3f55..44264c52 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -13,10 +13,20 @@ pub const IS_CPU_CYCLE: usize = IS_BOOTSTRAP_CONTRACT + 1; /// If CPU cycle: The opcode being decoded, in {0, ..., 255}. pub const OPCODE: usize = IS_CPU_CYCLE + 1; +<<<<<<< HEAD /// If CPU cycle: flags for EVM instructions. PUSHn, DUPn, and SWAPn only get one flag each. Invalid /// opcodes are split between a number of flags for practical reasons. Exactly one of these flags /// must be 1. pub const IS_STOP: usize = OPCODE + 1; +======= +pub const KECCAK_DUMMY: usize = OPCODE + 1; +pub const MEMORY_DUMMY: usize = KECCAK_DUMMY + 1; + +// If CPU cycle: flags for EVM instructions. PUSHn, DUPn, and SWAPn only get one flag each. Invalid +// opcodes are split between a number of flags for practical reasons. Exactly one of these flags +// must be 1. +pub const IS_STOP: usize = MEMORY_DUMMY + 1; +>>>>>>> ade7e5e0 (updated all_stark framework to include memory stark (doesn't pass yet)) pub const IS_ADD: usize = IS_STOP + 1; pub const IS_MUL: usize = IS_ADD + 1; pub const IS_SUB: usize = IS_MUL + 1; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index ad3d0b76..11219388 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,9 +1,14 @@ +use std::collections::HashMap; use std::marker::PhantomData; use itertools::{izip, multiunzip}; use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; +use plonky2::timed; +use plonky2::util::timing::TimingTree; +use rand::{thread_rng, Rng}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::memory::registers::{ @@ -15,7 +20,7 @@ use crate::memory::registers::{ SORTED_MEMORY_TIMESTAMP, }; use crate::stark::Stark; -use crate::util::permuted_cols; +use crate::util::{permuted_cols, trace_rows_to_poly_values}; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; #[derive(Default)] @@ -42,6 +47,52 @@ pub struct MemoryStark { pub(crate) f: PhantomData, } +pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, [F; 8], F, F)> { + let mut memory_ops = Vec::new(); + + let mut rng = thread_rng(); + + let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); + let mut cur_timestamp = 0; + for i in 0..num_ops { + let is_read = if i == 0 { false } else { rng.gen() }; + let is_read_F = F::from_bool(is_read); + + let (context, segment, virt, vals) = if is_read { + let written: Vec<_> = current_memory_values.keys().collect(); + let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; + let &vals = current_memory_values + .get(&(context, segment, virt)) + .unwrap(); + + (context, segment, virt, vals) + } else { + let context = F::from_canonical_usize(rng.gen_range(0..256)); + let segment = F::from_canonical_usize(rng.gen_range(0..8)); + let virt = F::from_canonical_usize(rng.gen_range(0..20)); + + let val: [u32; 8] = rng.gen(); + let vals: [F; 8] = val + .iter() + .map(|&x| F::from_canonical_u32(x)) + .collect::>() + .try_into() + .unwrap(); + + current_memory_values.insert((context, segment, virt), vals); + + (context, segment, virt, vals) + }; + + let timestamp = F::from_canonical_usize(cur_timestamp); + cur_timestamp += 1; + + memory_ops.push((context, segment, virt, vals, is_read_F, timestamp)) + } + + memory_ops +} + pub fn sort_memory_ops( context: &[F], segment: &[F], @@ -152,7 +203,7 @@ pub fn generate_range_check_value( } impl, const D: usize> MemoryStark { - fn generate_trace_rows( + pub(crate) fn generate_trace_rows( &self, memory_ops: Vec<(F, F, F, [F; 8], F, F)>, ) -> Vec<[F; NUM_REGISTERS]> { @@ -175,7 +226,7 @@ impl, const D: usize> MemoryStark { self.generate_memory(&mut trace_cols); - let mut trace_rows = vec![[F::ZERO; NUM_REGISTERS]]; + let mut trace_rows = vec![[F::ZERO; NUM_REGISTERS]; num_ops]; for (i, col) in trace_cols.iter().enumerate() { for (j, &val) in col.iter().enumerate() { trace_rows[j][i] = val; @@ -231,7 +282,7 @@ impl, const D: usize> MemoryStark { trace_cols[MEMORY_VIRTUAL_FIRST_CHANGE] = virtual_first_change; trace_cols[MEMORY_RANGE_CHECK] = range_check_value; - trace_cols[MEMORY_COUNTER] = (0..trace_cols.len()) + trace_cols[MEMORY_COUNTER] = (0..trace_cols[0].len()) .map(|i| F::from_canonical_usize(i)) .collect(); @@ -240,6 +291,29 @@ impl, const D: usize> MemoryStark { trace_cols[MEMORY_RANGE_CHECK_PERMUTED] = permuted_inputs; trace_cols[MEMORY_COUNTER_PERMUTED] = permuted_table; } + + pub fn generate_trace( + &self, + memory_ops: Vec<(F, F, F, [F; 8], F, F)>, + ) -> Vec> { + let mut timing = TimingTree::new("generate trace", log::Level::Debug); + + // Generate the witness, except for permuted columns in the lookup argument. + let trace_rows = timed!( + &mut timing, + "generate trace rows", + self.generate_trace_rows(memory_ops) + ); + + let trace_polys = timed!( + &mut timing, + "convert to PolynomialValues", + trace_rows_to_poly_values(trace_rows) + ); + + timing.print(); + trace_polys + } } impl, const D: usize> Stark for MemoryStark { @@ -287,10 +361,10 @@ impl, const D: usize> Stark for MemoryStark = HashMap::new(); - let mut cur_timestamp = 0; - for i in 0..num_ops { - let is_read = if i == 0 { false } else { rng.gen() }; - let is_read_F = F::from_bool(is_read); - - let (context, segment, virt, vals) = if is_read { - let written: Vec<_> = current_memory_values.keys().collect(); - let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; - let &vals = current_memory_values - .get(&(context, segment, virt)) - .unwrap(); - - (context, segment, virt, vals) - } else { - let context = F::from_canonical_usize(rng.gen_range(0..256)); - let segment = F::from_canonical_usize(rng.gen_range(0..8)); - let virt = F::from_canonical_usize(rng.gen_range(0..20)); - - let val: [u32; 8] = rng.gen(); - let vals: [F; 8] = val - .iter() - .map(|&x| F::from_canonical_u32(x)) - .collect::>() - .try_into() - .unwrap(); - - current_memory_values.insert((context, segment, virt), vals); - - (context, segment, virt, vals) - }; - - let timestamp = F::from_canonical_usize(cur_timestamp); - cur_timestamp += 1; - - memory_ops.push((context, segment, virt, vals, is_read_F, timestamp)) - } + let memory_ops = generate_random_memory_ops(num_ops); let rows = stark.generate_trace_rows(memory_ops); diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 88a45e8b..ba4fac52 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -22,6 +22,7 @@ use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{cross_table_lookup_data, CtlCheckVars, CtlData}; use crate::keccak::keccak_stark::KeccakStark; use crate::logic::LogicStark; +use crate::memory::memory_stark::MemoryStark; use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_grand_product_challenge_sets, GrandProductChallengeSet, @@ -49,6 +50,8 @@ where [(); KeccakStark::::PUBLIC_INPUTS]:, [(); LogicStark::::COLUMNS]:, [(); LogicStark::::PUBLIC_INPUTS]:, + [(); MemoryStark::::COLUMNS]:, + [(); MemoryStark::::PUBLIC_INPUTS]:, { let num_starks = Table::num_tables(); debug_assert_eq!(num_starks, trace_poly_values.len()); @@ -132,8 +135,21 @@ where &mut challenger, timing, )?; + let memory_proof = prove_single_table( + &all_stark.memory_stark, + config, + &trace_poly_values[Table::Memory as usize], + &trace_commitments[Table::Memory as usize], + &ctl_data_per_table[Table::Memory as usize], + public_inputs[Table::Memory as usize] + .clone() + .try_into() + .unwrap(), + &mut challenger, + timing, + )?; - let stark_proofs = vec![cpu_proof, keccak_proof, logic_proof]; + let stark_proofs = vec![cpu_proof, keccak_proof, logic_proof, memory_proof]; debug_assert_eq!(stark_proofs.len(), num_starks); Ok(AllProof { stark_proofs }) diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index fe83cc28..37e108cb 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -18,6 +18,7 @@ use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CtlCheckVarsTarget}; use crate::keccak::keccak_stark::KeccakStark; use crate::logic::LogicStark; +use crate::memory::memory_stark::MemoryStark; use crate::permutation::PermutationCheckDataTarget; use crate::proof::{ AllProof, AllProofChallengesTarget, AllProofTarget, StarkOpeningSetTarget, StarkProof, @@ -44,6 +45,8 @@ pub fn verify_proof_circuit< [(); KeccakStark::::PUBLIC_INPUTS]:, [(); LogicStark::::COLUMNS]:, [(); LogicStark::::PUBLIC_INPUTS]:, + [(); MemoryStark::::COLUMNS]:, + [(); MemoryStark::::PUBLIC_INPUTS]:, C::Hasher: AlgebraicHasher, { let AllProofChallengesTarget { @@ -57,6 +60,7 @@ pub fn verify_proof_circuit< cpu_stark, keccak_stark, logic_stark, + memory_stark, cross_table_lookups, } = all_stark; @@ -91,6 +95,14 @@ pub fn verify_proof_circuit< &ctl_vars_per_table[Table::Logic as usize], inner_config, ); + verify_stark_proof_with_challenges_circuit::( + builder, + memory_stark, + &all_proof.stark_proofs[Table::Memory as usize], + &stark_challenges[Table::Memory as usize], + &ctl_vars_per_table[Table::Memory as usize], + inner_config, + ); verify_cross_table_lookups_circuit::( builder, @@ -291,6 +303,20 @@ pub fn add_virtual_all_proof, const D: usize>( public_inputs, } }, + { + let proof = add_virtual_stark_proof( + builder, + all_stark.memory_stark, + config, + degree_bits[Table::Memory as usize], + nums_ctl_zs[Table::Memory as usize], + ); + let public_inputs = builder.add_virtual_targets(KeccakStark::::PUBLIC_INPUTS); + StarkProofWithPublicInputsTarget { + proof, + public_inputs, + } + }, ]; assert_eq!(stark_proofs.len(), Table::num_tables()); diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 62f5fc4f..a26e1b16 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -13,6 +13,7 @@ use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars}; use crate::keccak::keccak_stark::KeccakStark; use crate::logic::LogicStark; +use crate::memory::memory_stark::MemoryStark; use crate::permutation::PermutationCheckVars; use crate::proof::{ AllProof, AllProofChallenges, StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs, @@ -33,6 +34,8 @@ where [(); KeccakStark::::PUBLIC_INPUTS]:, [(); LogicStark::::COLUMNS]:, [(); LogicStark::::PUBLIC_INPUTS]:, + [(); MemoryStark::::COLUMNS]:, + [(); MemoryStark::::PUBLIC_INPUTS]:, [(); C::Hasher::HASH_SIZE]:, { let AllProofChallenges { @@ -46,6 +49,7 @@ where cpu_stark, keccak_stark, logic_stark, + memory_stark, cross_table_lookups, } = all_stark; @@ -70,6 +74,13 @@ where &ctl_vars_per_table[Table::Keccak as usize], config, )?; + verify_stark_proof_with_challenges( + memory_stark, + &all_proof.stark_proofs[Table::Memory as usize], + &stark_challenges[Table::Memory as usize], + &ctl_vars_per_table[Table::Memory as usize], + config, + )?; verify_stark_proof_with_challenges( logic_stark, &all_proof.stark_proofs[Table::Logic as usize], From 59f3a763f094083d322e7c2e85b8179d81530ab8 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 16:38:31 -0700 Subject: [PATCH 12/46] transition constraints, and debugging --- evm/src/all_stark.rs | 2 ++ evm/src/memory/memory_stark.rs | 50 +++++++++++++++++----------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index ffc1e404..27a03021 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -24,6 +24,7 @@ impl, const D: usize> AllStark { self.cpu_stark.num_permutation_batches(config), self.keccak_stark.num_permutation_batches(config), self.logic_stark.num_permutation_batches(config), + self.memory_stark.num_permutation_batches(config), ]; debug_assert_eq!(ans.len(), Table::num_tables()); ans @@ -34,6 +35,7 @@ impl, const D: usize> AllStark { self.cpu_stark.permutation_batch_size(), self.keccak_stark.permutation_batch_size(), self.logic_stark.permutation_batch_size(), + self.memory_stark.permutation_batch_size(), ]; debug_assert_eq!(ans.len(), Table::num_tables()); ans diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 11219388..b2fe7dc9 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -361,30 +361,30 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { From ef3f023666774eb6cc45b6d5115733b711ccc0e8 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 13 Jun 2022 14:08:18 -0700 Subject: [PATCH 13/46] transpose fix --- evm/src/memory/memory_stark.rs | 76 ++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index b2fe7dc9..566415ca 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::marker::PhantomData; -use itertools::{izip, multiunzip}; +use itertools::{izip, multiunzip, Itertools}; use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::packed_field::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -72,12 +72,7 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, let virt = F::from_canonical_usize(rng.gen_range(0..20)); let val: [u32; 8] = rng.gen(); - let vals: [F; 8] = val - .iter() - .map(|&x| F::from_canonical_u32(x)) - .collect::>() - .try_into() - .unwrap(); + let vals: [F; 8] = val.map(F::from_canonical_u32); current_memory_values.insert((context, segment, virt), vals); @@ -97,11 +92,11 @@ pub fn sort_memory_ops( context: &[F], segment: &[F], virtuals: &[F], - values: &[Vec], + values: &Vec<[F; 8]>, is_read: &[F], timestamp: &[F], -) -> (Vec, Vec, Vec, Vec>, Vec, Vec) { - let mut ops: Vec<(F, F, F, Vec, F, F)> = izip!( +) -> (Vec, Vec, Vec, Vec<[F; 8]>, Vec, Vec) { + let mut ops: Vec<(F, F, F, [F; 8], F, F)> = izip!( context.iter().cloned(), segment.iter().cloned(), virtuals.iter().cloned(), @@ -111,19 +106,13 @@ pub fn sort_memory_ops( ) .collect(); - ops.sort_by(|&(c1, s1, v1, _, _, t1), &(c2, s2, v2, _, _, t2)| { + ops.sort_by_key(|&(c, s, v, _, _, t)| { ( - c1.to_noncanonical_u64(), - s1.to_noncanonical_u64(), - v1.to_noncanonical_u64(), - t1.to_noncanonical_u64(), + c.to_noncanonical_u64(), + s.to_noncanonical_u64(), + v.to_noncanonical_u64(), + t.to_noncanonical_u64(), ) - .cmp(&( - c2.to_noncanonical_u64(), - s2.to_noncanonical_u64(), - v2.to_noncanonical_u64(), - t2.to_noncanonical_u64(), - )) }); multiunzip(ops) @@ -236,12 +225,21 @@ impl, const D: usize> MemoryStark { } fn generate_memory(&self, trace_cols: &mut [Vec]) { + let num_trace_rows = trace_cols[0].len(); + let context = &trace_cols[MEMORY_ADDR_CONTEXT]; let segment = &trace_cols[MEMORY_ADDR_SEGMENT]; let virtuals = &trace_cols[MEMORY_ADDR_VIRTUAL]; - let values: Vec> = (0..8) - .map(|i| &trace_cols[memory_value_limb(i)]) - .cloned() + let values: Vec<[F; 8]> = (0..num_trace_rows) + .map(|i| { + let arr: [F; 8] = (0..8) + .map(|j| &trace_cols[memory_value_limb(j)][i]) + .cloned() + .collect_vec() + .try_into() + .unwrap(); + arr + }) .collect(); let is_read = &trace_cols[MEMORY_IS_READ]; let timestamp = &trace_cols[MEMORY_TIMESTAMP]; @@ -271,8 +269,10 @@ impl, const D: usize> MemoryStark { trace_cols[SORTED_MEMORY_ADDR_CONTEXT] = sorted_context; trace_cols[SORTED_MEMORY_ADDR_SEGMENT] = sorted_segment; trace_cols[SORTED_MEMORY_ADDR_VIRTUAL] = sorted_virtual; - for i in 0..8 { - trace_cols[sorted_memory_value_limb(i)] = sorted_values[i].clone(); + for i in 0..num_trace_rows { + for j in 0..8 { + trace_cols[sorted_memory_value_limb(j)][i] = sorted_values[i][j]; + } } trace_cols[SORTED_MEMORY_IS_READ] = sorted_is_read; trace_cols[SORTED_MEMORY_TIMESTAMP] = sorted_timestamp; @@ -367,19 +367,23 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark Date: Mon, 13 Jun 2022 14:36:48 -0700 Subject: [PATCH 14/46] uncommenting --- evm/src/memory/memory_stark.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 566415ca..5f824c54 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -383,12 +383,12 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { From affcd657167cc702ea36cfe739833abfcad5834d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 13 Jun 2022 14:36:57 -0700 Subject: [PATCH 15/46] fmt --- evm/src/memory/memory_stark.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 5f824c54..a6590db1 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -375,8 +375,10 @@ impl, const D: usize> Stark for MemoryStark Date: Thu, 23 Jun 2022 14:00:44 -0700 Subject: [PATCH 16/46] all_stark --- evm/src/cpu/columns.rs | 47 +++++++++--- evm/src/lib.rs | 1 + evm/src/memory/memory_stark.rs | 130 ++++++++++++--------------------- evm/src/memory/registers.rs | 14 ++-- 4 files changed, 93 insertions(+), 99 deletions(-) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 44264c52..16c13de6 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -13,12 +13,6 @@ pub const IS_CPU_CYCLE: usize = IS_BOOTSTRAP_CONTRACT + 1; /// If CPU cycle: The opcode being decoded, in {0, ..., 255}. pub const OPCODE: usize = IS_CPU_CYCLE + 1; -<<<<<<< HEAD -/// If CPU cycle: flags for EVM instructions. PUSHn, DUPn, and SWAPn only get one flag each. Invalid -/// opcodes are split between a number of flags for practical reasons. Exactly one of these flags -/// must be 1. -pub const IS_STOP: usize = OPCODE + 1; -======= pub const KECCAK_DUMMY: usize = OPCODE + 1; pub const MEMORY_DUMMY: usize = KECCAK_DUMMY + 1; @@ -26,7 +20,6 @@ pub const MEMORY_DUMMY: usize = KECCAK_DUMMY + 1; // opcodes are split between a number of flags for practical reasons. Exactly one of these flags // must be 1. pub const IS_STOP: usize = MEMORY_DUMMY + 1; ->>>>>>> ade7e5e0 (updated all_stark framework to include memory stark (doesn't pass yet)) pub const IS_ADD: usize = IS_STOP + 1; pub const IS_MUL: usize = IS_ADD + 1; pub const IS_SUB: usize = IS_MUL + 1; @@ -154,7 +147,7 @@ pub const OPCODE_BITS: [usize; 8] = [ pub const IS_KECCAK: usize = OPCODE_BITS[OPCODE_BITS.len() - 1] + 1; pub const START_KECCAK_INPUT: usize = IS_KECCAK + 1; -#[allow(dead_code)] // TODO: Remove when used +// TODO: Remove when used pub const KECCAK_INPUT_LIMBS: Range = START_KECCAK_INPUT..START_KECCAK_INPUT + 50; pub const START_KECCAK_OUTPUT: usize = KECCAK_INPUT_LIMBS.end; @@ -169,4 +162,40 @@ pub const LOGIC_OUTPUT: Range = LOGIC_INPUT1.end..LOGIC_INPUT1.end + 16; pub const SIMPLE_LOGIC_DIFF: usize = LOGIC_OUTPUT.end; pub const SIMPLE_LOGIC_DIFF_INV: usize = SIMPLE_LOGIC_DIFF + 1; -pub const NUM_CPU_COLUMNS: usize = SIMPLE_LOGIC_DIFF_INV + 1; +const NUM_MEMORY_OPS: usize = 4; +const NUM_MEMORY_VALUE_LIMBS: usize = 8; + +pub(crate) const MEMORY_TIMESTAMP: usize = SIMPLE_LOGIC_DIFF_INV + 1; + +const USES_MEMOP_START: usize = MEMORY_TIMESTAMP + 1; +pub const fn uses_memop(op: usize) -> usize { + debug_assert!(op < NUM_MEMORY_OPS); + USES_MEMOP_START + op +} + +const MEMOP_ISREAD_START: usize = USES_MEMOP_START + NUM_MEMORY_OPS; +pub const fn memop_is_read(op: usize) -> usize { + MEMOP_ISREAD_START + op +} + +const MEMOP_ADDR_CONTEXT_START: usize = MEMOP_ISREAD_START + NUM_MEMORY_OPS; +pub const fn memop_addr_context(op: usize) -> usize { + MEMOP_ADDR_CONTEXT_START + op +} + +const MEMOP_ADDR_SEGMENT_START: usize = MEMOP_ADDR_CONTEXT_START + NUM_MEMORY_OPS; +pub const fn memop_addr_segment(op: usize) -> usize { + MEMOP_ADDR_SEGMENT_START + op +} + +const MEMOP_ADDR_VIRTUAL_START: usize = MEMOP_ADDR_SEGMENT_START + NUM_MEMORY_OPS; +pub const fn memop_addr_virtual(op: usize) -> usize { + MEMOP_ADDR_VIRTUAL_START + op +} + +const MEMOP_ADDR_VALUE_START: usize = MEMOP_ADDR_VIRTUAL_START + NUM_MEMORY_OPS; +pub const fn memop_value(op: usize, limb: usize) -> usize { + MEMOP_ADDR_VALUE_START + op * NUM_MEMORY_VALUE_LIMBS + limb +} + +pub const NUM_CPU_COLUMNS: usize = MEMOP_ADDR_VALUE_START + NUM_MEMORY_OPS * NUM_MEMORY_VALUE_LIMBS; diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 422fae29..f60ea23c 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -3,6 +3,7 @@ #![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 a6590db1..a675bcfd 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -47,7 +47,7 @@ pub struct MemoryStark { pub(crate) f: PhantomData, } -pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, [F; 8], F, F)> { +pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, F, F, [F; 8])> { let mut memory_ops = Vec::new(); let mut rng = thread_rng(); @@ -82,31 +82,31 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, let timestamp = F::from_canonical_usize(cur_timestamp); cur_timestamp += 1; - memory_ops.push((context, segment, virt, vals, is_read_F, timestamp)) + memory_ops.push((timestamp, is_read_F, context, segment, virt, vals)) } memory_ops } pub fn sort_memory_ops( + timestamp: &[F], + is_read: &[F], context: &[F], segment: &[F], virtuals: &[F], values: &Vec<[F; 8]>, - is_read: &[F], - timestamp: &[F], -) -> (Vec, Vec, Vec, Vec<[F; 8]>, Vec, Vec) { - let mut ops: Vec<(F, F, F, [F; 8], F, F)> = izip!( +) -> (Vec, Vec, Vec, Vec, Vec, Vec<[F; 8]>) { + let mut ops: Vec<(F, F, F, F, F, [F; 8])> = izip!( + timestamp.iter().cloned(), + is_read.iter().cloned(), context.iter().cloned(), segment.iter().cloned(), virtuals.iter().cloned(), values.iter().cloned(), - is_read.iter().cloned(), - timestamp.iter().cloned() ) .collect(); - ops.sort_by_key(|&(c, s, v, _, _, t)| { + ops.sort_by_key(|&(t, _, c, s, v, _)| { ( c.to_noncanonical_u64(), s.to_noncanonical_u64(), @@ -173,7 +173,7 @@ pub fn generate_range_check_value( let mut range_check = Vec::new(); for idx in 0..num_ops - 1 { - let this_timestamp_first_change = F::ONE + let this_address_unchanged = F::ONE - context_first_change[idx] - segment_first_change[idx] - virtual_first_change[idx]; @@ -182,7 +182,7 @@ pub fn generate_range_check_value( context_first_change[idx] * (context[idx + 1] - context[idx] - F::ONE) + segment_first_change[idx] * (segment[idx + 1] - segment[idx] - F::ONE) + virtual_first_change[idx] * (virtuals[idx + 1] - virtuals[idx] - F::ONE) - + this_timestamp_first_change * (timestamp[idx + 1] - timestamp[idx] - F::ONE), + + this_address_unchanged * (timestamp[idx + 1] - timestamp[idx] - F::ONE), ); } @@ -194,7 +194,7 @@ 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; 8], F, F)>, + memory_ops: Vec<(F, F, F, F, F, [F; 8])>, ) -> Vec<[F; NUM_REGISTERS]> { let num_ops = memory_ops.len(); @@ -202,15 +202,15 @@ impl, const D: usize> MemoryStark { .try_into() .unwrap(); for i in 0..num_ops { - let (context, segment, virt, values, is_read, timestamp) = memory_ops[i]; + let (timestamp, is_read, context, segment, virt, values) = memory_ops[i]; + trace_cols[MEMORY_TIMESTAMP][i] = timestamp; + trace_cols[MEMORY_IS_READ][i] = is_read; trace_cols[MEMORY_ADDR_CONTEXT][i] = context; trace_cols[MEMORY_ADDR_SEGMENT][i] = segment; trace_cols[MEMORY_ADDR_VIRTUAL][i] = virt; for j in 0..8 { trace_cols[memory_value_limb(j)][i] = values[j]; } - trace_cols[MEMORY_IS_READ][i] = is_read; - trace_cols[MEMORY_TIMESTAMP][i] = timestamp; } self.generate_memory(&mut trace_cols); @@ -227,6 +227,8 @@ impl, const D: usize> MemoryStark { fn generate_memory(&self, trace_cols: &mut [Vec]) { let num_trace_rows = trace_cols[0].len(); + let timestamp = &trace_cols[MEMORY_TIMESTAMP]; + let is_read = &trace_cols[MEMORY_IS_READ]; let context = &trace_cols[MEMORY_ADDR_CONTEXT]; let segment = &trace_cols[MEMORY_ADDR_SEGMENT]; let virtuals = &trace_cols[MEMORY_ADDR_VIRTUAL]; @@ -241,17 +243,15 @@ impl, const D: usize> MemoryStark { arr }) .collect(); - let is_read = &trace_cols[MEMORY_IS_READ]; - let timestamp = &trace_cols[MEMORY_TIMESTAMP]; let ( + sorted_timestamp, + sorted_is_read, sorted_context, sorted_segment, sorted_virtual, sorted_values, - sorted_is_read, - sorted_timestamp, - ) = sort_memory_ops(context, segment, virtuals, &values, is_read, timestamp); + ) = sort_memory_ops(timestamp, is_read, context, segment, virtuals, &values); let (context_first_change, segment_first_change, virtual_first_change) = generate_first_change_flags(&sorted_context, &sorted_segment, &sorted_virtual); @@ -266,6 +266,8 @@ impl, const D: usize> MemoryStark { &virtual_first_change, ); + trace_cols[SORTED_MEMORY_TIMESTAMP] = sorted_timestamp; + trace_cols[SORTED_MEMORY_IS_READ] = sorted_is_read; trace_cols[SORTED_MEMORY_ADDR_CONTEXT] = sorted_context; trace_cols[SORTED_MEMORY_ADDR_SEGMENT] = sorted_segment; trace_cols[SORTED_MEMORY_ADDR_VIRTUAL] = sorted_virtual; @@ -274,8 +276,6 @@ impl, const D: usize> MemoryStark { trace_cols[sorted_memory_value_limb(j)][i] = sorted_values[i][j]; } } - trace_cols[SORTED_MEMORY_IS_READ] = sorted_is_read; - trace_cols[SORTED_MEMORY_TIMESTAMP] = sorted_timestamp; trace_cols[MEMORY_CONTEXT_FIRST_CHANGE] = context_first_change; trace_cols[MEMORY_SEGMENT_FIRST_CHANGE] = segment_first_change; @@ -294,7 +294,7 @@ impl, const D: usize> MemoryStark { pub fn generate_trace( &self, - memory_ops: Vec<(F, F, F, [F; 8], F, F)>, + memory_ops: Vec<(F, F, F, F, F, [F; 8])>, ) -> Vec> { let mut timing = TimingTree::new("generate trace", log::Level::Debug); @@ -330,27 +330,27 @@ impl, const D: usize> Stark for MemoryStark = (0..8) .map(|i| vars.local_values[sorted_memory_value_limb(i)]) .collect(); - let timestamp = vars.local_values[SORTED_MEMORY_TIMESTAMP]; + let next_timestamp = vars.next_values[SORTED_MEMORY_TIMESTAMP]; + let next_is_read = vars.next_values[SORTED_MEMORY_IS_READ]; let next_addr_context = vars.next_values[SORTED_MEMORY_ADDR_CONTEXT]; let next_addr_segment = vars.next_values[SORTED_MEMORY_ADDR_SEGMENT]; let next_addr_virtual = vars.next_values[SORTED_MEMORY_ADDR_VIRTUAL]; let next_values: Vec<_> = (0..8) .map(|i| vars.next_values[sorted_memory_value_limb(i)]) .collect(); - let next_is_read = vars.next_values[SORTED_MEMORY_IS_READ]; - let next_timestamp = vars.next_values[SORTED_MEMORY_TIMESTAMP]; let context_first_change = vars.local_values[MEMORY_CONTEXT_FIRST_CHANGE]; let segment_first_change = vars.local_values[MEMORY_SEGMENT_FIRST_CHANGE]; let virtual_first_change = vars.local_values[MEMORY_VIRTUAL_FIRST_CHANGE]; - let timestamp_first_change = + let address_unchanged = one - context_first_change - segment_first_change - virtual_first_change; let range_check = vars.local_values[MEMORY_RANGE_CHECK]; @@ -358,13 +358,13 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark(stark) } - - #[test] - fn test_memory_stark() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = MemoryStark; - - let stark = S { - f: Default::default(), - }; - - const MAX_CONTEXT: usize = 256; - const MAX_SEGMENT: usize = 8; - const MAX_VIRTUAL: usize = 1 << 12; - - let num_ops = 20; - let memory_ops = generate_random_memory_ops(num_ops); - - let rows = stark.generate_trace_rows(memory_ops); - - Ok(()) - } } diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index 885fda19..25b3cb5a 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -1,6 +1,8 @@ //! Memory unit. -pub(crate) const MEMORY_ADDR_CONTEXT: usize = 0; +pub(crate) const MEMORY_TIMESTAMP: usize = 0; +pub(crate) const MEMORY_IS_READ: usize = MEMORY_TIMESTAMP + 1; +pub(crate) const MEMORY_ADDR_CONTEXT: usize = MEMORY_IS_READ + 1; pub(crate) const MEMORY_ADDR_SEGMENT: usize = MEMORY_ADDR_CONTEXT + 1; pub(crate) const MEMORY_ADDR_VIRTUAL: usize = MEMORY_ADDR_SEGMENT + 1; pub(crate) const MEMORY_VALUE_START: usize = MEMORY_ADDR_VIRTUAL + 1; @@ -9,10 +11,9 @@ pub const fn memory_value_limb(i: usize) -> usize { MEMORY_VALUE_START + i } -pub(crate) const MEMORY_IS_READ: usize = MEMORY_VALUE_START + 8; -pub(crate) const MEMORY_TIMESTAMP: usize = MEMORY_IS_READ + 1; - -pub(crate) const SORTED_MEMORY_ADDR_CONTEXT: usize = MEMORY_TIMESTAMP + 1; +pub(crate) const SORTED_MEMORY_TIMESTAMP: usize = MEMORY_VALUE_START + 8; +pub(crate) const SORTED_MEMORY_IS_READ: usize = SORTED_MEMORY_TIMESTAMP + 1; +pub(crate) const SORTED_MEMORY_ADDR_CONTEXT: usize = SORTED_MEMORY_IS_READ + 1; pub(crate) const SORTED_MEMORY_ADDR_SEGMENT: usize = SORTED_MEMORY_ADDR_CONTEXT + 1; pub(crate) const SORTED_MEMORY_ADDR_VIRTUAL: usize = SORTED_MEMORY_ADDR_SEGMENT + 1; pub(crate) const SORTED_MEMORY_VALUE_START: usize = SORTED_MEMORY_ADDR_VIRTUAL + 1; @@ -21,9 +22,6 @@ pub const fn sorted_memory_value_limb(i: usize) -> usize { SORTED_MEMORY_VALUE_START + i } -pub(crate) const SORTED_MEMORY_IS_READ: usize = SORTED_MEMORY_VALUE_START + 8; -pub(crate) const SORTED_MEMORY_TIMESTAMP: usize = SORTED_MEMORY_IS_READ + 1; - pub(crate) const MEMORY_CONTEXT_FIRST_CHANGE: usize = SORTED_MEMORY_TIMESTAMP + 1; pub(crate) const MEMORY_SEGMENT_FIRST_CHANGE: usize = MEMORY_CONTEXT_FIRST_CHANGE + 1; pub(crate) const MEMORY_VIRTUAL_FIRST_CHANGE: usize = MEMORY_SEGMENT_FIRST_CHANGE + 1; From 8155e90d160957bbd8e350a78b3a2a25124ecf76 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 16:51:14 -0700 Subject: [PATCH 17/46] fixes --- evm/src/memory/registers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index 25b3cb5a..2107ee81 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -22,7 +22,7 @@ pub const fn sorted_memory_value_limb(i: usize) -> usize { SORTED_MEMORY_VALUE_START + i } -pub(crate) const MEMORY_CONTEXT_FIRST_CHANGE: usize = SORTED_MEMORY_TIMESTAMP + 1; +pub(crate) const MEMORY_CONTEXT_FIRST_CHANGE: usize = SORTED_MEMORY_VALUE_START + 8; pub(crate) const MEMORY_SEGMENT_FIRST_CHANGE: usize = MEMORY_CONTEXT_FIRST_CHANGE + 1; pub(crate) const MEMORY_VIRTUAL_FIRST_CHANGE: usize = MEMORY_SEGMENT_FIRST_CHANGE + 1; From bf58c203298cfc751833cb1bb0f8146250e7061c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 16:51:32 -0700 Subject: [PATCH 18/46] updates to registers, new cross-table lookups --- evm/src/all_stark.rs | 3 +- evm/src/memory/memory_stark.rs | 134 ++++++++++++++++----------------- evm/src/memory/registers.rs | 58 ++++++++------ 3 files changed, 102 insertions(+), 93 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 27a03021..59a3ad3d 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -87,7 +87,7 @@ mod tests { use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; use crate::verifier::verify_proof; - use crate::{cpu, keccak}; + use crate::{cpu, keccak, memory}; const D: usize = 2; type C = PoseidonGoldilocksConfig; @@ -206,6 +206,7 @@ mod tests { cpu_stark.generate(&mut row); cpu_trace_rows.push(row); } + for i in 0..num_memory_ops { let mem_timestamp: usize = memory_trace[memory::registers::TIMESTAMP].values[i] .to_canonical_u64() diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index a675bcfd..ddc0f056 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -12,12 +12,10 @@ use rand::{thread_rng, Rng}; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::memory::registers::{ - memory_value_limb, sorted_memory_value_limb, MEMORY_ADDR_CONTEXT, MEMORY_ADDR_SEGMENT, - MEMORY_ADDR_VIRTUAL, MEMORY_CONTEXT_FIRST_CHANGE, MEMORY_COUNTER, MEMORY_COUNTER_PERMUTED, - MEMORY_IS_READ, MEMORY_RANGE_CHECK, MEMORY_RANGE_CHECK_PERMUTED, MEMORY_SEGMENT_FIRST_CHANGE, - MEMORY_TIMESTAMP, MEMORY_VIRTUAL_FIRST_CHANGE, NUM_REGISTERS, SORTED_MEMORY_ADDR_CONTEXT, - SORTED_MEMORY_ADDR_SEGMENT, SORTED_MEMORY_ADDR_VIRTUAL, SORTED_MEMORY_IS_READ, - SORTED_MEMORY_TIMESTAMP, + 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, + SEGMENT_FIRST_CHANGE, SORTED_ADDR_CONTEXT, SORTED_ADDR_SEGMENT, SORTED_ADDR_VIRTUAL, + SORTED_IS_READ, SORTED_TIMESTAMP, TIMESTAMP, VIRTUAL_FIRST_CHANGE, }; use crate::stark::Stark; use crate::util::{permuted_cols, trace_rows_to_poly_values}; @@ -203,13 +201,13 @@ impl, const D: usize> MemoryStark { .unwrap(); for i in 0..num_ops { let (timestamp, is_read, context, segment, virt, values) = memory_ops[i]; - trace_cols[MEMORY_TIMESTAMP][i] = timestamp; - trace_cols[MEMORY_IS_READ][i] = is_read; - trace_cols[MEMORY_ADDR_CONTEXT][i] = context; - trace_cols[MEMORY_ADDR_SEGMENT][i] = segment; - trace_cols[MEMORY_ADDR_VIRTUAL][i] = virt; + 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[memory_value_limb(j)][i] = values[j]; + trace_cols[value_limb(j)][i] = values[j]; } } @@ -227,15 +225,15 @@ impl, const D: usize> MemoryStark { fn generate_memory(&self, trace_cols: &mut [Vec]) { let num_trace_rows = trace_cols[0].len(); - let timestamp = &trace_cols[MEMORY_TIMESTAMP]; - let is_read = &trace_cols[MEMORY_IS_READ]; - let context = &trace_cols[MEMORY_ADDR_CONTEXT]; - let segment = &trace_cols[MEMORY_ADDR_SEGMENT]; - let virtuals = &trace_cols[MEMORY_ADDR_VIRTUAL]; + let timestamp = &trace_cols[TIMESTAMP]; + let is_read = &trace_cols[IS_READ]; + let context = &trace_cols[ADDR_CONTEXT]; + let segment = &trace_cols[ADDR_SEGMENT]; + let virtuals = &trace_cols[ADDR_VIRTUAL]; let values: Vec<[F; 8]> = (0..num_trace_rows) .map(|i| { let arr: [F; 8] = (0..8) - .map(|j| &trace_cols[memory_value_limb(j)][i]) + .map(|j| &trace_cols[value_limb(j)][i]) .cloned() .collect_vec() .try_into() @@ -266,30 +264,30 @@ impl, const D: usize> MemoryStark { &virtual_first_change, ); - trace_cols[SORTED_MEMORY_TIMESTAMP] = sorted_timestamp; - trace_cols[SORTED_MEMORY_IS_READ] = sorted_is_read; - trace_cols[SORTED_MEMORY_ADDR_CONTEXT] = sorted_context; - trace_cols[SORTED_MEMORY_ADDR_SEGMENT] = sorted_segment; - trace_cols[SORTED_MEMORY_ADDR_VIRTUAL] = sorted_virtual; + trace_cols[SORTED_TIMESTAMP] = sorted_timestamp; + trace_cols[SORTED_IS_READ] = sorted_is_read; + trace_cols[SORTED_ADDR_CONTEXT] = sorted_context; + trace_cols[SORTED_ADDR_SEGMENT] = sorted_segment; + trace_cols[SORTED_ADDR_VIRTUAL] = sorted_virtual; for i in 0..num_trace_rows { for j in 0..8 { - trace_cols[sorted_memory_value_limb(j)][i] = sorted_values[i][j]; + trace_cols[sorted_value_limb(j)][i] = sorted_values[i][j]; } } - trace_cols[MEMORY_CONTEXT_FIRST_CHANGE] = context_first_change; - trace_cols[MEMORY_SEGMENT_FIRST_CHANGE] = segment_first_change; - trace_cols[MEMORY_VIRTUAL_FIRST_CHANGE] = virtual_first_change; + trace_cols[CONTEXT_FIRST_CHANGE] = context_first_change; + trace_cols[SEGMENT_FIRST_CHANGE] = segment_first_change; + trace_cols[VIRTUAL_FIRST_CHANGE] = virtual_first_change; - trace_cols[MEMORY_RANGE_CHECK] = range_check_value; - trace_cols[MEMORY_COUNTER] = (0..trace_cols[0].len()) + trace_cols[RANGE_CHECK] = range_check_value; + trace_cols[COUNTER] = (0..trace_cols[0].len()) .map(|i| F::from_canonical_usize(i)) .collect(); let (permuted_inputs, permuted_table) = - permuted_cols(&trace_cols[MEMORY_RANGE_CHECK], &trace_cols[MEMORY_COUNTER]); - trace_cols[MEMORY_RANGE_CHECK_PERMUTED] = permuted_inputs; - trace_cols[MEMORY_COUNTER_PERMUTED] = permuted_table; + permuted_cols(&trace_cols[RANGE_CHECK], &trace_cols[COUNTER]); + trace_cols[RANGE_CHECK_PERMUTED] = permuted_inputs; + trace_cols[COUNTER_PERMUTED] = permuted_table; } pub fn generate_trace( @@ -330,30 +328,30 @@ impl, const D: usize> Stark for MemoryStark = (0..8) - .map(|i| vars.local_values[sorted_memory_value_limb(i)]) + .map(|i| vars.local_values[sorted_value_limb(i)]) .collect(); - let next_timestamp = vars.next_values[SORTED_MEMORY_TIMESTAMP]; - let next_is_read = vars.next_values[SORTED_MEMORY_IS_READ]; - let next_addr_context = vars.next_values[SORTED_MEMORY_ADDR_CONTEXT]; - let next_addr_segment = vars.next_values[SORTED_MEMORY_ADDR_SEGMENT]; - let next_addr_virtual = vars.next_values[SORTED_MEMORY_ADDR_VIRTUAL]; + let next_timestamp = vars.next_values[SORTED_TIMESTAMP]; + let next_is_read = vars.next_values[SORTED_IS_READ]; + let next_addr_context = vars.next_values[SORTED_ADDR_CONTEXT]; + let next_addr_segment = vars.next_values[SORTED_ADDR_SEGMENT]; + let next_addr_virtual = vars.next_values[SORTED_ADDR_VIRTUAL]; let next_values: Vec<_> = (0..8) - .map(|i| vars.next_values[sorted_memory_value_limb(i)]) + .map(|i| vars.next_values[sorted_value_limb(i)]) .collect(); - let context_first_change = vars.local_values[MEMORY_CONTEXT_FIRST_CHANGE]; - let segment_first_change = vars.local_values[MEMORY_SEGMENT_FIRST_CHANGE]; - let virtual_first_change = vars.local_values[MEMORY_VIRTUAL_FIRST_CHANGE]; + let context_first_change = vars.local_values[CONTEXT_FIRST_CHANGE]; + let segment_first_change = vars.local_values[SEGMENT_FIRST_CHANGE]; + let virtual_first_change = vars.local_values[VIRTUAL_FIRST_CHANGE]; let address_unchanged = one - context_first_change - segment_first_change - virtual_first_change; - let range_check = vars.local_values[MEMORY_RANGE_CHECK]; + let range_check = vars.local_values[RANGE_CHECK]; let not_context_first_change = one - context_first_change; let not_segment_first_change = one - segment_first_change; @@ -391,9 +389,9 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark = (0..8) - .map(|i| vars.local_values[sorted_memory_value_limb(i)]) + .map(|i| vars.local_values[sorted_value_limb(i)]) .collect(); - let timestamp = vars.local_values[SORTED_MEMORY_TIMESTAMP]; + let timestamp = vars.local_values[SORTED_TIMESTAMP]; - let next_addr_context = vars.next_values[SORTED_MEMORY_ADDR_CONTEXT]; - let next_addr_segment = vars.next_values[SORTED_MEMORY_ADDR_SEGMENT]; - let next_addr_virtual = vars.next_values[SORTED_MEMORY_ADDR_VIRTUAL]; + let next_addr_context = vars.next_values[SORTED_ADDR_CONTEXT]; + let next_addr_segment = vars.next_values[SORTED_ADDR_SEGMENT]; + let next_addr_virtual = vars.next_values[SORTED_ADDR_VIRTUAL]; let next_values: Vec<_> = (0..8) - .map(|i| vars.next_values[sorted_memory_value_limb(i)]) + .map(|i| vars.next_values[sorted_value_limb(i)]) .collect(); - let next_is_read = vars.next_values[SORTED_MEMORY_IS_READ]; - let next_timestamp = vars.next_values[SORTED_MEMORY_TIMESTAMP]; + let next_is_read = vars.next_values[SORTED_IS_READ]; + let next_timestamp = vars.next_values[SORTED_TIMESTAMP]; - let context_first_change = vars.local_values[MEMORY_CONTEXT_FIRST_CHANGE]; - let segment_first_change = vars.local_values[MEMORY_SEGMENT_FIRST_CHANGE]; - let virtual_first_change = vars.local_values[MEMORY_VIRTUAL_FIRST_CHANGE]; + let context_first_change = vars.local_values[CONTEXT_FIRST_CHANGE]; + let segment_first_change = vars.local_values[SEGMENT_FIRST_CHANGE]; + let virtual_first_change = vars.local_values[VIRTUAL_FIRST_CHANGE]; let address_unchanged = { let mut cur = builder.sub_extension(one, context_first_change); cur = builder.sub_extension(cur, segment_first_change); builder.sub_extension(cur, virtual_first_change) }; - let range_check = vars.local_values[MEMORY_RANGE_CHECK]; + let range_check = vars.local_values[RANGE_CHECK]; let not_context_first_change = builder.sub_extension(one, context_first_change); let not_segment_first_change = builder.sub_extension(one, segment_first_change); @@ -522,9 +520,9 @@ impl, const D: usize> Stark for MemoryStark usize { - MEMORY_VALUE_START + i +pub(crate) const TIMESTAMP: usize = 0; +pub(crate) const IS_READ: usize = TIMESTAMP + 1; +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; +pub(crate) 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 } -pub(crate) const SORTED_MEMORY_TIMESTAMP: usize = MEMORY_VALUE_START + 8; -pub(crate) const SORTED_MEMORY_IS_READ: usize = SORTED_MEMORY_TIMESTAMP + 1; -pub(crate) const SORTED_MEMORY_ADDR_CONTEXT: usize = SORTED_MEMORY_IS_READ + 1; -pub(crate) const SORTED_MEMORY_ADDR_SEGMENT: usize = SORTED_MEMORY_ADDR_CONTEXT + 1; -pub(crate) const SORTED_MEMORY_ADDR_VIRTUAL: usize = SORTED_MEMORY_ADDR_SEGMENT + 1; -pub(crate) const SORTED_MEMORY_VALUE_START: usize = SORTED_MEMORY_ADDR_VIRTUAL + 1; +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; +pub(crate) const SORTED_ADDR_SEGMENT: usize = SORTED_ADDR_CONTEXT + 1; +pub(crate) const SORTED_ADDR_VIRTUAL: usize = SORTED_ADDR_SEGMENT + 1; +pub(crate) const SORTED_VALUE_START: usize = SORTED_ADDR_VIRTUAL + 1; -pub const fn sorted_memory_value_limb(i: usize) -> usize { - SORTED_MEMORY_VALUE_START + i +pub(crate) const fn sorted_value_limb(i: usize) -> usize { + debug_assert!(i < NUM_MEMORY_VALUE_LIMBS); + SORTED_VALUE_START + i } -pub(crate) const MEMORY_CONTEXT_FIRST_CHANGE: usize = SORTED_MEMORY_VALUE_START + 8; -pub(crate) const MEMORY_SEGMENT_FIRST_CHANGE: usize = MEMORY_CONTEXT_FIRST_CHANGE + 1; -pub(crate) const MEMORY_VIRTUAL_FIRST_CHANGE: usize = MEMORY_SEGMENT_FIRST_CHANGE + 1; +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; -pub(crate) const MEMORY_RANGE_CHECK: usize = MEMORY_VIRTUAL_FIRST_CHANGE + 1; -pub(crate) const MEMORY_COUNTER: usize = MEMORY_RANGE_CHECK + 1; -pub(crate) const MEMORY_RANGE_CHECK_PERMUTED: usize = MEMORY_COUNTER + 1; -pub(crate) const MEMORY_COUNTER_PERMUTED: usize = MEMORY_RANGE_CHECK_PERMUTED + 1; +pub(crate) const RANGE_CHECK: usize = VIRTUAL_FIRST_CHANGE + 1; +pub(crate) const COUNTER: usize = RANGE_CHECK + 1; +pub(crate) const RANGE_CHECK_PERMUTED: usize = COUNTER + 1; +pub(crate) const COUNTER_PERMUTED: usize = RANGE_CHECK_PERMUTED + 1; -pub(crate) const NUM_REGISTERS: usize = MEMORY_COUNTER_PERMUTED + 1; +pub(crate) const fn is_memop(i: usize) -> usize { + debug_assert!(i < NUM_MEMORY_OPS); + COUNTER_PERMUTED + i +} + +pub(crate) const NUM_REGISTERS: usize = COUNTER_PERMUTED + NUM_MEMORY_OPS; From 398e75de4e30aef6ff60f7198ec91aa95a30247b Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 15 Jun 2022 11:01:40 -0700 Subject: [PATCH 19/46] fix --- evm/src/memory/memory_stark.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index ddc0f056..3997bc6a 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -51,7 +51,6 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, let mut rng = thread_rng(); let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); - let mut cur_timestamp = 0; for i in 0..num_ops { let is_read = if i == 0 { false } else { rng.gen() }; let is_read_F = F::from_bool(is_read); @@ -77,8 +76,7 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, (context, segment, virt, vals) }; - let timestamp = F::from_canonical_usize(cur_timestamp); - cur_timestamp += 1; + let timestamp = F::from_canonical_usize(i); memory_ops.push((timestamp, is_read_F, context, segment, virt, vals)) } From 5707732b75906b87a3c5a7bff6b1c82863ed5c4a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 16:52:42 -0700 Subject: [PATCH 20/46] fix --- evm/src/all_stark.rs | 1 - evm/src/memory/memory_stark.rs | 22 +++++++++++----------- evm/src/memory/registers.rs | 9 +++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 59a3ad3d..85064fe7 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -206,7 +206,6 @@ mod tests { cpu_stark.generate(&mut row); cpu_trace_rows.push(row); } - for i in 0..num_memory_ops { let mem_timestamp: usize = memory_trace[memory::registers::TIMESTAMP].values[i] .to_canonical_u64() diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 3997bc6a..d74c65e8 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -90,7 +90,7 @@ pub fn sort_memory_ops( context: &[F], segment: &[F], virtuals: &[F], - values: &Vec<[F; 8]>, + values: &[[F; 8]], ) -> (Vec, Vec, Vec, Vec, Vec, Vec<[F; 8]>) { let mut ops: Vec<(F, F, F, F, F, [F; 8])> = izip!( timestamp.iter().cloned(), @@ -115,9 +115,9 @@ pub fn sort_memory_ops( } pub fn generate_first_change_flags( - context: &Vec, - segment: &Vec, - virtuals: &Vec, + context: &[F], + segment: &[F], + virtuals: &[F], ) -> (Vec, Vec, Vec) { let num_ops = context.len(); let mut context_first_change = Vec::new(); @@ -157,13 +157,13 @@ pub fn generate_first_change_flags( } pub fn generate_range_check_value( - context: &Vec, - segment: &Vec, - virtuals: &Vec, - timestamp: &Vec, - context_first_change: &Vec, - segment_first_change: &Vec, - virtual_first_change: &Vec, + context: &[F], + segment: &[F], + virtuals: &[F], + timestamp: &[F], + context_first_change: &[F], + segment_first_change: &[F], + virtual_first_change: &[F], ) -> Vec { let num_ops = context.len(); let mut range_check = Vec::new(); diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index 196c62c8..c81408c8 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -8,8 +8,8 @@ pub(crate) const IS_READ: usize = TIMESTAMP + 1; 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; -pub(crate) const VALUE_START: usize = ADDR_VIRTUAL + 1; +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 @@ -20,8 +20,8 @@ pub(crate) const SORTED_IS_READ: usize = SORTED_TIMESTAMP + 1; pub(crate) const SORTED_ADDR_CONTEXT: usize = SORTED_IS_READ + 1; pub(crate) const SORTED_ADDR_SEGMENT: usize = SORTED_ADDR_CONTEXT + 1; pub(crate) const SORTED_ADDR_VIRTUAL: usize = SORTED_ADDR_SEGMENT + 1; -pub(crate) const SORTED_VALUE_START: usize = SORTED_ADDR_VIRTUAL + 1; +const SORTED_VALUE_START: usize = SORTED_ADDR_VIRTUAL + 1; pub(crate) const fn sorted_value_limb(i: usize) -> usize { debug_assert!(i < NUM_MEMORY_VALUE_LIMBS); SORTED_VALUE_START + i @@ -36,9 +36,10 @@ pub(crate) const COUNTER: usize = RANGE_CHECK + 1; pub(crate) const RANGE_CHECK_PERMUTED: usize = COUNTER + 1; pub(crate) const COUNTER_PERMUTED: usize = RANGE_CHECK_PERMUTED + 1; +const IS_MEMOP_START: usize = COUNTER_PERMUTED + 1; pub(crate) const fn is_memop(i: usize) -> usize { debug_assert!(i < NUM_MEMORY_OPS); - COUNTER_PERMUTED + i + IS_MEMOP_START + i } -pub(crate) const NUM_REGISTERS: usize = COUNTER_PERMUTED + NUM_MEMORY_OPS; +pub(crate) const NUM_REGISTERS: usize = IS_MEMOP_START + NUM_MEMORY_OPS; From 105852589f5ea3c7b8c022df1f62d9f0f5569299 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 16 Jun 2022 16:12:27 -0700 Subject: [PATCH 21/46] removed accidentally added files --- plonky2/src/gadgets/curve_msm.rs | 71 --------- starky/src/factorial_stark.rs | 244 ----------------------------- starky/src/permutation_stark.rs | 253 ------------------------------- 3 files changed, 568 deletions(-) delete mode 100644 plonky2/src/gadgets/curve_msm.rs delete mode 100644 starky/src/factorial_stark.rs delete mode 100644 starky/src/permutation_stark.rs diff --git a/plonky2/src/gadgets/curve_msm.rs b/plonky2/src/gadgets/curve_msm.rs deleted file mode 100644 index 60d1ddc4..00000000 --- a/plonky2/src/gadgets/curve_msm.rs +++ /dev/null @@ -1,71 +0,0 @@ -use plonky2_field::extension_field::Extendable; -use plonky2_field::field_types::Field; - -use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar}; -use crate::gadgets::curve::AffinePointTarget; -use crate::gadgets::nonnative::NonNativeTarget; -use crate::hash::hash_types::RichField; -use crate::plonk::circuit_builder::CircuitBuilder; - -const DIGITS_PER_CHUNK: usize = 80; - -const WINDOW_SIZE: usize = 4; - -pub struct MsmPrecomputationTarget { - /// For each generator (in the order they were passed to `msm_precompute`), contains a vector - /// of powers, i.e. [(2^w)^i] for i < DIGITS. - powers_per_generator: Vec>>, -} - -impl, const D: usize> CircuitBuilder { - pub fn precompute_single_generator( - &mut self, - g: AffinePointTarget, - ) -> Vec> { - let digits = (C::ScalarField::BITS + WINDOW_SIZE - 1) / WINDOW_SIZE; - let mut powers: Vec> = Vec::with_capacity(digits); - powers.push(g); - for i in 1..digits { - let mut power_i_proj = powers[i - 1].clone(); - for _j in 0..WINDOW_SIZE { - power_i_proj = self.curve_double(&power_i_proj); - } - powers.push(power_i_proj); - } - powers - } - - pub fn msm_precompute( - &mut self, - generators: &[AffinePointTarget], - ) -> MsmPrecomputationTarget { - MsmPrecomputationTarget { - powers_per_generator: generators - .into_iter() - .map(|g| self.precompute_single_generator(g.clone())) - .collect(), - w, - } - } - - pub fn msm_execute( - &mut self, - precomputation: &MsmPrecomputationTarget, - scalars: &[NonNativeTarget], - ) -> AffinePointTarget { - debug_assert_eq!(precomputation.powers_per_generator.len(), scalars.len()); - - let digits = (C::ScalarField::BITS + WINDOW_SIZE - 1) / WINDOW_SIZE; - let base = 1 << WINDOW_SIZE; - - for (i, scalar) in scalars.iter().enumerate() { - let digits = self.split_nonnative_to_4_bit_limbs(scalar); - } - - let digits: Vec<_> = (0..base).map(|i| self.constant(F::from_canonical_usize(i))).collect(); - let mut digit_acc: Vec> = Vec::new(); - for i in 0..base { - - } - } -} diff --git a/starky/src/factorial_stark.rs b/starky/src/factorial_stark.rs deleted file mode 100644 index 6650de25..00000000 --- a/starky/src/factorial_stark.rs +++ /dev/null @@ -1,244 +0,0 @@ -use std::marker::PhantomData; - -use plonky2::field::extension_field::{Extendable, FieldExtension}; -use plonky2::field::packed_field::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::hash::hash_types::RichField; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::permutation::PermutationPair; -use crate::stark::Stark; -use crate::util::trace_rows_to_poly_values; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; - -/// Toy STARK system used for testing. -/// Computes a Factorial sequence with state `[fact, n]` using the state transition -/// `fact' <- fact * (n + 1), n' <- n + 1`. -#[derive(Copy, Clone)] -struct FactorialStark, const D: usize> { - num_rows: usize, - _phantom: PhantomData, -} - -impl, const D: usize> FactorialStark { - // The first public input is `x0`. - const PI_INDEX_X0: usize = 0; - // The second public input is the first element of the last row, which should be equal to - // `num_rows` factorial. - const PI_INDEX_RES: usize = 1; - - fn new(num_rows: usize) -> Self { - Self { - num_rows, - _phantom: PhantomData, - } - } - - /// Generate the trace using `x0, 1` as initial state values. - fn generate_trace(&self, x0: F) -> Vec> { - let mut trace_rows = (0..self.num_rows) - .scan([x0, F::ONE], |acc, _| { - let tmp = *acc; - acc[0] = tmp[0] * (tmp[1] + F::ONE); - acc[1] = tmp[1] + F::ONE; - Some(tmp) - }) - .collect::>(); - trace_rows_to_poly_values(trace_rows) - } -} - -impl, const D: usize> Stark for FactorialStark { - const COLUMNS: usize = 2; - const PUBLIC_INPUTS: usize = 2; - - fn eval_packed_generic( - &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - // Check public inputs. - yield_constr - .constraint_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); - yield_constr - .constraint_last_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_RES]); - - // x0' <- x0 * (x1 + 1) - yield_constr.constraint_transition( - vars.next_values[0] - vars.local_values[0] * (vars.local_values[1] + FE::ONE), - ); - // x1' <- x1 + 1 - yield_constr.constraint_transition(vars.next_values[1] - vars.local_values[1] - FE::ONE); - } - - fn eval_ext_recursively( - &self, - builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - // Check public inputs. - let pis_constraints = [ - builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]), - builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_RES]), - ]; - yield_constr.constraint_first_row(builder, pis_constraints[0]); - yield_constr.constraint_last_row(builder, pis_constraints[1]); - - let one = builder.one_extension(); - // x0' <- x0 * (x1 + 1) - let first_col_constraint = { - let tmp1 = builder.add_extension(vars.local_values[1], one); - let tmp2 = builder.mul_extension(vars.local_values[0], tmp1); - builder.sub_extension(vars.next_values[0], tmp2) - }; - yield_constr.constraint_transition(builder, first_col_constraint); - // x1' <- x1 + 1 - let second_col_constraint = { - let tmp = builder.add_extension(vars.local_values[1], one); - builder.sub_extension(vars.next_values[1], tmp) - }; - yield_constr.constraint_transition(builder, second_col_constraint); - } - - fn constraint_degree(&self) -> usize { - 2 - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use plonky2::field::extension_field::Extendable; - use plonky2::field::field_types::Field; - use plonky2::hash::hash_types::RichField; - use plonky2::iop::witness::PartialWitness; - use plonky2::plonk::circuit_builder::CircuitBuilder; - use plonky2::plonk::circuit_data::CircuitConfig; - use plonky2::plonk::config::{ - AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, - }; - use plonky2::util::timing::TimingTree; - - use crate::config::StarkConfig; - use crate::factorial_stark::FactorialStark; - use crate::proof::StarkProofWithPublicInputs; - use crate::prover::prove; - use crate::recursive_verifier::{ - add_virtual_stark_proof_with_pis, recursively_verify_stark_proof, - set_stark_proof_with_pis_target, - }; - use crate::stark::Stark; - use crate::stark_testing::test_stark_low_degree; - use crate::verifier::verify_stark_proof; - - fn factorial(n: usize, x0: F) -> F { - (0..n) - .fold((x0, F::ONE), |x, _| (x.0 * (x.1 + F::ONE), x.1 + F::ONE)) - .0 - } - - #[test] - fn test_factorial_stark() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FactorialStark; - - let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 3; - let public_inputs = [F::ONE, factorial(num_rows - 1, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0]); - let proof = prove::( - stark, - &config, - trace, - public_inputs, - &mut TimingTree::default(), - )?; - - verify_stark_proof(stark, proof, &config) - } - - #[test] - fn test_factorial_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FactorialStark; - - let num_rows = 1 << 3; - let stark = S::new(num_rows); - test_stark_low_degree(stark) - } - - #[test] - fn test_recursive_stark_verifier() -> Result<()> { - init_logger(); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FactorialStark; - - let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 5; - let public_inputs = [F::ONE, factorial(num_rows - 1, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0]); - let proof = prove::( - stark, - &config, - trace, - public_inputs, - &mut TimingTree::default(), - )?; - verify_stark_proof(stark, proof.clone(), &config)?; - - recursive_proof::(stark, proof, &config, true) - } - - fn recursive_proof< - F: RichField + Extendable, - C: GenericConfig, - S: Stark + Copy, - InnerC: GenericConfig, - const D: usize, - >( - stark: S, - inner_proof: StarkProofWithPublicInputs, - inner_config: &StarkConfig, - print_gate_counts: bool, - ) -> Result<()> - where - InnerC::Hasher: AlgebraicHasher, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, - [(); C::Hasher::HASH_SIZE]:, - { - let circuit_config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(circuit_config); - let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); - let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); - - recursively_verify_stark_proof::(&mut builder, stark, pt, inner_config); - - if print_gate_counts { - builder.print_gate_counts(0); - } - - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof) - } - - fn init_logger() { - let _ = env_logger::builder().format_timestamp(None).try_init(); - } -} diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs deleted file mode 100644 index b227847d..00000000 --- a/starky/src/permutation_stark.rs +++ /dev/null @@ -1,253 +0,0 @@ -use std::marker::PhantomData; - -use plonky2::field::extension_field::{Extendable, FieldExtension}; -use plonky2::field::packed_field::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::hash::hash_types::RichField; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::permutation::PermutationPair; -use crate::stark::Stark; -use crate::util::trace_rows_to_poly_values; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; - -/// Toy STARK system used for testing. -/// Proves that the last three public inputs are a sorted version of the first three. -/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. -/// Note: The `i, j` columns are only used to test the permutation argument. -#[derive(Copy, Clone)] -struct SortingStark, const D: usize> { - num_rows: usize, - _phantom: PhantomData, -} - -impl, const D: usize> SortingStark { - // The first public input is `a`. - const PI_INDEX_X0: usize = 0; - // The second public input is `b`. - const PI_INDEX_X1: usize = 1; - // The second public input is `c`. - const PI_INDEX_X1: usize = 2; - // The third public input is the second element of the last row, which should be equal to the - // `num_rows`-th Fibonacci number. - const PI_INDEX_RES: usize = 2; - - fn new(num_rows: usize) -> Self { - Self { - num_rows, - _phantom: PhantomData, - } - } - - /// Generate the trace using `x0, x1, 0, 1` as initial state values. - fn generate_trace(&self, x0: F, x1: F) -> Vec> { - let mut trace_rows = (0..self.num_rows) - .scan([x0, x1, F::ZERO, F::ONE], |acc, _| { - let tmp = *acc; - acc[0] = tmp[1]; - acc[1] = tmp[0] + tmp[1]; - acc[2] = tmp[2] + F::ONE; - acc[3] = tmp[3] + F::ONE; - Some(tmp) - }) - .collect::>(); - trace_rows[self.num_rows - 1][3] = F::ZERO; // So that column 2 and 3 are permutation of one another. - trace_rows_to_poly_values(trace_rows) - } -} - -impl, const D: usize> Stark for SortingStark { - const COLUMNS: usize = 4; - const PUBLIC_INPUTS: usize = 3; - - fn eval_packed_generic( - &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - // Check public inputs. - yield_constr - .constraint_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); - yield_constr - .constraint_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); - yield_constr - .constraint_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); - - // x0' <- x1 - yield_constr.constraint_transition(vars.next_values[0] - vars.local_values[1]); - // x1' <- x0 + x1 - yield_constr.constraint_transition( - vars.next_values[1] - vars.local_values[0] - vars.local_values[1], - ); - } - - fn eval_ext_recursively( - &self, - builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - // Check public inputs. - let pis_constraints = [ - builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]), - builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_X1]), - builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_RES]), - ]; - yield_constr.constraint_first_row(builder, pis_constraints[0]); - yield_constr.constraint_first_row(builder, pis_constraints[1]); - yield_constr.constraint_last_row(builder, pis_constraints[2]); - - // x0' <- x1 - let first_col_constraint = builder.sub_extension(vars.next_values[0], vars.local_values[1]); - yield_constr.constraint_transition(builder, first_col_constraint); - // x1' <- x0 + x1 - let second_col_constraint = { - let tmp = builder.sub_extension(vars.next_values[1], vars.local_values[0]); - builder.sub_extension(tmp, vars.local_values[1]) - }; - yield_constr.constraint_transition(builder, second_col_constraint); - } - - fn constraint_degree(&self) -> usize { - 2 - } - - fn permutation_pairs(&self) -> Vec { - vec![PermutationPair::singletons(2, 3)] - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use plonky2::field::extension_field::Extendable; - use plonky2::field::field_types::Field; - use plonky2::hash::hash_types::RichField; - use plonky2::iop::witness::PartialWitness; - use plonky2::plonk::circuit_builder::CircuitBuilder; - use plonky2::plonk::circuit_data::CircuitConfig; - use plonky2::plonk::config::{ - AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, - }; - use plonky2::util::timing::TimingTree; - - use crate::config::StarkConfig; - use crate::fibonacci_stark::FibonacciStark; - use crate::proof::StarkProofWithPublicInputs; - use crate::prover::prove; - use crate::recursive_verifier::{ - add_virtual_stark_proof_with_pis, recursively_verify_stark_proof, - set_stark_proof_with_pis_target, - }; - use crate::stark::Stark; - use crate::stark_testing::test_stark_low_degree; - use crate::verifier::verify_stark_proof; - - fn fibonacci(n: usize, x0: F, x1: F) -> F { - (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 - } - - #[test] - fn test_fibonacci_stark() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - - let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 5; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( - stark, - &config, - trace, - public_inputs, - &mut TimingTree::default(), - )?; - - verify_stark_proof(stark, proof, &config) - } - - #[test] - fn test_fibonacci_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - - let num_rows = 1 << 5; - let stark = S::new(num_rows); - test_stark_low_degree(stark) - } - - #[test] - fn test_recursive_stark_verifier() -> Result<()> { - init_logger(); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - - let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 5; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( - stark, - &config, - trace, - public_inputs, - &mut TimingTree::default(), - )?; - verify_stark_proof(stark, proof.clone(), &config)?; - - recursive_proof::(stark, proof, &config, true) - } - - fn recursive_proof< - F: RichField + Extendable, - C: GenericConfig, - S: Stark + Copy, - InnerC: GenericConfig, - const D: usize, - >( - stark: S, - inner_proof: StarkProofWithPublicInputs, - inner_config: &StarkConfig, - print_gate_counts: bool, - ) -> Result<()> - where - InnerC::Hasher: AlgebraicHasher, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, - [(); C::Hasher::HASH_SIZE]:, - { - let circuit_config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(circuit_config); - let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); - let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); - - recursively_verify_stark_proof::(&mut builder, stark, pt, inner_config); - - if print_gate_counts { - builder.print_gate_counts(0); - } - - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof) - } - - fn init_logger() { - let _ = env_logger::builder().format_timestamp(None).try_init(); - } -} From 5707baee237870bececde90f0df43ea1e95f7a7e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 23 Jun 2022 14:00:56 -0700 Subject: [PATCH 22/46] addressed comments --- evm/src/cpu/columns.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 16c13de6..5523fa7c 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -13,13 +13,10 @@ pub const IS_CPU_CYCLE: usize = IS_BOOTSTRAP_CONTRACT + 1; /// If CPU cycle: The opcode being decoded, in {0, ..., 255}. pub const OPCODE: usize = IS_CPU_CYCLE + 1; -pub const KECCAK_DUMMY: usize = OPCODE + 1; -pub const MEMORY_DUMMY: usize = KECCAK_DUMMY + 1; - // If CPU cycle: flags for EVM instructions. PUSHn, DUPn, and SWAPn only get one flag each. Invalid // opcodes are split between a number of flags for practical reasons. Exactly one of these flags // must be 1. -pub const IS_STOP: usize = MEMORY_DUMMY + 1; +pub const IS_STOP: usize = OPCODE + 1; pub const IS_ADD: usize = IS_STOP + 1; pub const IS_MUL: usize = IS_ADD + 1; pub const IS_SUB: usize = IS_MUL + 1; @@ -147,7 +144,6 @@ pub const OPCODE_BITS: [usize; 8] = [ pub const IS_KECCAK: usize = OPCODE_BITS[OPCODE_BITS.len() - 1] + 1; pub const START_KECCAK_INPUT: usize = IS_KECCAK + 1; -// TODO: Remove when used pub const KECCAK_INPUT_LIMBS: Range = START_KECCAK_INPUT..START_KECCAK_INPUT + 50; pub const START_KECCAK_OUTPUT: usize = KECCAK_INPUT_LIMBS.end; @@ -167,6 +163,7 @@ const NUM_MEMORY_VALUE_LIMBS: usize = 8; pub(crate) const MEMORY_TIMESTAMP: usize = SIMPLE_LOGIC_DIFF_INV + 1; + const USES_MEMOP_START: usize = MEMORY_TIMESTAMP + 1; pub const fn uses_memop(op: usize) -> usize { debug_assert!(op < NUM_MEMORY_OPS); @@ -175,26 +172,32 @@ pub const fn uses_memop(op: usize) -> usize { const MEMOP_ISREAD_START: usize = USES_MEMOP_START + NUM_MEMORY_OPS; pub const fn memop_is_read(op: usize) -> usize { + debug_assert!(op < NUM_MEMORY_OPS); MEMOP_ISREAD_START + op } const MEMOP_ADDR_CONTEXT_START: usize = MEMOP_ISREAD_START + NUM_MEMORY_OPS; pub const fn memop_addr_context(op: usize) -> usize { + debug_assert!(op < NUM_MEMORY_OPS); MEMOP_ADDR_CONTEXT_START + op } const MEMOP_ADDR_SEGMENT_START: usize = MEMOP_ADDR_CONTEXT_START + NUM_MEMORY_OPS; pub const fn memop_addr_segment(op: usize) -> usize { + debug_assert!(op < NUM_MEMORY_OPS); MEMOP_ADDR_SEGMENT_START + op } const MEMOP_ADDR_VIRTUAL_START: usize = MEMOP_ADDR_SEGMENT_START + NUM_MEMORY_OPS; pub const fn memop_addr_virtual(op: usize) -> usize { + debug_assert!(op < NUM_MEMORY_OPS); MEMOP_ADDR_VIRTUAL_START + op } const MEMOP_ADDR_VALUE_START: usize = MEMOP_ADDR_VIRTUAL_START + NUM_MEMORY_OPS; pub const fn memop_value(op: usize, limb: usize) -> usize { + debug_assert!(op < NUM_MEMORY_OPS); + debug_assert!(limb < NUM_MEMORY_VALUE_LIMBS); MEMOP_ADDR_VALUE_START + op * NUM_MEMORY_VALUE_LIMBS + limb } From 939e63189b6242c8660ffef2bf02961f569cfa68 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 16:52:59 -0700 Subject: [PATCH 23/46] rename --- evm/src/cpu/columns.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 5523fa7c..3c90c673 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -161,10 +161,10 @@ pub const SIMPLE_LOGIC_DIFF_INV: usize = SIMPLE_LOGIC_DIFF + 1; const NUM_MEMORY_OPS: usize = 4; const NUM_MEMORY_VALUE_LIMBS: usize = 8; -pub(crate) const MEMORY_TIMESTAMP: usize = SIMPLE_LOGIC_DIFF_INV + 1; +pub(crate) const CLOCK: usize = SIMPLE_LOGIC_DIFF_INV + 1; -const USES_MEMOP_START: usize = MEMORY_TIMESTAMP + 1; +const USES_MEMOP_START: usize = CLOCK + 1; pub const fn uses_memop(op: usize) -> usize { debug_assert!(op < NUM_MEMORY_OPS); USES_MEMOP_START + op From 0514cd9646fa9f182c96dfb7bbd2e5e9672f782d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 16 Jun 2022 23:37:51 -0700 Subject: [PATCH 24/46] addressed comments --- evm/src/cpu/columns.rs | 1 - evm/src/memory/memory_stark.rs | 27 +++++++++------------------ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 3c90c673..296744c4 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -163,7 +163,6 @@ const NUM_MEMORY_VALUE_LIMBS: usize = 8; pub(crate) const CLOCK: usize = SIMPLE_LOGIC_DIFF_INV + 1; - 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/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index d74c65e8..09e3320d 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -124,25 +124,16 @@ pub fn generate_first_change_flags( let mut segment_first_change = Vec::new(); let mut virtual_first_change = Vec::new(); for idx in 0..num_ops - 1 { - let this_context_first_change = if context[idx] != context[idx + 1] { - F::ONE - } else { - F::ZERO - }; - let this_segment_first_change = if segment[idx] != segment[idx + 1] { - F::ONE * (F::ONE - this_context_first_change) - } else { - F::ZERO - }; - let this_virtual_first_change = if virtuals[idx] != virtuals[idx + 1] { - F::ONE * (F::ONE - this_context_first_change) * (F::ONE - this_segment_first_change) - } else { - F::ZERO - }; + let this_context_first_change = context[idx] != context[idx + 1]; + let this_segment_first_change = + segment[idx] != segment[idx + 1] && !this_context_first_change; + let this_virtual_first_change = virtuals[idx] != virtuals[idx + 1] + && !this_segment_first_change + && !this_context_first_change; - context_first_change.push(this_context_first_change); - segment_first_change.push(this_segment_first_change); - virtual_first_change.push(this_virtual_first_change); + context_first_change.push(F::from_bool(this_context_first_change)); + segment_first_change.push(F::from_bool(this_segment_first_change)); + virtual_first_change.push(F::from_bool(this_virtual_first_change)); } context_first_change.push(F::ZERO); From 3898ccd04123cc3427348b1c38afab3ca2711c92 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 16 Jun 2022 23:38:17 -0700 Subject: [PATCH 25/46] okay you win, clippy --- evm/src/memory/memory_stark.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 09e3320d..ae446f42 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -53,7 +53,7 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec<(F, F, F, let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); for i in 0..num_ops { let is_read = if i == 0 { false } else { rng.gen() }; - let is_read_F = F::from_bool(is_read); + let is_read_field = F::from_bool(is_read); let (context, segment, virt, vals) = if is_read { let written: Vec<_> = current_memory_values.keys().collect(); @@ -78,7 +78,7 @@ 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_F, context, segment, virt, vals)) + memory_ops.push((timestamp, is_read_field, context, segment, virt, vals)) } memory_ops From 6884b1aedbb7be65ca2cebe9f1ec0f5ab207b461 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 16 Jun 2022 23:38:56 -0700 Subject: [PATCH 26/46] addressed comment --- evm/src/memory/memory_stark.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index ae446f42..9879a573 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -120,9 +120,9 @@ pub fn generate_first_change_flags( virtuals: &[F], ) -> (Vec, Vec, Vec) { let num_ops = context.len(); - let mut context_first_change = Vec::new(); - let mut segment_first_change = Vec::new(); - let mut virtual_first_change = Vec::new(); + let mut context_first_change = Vec::with_capacity(num_ops); + let mut segment_first_change = Vec::with_capacity(num_ops); + let mut virtual_first_change = Vec::with_capacity(num_ops); for idx in 0..num_ops - 1 { let this_context_first_change = context[idx] != context[idx + 1]; let this_segment_first_change = From dace10a291e659ba65ead0bb5fb8aa83070cd993 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 16 Jun 2022 23:39:32 -0700 Subject: [PATCH 27/46] removed structs --- evm/src/memory/memory_stark.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 9879a573..f05a2749 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -21,23 +21,6 @@ use crate::stark::Stark; use crate::util::{permuted_cols, trace_rows_to_poly_values}; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; -#[derive(Default)] -pub struct TransactionMemory { - pub calls: Vec, -} - -/// A virtual memory space specific to the current contract call. -pub struct ContractMemory { - pub code: MemorySegment, - pub main: MemorySegment, - pub calldata: MemorySegment, - pub returndata: MemorySegment, -} - -pub struct MemorySegment { - pub content: Vec, -} - pub(crate) const NUM_PUBLIC_INPUTS: usize = 0; #[derive(Copy, Clone)] @@ -102,7 +85,7 @@ pub fn sort_memory_ops( ) .collect(); - ops.sort_by_key(|&(t, _, c, s, v, _)| { + ops.sort_unstable_by_key(|&(t, _, c, s, v, _)| { ( c.to_noncanonical_u64(), s.to_noncanonical_u64(), From 08be9811cdca13154152a4bfcb776ea4026ba0af Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 23 Jun 2022 14:01:18 -0700 Subject: [PATCH 28/46] timestamp fixes --- evm/src/all_stark.rs | 2 +- evm/src/cpu/columns.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 85064fe7..1be4c6ea 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -71,12 +71,12 @@ mod tests { use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; - use crate::cpu::columns::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS}; use crate::cpu::cpu_stark::{self as cpu_stark_mod, CpuStark}; use crate::keccak::keccak_stark::{ self as keccak_stark_mod, KeccakStark, NUM_INPUTS, NUM_ROUNDS, }; use crate::logic::{self, LogicStark}; + use crate::cpu::columns::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS, NUM_MEMORY_OPS}; use crate::cross_table_lookup::{Column, CrossTableLookup, TableWithColumns}; use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark}; use crate::proof::AllProof; diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 296744c4..51e47abb 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -158,8 +158,8 @@ pub const LOGIC_OUTPUT: Range = LOGIC_INPUT1.end..LOGIC_INPUT1.end + 16; pub const SIMPLE_LOGIC_DIFF: usize = LOGIC_OUTPUT.end; pub const SIMPLE_LOGIC_DIFF_INV: usize = SIMPLE_LOGIC_DIFF + 1; -const NUM_MEMORY_OPS: usize = 4; -const NUM_MEMORY_VALUE_LIMBS: usize = 8; +pub(crate) const NUM_MEMORY_OPS: usize = 4; +pub(crate) const NUM_MEMORY_VALUE_LIMBS: usize = 8; pub(crate) const CLOCK: usize = SIMPLE_LOGIC_DIFF_INV + 1; From d2eb3b141b28dcff8cee8dc83d2d65e82f9db313 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 11:09:01 -0700 Subject: [PATCH 29/46] addressed comments --- evm/src/cpu/columns.rs | 1 + evm/src/lib.rs | 1 - evm/src/memory/memory_stark.rs | 56 +++++++++++++++++++++++++--------- evm/src/memory/registers.rs | 11 +++++++ 4 files changed, 53 insertions(+), 16 deletions(-) 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); From 9f22cc72c3f7a1ad01e7b410cf7a0f4df265d760 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 11:11:33 -0700 Subject: [PATCH 30/46] allow 'unused' functions --- evm/src/cpu/columns.rs | 2 ++ evm/src/memory/registers.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 31e25884..811a2841 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use std::ops::Range; /// Filter. 1 if the row is part of bootstrapping the kernel code, 0 otherwise. diff --git a/evm/src/memory/registers.rs b/evm/src/memory/registers.rs index b89a9692..2e458cfb 100644 --- a/evm/src/memory/registers.rs +++ b/evm/src/memory/registers.rs @@ -48,6 +48,7 @@ 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; +#[allow(dead_code)] pub(crate) const fn is_memop(i: usize) -> usize { debug_assert!(i < NUM_MEMORY_OPS); IS_MEMOP_START + i From a860eb7727ffbd19795a19ebabd215186eb8397a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 13:11:28 -0700 Subject: [PATCH 31/46] fixes --- evm/src/memory/memory_stark.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index a9ccec0a..07afa34d 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -57,7 +57,9 @@ pub fn generate_random_memory_ops(num_ops: usize) -> Vec, const D: usize> Stark for MemoryStark Date: Fri, 17 Jun 2022 17:06:19 -0700 Subject: [PATCH 32/46] fixes --- evm/src/all_stark.rs | 64 ++++++++++++++++++++++++++++++---- evm/src/memory/memory_stark.rs | 6 ++-- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 1be4c6ea..9e1bcd76 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -60,7 +60,7 @@ impl Table { mod tests { use anyhow::Result; use itertools::{izip, Itertools}; - use plonky2::field::field_types::Field; + use plonky2::field::field_types::{Field, PrimeField64}; use plonky2::field::polynomial::PolynomialValues; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_builder::CircuitBuilder; @@ -136,14 +136,24 @@ mod tests { } trace_rows_to_poly_values(trace_rows) } + + fn make_memory_trace( + num_memory_ops: usize, + memory_stark: &MemoryStark, + rng: &mut R, + ) -> Vec> { + let memory_ops = generate_random_memory_ops(num_memory_ops, rng); + memory_stark.generate_trace(memory_ops) + } fn make_cpu_trace( num_keccak_perms: usize, num_logic_rows: usize, + num_memory_ops: usize, cpu_stark: &CpuStark, keccak_trace: &[PolynomialValues], logic_trace: &[PolynomialValues], - memory_trace: &[PolynomialValues], + memory_trace: &mut [PolynomialValues], ) -> Vec> { let keccak_input_limbs: Vec<[F; 2 * NUM_INPUTS]> = (0..num_keccak_perms) .map(|i| { @@ -257,6 +267,7 @@ mod tests { let keccak_trace = make_keccak_trace(num_keccak_perms, &keccak_stark, &mut rng); let logic_trace = make_logic_trace(num_logic_rows, &logic_stark, &mut rng); + let mut memory_trace = make_memory_trace(num_memory_ops, &memory_stark, &mut rng); let cpu_trace = make_cpu_trace( num_keccak_perms, num_logic_rows, @@ -264,10 +275,9 @@ mod tests { &cpu_stark, &keccak_trace, &logic_trace, + &mut memory_trace, ); - let memory_ops = generate_random_memory_ops(num_memory_ops); - let memory_trace = memory_stark.generate_trace(memory_ops); let mut cpu_keccak_input_output = cpu::columns::KECCAK_INPUT_LIMBS.collect::>(); cpu_keccak_input_output.extend(cpu::columns::KECCAK_OUTPUT_LIMBS); @@ -301,7 +311,34 @@ mod tests { res }; - let cross_table_lookups = vec![ + let cpu_memory_cols: Vec> = (0..NUM_MEMORY_OPS) + .map(|op| { + let mut cols = vec![Column::linear_combination_with_constant( + [(cpu::columns::CLOCK, F::from_canonical_usize(NUM_MEMORY_OPS))], + F::from_canonical_usize(op), + )]; + cols.extend(Column::singles([ + cpu::columns::memop_is_read(op), + cpu::columns::memop_addr_context(op), + cpu::columns::memop_addr_segment(op), + cpu::columns::memop_addr_virtual(op), + ])); + cols.extend(Column::singles( + (0..8).map(|j| cpu::columns::memop_value(op, j)), + )); + cols + }) + .collect(); + let mut memory_memory_cols = vec![ + memory::registers::TIMESTAMP, + memory::registers::IS_READ, + memory::registers::ADDR_CONTEXT, + memory::registers::ADDR_SEGMENT, + memory::registers::ADDR_VIRTUAL, + ]; + memory_memory_cols.extend((0..8).map(memory::registers::value_limb)); + + let mut cross_table_lookups = vec![ CrossTableLookup::new( vec![TableWithColumns::new( Table::Cpu, @@ -325,6 +362,21 @@ mod tests { None, ), ]; + cross_table_lookups.extend((0..NUM_MEMORY_OPS).map(|op| { + CrossTableLookup::new( + vec![TableWithColumns::new( + Table::Cpu, + cpu_memory_cols[op].clone(), + Some(Column::single(cpu::columns::uses_memop(op))), + )], + TableWithColumns::new( + Table::Memory, + Column::singles(memory_memory_cols.clone()).collect(), + Some(Column::single(memory::registers::is_memop(op))), + ), + None, + ) + })); let all_stark = AllStark { cpu_stark, @@ -338,7 +390,7 @@ mod tests { &all_stark, config, vec![cpu_trace, keccak_trace, logic_trace, memory_trace], - vec![vec![]; 3], + vec![vec![]; 4], &mut TimingTree::default(), )?; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 07afa34d..0a75eaae 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -8,7 +8,7 @@ use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; -use rand::{thread_rng, Rng}; +use rand::Rng; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::memory::registers::{ @@ -38,11 +38,9 @@ pub struct MemoryOp { value: [F; 8], } -pub fn generate_random_memory_ops(num_ops: usize) -> Vec> { +pub fn generate_random_memory_ops(num_ops: usize, rng: &mut R) -> Vec> { let mut memory_ops = Vec::new(); - let mut rng = thread_rng(); - let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); for i in 0..num_ops { let is_read = if i == 0 { false } else { rng.gen() }; From 2d7f2b47c2d4f395abaffb0befd77fc4b844080c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 17:06:32 -0700 Subject: [PATCH 33/46] fmt --- evm/src/all_stark.rs | 3 +-- evm/src/memory/memory_stark.rs | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 9e1bcd76..14b4efc1 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -136,7 +136,7 @@ mod tests { } trace_rows_to_poly_values(trace_rows) } - + fn make_memory_trace( num_memory_ops: usize, memory_stark: &MemoryStark, @@ -278,7 +278,6 @@ mod tests { &mut memory_trace, ); - let mut cpu_keccak_input_output = cpu::columns::KECCAK_INPUT_LIMBS.collect::>(); cpu_keccak_input_output.extend(cpu::columns::KECCAK_OUTPUT_LIMBS); let mut keccak_keccak_input_output = (0..2 * NUM_INPUTS) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 0a75eaae..61b4f8d0 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -38,7 +38,10 @@ pub struct MemoryOp { value: [F; 8], } -pub fn generate_random_memory_ops(num_ops: usize, rng: &mut R) -> Vec> { +pub fn generate_random_memory_ops( + num_ops: usize, + rng: &mut R, +) -> Vec> { let mut memory_ops = Vec::new(); let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); From de52e630e8a0ed832c53ad56804f4be60033f721 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 17 Jun 2022 23:32:46 -0700 Subject: [PATCH 34/46] addressed comments --- evm/src/cpu/columns.rs | 2 ++ evm/src/memory/memory_stark.rs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 811a2841..8751398e 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -1,3 +1,4 @@ +// TODO: remove when possible. #![allow(dead_code)] use std::ops::Range; @@ -165,6 +166,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 { diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 61b4f8d0..e7d63df2 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -370,11 +370,11 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark Date: Fri, 17 Jun 2022 23:32:53 -0700 Subject: [PATCH 35/46] fmt --- evm/src/cpu/columns.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/evm/src/cpu/columns.rs b/evm/src/cpu/columns.rs index 8751398e..c603ceb6 100644 --- a/evm/src/cpu/columns.rs +++ b/evm/src/cpu/columns.rs @@ -166,7 +166,6 @@ 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 { From 94348198294549f5ee19b28c8c115e9d474d873c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 21 Jun 2022 14:35:19 -0700 Subject: [PATCH 36/46] addressed comments --- evm/src/all_stark.rs | 7 ++-- evm/src/lib.rs | 1 + evm/src/memory/memory_stark.rs | 50 ++++++++--------------- evm/src/util.rs | 72 +--------------------------------- 4 files changed, 22 insertions(+), 108 deletions(-) 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) -} From 593d3eeffd12af7a543c47fc17038e0b2f5f6038 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 21 Jun 2022 15:49:01 -0700 Subject: [PATCH 37/46] fixes --- evm/src/all_stark.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index a4fce3dd..9fc639ac 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -217,17 +217,14 @@ mod tests { cpu_trace_rows.push(row); } for i in 0..num_memory_ops { - let mem_timestamp: usize = memory_trace[memory::registers::TIMESTAMP].values[i] - .to_canonical_u64() - .try_into() - .unwrap(); + let mem_timestamp = memory_trace[memory::registers::TIMESTAMP].values[i]; 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; - cpu_trace_rows[i][cpu::columns::CLOCK] = F::from_canonical_usize(clock); + cpu_trace_rows[i][cpu::columns::CLOCK] = clock; cpu_trace_rows[i][cpu::columns::memop_is_read(op)] = memory_trace[memory::registers::IS_READ].values[i]; cpu_trace_rows[i][cpu::columns::memop_addr_context(op)] = @@ -313,16 +310,13 @@ mod tests { let cpu_memory_cols: Vec> = (0..NUM_MEMORY_OPS) .map(|op| { - let mut cols = vec![Column::linear_combination_with_constant( - [(cpu::columns::CLOCK, F::from_canonical_usize(NUM_MEMORY_OPS))], - F::from_canonical_usize(op), - )]; - cols.extend(Column::singles([ + let mut cols = Column::singles([ + cpu::columns::CLOCK, cpu::columns::memop_is_read(op), cpu::columns::memop_addr_context(op), cpu::columns::memop_addr_segment(op), cpu::columns::memop_addr_virtual(op), - ])); + ]).collect_vec(); cols.extend(Column::singles( (0..8).map(|j| cpu::columns::memop_value(op, j)), )); From f16db8c5648da957585c3d0b15bd38f749b0afe9 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 21 Jun 2022 15:49:08 -0700 Subject: [PATCH 38/46] fmt --- evm/src/all_stark.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 9fc639ac..148fe528 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -316,7 +316,8 @@ mod tests { cpu::columns::memop_addr_context(op), cpu::columns::memop_addr_segment(op), cpu::columns::memop_addr_virtual(op), - ]).collect_vec(); + ]) + .collect_vec(); cols.extend(Column::singles( (0..8).map(|j| cpu::columns::memop_value(op, j)), )); From f04f2bc381bfe17299b06650a57f4266a2128f66 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 21 Jun 2022 15:55:20 -0700 Subject: [PATCH 39/46] multiple indices per timestamp --- evm/src/memory/memory_stark.rs | 75 +++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 7387653d..244af688 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::marker::PhantomData; use itertools::{izip, multiunzip, Itertools}; @@ -52,41 +52,48 @@ pub fn generate_random_memory_ops( let is_read = if i == 0 { false } else { rng.gen() }; let is_read_field = F::from_bool(is_read); - let (context, segment, virt, vals) = if is_read { - let written: Vec<_> = current_memory_values.keys().collect(); - let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; - let &vals = current_memory_values - .get(&(context, segment, virt)) - .unwrap(); - - (context, segment, virt, vals) - } else { - // TODO: with taller memory table or more padding (to enable range-checking bigger diffs), - // test larger address values. - let context = F::from_canonical_usize(rng.gen_range(0..40)); - let segment = F::from_canonical_usize(rng.gen_range(0..8)); - let virt = F::from_canonical_usize(rng.gen_range(0..20)); - - let val: [u32; 8] = rng.gen(); - let vals: [F; 8] = val.map(F::from_canonical_u32); - - current_memory_values.insert((context, segment, virt), vals); - - (context, segment, virt, vals) - }; - let timestamp = F::from_canonical_usize(i); - let channel_index = rng.gen_range(0..4); + let mut used_indices = HashSet::new(); + for _ in 0..2 { + let mut channel_index = rng.gen_range(0..4); + while used_indices.contains(&channel_index) { + channel_index = rng.gen_range(0..4); + } + used_indices.insert(channel_index); - memory_ops.push(MemoryOp { - channel_index, - timestamp, - is_read: is_read_field, - context, - segment, - virt, - value: vals, - }); + let (context, segment, virt, vals) = if is_read { + let written: Vec<_> = current_memory_values.keys().collect(); + let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; + let &vals = current_memory_values + .get(&(context, segment, virt)) + .unwrap(); + + (context, segment, virt, vals) + } else { + // TODO: with taller memory table or more padding (to enable range-checking bigger diffs), + // test larger address values. + let context = F::from_canonical_usize(rng.gen_range(0..40)); + let segment = F::from_canonical_usize(rng.gen_range(0..8)); + let virt = F::from_canonical_usize(rng.gen_range(0..20)); + + let val: [u32; 8] = rng.gen(); + let vals: [F; 8] = val.map(F::from_canonical_u32); + + current_memory_values.insert((context, segment, virt), vals); + + (context, segment, virt, vals) + }; + + memory_ops.push(MemoryOp { + channel_index, + timestamp, + is_read: is_read_field, + context, + segment, + virt, + value: vals, + }); + } } memory_ops From d6983951a4743be405b7cc21ff6ad8bde13290bf Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Tue, 21 Jun 2022 16:11:00 -0700 Subject: [PATCH 40/46] attempted fix --- evm/src/all_stark.rs | 21 ++++++++++++++------- evm/src/memory/memory_stark.rs | 13 +++++++++---- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 148fe528..b883398a 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -216,6 +216,8 @@ mod tests { cpu_stark.generate(&mut row); cpu_trace_rows.push(row); } + let mut current_cpu_index = 0; + let mut last_timestamp = memory_trace[memory::registers::TIMESTAMP].values[0]; for i in 0..num_memory_ops { let mem_timestamp = memory_trace[memory::registers::TIMESTAMP].values[i]; let clock = mem_timestamp; @@ -223,20 +225,25 @@ mod tests { .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; - cpu_trace_rows[i][cpu::columns::CLOCK] = clock; - cpu_trace_rows[i][cpu::columns::memop_is_read(op)] = + cpu_trace_rows[current_cpu_index][cpu::columns::uses_memop(op)] = F::ONE; + cpu_trace_rows[current_cpu_index][cpu::columns::CLOCK] = clock; + cpu_trace_rows[current_cpu_index][cpu::columns::memop_is_read(op)] = memory_trace[memory::registers::IS_READ].values[i]; - cpu_trace_rows[i][cpu::columns::memop_addr_context(op)] = + cpu_trace_rows[current_cpu_index][cpu::columns::memop_addr_context(op)] = memory_trace[memory::registers::ADDR_CONTEXT].values[i]; - cpu_trace_rows[i][cpu::columns::memop_addr_segment(op)] = + cpu_trace_rows[current_cpu_index][cpu::columns::memop_addr_segment(op)] = memory_trace[memory::registers::ADDR_SEGMENT].values[i]; - cpu_trace_rows[i][cpu::columns::memop_addr_virtual(op)] = + cpu_trace_rows[current_cpu_index][cpu::columns::memop_addr_virtual(op)] = memory_trace[memory::registers::ADDR_VIRTUAL].values[i]; for j in 0..8 { - cpu_trace_rows[i][cpu::columns::memop_value(op, j)] = + cpu_trace_rows[current_cpu_index][cpu::columns::memop_value(op, j)] = memory_trace[memory::registers::value_limb(j)].values[i]; } + + if mem_timestamp != last_timestamp { + current_cpu_index += 1; + last_timestamp = mem_timestamp; + } } trace_rows_to_poly_values(cpu_trace_rows) } diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 244af688..4de666d2 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -49,11 +49,9 @@ pub fn generate_random_memory_ops( let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); for i in 0..num_ops { - let is_read = if i == 0 { false } else { rng.gen() }; - let is_read_field = F::from_bool(is_read); - let timestamp = F::from_canonical_usize(i); let mut used_indices = HashSet::new(); + let mut new_writes_this_cycle = HashSet::new(); for _ in 0..2 { let mut channel_index = rng.gen_range(0..4); while used_indices.contains(&channel_index) { @@ -61,6 +59,9 @@ pub fn generate_random_memory_ops( } used_indices.insert(channel_index); + let is_read = if i == 0 { false } else { rng.gen() }; + let is_read_field = F::from_bool(is_read); + let (context, segment, virt, vals) = if is_read { let written: Vec<_> = current_memory_values.keys().collect(); let &(context, segment, virt) = written[rng.gen_range(0..written.len())]; @@ -79,7 +80,7 @@ pub fn generate_random_memory_ops( let val: [u32; 8] = rng.gen(); let vals: [F; 8] = val.map(F::from_canonical_u32); - current_memory_values.insert((context, segment, virt), vals); + new_writes_this_cycle.insert(((context, segment, virt), vals)); (context, segment, virt, vals) }; @@ -94,6 +95,10 @@ pub fn generate_random_memory_ops( value: vals, }); } + for (k, v) in new_writes_this_cycle { + current_memory_values.insert(k, v); + } + } memory_ops From 29fa32465cafe7a238b00fd1addc6d9485c31ce2 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 22 Jun 2022 12:24:52 -0700 Subject: [PATCH 41/46] fixes --- evm/src/all_stark.rs | 38 ++++++++++++++++++++-------------- evm/src/memory/memory_stark.rs | 15 +++++++------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index b883398a..f6010f86 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -224,6 +224,16 @@ mod tests { let op = (0..4) .filter(|&o| memory_trace[memory::registers::is_memop(o)].values[i] == F::ONE) .collect_vec()[0]; + + if mem_timestamp != last_timestamp { + current_cpu_index += 1; + last_timestamp = mem_timestamp; + } + + dbg!(i); + dbg!(mem_timestamp); + dbg!(current_cpu_index); + dbg!(op); cpu_trace_rows[current_cpu_index][cpu::columns::uses_memop(op)] = F::ONE; cpu_trace_rows[current_cpu_index][cpu::columns::CLOCK] = clock; @@ -240,10 +250,6 @@ mod tests { memory_trace[memory::registers::value_limb(j)].values[i]; } - if mem_timestamp != last_timestamp { - current_cpu_index += 1; - last_timestamp = mem_timestamp; - } } trace_rows_to_poly_values(cpu_trace_rows) } @@ -318,27 +324,27 @@ mod tests { let cpu_memory_cols: Vec> = (0..NUM_MEMORY_OPS) .map(|op| { let mut cols = Column::singles([ - cpu::columns::CLOCK, + // cpu::columns::CLOCK, cpu::columns::memop_is_read(op), - cpu::columns::memop_addr_context(op), - cpu::columns::memop_addr_segment(op), - cpu::columns::memop_addr_virtual(op), + // cpu::columns::memop_addr_context(op), + // cpu::columns::memop_addr_segment(op), + // cpu::columns::memop_addr_virtual(op), ]) .collect_vec(); - cols.extend(Column::singles( - (0..8).map(|j| cpu::columns::memop_value(op, j)), - )); + // cols.extend(Column::singles( + // (0..8).map(|j| cpu::columns::memop_value(op, j)), + // )); cols }) .collect(); let mut memory_memory_cols = vec![ - memory::registers::TIMESTAMP, + // memory::registers::TIMESTAMP, memory::registers::IS_READ, - memory::registers::ADDR_CONTEXT, - memory::registers::ADDR_SEGMENT, - memory::registers::ADDR_VIRTUAL, + // memory::registers::ADDR_CONTEXT, + // memory::registers::ADDR_SEGMENT, + // memory::registers::ADDR_VIRTUAL, ]; - memory_memory_cols.extend((0..8).map(memory::registers::value_limb)); + // memory_memory_cols.extend((0..8).map(memory::registers::value_limb)); let mut cross_table_lookups = vec![ CrossTableLookup::new( diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 4de666d2..feecc19c 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -50,14 +50,15 @@ pub fn generate_random_memory_ops( let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); for i in 0..num_ops { let timestamp = F::from_canonical_usize(i); - let mut used_indices = HashSet::new(); + // let mut used_indices = HashSet::new(); let mut new_writes_this_cycle = HashSet::new(); - for _ in 0..2 { - let mut channel_index = rng.gen_range(0..4); - while used_indices.contains(&channel_index) { - channel_index = rng.gen_range(0..4); - } - used_indices.insert(channel_index); + for _ in 0..1 { + // let mut channel_index = rng.gen_range(0..4); + // while used_indices.contains(&channel_index) { + // channel_index = rng.gen_range(0..4); + // } + // used_indices.insert(channel_index); + let channel_index = rng.gen_range(0..4); let is_read = if i == 0 { false } else { rng.gen() }; let is_read_field = F::from_bool(is_read); From 798b01d050286357dcddd63360ab8de0e3900f00 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 22 Jun 2022 12:34:58 -0700 Subject: [PATCH 42/46] added lookup file --- evm/src/lookup.rs | 137 +++++++++++++++++++++++++++++++++ evm/src/memory/memory_stark.rs | 15 ++-- 2 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 evm/src/lookup.rs diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs new file mode 100644 index 00000000..70f1467b --- /dev/null +++ b/evm/src/lookup.rs @@ -0,0 +1,137 @@ +use std::cmp::Ordering; + +use itertools::Itertools; +use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::{Field, PrimeField64}; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +pub(crate) fn eval_lookups< + F: Field, + P: PackedField, + const COLS: usize, + const PUB_INPUTS: usize, +>( + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + col_permuted_input: usize, + col_permuted_table: usize, +) { + let local_perm_input = vars.local_values[col_permuted_input]; + let next_perm_table = vars.next_values[col_permuted_table]; + let next_perm_input = vars.next_values[col_permuted_input]; + + // A "vertical" diff between the local and next permuted inputs. + let diff_input_prev = next_perm_input - local_perm_input; + // A "horizontal" diff between the next permuted input and permuted table value. + let diff_input_table = next_perm_input - next_perm_table; + + yield_constr.constraint(diff_input_prev * diff_input_table); + + // This is actually constraining the first row, as per the spec, since `diff_input_table` + // is a diff of the next row's values. In the context of `constraint_last_row`, the next + // row is the first row. + yield_constr.constraint_last_row(diff_input_table); +} + +pub(crate) fn eval_lookups_circuit< + F: RichField + Extendable, + const D: usize, + const COLS: usize, + const PUB_INPUTS: usize, +>( + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + col_permuted_input: usize, + col_permuted_table: usize, +) { + let local_perm_input = vars.local_values[col_permuted_input]; + let next_perm_table = vars.next_values[col_permuted_table]; + let next_perm_input = vars.next_values[col_permuted_input]; + + // A "vertical" diff between the local and next permuted inputs. + let diff_input_prev = builder.sub_extension(next_perm_input, local_perm_input); + // A "horizontal" diff between the next permuted input and permuted table value. + let diff_input_table = builder.sub_extension(next_perm_input, next_perm_table); + + let diff_product = builder.mul_extension(diff_input_prev, diff_input_table); + yield_constr.constraint(builder, diff_product); + + // This is actually constraining the first row, as per the spec, since `diff_input_table` + // is a diff of the next row's values. In the context of `constraint_last_row`, the next + // row is the first row. + yield_constr.constraint_last_row(builder, diff_input_table); +} + +/// 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) +} diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index feecc19c..4de666d2 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -50,15 +50,14 @@ pub fn generate_random_memory_ops( let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); for i in 0..num_ops { let timestamp = F::from_canonical_usize(i); - // let mut used_indices = HashSet::new(); + let mut used_indices = HashSet::new(); let mut new_writes_this_cycle = HashSet::new(); - for _ in 0..1 { - // let mut channel_index = rng.gen_range(0..4); - // while used_indices.contains(&channel_index) { - // channel_index = rng.gen_range(0..4); - // } - // used_indices.insert(channel_index); - let channel_index = rng.gen_range(0..4); + for _ in 0..2 { + let mut channel_index = rng.gen_range(0..4); + while used_indices.contains(&channel_index) { + channel_index = rng.gen_range(0..4); + } + used_indices.insert(channel_index); let is_read = if i == 0 { false } else { rng.gen() }; let is_read_field = F::from_bool(is_read); From d911eecd37ae2252e318b8b8f5f05024f91f0bdd Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 23 Jun 2022 11:02:17 -0700 Subject: [PATCH 43/46] fixes --- evm/src/all_stark.rs | 33 ++++++++++------------ evm/src/memory/memory_stark.rs | 50 +++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index f6010f86..4ac6ebbd 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -229,11 +229,6 @@ mod tests { current_cpu_index += 1; last_timestamp = mem_timestamp; } - - dbg!(i); - dbg!(mem_timestamp); - dbg!(current_cpu_index); - dbg!(op); cpu_trace_rows[current_cpu_index][cpu::columns::uses_memop(op)] = F::ONE; cpu_trace_rows[current_cpu_index][cpu::columns::CLOCK] = clock; @@ -323,28 +318,28 @@ mod tests { let cpu_memory_cols: Vec> = (0..NUM_MEMORY_OPS) .map(|op| { - let mut cols = Column::singles([ - // cpu::columns::CLOCK, + let mut cols: Vec> = Column::singles([ + cpu::columns::CLOCK, cpu::columns::memop_is_read(op), - // cpu::columns::memop_addr_context(op), - // cpu::columns::memop_addr_segment(op), - // cpu::columns::memop_addr_virtual(op), + cpu::columns::memop_addr_context(op), + cpu::columns::memop_addr_segment(op), + cpu::columns::memop_addr_virtual(op), ]) .collect_vec(); - // cols.extend(Column::singles( - // (0..8).map(|j| cpu::columns::memop_value(op, j)), - // )); + cols.extend(Column::singles( + (0..8).map(|j| cpu::columns::memop_value(op, j)), + )); cols }) .collect(); let mut memory_memory_cols = vec![ - // memory::registers::TIMESTAMP, + memory::registers::TIMESTAMP, memory::registers::IS_READ, - // memory::registers::ADDR_CONTEXT, - // memory::registers::ADDR_SEGMENT, - // memory::registers::ADDR_VIRTUAL, + memory::registers::ADDR_CONTEXT, + memory::registers::ADDR_SEGMENT, + memory::registers::ADDR_VIRTUAL, ]; - // memory_memory_cols.extend((0..8).map(memory::registers::value_limb)); + memory_memory_cols.extend((0..8).map(memory::registers::value_limb)); let mut cross_table_lookups = vec![ CrossTableLookup::new( @@ -370,7 +365,7 @@ mod tests { None, ), ]; - cross_table_lookups.extend((0..NUM_MEMORY_OPS).map(|op| { + cross_table_lookups.extend((0..1).map(|op| { CrossTableLookup::new( vec![TableWithColumns::new( Table::Cpu, diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 4de666d2..1daf5319 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -48,10 +48,11 @@ pub fn generate_random_memory_ops( let mut memory_ops = Vec::new(); let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); - for i in 0..num_ops { + let num_cycles = num_ops / 2; + for i in 0..num_cycles { let timestamp = F::from_canonical_usize(i); let mut used_indices = HashSet::new(); - let mut new_writes_this_cycle = HashSet::new(); + let mut new_writes_this_cycle = HashMap::new(); for _ in 0..2 { let mut channel_index = rng.gen_range(0..4); while used_indices.contains(&channel_index) { @@ -73,14 +74,19 @@ pub fn generate_random_memory_ops( } else { // TODO: with taller memory table or more padding (to enable range-checking bigger diffs), // test larger address values. - let context = F::from_canonical_usize(rng.gen_range(0..40)); - let segment = F::from_canonical_usize(rng.gen_range(0..8)); - let virt = F::from_canonical_usize(rng.gen_range(0..20)); + let mut context = F::from_canonical_usize(rng.gen_range(0..40)); + let mut segment = F::from_canonical_usize(rng.gen_range(0..8)); + let mut virt = F::from_canonical_usize(rng.gen_range(0..20)); + while new_writes_this_cycle.contains_key(&(context, segment, virt)) { + context = F::from_canonical_usize(rng.gen_range(0..40)); + segment = F::from_canonical_usize(rng.gen_range(0..8)); + virt = F::from_canonical_usize(rng.gen_range(0..20)); + } let val: [u32; 8] = rng.gen(); let vals: [F; 8] = val.map(F::from_canonical_u32); - new_writes_this_cycle.insert(((context, segment, virt), vals)); + new_writes_this_cycle.insert((context, segment, virt), vals); (context, segment, virt, vals) }; @@ -372,33 +378,33 @@ impl, const D: usize> Stark for MemoryStark Date: Thu, 23 Jun 2022 11:52:27 -0700 Subject: [PATCH 44/46] fix and cleanup --- evm/src/all_stark.rs | 3 +-- evm/src/memory/memory_stark.rs | 41 +++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 4ac6ebbd..10f7acfa 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -224,7 +224,7 @@ mod tests { let op = (0..4) .filter(|&o| memory_trace[memory::registers::is_memop(o)].values[i] == F::ONE) .collect_vec()[0]; - + if mem_timestamp != last_timestamp { current_cpu_index += 1; last_timestamp = mem_timestamp; @@ -244,7 +244,6 @@ mod tests { cpu_trace_rows[current_cpu_index][cpu::columns::memop_value(op, j)] = memory_trace[memory::registers::value_limb(j)].values[i]; } - } trace_rows_to_poly_values(cpu_trace_rows) } diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 1daf5319..2b5c54b1 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -53,6 +53,7 @@ pub fn generate_random_memory_ops( let timestamp = F::from_canonical_usize(i); let mut used_indices = HashSet::new(); let mut new_writes_this_cycle = HashMap::new(); + let mut has_read = false; for _ in 0..2 { let mut channel_index = rng.gen_range(0..4); while used_indices.contains(&channel_index) { @@ -60,7 +61,12 @@ pub fn generate_random_memory_ops( } used_indices.insert(channel_index); - let is_read = if i == 0 { false } else { rng.gen() }; + let is_read = if i == 0 { + false + } else { + !has_read && rng.gen() + }; + has_read = has_read || is_read; let is_read_field = F::from_bool(is_read); let (context, segment, virt, vals) = if is_read { @@ -104,7 +110,6 @@ pub fn generate_random_memory_ops( for (k, v) in new_writes_this_cycle { current_memory_values.insert(k, v); } - } memory_ops @@ -378,33 +383,33 @@ impl, const D: usize> Stark for MemoryStark Date: Thu, 23 Jun 2022 11:52:58 -0700 Subject: [PATCH 45/46] cleanup --- evm/src/all_stark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 10f7acfa..ba6ae397 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -60,7 +60,7 @@ impl Table { mod tests { use anyhow::Result; use itertools::{izip, Itertools}; - use plonky2::field::field_types::{Field, PrimeField64}; + use plonky2::field::field_types::Field; use plonky2::field::polynomial::PolynomialValues; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_builder::CircuitBuilder; From 4a7ebf057b03b8533698d92564b24d97eb6b5911 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 23 Jun 2022 14:36:14 -0700 Subject: [PATCH 46/46] updated in line with main changes --- evm/src/all_stark.rs | 75 +++++----------------------------- evm/src/cpu/cpu_stark.rs | 24 ++++++++--- evm/src/memory/memory_stark.rs | 13 ++++++ 3 files changed, 42 insertions(+), 70 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index ba6ae397..0a8aee50 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -71,14 +71,16 @@ mod tests { use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; + use crate::cpu::columns::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS, NUM_MEMORY_OPS}; use crate::cpu::cpu_stark::{self as cpu_stark_mod, CpuStark}; + use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns}; use crate::keccak::keccak_stark::{ self as keccak_stark_mod, KeccakStark, NUM_INPUTS, NUM_ROUNDS, }; use crate::logic::{self, LogicStark}; - use crate::cpu::columns::{KECCAK_INPUT_LIMBS, KECCAK_OUTPUT_LIMBS, NUM_MEMORY_OPS}; - use crate::cross_table_lookup::{Column, CrossTableLookup, TableWithColumns}; - use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark}; + use crate::memory::memory_stark::{ + self as memory_stark_mod, generate_random_memory_ops, MemoryStark, + }; use crate::proof::AllProof; use crate::prover::prove; use crate::recursive_verifier::{ @@ -283,63 +285,6 @@ mod tests { &mut memory_trace, ); - let mut cpu_keccak_input_output = cpu::columns::KECCAK_INPUT_LIMBS.collect::>(); - cpu_keccak_input_output.extend(cpu::columns::KECCAK_OUTPUT_LIMBS); - let mut keccak_keccak_input_output = (0..2 * NUM_INPUTS) - .map(keccak::registers::reg_input_limb) - .collect::>(); - keccak_keccak_input_output.extend(Column::singles( - (0..2 * NUM_INPUTS).map(keccak::registers::reg_output_limb), - )); - - let cpu_logic_input_output = { - let mut res = vec![ - cpu::columns::IS_AND, - cpu::columns::IS_OR, - cpu::columns::IS_XOR, - ]; - res.extend(cpu::columns::LOGIC_INPUT0); - res.extend(cpu::columns::LOGIC_INPUT1); - res.extend(cpu::columns::LOGIC_OUTPUT); - res - }; - let logic_logic_input_output = { - let mut res = vec![ - logic::columns::IS_AND, - logic::columns::IS_OR, - logic::columns::IS_XOR, - ]; - res.extend(logic::columns::INPUT0_PACKED); - res.extend(logic::columns::INPUT1_PACKED); - res.extend(logic::columns::RESULT); - res - }; - - let cpu_memory_cols: Vec> = (0..NUM_MEMORY_OPS) - .map(|op| { - let mut cols: Vec> = Column::singles([ - cpu::columns::CLOCK, - cpu::columns::memop_is_read(op), - cpu::columns::memop_addr_context(op), - cpu::columns::memop_addr_segment(op), - cpu::columns::memop_addr_virtual(op), - ]) - .collect_vec(); - cols.extend(Column::singles( - (0..8).map(|j| cpu::columns::memop_value(op, j)), - )); - cols - }) - .collect(); - let mut memory_memory_cols = vec![ - memory::registers::TIMESTAMP, - memory::registers::IS_READ, - memory::registers::ADDR_CONTEXT, - memory::registers::ADDR_SEGMENT, - memory::registers::ADDR_VIRTUAL, - ]; - memory_memory_cols.extend((0..8).map(memory::registers::value_limb)); - let mut cross_table_lookups = vec![ CrossTableLookup::new( vec![TableWithColumns::new( @@ -364,17 +309,17 @@ mod tests { None, ), ]; - cross_table_lookups.extend((0..1).map(|op| { + cross_table_lookups.extend((0..NUM_MEMORY_OPS).map(|op| { CrossTableLookup::new( vec![TableWithColumns::new( Table::Cpu, - cpu_memory_cols[op].clone(), - Some(Column::single(cpu::columns::uses_memop(op))), + cpu_stark_mod::ctl_data_memory(op), + Some(cpu_stark_mod::ctl_filter_memory(op)), )], TableWithColumns::new( Table::Memory, - Column::singles(memory_memory_cols.clone()).collect(), - Some(Column::single(memory::registers::is_memop(op))), + memory_stark_mod::ctl_data(), + Some(memory_stark_mod::ctl_filter(op)), ), None, ) diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 0cb5e2bf..4b4d47d9 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -1,5 +1,6 @@ use std::marker::PhantomData; +use itertools::Itertools; use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::field::packed_field::PackedField; @@ -23,11 +24,7 @@ pub fn ctl_filter_keccak() -> Column { } pub fn ctl_data_logic() -> Vec> { - let mut res = vec![ - Column::single(columns::IS_AND), - Column::single(columns::IS_OR), - Column::single(columns::IS_XOR), - ]; + let mut res = Column::singles([columns::IS_AND, columns::IS_OR, columns::IS_XOR]).collect_vec(); res.extend(columns::LOGIC_INPUT0.map(Column::single)); res.extend(columns::LOGIC_INPUT1.map(Column::single)); res.extend(columns::LOGIC_OUTPUT.map(Column::single)); @@ -38,6 +35,23 @@ pub fn ctl_filter_logic() -> Column { Column::sum([columns::IS_AND, columns::IS_OR, columns::IS_XOR]) } +pub fn ctl_data_memory(op: usize) -> Vec> { + let mut cols: Vec> = Column::singles([ + columns::CLOCK, + columns::memop_is_read(op), + columns::memop_addr_context(op), + columns::memop_addr_segment(op), + columns::memop_addr_virtual(op), + ]) + .collect_vec(); + cols.extend(Column::singles((0..8).map(|j| columns::memop_value(op, j)))); + cols +} + +pub fn ctl_filter_memory(op: usize) -> Column { + Column::single(columns::uses_memop(op)) +} + #[derive(Copy, Clone)] pub struct CpuStark { pub f: PhantomData, diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 2b5c54b1..db0a2b12 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -3,6 +3,7 @@ use std::marker::PhantomData; use itertools::{izip, multiunzip, Itertools}; use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::field_types::Field; use plonky2::field::packed_field::PackedField; use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; @@ -12,6 +13,7 @@ use rand::Rng; use super::registers::is_memop; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookup::Column; 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, @@ -26,6 +28,17 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub(crate) const NUM_PUBLIC_INPUTS: usize = 0; +pub fn ctl_data() -> Vec> { + let mut res = Column::singles([TIMESTAMP, IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) + .collect_vec(); + res.extend(Column::singles((0..8).map(value_limb))); + res +} + +pub fn ctl_filter(op: usize) -> Column { + Column::single(is_memop(op)) +} + #[derive(Copy, Clone)] pub struct MemoryStark { pub(crate) f: PhantomData,