From 508d4788726aa816f4637e76f84895e26ad7a008 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 8 Jul 2022 23:12:29 -0400 Subject: [PATCH 01/30] arithmetic_u32::tests: Extract get_wires() from test_gate_constraint(). Use F::from_noncanonical_u64 for addend, so that we can reuse get_wires in our canonicity check test. --- u32/src/gates/arithmetic_u32.rs | 95 ++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/u32/src/gates/arithmetic_u32.rs b/u32/src/gates/arithmetic_u32.rs index c46c9e47..d2a3860e 100644 --- a/u32/src/gates/arithmetic_u32.rs +++ b/u32/src/gates/arithmetic_u32.rs @@ -347,8 +347,10 @@ mod tests { use plonky2::gates::gate::Gate; use plonky2::gates::gate_testing::{test_eval_fns, test_low_degree}; use plonky2::hash::hash_types::HashOut; + use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use plonky2::plonk::vars::EvaluationVars; + use plonky2_field::extension::Extendable; use plonky2_field::goldilocks_field::GoldilocksField; use plonky2_field::types::Field; use rand::Rng; @@ -374,6 +376,52 @@ mod tests { }) } + fn get_wires< + F: RichField + Extendable, + FF: From, + const D: usize, + const NUM_U32_ARITHMETIC_OPS: usize, + >( + multiplicands_0: Vec, + multiplicands_1: Vec, + addends: Vec, + ) -> Vec { + let mut v0 = Vec::new(); + let mut v1 = Vec::new(); + + let limb_bits = U32ArithmeticGate::::limb_bits(); + let num_limbs = U32ArithmeticGate::::num_limbs(); + let limb_base = 1 << limb_bits; + for c in 0..NUM_U32_ARITHMETIC_OPS { + let m0 = multiplicands_0[c]; + let m1 = multiplicands_1[c]; + let a = addends[c]; + + let mut output = m0 * m1 + a; + let output_low = output & ((1 << 32) - 1); + let output_high = output >> 32; + + let mut output_limbs = Vec::with_capacity(num_limbs); + for _i in 0..num_limbs { + output_limbs.push(output % limb_base); + output /= limb_base; + } + let mut output_limbs_f: Vec<_> = output_limbs + .into_iter() + .map(F::from_canonical_u64) + .collect(); + + v0.push(F::from_canonical_u64(m0)); + v0.push(F::from_canonical_u64(m1)); + v0.push(F::from_noncanonical_u64(a)); + v0.push(F::from_canonical_u64(output_low)); + v0.push(F::from_canonical_u64(output_high)); + v1.append(&mut output_limbs_f); + } + + v0.iter().chain(v1.iter()).map(|&x| x.into()).collect() + } + #[test] fn test_gate_constraint() { const D: usize = 2; @@ -382,47 +430,6 @@ mod tests { type FF = >::FE; const NUM_U32_ARITHMETIC_OPS: usize = 3; - fn get_wires( - multiplicands_0: Vec, - multiplicands_1: Vec, - addends: Vec, - ) -> Vec { - let mut v0 = Vec::new(); - let mut v1 = Vec::new(); - - let limb_bits = U32ArithmeticGate::::limb_bits(); - let num_limbs = U32ArithmeticGate::::num_limbs(); - let limb_base = 1 << limb_bits; - for c in 0..NUM_U32_ARITHMETIC_OPS { - let m0 = multiplicands_0[c]; - let m1 = multiplicands_1[c]; - let a = addends[c]; - - let mut output = m0 * m1 + a; - let output_low = output & ((1 << 32) - 1); - let output_high = output >> 32; - - let mut output_limbs = Vec::with_capacity(num_limbs); - for _i in 0..num_limbs { - output_limbs.push(output % limb_base); - output /= limb_base; - } - let mut output_limbs_f: Vec<_> = output_limbs - .into_iter() - .map(F::from_canonical_u64) - .collect(); - - v0.push(F::from_canonical_u64(m0)); - v0.push(F::from_canonical_u64(m1)); - v0.push(F::from_canonical_u64(a)); - v0.push(F::from_canonical_u64(output_low)); - v0.push(F::from_canonical_u64(output_high)); - v1.append(&mut output_limbs_f); - } - - v0.iter().chain(v1.iter()).map(|&x| x.into()).collect() - } - let mut rng = rand::thread_rng(); let multiplicands_0: Vec<_> = (0..NUM_U32_ARITHMETIC_OPS) .map(|_| rng.gen::() as u64) @@ -441,7 +448,11 @@ mod tests { let vars = EvaluationVars { local_constants: &[], - local_wires: &get_wires(multiplicands_0, multiplicands_1, addends), + local_wires: &get_wires::( + multiplicands_0, + multiplicands_1, + addends, + ), public_inputs_hash: &HashOut::rand(), }; From fbffd60212601ed5a423eb3ac7d20929a4964009 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sat, 9 Jul 2022 06:21:11 -0400 Subject: [PATCH 02/30] arithmetic_u32::tests: Add test_canonicity check. This test should fail because its output is the non-canonical value p = (u32::MAX, 1). However, since the U32ArithmeticGate currently permits non-canonical outputs, this test passes. --- u32/src/gates/arithmetic_u32.rs | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/u32/src/gates/arithmetic_u32.rs b/u32/src/gates/arithmetic_u32.rs index d2a3860e..ccbdcacf 100644 --- a/u32/src/gates/arithmetic_u32.rs +++ b/u32/src/gates/arithmetic_u32.rs @@ -461,4 +461,39 @@ mod tests { "Gate constraints are not satisfied." ); } + + #[test] + fn test_canonicity() { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type FF = >::FE; + const NUM_U32_ARITHMETIC_OPS: usize = 3; + + let multiplicands_0 = vec![0; NUM_U32_ARITHMETIC_OPS]; + let multiplicands_1 = vec![0; NUM_U32_ARITHMETIC_OPS]; + // A non-canonical addend will produce a non-canonical output using + // get_wires. + let addends = vec![0xFFFFFFFF00000001; NUM_U32_ARITHMETIC_OPS]; + + let gate = U32ArithmeticGate:: { + num_ops: NUM_U32_ARITHMETIC_OPS, + _phantom: PhantomData, + }; + + let vars = EvaluationVars { + local_constants: &[], + local_wires: &get_wires::( + multiplicands_0, + multiplicands_1, + addends, + ), + public_inputs_hash: &HashOut::rand(), + }; + + assert!( + !gate.eval_unfiltered(vars).iter().all(|x| x.is_zero()), + "Non-canonical output should not pass constraints." + ); + } } From 2c48b117ede41d859d8161a4f6fc899976b57d72 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Sat, 9 Jul 2022 06:06:12 -0400 Subject: [PATCH 03/30] arithmetic_u32: Introduce Self::routed_wires_per_op() method. This removes the use of hard-coded 5 everywhere. --- u32/src/gates/arithmetic_u32.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/u32/src/gates/arithmetic_u32.rs b/u32/src/gates/arithmetic_u32.rs index ccbdcacf..4cd57fed 100644 --- a/u32/src/gates/arithmetic_u32.rs +++ b/u32/src/gates/arithmetic_u32.rs @@ -36,31 +36,30 @@ impl, const D: usize> U32ArithmeticGate { } pub(crate) fn num_ops(config: &CircuitConfig) -> usize { - let wires_per_op = 5 + Self::num_limbs(); - let routed_wires_per_op = 5; - (config.num_wires / wires_per_op).min(config.num_routed_wires / routed_wires_per_op) + let wires_per_op = Self::routed_wires_per_op() + Self::num_limbs(); + (config.num_wires / wires_per_op).min(config.num_routed_wires / Self::routed_wires_per_op()) } pub fn wire_ith_multiplicand_0(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); - 5 * i + Self::routed_wires_per_op() * i } pub fn wire_ith_multiplicand_1(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); - 5 * i + 1 + Self::routed_wires_per_op() * i + 1 } pub fn wire_ith_addend(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); - 5 * i + 2 + Self::routed_wires_per_op() * i + 2 } pub fn wire_ith_output_low_half(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); - 5 * i + 3 + Self::routed_wires_per_op() * i + 3 } pub fn wire_ith_output_high_half(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); - 5 * i + 4 + Self::routed_wires_per_op() * i + 4 } pub fn limb_bits() -> usize { @@ -70,10 +69,14 @@ impl, const D: usize> U32ArithmeticGate { 64 / Self::limb_bits() } + pub fn routed_wires_per_op() -> usize { + 5 + } + pub fn wire_ith_output_jth_limb(&self, i: usize, j: usize) -> usize { debug_assert!(i < self.num_ops); debug_assert!(j < Self::num_limbs()); - 5 * self.num_ops + Self::num_limbs() * i + j + Self::routed_wires_per_op() * self.num_ops + Self::num_limbs() * i + j } } @@ -211,7 +214,7 @@ impl, const D: usize> Gate for U32ArithmeticG } fn num_wires(&self) -> usize { - self.num_ops * (5 + Self::num_limbs()) + self.num_ops * (Self::routed_wires_per_op() + Self::num_limbs()) } fn num_constants(&self) -> usize { From 5bf545c5b51eb5fc70939c737f4ed8eaa5d1ca85 Mon Sep 17 00:00:00 2001 From: therealyingtong Date: Fri, 8 Jul 2022 23:07:30 -0400 Subject: [PATCH 04/30] arithmetic_u32: Introduce canonicity check. --- u32/src/gates/arithmetic_u32.rs | 96 +++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/u32/src/gates/arithmetic_u32.rs b/u32/src/gates/arithmetic_u32.rs index 4cd57fed..c05ed86c 100644 --- a/u32/src/gates/arithmetic_u32.rs +++ b/u32/src/gates/arithmetic_u32.rs @@ -57,22 +57,26 @@ impl, const D: usize> U32ArithmeticGate { debug_assert!(i < self.num_ops); Self::routed_wires_per_op() * i + 3 } + pub fn wire_ith_output_high_half(&self, i: usize) -> usize { debug_assert!(i < self.num_ops); Self::routed_wires_per_op() * i + 4 } + pub fn wire_ith_inverse(&self, i: usize) -> usize { + debug_assert!(i < self.num_ops); + Self::routed_wires_per_op() * i + 5 + } + pub fn limb_bits() -> usize { 2 } pub fn num_limbs() -> usize { 64 / Self::limb_bits() } - pub fn routed_wires_per_op() -> usize { - 5 + 6 } - pub fn wire_ith_output_jth_limb(&self, i: usize, j: usize) -> usize { debug_assert!(i < self.num_ops); debug_assert!(j < Self::num_limbs()); @@ -96,9 +100,28 @@ impl, const D: usize> Gate for U32ArithmeticG let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + let inverse = vars.local_wires[self.wire_ith_inverse(i)]; - let base = F::Extension::from_canonical_u64(1 << 32u64); - let combined_output = output_high * base + output_low; + // Check canonicity of combined_output = output_high * 2^32 + output_low + let combined_output = { + let base = F::Extension::from_canonical_u64(1 << 32u64); + let one = F::Extension::ONE; + let u32_max = F::Extension::from_canonical_u32(u32::MAX); + + // This is zero if and only if the high limb is `u32::MAX`. + // u32::MAX - output_high + let diff = u32_max - output_high; + // If this is zero, the diff is invertible, so the high limb is not `u32::MAX`. + // inverse * diff - 1 + let hi_not_max = inverse * diff - one; + // If this is zero, either the high limb is not `u32::MAX`, or the low limb is zero. + // hi_not_max * limb_0_u32 + let hi_not_max_or_lo_zero = hi_not_max * output_low; + + constraints.push(hi_not_max_or_lo_zero); + + output_high * base + output_low + }; constraints.push(combined_output - computed_output); @@ -155,10 +178,27 @@ impl, const D: usize> Gate for U32ArithmeticG let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + let inverse = vars.local_wires[self.wire_ith_inverse(i)]; - let base: F::Extension = F::from_canonical_u64(1 << 32u64).into(); - let base_target = builder.constant_extension(base); - let combined_output = builder.mul_add_extension(output_high, base_target, output_low); + // Check canonicity of combined_output = output_high * 2^32 + output_low + let combined_output = { + let base: F::Extension = F::from_canonical_u64(1 << 32u64).into(); + let base_target = builder.constant_extension(base); + let one = builder.one_extension(); + let u32_max = + builder.constant_extension(F::Extension::from_canonical_u32(u32::MAX)); + + // This is zero if and only if the high limb is `u32::MAX`. + let diff = builder.sub_extension(u32_max, output_high); + // If this is zero, the diff is invertible, so the high limb is not `u32::MAX`. + let hi_not_max = builder.mul_sub_extension(inverse, diff, one); + // If this is zero, either the high limb is not `u32::MAX`, or the low limb is zero. + let hi_not_max_or_lo_zero = builder.mul_extension(hi_not_max, output_low); + + constraints.push(hi_not_max_or_lo_zero); + + builder.mul_add_extension(output_high, base_target, output_low) + }; constraints.push(builder.sub_extension(combined_output, computed_output)); @@ -226,7 +266,7 @@ impl, const D: usize> Gate for U32ArithmeticG } fn num_constraints(&self) -> usize { - self.num_ops * (3 + Self::num_limbs()) + self.num_ops * (4 + Self::num_limbs()) } } @@ -247,9 +287,27 @@ impl, const D: usize> PackedEvaluableBase let output_low = vars.local_wires[self.wire_ith_output_low_half(i)]; let output_high = vars.local_wires[self.wire_ith_output_high_half(i)]; + let inverse = vars.local_wires[self.wire_ith_inverse(i)]; - let base = F::from_canonical_u64(1 << 32u64); - let combined_output = output_high * base + output_low; + let combined_output = { + let base = P::from(F::from_canonical_u64(1 << 32u64)); + let one = P::ONES; + let u32_max = P::from(F::from_canonical_u32(u32::MAX)); + + // This is zero if and only if the high limb is `u32::MAX`. + // u32::MAX - output_high + let diff = u32_max - output_high; + // If this is zero, the diff is invertible, so the high limb is not `u32::MAX`. + // inverse * diff - 1 + let hi_not_max = inverse * diff - one; + // If this is zero, either the high limb is not `u32::MAX`, or the low limb is zero. + // hi_not_max * limb_0_u32 + let hi_not_max_or_lo_zero = hi_not_max * output_low; + + yield_constr.one(hi_not_max_or_lo_zero); + + output_high * base + output_low + }; yield_constr.one(combined_output - computed_output); @@ -325,6 +383,15 @@ impl, const D: usize> SimpleGenerator out_buffer.set_wire(output_high_wire, output_high); out_buffer.set_wire(output_low_wire, output_low); + let diff = u32::MAX as u64 - output_high_u64; + let inverse = if diff == 0 { + F::ZERO + } else { + F::from_canonical_u64(diff).inverse() + }; + let inverse_wire = local_wire(self.gate.wire_ith_inverse(self.i)); + out_buffer.set_wire(inverse_wire, inverse); + let num_limbs = U32ArithmeticGate::::num_limbs(); let limb_base = 1 << U32ArithmeticGate::::limb_bits(); let output_limbs_u64 = unfold((), move |_| { @@ -403,6 +470,12 @@ mod tests { let mut output = m0 * m1 + a; let output_low = output & ((1 << 32) - 1); let output_high = output >> 32; + let diff = u32::MAX as u64 - output_high; + let inverse = if diff == 0 { + F::ZERO + } else { + F::from_canonical_u64(diff).inverse() + }; let mut output_limbs = Vec::with_capacity(num_limbs); for _i in 0..num_limbs { @@ -419,6 +492,7 @@ mod tests { v0.push(F::from_noncanonical_u64(a)); v0.push(F::from_canonical_u64(output_low)); v0.push(F::from_canonical_u64(output_high)); + v0.push(inverse); v1.append(&mut output_limbs_f); } From 91fcf262896a4f298b8e9e162ac1f3d4988ae4e1 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 11 Jul 2022 11:07:16 +0200 Subject: [PATCH 05/30] Better CTL error --- evm/src/all_stark.rs | 8 ++- evm/src/cross_table_lookup.rs | 112 ++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index d1f993cd..1f54a4ce 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -47,7 +47,7 @@ impl, const D: usize> AllStark { } } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub enum Table { Cpu = 0, Keccak = 1, @@ -130,6 +130,7 @@ mod tests { use crate::all_stark::{all_cross_table_lookups, AllStark}; use crate::config::StarkConfig; use crate::cpu::cpu_stark::CpuStark; + use crate::cross_table_lookup::testutils::check_ctls; use crate::keccak::keccak_stark::{KeccakStark, NUM_INPUTS, NUM_ROUNDS}; use crate::logic::{self, LogicStark}; use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark}; @@ -356,10 +357,13 @@ mod tests { cross_table_lookups: all_cross_table_lookups(), }; + let traces = vec![cpu_trace, keccak_trace, logic_trace, memory_trace]; + check_ctls(&traces, &all_stark.cross_table_lookups); + let proof = prove::( &all_stark, config, - vec![cpu_trace, keccak_trace, logic_trace, memory_trace], + traces, vec![vec![]; 4], &mut TimingTree::default(), )?; diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 60ce25d7..f316a17a 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -649,3 +649,115 @@ pub(crate) fn verify_cross_table_lookups_circuit< } debug_assert!(ctl_zs_openings.iter_mut().all(|iter| iter.next().is_none())); } + +#[cfg(test)] +pub(crate) mod testutils { + use std::collections::HashMap; + + use plonky2::field::polynomial::PolynomialValues; + use plonky2::field::types::Field; + + use crate::all_stark::Table; + use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns}; + + type MultiSet = HashMap, Vec<(Table, usize)>>; + + fn process_table( + trace_poly_values: &[Vec>], + table: &TableWithColumns, + multiset: &mut MultiSet, + ) { + let trace = &trace_poly_values[table.table as usize]; + for i in 0..trace[0].len() { + let filter = if let Some(column) = &table.filter_column { + column.eval_table(trace, i) + } else { + F::ONE + }; + if filter.is_one() { + let row = table + .columns + .iter() + .map(|c| c.eval_table(trace, i)) + .collect::>(); + multiset.entry(row).or_default().push((table.table, i)); + } else { + assert_eq!(filter, F::ZERO, "Non-binary filter?") + } + } + } + + fn check_ctl( + trace_poly_values: &[Vec>], + ctl: &CrossTableLookup, + ctl_index: usize, + ) { + let CrossTableLookup { + looking_tables, + looked_table, + default, + } = ctl; + let mut looking_multiset = MultiSet::::new(); + let mut looked_multiset = MultiSet::::new(); + + for table in looking_tables { + process_table(trace_poly_values, table, &mut looking_multiset); + } + process_table(trace_poly_values, looked_table, &mut looked_multiset); + + let empty = &vec![]; + let mut extra_default_count = default.as_ref().map(|_| 0); + for (row, looking_locations) in &looking_multiset { + let looked_locations = looked_multiset.get(row).unwrap_or_else(|| empty); + if let Some(default) = default { + if row == default { + *extra_default_count.as_mut().unwrap() += + looking_locations.len() - looked_locations.len(); + continue; + } + } + check_locations(looking_locations, looked_locations, ctl_index, row); + } + if let Some(count) = extra_default_count { + assert_eq!( + count, + looking_tables + .iter() + .map(|table| trace_poly_values[table.table as usize][0].len()) + .sum::() + - trace_poly_values[looked_table.table as usize][0].len() + ); + } + for (row, looked_locations) in &looked_multiset { + let looking_locations = looking_multiset.get(row).unwrap_or_else(|| empty); + check_locations(looking_locations, looked_locations, ctl_index, row); + } + } + + fn check_locations( + looking_locations: &[(Table, usize)], + looked_locations: &[(Table, usize)], + ctl_index: usize, + row: &[F], + ) { + if looking_locations.len() != looked_locations.len() { + panic!( + "CTL #{ctl_index}:\n\ + Row {row:?} is present {l0} times in the looking tables, but {l1} times in the looked table.\n\ + Looking locations (Table, Row index): {looking_locations:?}.\n\ + Looked locations (Table, Row index): {looked_locations:?}.", + l0 = looking_locations.len(), + l1 = looked_locations.len(), + ); + } + } + + pub(crate) fn check_ctls( + trace_poly_values: &[Vec>], + cross_table_lookups: &[CrossTableLookup], + ) { + for (i, ctl) in cross_table_lookups.iter().enumerate() { + check_ctl(trace_poly_values, ctl, i); + } + } +} From 36c8aa34c16bb4d348ea0f2f47598a7512d46511 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 11 Jul 2022 14:13:07 +0200 Subject: [PATCH 06/30] Comments --- evm/src/cross_table_lookup.rs | 117 ++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index f316a17a..b243d760 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -662,6 +662,67 @@ pub(crate) mod testutils { type MultiSet = HashMap, Vec<(Table, usize)>>; + /// Check that the provided traces and cross-table lookups are consistent. + pub(crate) fn check_ctls( + trace_poly_values: &[Vec>], + cross_table_lookups: &[CrossTableLookup], + ) { + for (i, ctl) in cross_table_lookups.iter().enumerate() { + check_ctl(trace_poly_values, ctl, i); + } + } + + fn check_ctl( + trace_poly_values: &[Vec>], + ctl: &CrossTableLookup, + ctl_index: usize, + ) { + let CrossTableLookup { + looking_tables, + looked_table, + default, + } = ctl; + let mut looking_multiset = MultiSet::::new(); + let mut looked_multiset = MultiSet::::new(); + + for table in looking_tables { + process_table(trace_poly_values, table, &mut looking_multiset); + } + process_table(trace_poly_values, looked_table, &mut looked_multiset); + + let empty = &vec![]; + // Check that every row in the looking tables appears in the looked table the same number of times + // with some special logic for the default row. + let mut extra_default_count = default.as_ref().map(|_| 0); + for (row, looking_locations) in &looking_multiset { + let looked_locations = looked_multiset.get(row).unwrap_or_else(|| empty); + if let Some(default) = default { + if row == default { + *extra_default_count.as_mut().unwrap() += + looking_locations.len() - looked_locations.len(); + continue; + } + } + check_locations(looking_locations, looked_locations, ctl_index, row); + } + // Check that the number of extra default rows is correct. + if let Some(count) = extra_default_count { + assert_eq!( + count, + looking_tables + .iter() + .map(|table| trace_poly_values[table.table as usize][0].len()) + .sum::() + - trace_poly_values[looked_table.table as usize][0].len() + ); + } + // Check that every row in the looked tables appears in the looked table the same number of times. + for (row, looked_locations) in &looked_multiset { + let looking_locations = looking_multiset.get(row).unwrap_or_else(|| empty); + check_locations(looking_locations, looked_locations, ctl_index, row); + } + } + fn process_table( trace_poly_values: &[Vec>], table: &TableWithColumns, @@ -687,53 +748,6 @@ pub(crate) mod testutils { } } - fn check_ctl( - trace_poly_values: &[Vec>], - ctl: &CrossTableLookup, - ctl_index: usize, - ) { - let CrossTableLookup { - looking_tables, - looked_table, - default, - } = ctl; - let mut looking_multiset = MultiSet::::new(); - let mut looked_multiset = MultiSet::::new(); - - for table in looking_tables { - process_table(trace_poly_values, table, &mut looking_multiset); - } - process_table(trace_poly_values, looked_table, &mut looked_multiset); - - let empty = &vec![]; - let mut extra_default_count = default.as_ref().map(|_| 0); - for (row, looking_locations) in &looking_multiset { - let looked_locations = looked_multiset.get(row).unwrap_or_else(|| empty); - if let Some(default) = default { - if row == default { - *extra_default_count.as_mut().unwrap() += - looking_locations.len() - looked_locations.len(); - continue; - } - } - check_locations(looking_locations, looked_locations, ctl_index, row); - } - if let Some(count) = extra_default_count { - assert_eq!( - count, - looking_tables - .iter() - .map(|table| trace_poly_values[table.table as usize][0].len()) - .sum::() - - trace_poly_values[looked_table.table as usize][0].len() - ); - } - for (row, looked_locations) in &looked_multiset { - let looking_locations = looking_multiset.get(row).unwrap_or_else(|| empty); - check_locations(looking_locations, looked_locations, ctl_index, row); - } - } - fn check_locations( looking_locations: &[(Table, usize)], looked_locations: &[(Table, usize)], @@ -751,13 +765,4 @@ pub(crate) mod testutils { ); } } - - pub(crate) fn check_ctls( - trace_poly_values: &[Vec>], - cross_table_lookups: &[CrossTableLookup], - ) { - for (i, ctl) in cross_table_lookups.iter().enumerate() { - check_ctl(trace_poly_values, ctl, i); - } - } } From 3ff67e38dc281a3b23ef03c1f739bc03999b99a0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 11 Jul 2022 14:16:58 +0200 Subject: [PATCH 07/30] Minor --- evm/src/cross_table_lookup.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index b243d760..049821e0 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -757,9 +757,9 @@ pub(crate) mod testutils { if looking_locations.len() != looked_locations.len() { panic!( "CTL #{ctl_index}:\n\ - Row {row:?} is present {l0} times in the looking tables, but {l1} times in the looked table.\n\ - Looking locations (Table, Row index): {looking_locations:?}.\n\ - Looked locations (Table, Row index): {looked_locations:?}.", + Row {row:?} is present {l0} times in the looking tables, but {l1} times in the looked table.\n\ + Looking locations (Table, Row index): {looking_locations:?}.\n\ + Looked locations (Table, Row index): {looked_locations:?}.", l0 = looking_locations.len(), l1 = looked_locations.len(), ); From 50ebf39d37a5941ddc06115b672b68a2385e720e Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 11 Jul 2022 14:24:12 +0200 Subject: [PATCH 08/30] Comment --- evm/src/cross_table_lookup.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 049821e0..a01fbfa1 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -682,6 +682,9 @@ pub(crate) mod testutils { looked_table, default, } = ctl; + + // Maps `m` with `(table, i) in m[row]` iff the `i`-th row of `table` is equal to `row` and + // the filter is 1. Without default values, the CTL check holds iff `looking_multiset == looked_multiset`. let mut looking_multiset = MultiSet::::new(); let mut looked_multiset = MultiSet::::new(); From 5e27e7264dbe4be286a4158f6953ff454f3149c8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 11 Jul 2022 14:56:27 +0200 Subject: [PATCH 09/30] unwrap_or_else -> unwrap_or --- evm/src/cross_table_lookup.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index a01fbfa1..12d8984e 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -698,7 +698,7 @@ pub(crate) mod testutils { // with some special logic for the default row. let mut extra_default_count = default.as_ref().map(|_| 0); for (row, looking_locations) in &looking_multiset { - let looked_locations = looked_multiset.get(row).unwrap_or_else(|| empty); + let looked_locations = looked_multiset.get(row).unwrap_or(empty); if let Some(default) = default { if row == default { *extra_default_count.as_mut().unwrap() += @@ -721,7 +721,7 @@ pub(crate) mod testutils { } // Check that every row in the looked tables appears in the looked table the same number of times. for (row, looked_locations) in &looked_multiset { - let looking_locations = looking_multiset.get(row).unwrap_or_else(|| empty); + let looking_locations = looking_multiset.get(row).unwrap_or(empty); check_locations(looking_locations, looked_locations, ctl_index, row); } } From a98f267ff098ce380e0b9aab27b8b1977920e1c5 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:27:50 -0700 Subject: [PATCH 10/30] initial change --- evm/src/all_stark.rs | 37 ++++++++++++---------------------- evm/src/cpu/cpu_stark.rs | 8 +++++++- evm/src/memory/memory_stark.rs | 3 ++- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index d1f993cd..27751a5d 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -119,7 +119,7 @@ mod tests { use anyhow::Result; use itertools::{izip, Itertools}; use plonky2::field::polynomial::PolynomialValues; - use plonky2::field::types::Field; + use plonky2::field::types::{Field, PrimeField64}; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::CircuitConfig; @@ -133,7 +133,6 @@ mod tests { use crate::keccak::keccak_stark::{KeccakStark, NUM_INPUTS, NUM_ROUNDS}; use crate::logic::{self, LogicStark}; use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark}; - use crate::memory::NUM_CHANNELS; use crate::proof::AllProof; use crate::prover::prove; use crate::recursive_verifier::{ @@ -282,32 +281,22 @@ mod tests { cpu_stark.generate(row.borrow_mut()); cpu_trace_rows.push(row.into()); } - - let mut current_cpu_index = 0; - let mut last_timestamp = memory_trace[memory::columns::TIMESTAMP].values[0]; for i in 0..num_memory_ops { - let mem_timestamp = memory_trace[memory::columns::TIMESTAMP].values[i]; - let clock = mem_timestamp; - let op = (0..NUM_CHANNELS) - .filter(|&o| memory_trace[memory::columns::is_channel(o)].values[i] == F::ONE) - .collect_vec()[0]; - - if mem_timestamp != last_timestamp { - current_cpu_index += 1; - last_timestamp = mem_timestamp; - } + let mem_timestamp: usize = memory_trace[memory::columns::TIMESTAMP].values[i].to_canonical_u64().try_into().unwrap(); + let clock = mem_timestamp / 4; + let channel = mem_timestamp % 4; let row: &mut cpu::columns::CpuColumnsView = - cpu_trace_rows[current_cpu_index].borrow_mut(); + cpu_trace_rows[clock].borrow_mut(); - row.mem_channel_used[op] = F::ONE; - row.clock = clock; - row.mem_is_read[op] = memory_trace[memory::columns::IS_READ].values[i]; - row.mem_addr_context[op] = memory_trace[memory::columns::ADDR_CONTEXT].values[i]; - row.mem_addr_segment[op] = memory_trace[memory::columns::ADDR_SEGMENT].values[i]; - row.mem_addr_virtual[op] = memory_trace[memory::columns::ADDR_VIRTUAL].values[i]; + row.mem_channel_used[channel] = F::ONE; + row.clock = F::from_canonical_usize(clock); + row.mem_is_read[channel] = memory_trace[memory::columns::IS_READ].values[i]; + row.mem_addr_context[channel] = memory_trace[memory::columns::ADDR_CONTEXT].values[i]; + row.mem_addr_segment[channel] = memory_trace[memory::columns::ADDR_SEGMENT].values[i]; + row.mem_addr_virtual[channel] = memory_trace[memory::columns::ADDR_VIRTUAL].values[i]; for j in 0..8 { - row.mem_value[op][j] = memory_trace[memory::columns::value_limb(j)].values[i]; + row.mem_value[channel][j] = memory_trace[memory::columns::value_limb(j)].values[i]; } } trace_rows_to_poly_values(cpu_trace_rows) @@ -337,7 +326,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 mem_trace = make_memory_trace(num_memory_ops, &memory_stark, &mut rng); let cpu_trace = make_cpu_trace( num_keccak_perms, num_logic_rows, diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index ee0cf98e..304ccf7c 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -40,7 +40,6 @@ pub fn ctl_filter_logic() -> Column { pub fn ctl_data_memory(channel: usize) -> Vec> { debug_assert!(channel < NUM_CHANNELS); let mut cols: Vec> = Column::singles([ - COL_MAP.clock, COL_MAP.mem_is_read[channel], COL_MAP.mem_addr_context[channel], COL_MAP.mem_addr_segment[channel], @@ -48,6 +47,13 @@ pub fn ctl_data_memory(channel: usize) -> Vec> { ]) .collect_vec(); cols.extend(Column::singles(COL_MAP.mem_value[channel])); + + let scalar = F::from_canonical_usize(NUM_CHANNELS); + let addend = F::from_canonical_usize(channel); + cols.push(Column::linear_combination_with_constant( + vec![(COL_MAP.clock, scalar)], + addend, + )); cols } diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 3f7c26fc..d4962862 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -30,9 +30,10 @@ 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]) + let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) .collect_vec(); res.extend(Column::singles((0..8).map(value_limb))); + res.push(Column::single(TIMESTAMP)); res } From 181a132335e40a44e4ba668c0bc6c458382f7c22 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:28:07 -0700 Subject: [PATCH 11/30] fixes --- evm/src/all_stark.rs | 4 ++-- evm/src/memory/memory_stark.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 27751a5d..14d8cd73 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -283,8 +283,8 @@ mod tests { } for i in 0..num_memory_ops { let mem_timestamp: usize = memory_trace[memory::columns::TIMESTAMP].values[i].to_canonical_u64().try_into().unwrap(); - let clock = mem_timestamp / 4; - let channel = mem_timestamp % 4; + let clock = mem_timestamp / NUM_CHANNELS; + let channel = mem_timestamp % NUM_CHANNELS; let row: &mut cpu::columns::CpuColumnsView = cpu_trace_rows[clock].borrow_mut(); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index d4962862..5ea4da13 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -33,7 +33,7 @@ pub fn ctl_data() -> Vec> { let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) .collect_vec(); res.extend(Column::singles((0..8).map(value_limb))); - res.push(Column::single(TIMESTAMP)); + // res.push(Column::single(TIMESTAMP)); res } @@ -64,8 +64,7 @@ pub fn generate_random_memory_ops( let mut current_memory_values: HashMap<(F, F, F), [F; 8]> = HashMap::new(); let num_cycles = num_ops / 2; - for i in 0..num_cycles { - let timestamp = F::from_canonical_usize(i); + for clock in 0..num_cycles { let mut used_indices = HashSet::new(); let mut new_writes_this_cycle = HashMap::new(); let mut has_read = false; @@ -76,7 +75,7 @@ pub fn generate_random_memory_ops( } used_indices.insert(channel_index); - let is_read = if i == 0 { + let is_read = if clock == 0 { false } else { !has_read && rng.gen() @@ -112,6 +111,7 @@ pub fn generate_random_memory_ops( (context, segment, virt, vals) }; + let timestamp = F::from_canonical_usize(clock * NUM_CHANNELS + channel_index); memory_ops.push(MemoryOp { channel_index, timestamp, From c3e7652763c3866047fa085b119d0363efbd655c Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:28:17 -0700 Subject: [PATCH 12/30] updates --- evm/src/all_stark.rs | 1 + evm/src/lookup.rs | 1 + evm/src/memory/columns.rs | 25 ++++++++++++-------- evm/src/memory/memory_stark.rs | 42 +++++++++++++++++++++++++--------- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 14d8cd73..d9469ecc 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -364,6 +364,7 @@ mod tests { } #[test] + #[ignore] fn test_all_stark_recursive_verifier() -> Result<()> { init_logger(); diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index 2c93143f..6d58d3d1 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -133,5 +133,6 @@ pub fn permuted_cols(inputs: &[F], table: &[F]) -> (Vec, Vec permuted_table[ind] = val; } + dbg!((sorted_inputs.clone(), permuted_table.clone())); (sorted_inputs, permuted_table) } diff --git a/evm/src/memory/columns.rs b/evm/src/memory/columns.rs index d9fa927f..4c946838 100644 --- a/evm/src/memory/columns.rs +++ b/evm/src/memory/columns.rs @@ -1,5 +1,7 @@ //! Memory registers. +use std::ops::Range; + use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; pub(crate) const TIMESTAMP: usize = 0; @@ -36,20 +38,23 @@ pub(crate) const CONTEXT_FIRST_CHANGE: usize = SORTED_VALUE_START + 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 came from the `i`th channel of the memory bus. -const IS_CHANNEL_START: usize = COUNTER_PERMUTED + 1; +const IS_CHANNEL_START: usize = VIRTUAL_FIRST_CHANGE + 1; #[allow(dead_code)] pub(crate) const fn is_channel(channel: usize) -> usize { debug_assert!(channel < NUM_CHANNELS); IS_CHANNEL_START + channel } -pub(crate) const NUM_COLUMNS: usize = IS_CHANNEL_START + NUM_CHANNELS; +// We use a range check to ensure sorting. +pub(crate) const RANGE_CHECK: usize = IS_CHANNEL_START + NUM_CHANNELS; +// 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; + +// Columns to be padded with zeroes, before the permutation argument takes place. +pub(crate) const COLUMNS_TO_PAD: Range = TIMESTAMP..RANGE_CHECK + 1; + +pub(crate) const NUM_COLUMNS: usize = COUNTER_PERMUTED + 1; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 5ea4da13..b485bc69 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -11,17 +11,16 @@ use plonky2::timed; use plonky2::util::timing::TimingTree; use rand::Rng; -use super::columns::is_channel; 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::columns::{ - sorted_value_limb, value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, + is_channel, COLUMNS_TO_PAD, sorted_value_limb, value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, COUNTER, COUNTER_PERMUTED, IS_READ, NUM_COLUMNS, 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::memory::NUM_CHANNELS; +use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::permutation::PermutationPair; use crate::stark::Stark; use crate::util::trace_rows_to_poly_values; @@ -33,7 +32,7 @@ pub fn ctl_data() -> Vec> { let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) .collect_vec(); res.extend(Column::singles((0..8).map(value_limb))); - // res.push(Column::single(TIMESTAMP)); + res.push(Column::single(TIMESTAMP)); res } @@ -112,6 +111,7 @@ pub fn generate_random_memory_ops( }; let timestamp = F::from_canonical_usize(clock * NUM_CHANNELS + channel_index); + dbg!(timestamp); memory_ops.push(MemoryOp { channel_index, timestamp, @@ -201,27 +201,32 @@ pub fn generate_range_check_value( context_first_change: &[F], segment_first_change: &[F], virtual_first_change: &[F], -) -> Vec { +) -> (Vec, usize) { let num_ops = context.len(); let mut range_check = Vec::new(); + let mut max_timestamp_diff = 0; for idx in 0..num_ops - 1 { let this_address_unchanged = F::ONE - context_first_change[idx] - segment_first_change[idx] - virtual_first_change[idx]; + let timestamp_diff = timestamp[idx + 1] - timestamp[idx] - F::ONE; + if this_address_unchanged == F::ONE && timestamp_diff.to_canonical_u64() > max_timestamp_diff { + max_timestamp_diff = timestamp_diff.to_canonical_u64(); + } 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_address_unchanged * (timestamp[idx + 1] - timestamp[idx] - F::ONE), + + this_address_unchanged * timestamp_diff, ); } range_check.push(F::ZERO); - range_check + (range_check, max_timestamp_diff.try_into().unwrap()) } impl, const D: usize> MemoryStark { @@ -255,6 +260,9 @@ impl, const D: usize> MemoryStark { self.generate_memory(&mut trace_cols); + // The number of rows may have changed, if the range check required padding. + let num_ops = trace_cols[0].len(); + let mut trace_rows = vec![[F::ZERO; NUM_COLUMNS]; num_ops]; for (i, col) in trace_cols.iter().enumerate() { for (j, &val) in col.iter().enumerate() { @@ -296,7 +304,7 @@ impl, const D: usize> MemoryStark { 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( + let (range_check_value, max_timestamp_diff) = generate_range_check_value( &sorted_context, &sorted_segment, &sorted_virtual, @@ -305,6 +313,9 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); + dbg!(max_timestamp_diff); + let to_pad_to = max_timestamp_diff.next_power_of_two(); + let to_pad = to_pad_to - num_trace_rows; trace_cols[SORTED_TIMESTAMP] = sorted_timestamp; trace_cols[SORTED_IS_READ] = sorted_is_read; @@ -312,7 +323,7 @@ impl, const D: usize> MemoryStark { 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 { + for j in 0..VALUE_LIMBS { trace_cols[sorted_value_limb(j)][i] = sorted_values[i][j]; } } @@ -320,9 +331,14 @@ impl, const D: usize> MemoryStark { 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[RANGE_CHECK] = range_check_value; - trace_cols[COUNTER] = (0..num_trace_rows) + + for col in COLUMNS_TO_PAD { + trace_cols[col].splice(0..0, vec![F::ZERO; to_pad]); + } + + trace_cols[COUNTER] = (0..to_pad_to) .map(|i| F::from_canonical_usize(i)) .collect(); @@ -330,6 +346,10 @@ impl, const D: usize> MemoryStark { permuted_cols(&trace_cols[RANGE_CHECK], &trace_cols[COUNTER]); trace_cols[RANGE_CHECK_PERMUTED] = permuted_inputs; trace_cols[COUNTER_PERMUTED] = permuted_table; + + for i in 0..NUM_COLUMNS { + dbg!(i, trace_cols[i].len()); + } } pub fn generate_trace(&self, memory_ops: Vec>) -> Vec> { From b467a13d91ae6e491fdea3750d9432ba7e204350 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:28:34 -0700 Subject: [PATCH 13/30] fix --- evm/src/all_stark.rs | 4 +++- evm/src/memory/memory_stark.rs | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index d9469ecc..c986ab86 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -195,7 +195,7 @@ mod tests { num_memory_ops: usize, memory_stark: &MemoryStark, rng: &mut R, - ) -> Vec> { + ) -> (Vec>, usize) { let memory_ops = generate_random_memory_ops(num_memory_ops, rng); memory_stark.generate_trace(memory_ops) } @@ -327,6 +327,8 @@ 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 mem_trace = make_memory_trace(num_memory_ops, &memory_stark, &mut rng); + let mut memory_trace = mem_trace.0; + let num_memory_ops = mem_trace.1; let cpu_trace = make_cpu_trace( num_keccak_perms, num_logic_rows, diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index b485bc69..5f4e5599 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -226,7 +226,7 @@ pub fn generate_range_check_value( range_check.push(F::ZERO); - (range_check, max_timestamp_diff.try_into().unwrap()) + (range_check, max_timestamp_diff as usize) } impl, const D: usize> MemoryStark { @@ -352,7 +352,7 @@ impl, const D: usize> MemoryStark { } } - pub fn generate_trace(&self, memory_ops: Vec>) -> Vec> { + pub fn generate_trace(&self, memory_ops: Vec>) -> (Vec>, usize) { let mut timing = TimingTree::new("generate trace", log::Level::Debug); // Generate the witness. @@ -361,6 +361,7 @@ impl, const D: usize> MemoryStark { "generate trace rows", self.generate_trace_rows(memory_ops) ); + let num_ops = trace_rows.len(); let trace_polys = timed!( &mut timing, @@ -369,7 +370,7 @@ impl, const D: usize> MemoryStark { ); timing.print(); - trace_polys + (trace_polys, num_ops) } } From 83963c3a8f228fd349cbbfa50fc4f56953566d2d Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 6 Jul 2022 15:05:50 -0700 Subject: [PATCH 14/30] permutation pairs --- evm/src/all_stark.rs | 1 - evm/src/memory/memory_stark.rs | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index c986ab86..48b1be76 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -366,7 +366,6 @@ mod tests { } #[test] - #[ignore] fn test_all_stark_recursive_verifier() -> Result<()> { init_logger(); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 5f4e5599..1f202dd0 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -578,7 +578,16 @@ impl, const D: usize> Stark for MemoryStark Vec { + let mut unsorted_cols = vec![TIMESTAMP, IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]; + unsorted_cols.extend((0..VALUE_LIMBS).map(|i| value_limb(i))); + let mut sorted_cols = vec![SORTED_TIMESTAMP, SORTED_IS_READ, SORTED_ADDR_CONTEXT, SORTED_ADDR_SEGMENT, SORTED_ADDR_VIRTUAL]; + sorted_cols.extend((0..VALUE_LIMBS).map(|i| sorted_value_limb(i))); + let column_pairs: Vec<_> = unsorted_cols.iter().cloned().zip(sorted_cols.iter().cloned()).collect(); + vec![ + PermutationPair { + column_pairs + }, PermutationPair::singletons(RANGE_CHECK, RANGE_CHECK_PERMUTED), PermutationPair::singletons(COUNTER, COUNTER_PERMUTED), ] From f3ef6c9bf354bb040a9ca093a1c0e1c07a98a213 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:28:57 -0700 Subject: [PATCH 15/30] fix: ignore padding rows in constraints --- evm/src/all_stark.rs | 1 + evm/src/memory/columns.rs | 2 +- evm/src/memory/memory_stark.rs | 11 +++++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 48b1be76..c986ab86 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -366,6 +366,7 @@ mod tests { } #[test] + #[ignore] fn test_all_stark_recursive_verifier() -> Result<()> { init_logger(); diff --git a/evm/src/memory/columns.rs b/evm/src/memory/columns.rs index 4c946838..2fe42a93 100644 --- a/evm/src/memory/columns.rs +++ b/evm/src/memory/columns.rs @@ -54,7 +54,7 @@ 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; -// Columns to be padded with zeroes, before the permutation argument takes place. +// Columns to be padded at the top with zeroes, before the permutation argument takes place. pub(crate) const COLUMNS_TO_PAD: Range = TIMESTAMP..RANGE_CHECK + 1; pub(crate) const NUM_COLUMNS: usize = COUNTER_PERMUTED + 1; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 1f202dd0..ad03402b 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -32,7 +32,7 @@ pub fn ctl_data() -> Vec> { let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) .collect_vec(); res.extend(Column::singles((0..8).map(value_limb))); - res.push(Column::single(TIMESTAMP)); + // res.push(Column::single(TIMESTAMP)); res } @@ -404,6 +404,9 @@ impl, const D: usize> Stark for MemoryStark = (0..8) .map(|i| vars.next_values[sorted_value_limb(i)]) .collect(); + + // Indicator that this is a real row, not a row of padding. + let valid_row: P = (0..NUM_CHANNELS).map(|c| vars.local_values[is_channel(c)]).sum(); let context_first_change = vars.local_values[CONTEXT_FIRST_CHANGE]; let segment_first_change = vars.local_values[SEGMENT_FIRST_CHANGE]; @@ -431,9 +434,9 @@ impl, const D: usize> Stark for MemoryStark Date: Wed, 6 Jul 2022 17:03:44 -0700 Subject: [PATCH 16/30] updates to recursive constraints --- evm/src/memory/memory_stark.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index ad03402b..d20cf776 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -443,12 +443,12 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark Date: Wed, 6 Jul 2022 17:10:00 -0700 Subject: [PATCH 17/30] another padding-row constraint fix --- evm/src/memory/memory_stark.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index d20cf776..ed1af7ff 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -442,13 +442,13 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark Date: Thu, 7 Jul 2022 09:29:10 -0700 Subject: [PATCH 18/30] fmt --- evm/src/all_stark.rs | 1 - evm/src/memory/memory_stark.rs | 73 ++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index c986ab86..48b1be76 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -366,7 +366,6 @@ mod tests { } #[test] - #[ignore] fn test_all_stark_recursive_verifier() -> Result<()> { init_logger(); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index ed1af7ff..3fe8ddd8 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -15,10 +15,11 @@ 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::columns::{ - is_channel, COLUMNS_TO_PAD, sorted_value_limb, value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, CONTEXT_FIRST_CHANGE, - COUNTER, COUNTER_PERMUTED, IS_READ, NUM_COLUMNS, 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, + is_channel, sorted_value_limb, value_limb, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL, + COLUMNS_TO_PAD, CONTEXT_FIRST_CHANGE, COUNTER, COUNTER_PERMUTED, IS_READ, NUM_COLUMNS, + 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::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::permutation::PermutationPair; @@ -29,8 +30,8 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub(crate) const NUM_PUBLIC_INPUTS: usize = 0; pub fn ctl_data() -> Vec> { - let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]) - .collect_vec(); + let mut res = + Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]).collect_vec(); res.extend(Column::singles((0..8).map(value_limb))); // res.push(Column::single(TIMESTAMP)); res @@ -212,7 +213,9 @@ pub fn generate_range_check_value( - segment_first_change[idx] - virtual_first_change[idx]; let timestamp_diff = timestamp[idx + 1] - timestamp[idx] - F::ONE; - if this_address_unchanged == F::ONE && timestamp_diff.to_canonical_u64() > max_timestamp_diff { + if this_address_unchanged == F::ONE + && timestamp_diff.to_canonical_u64() > max_timestamp_diff + { max_timestamp_diff = timestamp_diff.to_canonical_u64(); } @@ -331,16 +334,14 @@ impl, const D: usize> MemoryStark { 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[RANGE_CHECK] = range_check_value; for col in COLUMNS_TO_PAD { trace_cols[col].splice(0..0, vec![F::ZERO; to_pad]); } - trace_cols[COUNTER] = (0..to_pad_to) - .map(|i| F::from_canonical_usize(i)) - .collect(); + trace_cols[COUNTER] = (0..to_pad_to).map(|i| F::from_canonical_usize(i)).collect(); let (permuted_inputs, permuted_table) = permuted_cols(&trace_cols[RANGE_CHECK], &trace_cols[COUNTER]); @@ -352,7 +353,10 @@ impl, const D: usize> MemoryStark { } } - pub fn generate_trace(&self, memory_ops: Vec>) -> (Vec>, usize) { + pub fn generate_trace( + &self, + memory_ops: Vec>, + ) -> (Vec>, usize) { let mut timing = TimingTree::new("generate trace", log::Level::Debug); // Generate the witness. @@ -404,9 +408,11 @@ impl, const D: usize> Stark for MemoryStark = (0..8) .map(|i| vars.next_values[sorted_value_limb(i)]) .collect(); - + // Indicator that this is a real row, not a row of padding. - let valid_row: P = (0..NUM_CHANNELS).map(|c| vars.local_values[is_channel(c)]).sum(); + let valid_row: P = (0..NUM_CHANNELS) + .map(|c| vars.local_values[is_channel(c)]) + .sum(); let context_first_change = vars.local_values[CONTEXT_FIRST_CHANGE]; let segment_first_change = vars.local_values[SEGMENT_FIRST_CHANGE]; @@ -434,9 +440,15 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark Vec { let mut unsorted_cols = vec![TIMESTAMP, IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]; unsorted_cols.extend((0..VALUE_LIMBS).map(|i| value_limb(i))); - let mut sorted_cols = vec![SORTED_TIMESTAMP, SORTED_IS_READ, SORTED_ADDR_CONTEXT, SORTED_ADDR_SEGMENT, SORTED_ADDR_VIRTUAL]; + let mut sorted_cols = vec![ + SORTED_TIMESTAMP, + SORTED_IS_READ, + SORTED_ADDR_CONTEXT, + SORTED_ADDR_SEGMENT, + SORTED_ADDR_VIRTUAL, + ]; sorted_cols.extend((0..VALUE_LIMBS).map(|i| sorted_value_limb(i))); - let column_pairs: Vec<_> = unsorted_cols.iter().cloned().zip(sorted_cols.iter().cloned()).collect(); + let column_pairs: Vec<_> = unsorted_cols + .iter() + .cloned() + .zip(sorted_cols.iter().cloned()) + .collect(); vec![ - PermutationPair { - column_pairs - }, + PermutationPair { column_pairs }, PermutationPair::singletons(RANGE_CHECK, RANGE_CHECK_PERMUTED), PermutationPair::singletons(COUNTER, COUNTER_PERMUTED), ] From 3a6f2ef25e5b1be9dd73a0852f8440788f46a61b Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 6 Jul 2022 21:23:43 -0700 Subject: [PATCH 19/30] 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 3fe8ddd8..5ce63d2a 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -607,7 +607,7 @@ impl, const D: usize> Stark for MemoryStark Vec { let mut unsorted_cols = vec![TIMESTAMP, IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]; - unsorted_cols.extend((0..VALUE_LIMBS).map(|i| value_limb(i))); + unsorted_cols.extend((0..VALUE_LIMBS).map(value_limb)); let mut sorted_cols = vec![ SORTED_TIMESTAMP, SORTED_IS_READ, @@ -615,7 +615,7 @@ impl, const D: usize> Stark for MemoryStark = unsorted_cols .iter() .cloned() From 6b2b745291a7a4602a2e729d09cb68fce0bc753e Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Wed, 6 Jul 2022 21:24:27 -0700 Subject: [PATCH 20/30] removed debug prints --- evm/src/lookup.rs | 1 - evm/src/memory/memory_stark.rs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/evm/src/lookup.rs b/evm/src/lookup.rs index 6d58d3d1..2c93143f 100644 --- a/evm/src/lookup.rs +++ b/evm/src/lookup.rs @@ -133,6 +133,5 @@ pub fn permuted_cols(inputs: &[F], table: &[F]) -> (Vec, Vec permuted_table[ind] = val; } - dbg!((sorted_inputs.clone(), permuted_table.clone())); (sorted_inputs, permuted_table) } diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 5ce63d2a..616a4105 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -112,7 +112,6 @@ pub fn generate_random_memory_ops( }; let timestamp = F::from_canonical_usize(clock * NUM_CHANNELS + channel_index); - dbg!(timestamp); memory_ops.push(MemoryOp { channel_index, timestamp, @@ -316,7 +315,6 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); - dbg!(max_timestamp_diff); let to_pad_to = max_timestamp_diff.next_power_of_two(); let to_pad = to_pad_to - num_trace_rows; @@ -347,10 +345,6 @@ impl, const D: usize> MemoryStark { permuted_cols(&trace_cols[RANGE_CHECK], &trace_cols[COUNTER]); trace_cols[RANGE_CHECK_PERMUTED] = permuted_inputs; trace_cols[COUNTER_PERMUTED] = permuted_table; - - for i in 0..NUM_COLUMNS { - dbg!(i, trace_cols[i].len()); - } } pub fn generate_trace( From 6655ee68e4b5ea226cce3d0fdf5298ae24dba906 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:29:35 -0700 Subject: [PATCH 21/30] restored timestamp column to CTL --- evm/src/cpu/cpu_stark.rs | 9 ++++++++- evm/src/memory/memory_stark.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 304ccf7c..86b04246 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -46,7 +46,14 @@ pub fn ctl_data_memory(channel: usize) -> Vec> { COL_MAP.mem_addr_virtual[channel], ]) .collect_vec(); - cols.extend(Column::singles(COL_MAP.mem_value[channel])); +cols.extend(Column::singles(COL_MAP.mem_value[channel])); + + let scalar = F::from_canonical_usize(NUM_CHANNELS); + let addend = F::from_canonical_usize(channel); + cols.push(Column::linear_combination_with_constant( + vec![(columns::CLOCK, scalar)], + addend, + )); let scalar = F::from_canonical_usize(NUM_CHANNELS); let addend = F::from_canonical_usize(channel); diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 616a4105..187435ed 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -33,7 +33,7 @@ pub fn ctl_data() -> Vec> { let mut res = Column::singles([IS_READ, ADDR_CONTEXT, ADDR_SEGMENT, ADDR_VIRTUAL]).collect_vec(); res.extend(Column::singles((0..8).map(value_limb))); - // res.push(Column::single(TIMESTAMP)); + res.push(Column::single(TIMESTAMP)); res } From afc5a4dc4fc435799db946e1904405c17b3950fa Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 09:52:38 -0700 Subject: [PATCH 22/30] fixes --- evm/src/all_stark.rs | 9 ++++++--- evm/src/cpu/cpu_stark.rs | 10 ++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 48b1be76..c242a1be 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -133,6 +133,7 @@ mod tests { use crate::keccak::keccak_stark::{KeccakStark, NUM_INPUTS, NUM_ROUNDS}; use crate::logic::{self, LogicStark}; use crate::memory::memory_stark::{generate_random_memory_ops, MemoryStark}; + use crate::memory::NUM_CHANNELS; use crate::proof::AllProof; use crate::prover::prove; use crate::recursive_verifier::{ @@ -282,12 +283,14 @@ mod tests { cpu_trace_rows.push(row.into()); } for i in 0..num_memory_ops { - let mem_timestamp: usize = memory_trace[memory::columns::TIMESTAMP].values[i].to_canonical_u64().try_into().unwrap(); + let mem_timestamp: usize = memory_trace[memory::columns::TIMESTAMP].values[i] + .to_canonical_u64() + .try_into() + .unwrap(); let clock = mem_timestamp / NUM_CHANNELS; let channel = mem_timestamp % NUM_CHANNELS; - let row: &mut cpu::columns::CpuColumnsView = - cpu_trace_rows[clock].borrow_mut(); + let row: &mut cpu::columns::CpuColumnsView = cpu_trace_rows[clock].borrow_mut(); row.mem_channel_used[channel] = F::ONE; row.clock = F::from_canonical_usize(clock); diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 86b04246..ad32dd98 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -46,14 +46,7 @@ pub fn ctl_data_memory(channel: usize) -> Vec> { COL_MAP.mem_addr_virtual[channel], ]) .collect_vec(); -cols.extend(Column::singles(COL_MAP.mem_value[channel])); - - let scalar = F::from_canonical_usize(NUM_CHANNELS); - let addend = F::from_canonical_usize(channel); - cols.push(Column::linear_combination_with_constant( - vec![(columns::CLOCK, scalar)], - addend, - )); + cols.extend(Column::singles(COL_MAP.mem_value[channel])); let scalar = F::from_canonical_usize(NUM_CHANNELS); let addend = F::from_canonical_usize(channel); @@ -61,6 +54,7 @@ cols.extend(Column::singles(COL_MAP.mem_value[channel])); vec![(COL_MAP.clock, scalar)], addend, )); + cols } From 9ad2958f7374303b4c7d45e761dc658378f23df1 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 13:55:30 -0700 Subject: [PATCH 23/30] 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 187435ed..dab5ba2d 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -315,7 +315,7 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); - let to_pad_to = max_timestamp_diff.next_power_of_two(); + let to_pad_to = max_timestamp_diff.next_power_of_two().max(num_trace_rows); let to_pad = to_pad_to - num_trace_rows; trace_cols[SORTED_TIMESTAMP] = sorted_timestamp; From 888cfe4ca4be77aabe1298d409f2e361f19bb8fa Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Thu, 7 Jul 2022 16:27:11 -0700 Subject: [PATCH 24/30] fix --- evm/src/all_stark.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index c242a1be..966b362c 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -290,16 +290,26 @@ mod tests { let clock = mem_timestamp / NUM_CHANNELS; let channel = mem_timestamp % NUM_CHANNELS; - let row: &mut cpu::columns::CpuColumnsView = cpu_trace_rows[clock].borrow_mut(); + let is_padding_row = (0..NUM_CHANNELS) + .map(|c| memory_trace[memory::columns::is_channel(c)].values[i]) + .all(|x| x == F::ZERO); - row.mem_channel_used[channel] = F::ONE; - row.clock = F::from_canonical_usize(clock); - row.mem_is_read[channel] = memory_trace[memory::columns::IS_READ].values[i]; - row.mem_addr_context[channel] = memory_trace[memory::columns::ADDR_CONTEXT].values[i]; - row.mem_addr_segment[channel] = memory_trace[memory::columns::ADDR_SEGMENT].values[i]; - row.mem_addr_virtual[channel] = memory_trace[memory::columns::ADDR_VIRTUAL].values[i]; - for j in 0..8 { - row.mem_value[channel][j] = memory_trace[memory::columns::value_limb(j)].values[i]; + if !is_padding_row { + let row: &mut cpu::columns::CpuColumnsView = cpu_trace_rows[clock].borrow_mut(); + + row.mem_channel_used[channel] = F::ONE; + row.clock = F::from_canonical_usize(clock); + row.mem_is_read[channel] = memory_trace[memory::columns::IS_READ].values[i]; + row.mem_addr_context[channel] = + memory_trace[memory::columns::ADDR_CONTEXT].values[i]; + row.mem_addr_segment[channel] = + memory_trace[memory::columns::ADDR_SEGMENT].values[i]; + row.mem_addr_virtual[channel] = + memory_trace[memory::columns::ADDR_VIRTUAL].values[i]; + for j in 0..8 { + row.mem_value[channel][j] = + memory_trace[memory::columns::value_limb(j)].values[i]; + } } } trace_rows_to_poly_values(cpu_trace_rows) From 49c208ec9b77d3dca700ac658680cd9f23917d89 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 8 Jul 2022 10:22:58 -0700 Subject: [PATCH 25/30] addressed comments --- evm/src/memory/columns.rs | 1 - evm/src/memory/memory_stark.rs | 19 ++++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/evm/src/memory/columns.rs b/evm/src/memory/columns.rs index 2fe42a93..214a7e4b 100644 --- a/evm/src/memory/columns.rs +++ b/evm/src/memory/columns.rs @@ -40,7 +40,6 @@ pub(crate) const VIRTUAL_FIRST_CHANGE: usize = SEGMENT_FIRST_CHANGE + 1; // Flags to indicate if this operation came from the `i`th channel of the memory bus. const IS_CHANNEL_START: usize = VIRTUAL_FIRST_CHANGE + 1; -#[allow(dead_code)] pub(crate) const fn is_channel(channel: usize) -> usize { debug_assert!(channel < NUM_CHANNELS); IS_CHANNEL_START + channel diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index dab5ba2d..6a1fc945 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -205,30 +205,23 @@ pub fn generate_range_check_value( let num_ops = context.len(); let mut range_check = Vec::new(); - let mut max_timestamp_diff = 0; for idx in 0..num_ops - 1 { let this_address_unchanged = F::ONE - context_first_change[idx] - segment_first_change[idx] - virtual_first_change[idx]; - let timestamp_diff = timestamp[idx + 1] - timestamp[idx] - F::ONE; - if this_address_unchanged == F::ONE - && timestamp_diff.to_canonical_u64() > max_timestamp_diff - { - max_timestamp_diff = timestamp_diff.to_canonical_u64(); - } - 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_address_unchanged * timestamp_diff, + + this_address_unchanged * (timestamp[idx + 1] - timestamp[idx] - F::ONE), ); } - range_check.push(F::ZERO); - (range_check, max_timestamp_diff as usize) + let max_diff = range_check.iter().map(F::to_canonical_u64).max().unwrap() as usize; + + (range_check, max_diff) } impl, const D: usize> MemoryStark { @@ -306,7 +299,7 @@ impl, const D: usize> MemoryStark { let (context_first_change, segment_first_change, virtual_first_change) = generate_first_change_flags(&sorted_context, &sorted_segment, &sorted_virtual); - let (range_check_value, max_timestamp_diff) = generate_range_check_value( + let (range_check_value, max_diff) = generate_range_check_value( &sorted_context, &sorted_segment, &sorted_virtual, @@ -315,7 +308,7 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); - let to_pad_to = max_timestamp_diff.next_power_of_two().max(num_trace_rows); + let to_pad_to = max_diff.next_power_of_two().max(num_trace_rows); let to_pad = to_pad_to - num_trace_rows; trace_cols[SORTED_TIMESTAMP] = sorted_timestamp; From 467f53202483c392e53ff745f482bf879582f072 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 8 Jul 2022 12:19:11 -0700 Subject: [PATCH 26/30] addressed comments --- evm/src/all_stark.rs | 4 +++- evm/src/memory/memory_stark.rs | 10 ++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 966b362c..b69104eb 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -198,7 +198,9 @@ mod tests { rng: &mut R, ) -> (Vec>, usize) { let memory_ops = generate_random_memory_ops(num_memory_ops, rng); - memory_stark.generate_trace(memory_ops) + let trace = memory_stark.generate_trace(memory_ops); + let num_ops = trace[0].values.len(); + (trace, num_ops) } fn make_cpu_trace( diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 6a1fc945..ed55b02a 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -308,7 +308,7 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); - let to_pad_to = max_diff.next_power_of_two().max(num_trace_rows); + let to_pad_to = (max_diff + 1).next_power_of_two().max(num_trace_rows); let to_pad = to_pad_to - num_trace_rows; trace_cols[SORTED_TIMESTAMP] = sorted_timestamp; @@ -343,7 +343,7 @@ impl, const D: usize> MemoryStark { pub fn generate_trace( &self, memory_ops: Vec>, - ) -> (Vec>, usize) { + ) -> Vec> { let mut timing = TimingTree::new("generate trace", log::Level::Debug); // Generate the witness. @@ -352,7 +352,6 @@ impl, const D: usize> MemoryStark { "generate trace rows", self.generate_trace_rows(memory_ops) ); - let num_ops = trace_rows.len(); let trace_polys = timed!( &mut timing, @@ -361,7 +360,7 @@ impl, const D: usize> MemoryStark { ); timing.print(); - (trace_polys, num_ops) + trace_polys } } @@ -604,8 +603,7 @@ impl, const D: usize> Stark for MemoryStark = unsorted_cols - .iter() - .cloned() + .into_iter() .zip(sorted_cols.iter().cloned()) .collect(); From b935605f69761f0c131f951abd9beb69d019f493 Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 8 Jul 2022 12:19:20 -0700 Subject: [PATCH 27/30] fmt --- evm/src/memory/memory_stark.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index ed55b02a..a11ec992 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -340,10 +340,7 @@ impl, const D: usize> MemoryStark { trace_cols[COUNTER_PERMUTED] = permuted_table; } - pub fn generate_trace( - &self, - memory_ops: Vec>, - ) -> Vec> { + pub fn generate_trace(&self, memory_ops: Vec>) -> Vec> { let mut timing = TimingTree::new("generate trace", log::Level::Debug); // Generate the witness. From bdae9bc33bebfd9edd51a2af790a4e2c319753ca Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Fri, 8 Jul 2022 12:23:01 -0700 Subject: [PATCH 28/30] update --- 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 a11ec992..3edab3fb 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -308,7 +308,7 @@ impl, const D: usize> MemoryStark { &segment_first_change, &virtual_first_change, ); - let to_pad_to = (max_diff + 1).next_power_of_two().max(num_trace_rows); + let to_pad_to = (max_diff + 1).max(num_trace_rows).next_power_of_two(); let to_pad = to_pad_to - num_trace_rows; trace_cols[SORTED_TIMESTAMP] = sorted_timestamp; From abc729f1d68593733ac0dcf017f0f96acae0933a Mon Sep 17 00:00:00 2001 From: Nicholas Ward Date: Mon, 11 Jul 2022 10:44:42 -0700 Subject: [PATCH 29/30] TODO --- evm/src/memory/memory_stark.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 3edab3fb..49ee1ee2 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -393,6 +393,7 @@ impl, const D: usize> Stark for MemoryStark Date: Mon, 11 Jul 2022 19:53:00 +0200 Subject: [PATCH 30/30] PR feedback + underflow check --- evm/src/cross_table_lookup.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 12d8984e..4097df7b 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -696,18 +696,32 @@ pub(crate) mod testutils { let empty = &vec![]; // Check that every row in the looking tables appears in the looked table the same number of times // with some special logic for the default row. - let mut extra_default_count = default.as_ref().map(|_| 0); for (row, looking_locations) in &looking_multiset { let looked_locations = looked_multiset.get(row).unwrap_or(empty); if let Some(default) = default { if row == default { - *extra_default_count.as_mut().unwrap() += - looking_locations.len() - looked_locations.len(); continue; } } check_locations(looking_locations, looked_locations, ctl_index, row); } + let extra_default_count = default.as_ref().map(|d| { + let looking_default_locations = looking_multiset.get(d).unwrap_or(empty); + let looked_default_locations = looked_multiset.get(d).unwrap_or(empty); + looking_default_locations + .len() + .checked_sub(looked_default_locations.len()) + .unwrap_or_else(|| { + // If underflow, panic. There should be more default rows in the looking side. + check_locations( + looking_default_locations, + looked_default_locations, + ctl_index, + d, + ); + unreachable!() + }) + }); // Check that the number of extra default rows is correct. if let Some(count) = extra_default_count { assert_eq!(