diff --git a/evm/src/all_stark.rs b/evm/src/all_stark.rs index 1b3e6151..26a43d59 100644 --- a/evm/src/all_stark.rs +++ b/evm/src/all_stark.rs @@ -11,9 +11,9 @@ use crate::cpu::membus::NUM_GP_CHANNELS; use crate::cross_table_lookup::{Column, CrossTableLookup, TableWithColumns}; use crate::keccak::keccak_stark; use crate::keccak::keccak_stark::KeccakStark; -use crate::keccak_memory::columns::KECCAK_WIDTH_BYTES; -use crate::keccak_memory::keccak_memory_stark; -use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; +use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; +use crate::keccak_sponge::keccak_sponge_stark; +use crate::keccak_sponge::keccak_sponge_stark::{num_logic_ctls, KeccakSpongeStark}; use crate::logic; use crate::logic::LogicStark; use crate::memory::memory_stark; @@ -24,7 +24,7 @@ use crate::stark::Stark; pub struct AllStark, const D: usize> { pub cpu_stark: CpuStark, pub keccak_stark: KeccakStark, - pub keccak_memory_stark: KeccakMemoryStark, + pub keccak_sponge_stark: KeccakSpongeStark, pub logic_stark: LogicStark, pub memory_stark: MemoryStark, pub cross_table_lookups: Vec>, @@ -35,7 +35,7 @@ impl, const D: usize> Default for AllStark { Self { cpu_stark: CpuStark::default(), keccak_stark: KeccakStark::default(), - keccak_memory_stark: KeccakMemoryStark::default(), + keccak_sponge_stark: KeccakSpongeStark::default(), logic_stark: LogicStark::default(), memory_stark: MemoryStark::default(), cross_table_lookups: all_cross_table_lookups(), @@ -48,7 +48,7 @@ impl, const D: usize> AllStark { [ self.cpu_stark.num_permutation_batches(config), self.keccak_stark.num_permutation_batches(config), - self.keccak_memory_stark.num_permutation_batches(config), + self.keccak_sponge_stark.num_permutation_batches(config), self.logic_stark.num_permutation_batches(config), self.memory_stark.num_permutation_batches(config), ] @@ -58,7 +58,7 @@ impl, const D: usize> AllStark { [ self.cpu_stark.permutation_batch_size(), self.keccak_stark.permutation_batch_size(), - self.keccak_memory_stark.permutation_batch_size(), + self.keccak_sponge_stark.permutation_batch_size(), self.logic_stark.permutation_batch_size(), self.memory_stark.permutation_batch_size(), ] @@ -69,19 +69,18 @@ impl, const D: usize> AllStark { pub enum Table { Cpu = 0, Keccak = 1, - KeccakMemory = 2, + KeccakSponge = 2, Logic = 3, Memory = 4, } pub(crate) const NUM_TABLES: usize = Table::Memory as usize + 1; -#[allow(unused)] // TODO: Should be used soon. pub(crate) fn all_cross_table_lookups() -> Vec> { - let mut ctls = vec![ctl_keccak(), ctl_logic(), ctl_memory(), ctl_keccak_memory()]; + let mut ctls = vec![ctl_keccak(), ctl_logic(), ctl_memory(), ctl_keccak_sponge()]; // TODO: Some CTLs temporarily disabled while we get them working. disable_ctl(&mut ctls[0]); - disable_ctl(&mut ctls[1]); + disable_ctl(&mut ctls[1]); // Enable once we populate logic log in keccak_sponge_log. disable_ctl(&mut ctls[2]); disable_ctl(&mut ctls[3]); ctls @@ -95,53 +94,52 @@ fn disable_ctl(ctl: &mut CrossTableLookup) { } fn ctl_keccak() -> CrossTableLookup { - let cpu_looking = TableWithColumns::new( - Table::Cpu, - cpu_stark::ctl_data_keccak(), - Some(cpu_stark::ctl_filter_keccak()), + let keccak_sponge_looking = TableWithColumns::new( + Table::KeccakSponge, + keccak_sponge_stark::ctl_looking_keccak(), + Some(keccak_sponge_stark::ctl_looking_keccak_filter()), ); - let keccak_memory_looking = TableWithColumns::new( - Table::KeccakMemory, - keccak_memory_stark::ctl_looking_keccak(), - Some(keccak_memory_stark::ctl_filter()), + let keccak_looked = TableWithColumns::new( + Table::Keccak, + keccak_stark::ctl_data(), + Some(keccak_stark::ctl_filter()), ); - CrossTableLookup::new( - vec![cpu_looking, keccak_memory_looking], - TableWithColumns::new( - Table::Keccak, - keccak_stark::ctl_data(), - Some(keccak_stark::ctl_filter()), - ), - None, - ) + CrossTableLookup::new(vec![keccak_sponge_looking], keccak_looked, None) } -fn ctl_keccak_memory() -> CrossTableLookup { - CrossTableLookup::new( - vec![TableWithColumns::new( - Table::Cpu, - cpu_stark::ctl_data_keccak_memory(), - Some(cpu_stark::ctl_filter_keccak_memory()), - )], - TableWithColumns::new( - Table::KeccakMemory, - keccak_memory_stark::ctl_looked_data(), - Some(keccak_memory_stark::ctl_filter()), - ), - None, - ) +fn ctl_keccak_sponge() -> CrossTableLookup { + let cpu_looking = TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_keccak_sponge(), + Some(cpu_stark::ctl_filter_keccak_sponge()), + ); + let keccak_sponge_looked = TableWithColumns::new( + Table::KeccakSponge, + keccak_sponge_stark::ctl_looked_data(), + Some(keccak_sponge_stark::ctl_looked_filter()), + ); + CrossTableLookup::new(vec![cpu_looking], keccak_sponge_looked, None) } fn ctl_logic() -> CrossTableLookup { - CrossTableLookup::new( - vec![TableWithColumns::new( - Table::Cpu, - cpu_stark::ctl_data_logic(), - Some(cpu_stark::ctl_filter_logic()), - )], - TableWithColumns::new(Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())), - None, - ) + let cpu_looking = TableWithColumns::new( + Table::Cpu, + cpu_stark::ctl_data_logic(), + Some(cpu_stark::ctl_filter_logic()), + ); + let mut all_lookers = vec![cpu_looking]; + for i in 0..num_logic_ctls() { + let keccak_sponge_looking = TableWithColumns::new( + Table::KeccakSponge, + keccak_sponge_stark::ctl_looking_logic(i), + // TODO: Double check, but I think it's the same filter for memory and logic? + Some(keccak_sponge_stark::ctl_looking_memory_filter(i)), + ); + all_lookers.push(keccak_sponge_looking); + } + let logic_looked = + TableWithColumns::new(Table::Logic, logic::ctl_data(), Some(logic::ctl_filter())); + CrossTableLookup::new(all_lookers, logic_looked, None) } fn ctl_memory() -> CrossTableLookup { @@ -157,674 +155,21 @@ fn ctl_memory() -> CrossTableLookup { Some(cpu_stark::ctl_filter_gp_memory(channel)), ) }); - let keccak_memory_reads = (0..KECCAK_WIDTH_BYTES).map(|i| { + let keccak_sponge_reads = (0..KECCAK_RATE_BYTES).map(|i| { TableWithColumns::new( - Table::KeccakMemory, - keccak_memory_stark::ctl_looking_memory(i, true), - Some(keccak_memory_stark::ctl_filter()), - ) - }); - let keccak_memory_writes = (0..KECCAK_WIDTH_BYTES).map(|i| { - TableWithColumns::new( - Table::KeccakMemory, - keccak_memory_stark::ctl_looking_memory(i, false), - Some(keccak_memory_stark::ctl_filter()), + Table::KeccakSponge, + keccak_sponge_stark::ctl_looking_memory(i), + Some(keccak_sponge_stark::ctl_looking_memory_filter(i)), ) }); let all_lookers = iter::once(cpu_memory_code_read) .chain(cpu_memory_gp_ops) - .chain(keccak_memory_reads) - .chain(keccak_memory_writes) + .chain(keccak_sponge_reads) .collect(); - CrossTableLookup::new( - all_lookers, - TableWithColumns::new( - Table::Memory, - memory_stark::ctl_data(), - Some(memory_stark::ctl_filter()), - ), - None, - ) -} - -#[cfg(test)] -mod tests { - use std::borrow::BorrowMut; - - use anyhow::Result; - use ethereum_types::U256; - use itertools::Itertools; - use plonky2::field::polynomial::PolynomialValues; - use plonky2::field::types::{Field, PrimeField64}; - use plonky2::iop::witness::PartialWitness; - use plonky2::plonk::circuit_builder::CircuitBuilder; - use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData}; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - use plonky2::util::timing::TimingTree; - use rand::{thread_rng, Rng}; - - use crate::all_stark::{AllStark, NUM_TABLES}; - use crate::config::StarkConfig; - use crate::cpu::cpu_stark::CpuStark; - use crate::cpu::kernel::aggregator::KERNEL; - use crate::cross_table_lookup::testutils::check_ctls; - use crate::keccak::keccak_stark::{KeccakStark, NUM_INPUTS, NUM_ROUNDS}; - use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; - use crate::logic::{self, LogicStark, Operation}; - use crate::memory::memory_stark::tests::generate_random_memory_ops; - use crate::memory::memory_stark::MemoryStark; - use crate::memory::NUM_CHANNELS; - use crate::proof::{AllProof, PublicValues}; - use crate::prover::prove_with_traces; - use crate::recursive_verifier::tests::recursively_verify_all_proof; - use crate::recursive_verifier::{ - add_virtual_recursive_all_proof, all_verifier_data_recursive_stark_proof, - set_recursive_all_proof_target, RecursiveAllProof, - }; - use crate::stark::Stark; - use crate::util::{limb_from_bits_le, trace_rows_to_poly_values}; - use crate::verifier::verify_proof; - use crate::{cpu, keccak, memory}; - - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - - fn make_keccak_trace( - num_keccak_perms: usize, - keccak_stark: &KeccakStark, - config: &StarkConfig, - rng: &mut R, - ) -> Vec> { - let keccak_inputs = (0..num_keccak_perms) - .map(|_| [0u64; NUM_INPUTS].map(|_| rng.gen())) - .collect_vec(); - keccak_stark.generate_trace( - keccak_inputs, - config.fri_config.num_cap_elements(), - &mut TimingTree::default(), - ) - } - - fn make_keccak_memory_trace( - keccak_memory_stark: &KeccakMemoryStark, - config: &StarkConfig, - ) -> Vec> { - keccak_memory_stark.generate_trace( - vec![], - config.fri_config.num_cap_elements(), - &mut TimingTree::default(), - ) - } - - fn make_logic_trace( - num_rows: usize, - logic_stark: &LogicStark, - config: &StarkConfig, - rng: &mut R, - ) -> Vec> { - let all_ops = [logic::Op::And, logic::Op::Or, logic::Op::Xor]; - let ops = (0..num_rows) - .map(|_| { - let op = all_ops[rng.gen_range(0..all_ops.len())]; - let input0 = U256(rng.gen()); - let input1 = U256(rng.gen()); - Operation::new(op, input0, input1) - }) - .collect(); - logic_stark.generate_trace( - ops, - config.fri_config.num_cap_elements(), - &mut TimingTree::default(), - ) - } - - fn make_memory_trace( - num_memory_ops: usize, - memory_stark: &MemoryStark, - rng: &mut R, - ) -> (Vec>, usize) { - let memory_ops = generate_random_memory_ops(num_memory_ops, rng); - let trace = memory_stark.generate_trace(memory_ops, &mut TimingTree::default()); - let num_ops = trace[0].values.len(); - (trace, num_ops) - } - - fn bits_from_opcode(opcode: u8) -> [F; 8] { - [ - F::from_bool(opcode & (1 << 0) != 0), - F::from_bool(opcode & (1 << 1) != 0), - F::from_bool(opcode & (1 << 2) != 0), - F::from_bool(opcode & (1 << 3) != 0), - F::from_bool(opcode & (1 << 4) != 0), - F::from_bool(opcode & (1 << 5) != 0), - F::from_bool(opcode & (1 << 6) != 0), - F::from_bool(opcode & (1 << 7) != 0), - ] - } - - 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: &mut [PolynomialValues], - ) -> Vec> { - let keccak_input_limbs: Vec<[F; 2 * NUM_INPUTS]> = (0..num_keccak_perms) - .map(|i| { - (0..2 * NUM_INPUTS) - .map(|j| { - keccak::columns::reg_input_limb(j) - .eval_table(keccak_trace, (i + 1) * NUM_ROUNDS - 1) - }) - .collect::>() - .try_into() - .unwrap() - }) - .collect(); - let keccak_output_limbs: Vec<[F; 2 * NUM_INPUTS]> = (0..num_keccak_perms) - .map(|i| { - (0..2 * NUM_INPUTS) - .map(|j| { - keccak_trace[keccak::columns::reg_output_limb(j)].values - [(i + 1) * NUM_ROUNDS - 1] - }) - .collect::>() - .try_into() - .unwrap() - }) - .collect(); - - let mut cpu_trace_rows: Vec<[F; CpuStark::::COLUMNS]> = vec![]; - let mut bootstrap_row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - bootstrap_row.is_bootstrap_kernel = F::ONE; - cpu_trace_rows.push(bootstrap_row.into()); - - for i in 0..num_keccak_perms { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_keccak = F::ONE; - let keccak = row.general.keccak_mut(); - for j in 0..2 * NUM_INPUTS { - keccak.input_limbs[j] = keccak_input_limbs[i][j]; - keccak.output_limbs[j] = keccak_output_limbs[i][j]; - } - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // Pad to `num_memory_ops` for memory testing. - for _ in cpu_trace_rows.len()..num_memory_ops { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.opcode_bits = bits_from_opcode(0x5b); - row.is_cpu_cycle = F::ONE; - row.is_kernel_mode = F::ONE; - row.program_counter = F::from_canonical_usize(KERNEL.global_labels["main"]); - cpu_stark.generate(row.borrow_mut()); - 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 clock = mem_timestamp / NUM_CHANNELS; - let channel = mem_timestamp % NUM_CHANNELS; - - let filter = memory_trace[memory::columns::FILTER].values[i]; - assert!(filter.is_one() || filter.is_zero()); - let is_actual_op = filter.is_one(); - - if is_actual_op { - let row: &mut cpu::columns::CpuColumnsView = cpu_trace_rows[clock].borrow_mut(); - row.clock = F::from_canonical_usize(clock); - - dbg!(channel, row.mem_channels.len()); - let channel = &mut row.mem_channels[channel]; - channel.used = F::ONE; - channel.is_read = memory_trace[memory::columns::IS_READ].values[i]; - channel.addr_context = memory_trace[memory::columns::ADDR_CONTEXT].values[i]; - channel.addr_segment = memory_trace[memory::columns::ADDR_SEGMENT].values[i]; - channel.addr_virtual = memory_trace[memory::columns::ADDR_VIRTUAL].values[i]; - for j in 0..8 { - channel.value[j] = memory_trace[memory::columns::value_limb(j)].values[i]; - } - } - } - - for i in 0..num_logic_rows { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.is_kernel_mode = F::ONE; - - // Since these are the first cycle rows, we must start with PC=main then increment. - row.program_counter = F::from_canonical_usize(KERNEL.global_labels["main"] + i); - row.opcode_bits = bits_from_opcode( - if logic_trace[logic::columns::IS_AND].values[i] != F::ZERO { - 0x16 - } else if logic_trace[logic::columns::IS_OR].values[i] != F::ZERO { - 0x17 - } else if logic_trace[logic::columns::IS_XOR].values[i] != F::ZERO { - 0x18 - } else { - panic!() - }, - ); - - let input0_bit_cols = logic::columns::limb_bit_cols_for_input(logic::columns::INPUT0); - for (col_cpu, limb_cols_logic) in - row.mem_channels[0].value.iter_mut().zip(input0_bit_cols) - { - *col_cpu = limb_from_bits_le(limb_cols_logic.map(|col| logic_trace[col].values[i])); - } - - let input1_bit_cols = logic::columns::limb_bit_cols_for_input(logic::columns::INPUT1); - for (col_cpu, limb_cols_logic) in - row.mem_channels[1].value.iter_mut().zip(input1_bit_cols) - { - *col_cpu = limb_from_bits_le(limb_cols_logic.map(|col| logic_trace[col].values[i])); - } - - for (col_cpu, col_logic) in row.mem_channels[2] - .value - .iter_mut() - .zip(logic::columns::RESULT) - { - *col_cpu = logic_trace[col_logic].values[i]; - } - - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // Trap to kernel - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - let last_row: cpu::columns::CpuColumnsView = - cpu_trace_rows[cpu_trace_rows.len() - 1].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0x0a); // `EXP` is implemented in software - row.is_kernel_mode = F::ONE; - row.program_counter = last_row.program_counter + F::ONE; - row.mem_channels[0].value = [ - row.program_counter, - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `EXIT_KERNEL` (to kernel) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0xf9); - row.is_kernel_mode = F::ONE; - row.program_counter = F::from_canonical_usize(KERNEL.global_labels["sys_exp"]); - row.mem_channels[0].value = [ - F::from_canonical_u16(15682), - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `JUMP` (in kernel mode) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0x56); - row.is_kernel_mode = F::ONE; - row.program_counter = F::from_canonical_u16(15682); - row.mem_channels[0].value = [ - F::from_canonical_u16(15106), - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.mem_channels[1].value = [ - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.general.jumps_mut().input0_upper_zero = F::ONE; - row.general.jumps_mut().dst_valid_or_kernel = F::ONE; - row.general.jumps_mut().input0_jumpable = F::ONE; - row.general.jumps_mut().input1_sum_inv = F::ONE; - row.general.jumps_mut().should_jump = F::ONE; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `EXIT_KERNEL` (to userspace) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0xf9); - row.is_kernel_mode = F::ONE; - row.program_counter = F::from_canonical_u16(15106); - row.mem_channels[0].value = [ - F::from_canonical_u16(63064), - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `JUMP` (taken) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0x56); - row.is_kernel_mode = F::ZERO; - row.program_counter = F::from_canonical_u16(63064); - row.mem_channels[0].value = [ - F::from_canonical_u16(3754), - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.mem_channels[1].value = [ - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.general.jumps_mut().input0_upper_zero = F::ONE; - row.general.jumps_mut().dst_valid = F::ONE; - row.general.jumps_mut().dst_valid_or_kernel = F::ONE; - row.general.jumps_mut().input0_jumpable = F::ONE; - row.general.jumps_mut().input1_sum_inv = F::ONE; - row.general.jumps_mut().should_jump = F::ONE; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `JUMPI` (taken) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0x57); - row.is_kernel_mode = F::ZERO; - row.program_counter = F::from_canonical_u16(3754); - row.mem_channels[0].value = [ - F::from_canonical_u16(37543), - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.mem_channels[1].value = [ - F::ZERO, - F::ZERO, - F::ZERO, - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.general.jumps_mut().input0_upper_zero = F::ONE; - row.general.jumps_mut().dst_valid = F::ONE; - row.general.jumps_mut().dst_valid_or_kernel = F::ONE; - row.general.jumps_mut().input0_jumpable = F::ONE; - row.general.jumps_mut().input1_sum_inv = F::ONE; - row.general.jumps_mut().should_jump = F::ONE; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `JUMPI` (not taken) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0x57); - row.is_kernel_mode = F::ZERO; - row.program_counter = F::from_canonical_u16(37543); - row.mem_channels[0].value = [ - F::from_canonical_u16(37543), - F::ZERO, - F::ZERO, - F::ZERO, - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.general.jumps_mut().input0_upper_sum_inv = F::ONE; - row.general.jumps_mut().dst_valid = F::ONE; - row.general.jumps_mut().dst_valid_or_kernel = F::ONE; - row.general.jumps_mut().input0_jumpable = F::ZERO; - row.general.jumps_mut().should_continue = F::ONE; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // `JUMP` (trapping) - { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - let last_row: cpu::columns::CpuColumnsView = - cpu_trace_rows[cpu_trace_rows.len() - 1].into(); - row.is_cpu_cycle = F::ONE; - row.opcode_bits = bits_from_opcode(0x56); - row.is_kernel_mode = F::ZERO; - row.program_counter = last_row.program_counter + F::ONE; - row.mem_channels[0].value = [ - F::from_canonical_u16(37543), - F::ZERO, - F::ZERO, - F::ZERO, - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.mem_channels[1].value = [ - F::ONE, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - F::ZERO, - ]; - row.general.jumps_mut().input0_upper_sum_inv = F::ONE; - row.general.jumps_mut().dst_valid = F::ONE; - row.general.jumps_mut().dst_valid_or_kernel = F::ONE; - row.general.jumps_mut().input0_jumpable = F::ZERO; - row.general.jumps_mut().input1_sum_inv = F::ONE; - row.general.jumps_mut().should_trap = F::ONE; - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // Pad to a power of two. - for i in 0..cpu_trace_rows.len().next_power_of_two() - cpu_trace_rows.len() { - let mut row: cpu::columns::CpuColumnsView = - [F::ZERO; CpuStark::::COLUMNS].into(); - row.opcode_bits = bits_from_opcode(0xff); - row.is_cpu_cycle = F::ONE; - row.is_kernel_mode = F::ONE; - row.program_counter = - F::from_canonical_usize(KERNEL.global_labels["fault_exception"] + i); - cpu_stark.generate(row.borrow_mut()); - cpu_trace_rows.push(row.into()); - } - - // Ensure we finish in a halted state. - { - let num_rows = cpu_trace_rows.len(); - let halt_label = F::from_canonical_usize(KERNEL.global_labels["halt_pc0"]); - - let last_row: &mut cpu::columns::CpuColumnsView = - cpu_trace_rows[num_rows - 1].borrow_mut(); - last_row.program_counter = halt_label; - } - - trace_rows_to_poly_values(cpu_trace_rows) - } - - fn get_proof(config: &StarkConfig) -> Result<(AllStark, AllProof)> { - let all_stark = AllStark::default(); - - let num_logic_rows = 62; - let num_memory_ops = 1 << 5; - - let mut rng = thread_rng(); - let num_keccak_perms = 2; - - let keccak_trace = - make_keccak_trace(num_keccak_perms, &all_stark.keccak_stark, config, &mut rng); - let keccak_memory_trace = make_keccak_memory_trace(&all_stark.keccak_memory_stark, config); - let logic_trace = - make_logic_trace(num_logic_rows, &all_stark.logic_stark, config, &mut rng); - let mem_trace = make_memory_trace(num_memory_ops, &all_stark.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, - num_memory_ops, - &all_stark.cpu_stark, - &keccak_trace, - &logic_trace, - &mut memory_trace, - ); - - let traces = [ - cpu_trace, - keccak_trace, - keccak_memory_trace, - logic_trace, - memory_trace, - ]; - check_ctls(&traces, &all_stark.cross_table_lookups); - - let public_values = PublicValues::default(); - let proof = prove_with_traces::( - &all_stark, - config, - traces, - public_values, - &mut TimingTree::default(), - )?; - - Ok((all_stark, proof)) - } - - #[test] - #[ignore] // Ignoring but not deleting so the test can serve as an API usage example - fn test_all_stark() -> Result<()> { - let config = StarkConfig::standard_fast_config(); - let (all_stark, proof) = get_proof(&config)?; - verify_proof(all_stark, proof, &config) - } - - #[test] - #[ignore] // Ignoring but not deleting so the test can serve as an API usage example - fn test_all_stark_recursive_verifier() -> Result<()> { - init_logger(); - - let config = StarkConfig::standard_fast_config(); - let (all_stark, proof) = get_proof(&config)?; - verify_proof(all_stark.clone(), proof.clone(), &config)?; - - recursive_proof(all_stark, proof, &config) - } - - fn recursive_proof( - inner_all_stark: AllStark, - inner_proof: AllProof, - inner_config: &StarkConfig, - ) -> Result<()> { - let circuit_config = CircuitConfig::standard_recursion_config(); - let recursive_all_proof = recursively_verify_all_proof( - &inner_all_stark, - &inner_proof, - inner_config, - &circuit_config, - )?; - - let verifier_data: [VerifierCircuitData; NUM_TABLES] = - all_verifier_data_recursive_stark_proof( - &inner_all_stark, - inner_proof.degree_bits(inner_config), - inner_config, - &circuit_config, - ); - let circuit_config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(circuit_config); - let mut pw = PartialWitness::new(); - let recursive_all_proof_target = - add_virtual_recursive_all_proof(&mut builder, &verifier_data); - set_recursive_all_proof_target(&mut pw, &recursive_all_proof_target, &recursive_all_proof); - RecursiveAllProof::verify_circuit( - &mut builder, - recursive_all_proof_target, - &verifier_data, - inner_all_stark.cross_table_lookups, - inner_config, - ); - - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof) - } - - fn init_logger() { - let _ = env_logger::builder().format_timestamp(None).try_init(); - } + let memory_looked = TableWithColumns::new( + Table::Memory, + memory_stark::ctl_data(), + Some(memory_stark::ctl_filter()), + ); + CrossTableLookup::new(all_lookers, memory_looked, None) } diff --git a/evm/src/cpu/bootstrap_kernel.rs b/evm/src/cpu/bootstrap_kernel.rs index 5480d2cd..ba45f738 100644 --- a/evm/src/cpu/bootstrap_kernel.rs +++ b/evm/src/cpu/bootstrap_kernel.rs @@ -13,66 +13,47 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::keccak_util::keccakf_u32s; +use crate::cpu::membus::NUM_GP_CHANNELS; use crate::generation::state::GenerationState; -use crate::keccak_sponge::columns::KECCAK_RATE_U32S; use crate::memory::segments::Segment; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; use crate::witness::memory::MemoryAddress; -use crate::witness::util::mem_write_gp_log_and_fill; - -/// We can't process more than `NUM_CHANNELS` bytes per row, since that's all the memory bandwidth -/// we have. We also can't process more than 4 bytes (or the number of bytes in a `u32`), since we -/// want them to fit in a single limb of Keccak input. -const BYTES_PER_ROW: usize = 4; +use crate::witness::util::{keccak_sponge_log, mem_write_gp_log_and_fill}; pub(crate) fn generate_bootstrap_kernel(state: &mut GenerationState) { - let mut sponge_state = [0u32; 50]; - let mut sponge_input_pos: usize = 0; - // Iterate through chunks of the code, such that we can write one chunk to memory per row. - for chunk in &KERNEL - .padded_code() - .iter() - .enumerate() - .chunks(BYTES_PER_ROW) - { - let mut current_cpu_row = CpuColumnsView::default(); - current_cpu_row.is_bootstrap_kernel = F::ONE; + for chunk in &KERNEL.code.iter().enumerate().chunks(NUM_GP_CHANNELS) { + let mut cpu_row = CpuColumnsView::default(); + cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + cpu_row.is_bootstrap_kernel = F::ONE; // Write this chunk to memory, while simultaneously packing its bytes into a u32 word. - let mut packed_bytes: u32 = 0; for (channel, (addr, &byte)) in chunk.enumerate() { let address = MemoryAddress::new(0, Segment::Code, addr); - let write = mem_write_gp_log_and_fill( - channel, - address, - state, - &mut current_cpu_row, - byte.into(), - ); + let write = + mem_write_gp_log_and_fill(channel, address, state, &mut cpu_row, byte.into()); state.traces.push_memory(write); - - packed_bytes = (packed_bytes << 8) | byte as u32; } - sponge_state[sponge_input_pos] = packed_bytes; - let keccak = current_cpu_row.general.keccak_mut(); - keccak.input_limbs = sponge_state.map(F::from_canonical_u32); - - sponge_input_pos = (sponge_input_pos + 1) % KECCAK_RATE_U32S; - // If we just crossed a multiple of KECCAK_RATE_LIMBS, then we've filled the Keccak input - // buffer, so it's time to absorb. - if sponge_input_pos == 0 { - current_cpu_row.is_keccak = F::ONE; - // TODO: Push sponge_state to Keccak inputs in traces. - keccakf_u32s(&mut sponge_state); - let keccak = current_cpu_row.general.keccak_mut(); - keccak.output_limbs = sponge_state.map(F::from_canonical_u32); - } - - state.traces.push_cpu(current_cpu_row); + state.traces.push_cpu(cpu_row); } + + let mut final_cpu_row = CpuColumnsView::default(); + final_cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + final_cpu_row.is_bootstrap_kernel = F::ONE; + final_cpu_row.is_keccak_sponge = F::ONE; + // The Keccak sponge CTL uses memory value columns for its inputs and outputs. + final_cpu_row.mem_channels[0].value[0] = F::ZERO; + final_cpu_row.mem_channels[1].value[0] = F::from_canonical_usize(Segment::Code as usize); + final_cpu_row.mem_channels[2].value[0] = F::ZERO; + final_cpu_row.mem_channels[3].value[0] = F::from_canonical_usize(state.traces.clock()); + final_cpu_row.mem_channels[4].value = KERNEL.code_hash.map(F::from_canonical_u32); + state.traces.push_cpu(final_cpu_row); + keccak_sponge_log( + state, + MemoryAddress::new(0, Segment::Code, 0), + KERNEL.code.clone(), + ); } pub(crate) fn eval_bootstrap_kernel>( @@ -90,24 +71,29 @@ pub(crate) fn eval_bootstrap_kernel>( let delta_is_bootstrap = next_is_bootstrap - local_is_bootstrap; yield_constr.constraint_transition(delta_is_bootstrap * (delta_is_bootstrap + P::ONES)); - // TODO: Constraints to enforce that, if IS_BOOTSTRAP_KERNEL, - // - If CLOCK is a multiple of KECCAK_RATE_LIMBS, activate the Keccak CTL, and ensure the output - // is copied to the next row (besides the first limb which will immediately be overwritten). - // - Otherwise, ensure that the Keccak input is copied to the next row (besides the next limb). - // - The next limb we add to the buffer is also written to memory. + // If this is a bootloading row and the i'th memory channel is used, it must have the right + // address, name context = 0, segment = Code, virt = clock * NUM_GP_CHANNELS + i. + let code_segment = F::from_canonical_usize(Segment::Code as usize); + for (i, channel) in local_values.mem_channels.iter().enumerate() { + let filter = local_is_bootstrap * channel.used; + yield_constr.constraint(filter * channel.addr_context); + yield_constr.constraint(filter * (channel.addr_segment - code_segment)); + let expected_virt = local_values.clock * F::from_canonical_usize(NUM_GP_CHANNELS) + + F::from_canonical_usize(i); + yield_constr.constraint(filter * (channel.addr_virtual - expected_virt)); + } - // If IS_BOOTSTRAP_KERNEL changed (from 1 to 0), check that - // - the clock is a multiple of KECCAK_RATE_LIMBS (TODO) + // If this is the final bootstrap row (i.e. delta_is_bootstrap = 1), check that + // - all memory channels are disabled (TODO) // - the current kernel hash matches a precomputed one for (&expected, actual) in KERNEL .code_hash .iter() - .zip(local_values.general.keccak().output_limbs) + .zip(local_values.mem_channels.last().unwrap().value) { let expected = P::from(F::from_canonical_u32(expected)); - let _diff = expected - actual; - // TODO: Not working yet. - // yield_constr.constraint_transition(delta_is_bootstrap * diff); + let diff = expected - actual; + yield_constr.constraint_transition(delta_is_bootstrap * diff); } } @@ -131,24 +117,39 @@ pub(crate) fn eval_bootstrap_kernel_circuit, const builder.mul_add_extension(delta_is_bootstrap, delta_is_bootstrap, delta_is_bootstrap); yield_constr.constraint_transition(builder, constraint); - // TODO: Constraints to enforce that, if IS_BOOTSTRAP_KERNEL, - // - If CLOCK is a multiple of KECCAK_RATE_LIMBS, activate the Keccak CTL, and ensure the output - // is copied to the next row (besides the first limb which will immediately be overwritten). - // - Otherwise, ensure that the Keccak input is copied to the next row (besides the next limb). - // - The next limb we add to the buffer is also written to memory. + // If this is a bootloading row and the i'th memory channel is used, it must have the right + // address, name context = 0, segment = Code, virt = clock * NUM_GP_CHANNELS + i. + let code_segment = + builder.constant_extension(F::Extension::from_canonical_usize(Segment::Code as usize)); + for (i, channel) in local_values.mem_channels.iter().enumerate() { + let filter = builder.mul_extension(local_is_bootstrap, channel.used); + let constraint = builder.mul_extension(filter, channel.addr_context); + yield_constr.constraint(builder, constraint); - // If IS_BOOTSTRAP_KERNEL changed (from 1 to 0), check that - // - the clock is a multiple of KECCAK_RATE_LIMBS (TODO) + let segment_diff = builder.sub_extension(channel.addr_segment, code_segment); + let constraint = builder.mul_extension(filter, segment_diff); + yield_constr.constraint(builder, constraint); + + let i_ext = builder.constant_extension(F::Extension::from_canonical_usize(i)); + let num_gp_channels_f = F::from_canonical_usize(NUM_GP_CHANNELS); + let expected_virt = + builder.mul_const_add_extension(num_gp_channels_f, local_values.clock, i_ext); + let virt_diff = builder.sub_extension(channel.addr_virtual, expected_virt); + let constraint = builder.mul_extension(filter, virt_diff); + yield_constr.constraint(builder, constraint); + } + + // If this is the final bootstrap row (i.e. delta_is_bootstrap = 1), check that + // - all memory channels are disabled (TODO) // - the current kernel hash matches a precomputed one for (&expected, actual) in KERNEL .code_hash .iter() - .zip(local_values.general.keccak().output_limbs) + .zip(local_values.mem_channels.last().unwrap().value) { let expected = builder.constant_extension(F::Extension::from_canonical_u32(expected)); let diff = builder.sub_extension(expected, actual); - let _constraint = builder.mul_extension(delta_is_bootstrap, diff); - // TODO: Not working yet. - // yield_constr.constraint_transition(builder, constraint); + let constraint = builder.mul_extension(delta_is_bootstrap, diff); + yield_constr.constraint_transition(builder, constraint); } } diff --git a/evm/src/cpu/columns/general.rs b/evm/src/cpu/columns/general.rs index de753182..67fe4256 100644 --- a/evm/src/cpu/columns/general.rs +++ b/evm/src/cpu/columns/general.rs @@ -6,7 +6,6 @@ use std::mem::{size_of, transmute}; /// operation is occurring at this row. #[derive(Clone, Copy)] pub(crate) union CpuGeneralColumnsView { - keccak: CpuKeccakView, arithmetic: CpuArithmeticView, logic: CpuLogicView, jumps: CpuJumpsView, @@ -14,16 +13,6 @@ pub(crate) union CpuGeneralColumnsView { } impl CpuGeneralColumnsView { - // SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn keccak(&self) -> &CpuKeccakView { - unsafe { &self.keccak } - } - - // SAFETY: Each view is a valid interpretation of the underlying array. - pub(crate) fn keccak_mut(&mut self) -> &mut CpuKeccakView { - unsafe { &mut self.keccak } - } - // SAFETY: Each view is a valid interpretation of the underlying array. pub(crate) fn arithmetic(&self) -> &CpuArithmeticView { unsafe { &self.arithmetic } @@ -94,12 +83,6 @@ impl BorrowMut<[T; NUM_SHARED_COLUMNS]> for CpuGeneralColumnsView { } } -#[derive(Copy, Clone)] -pub(crate) struct CpuKeccakView { - pub(crate) input_limbs: [T; 50], - pub(crate) output_limbs: [T; 50], -} - #[derive(Copy, Clone)] pub(crate) struct CpuArithmeticView { // TODO: Add "looking" columns for the arithmetic CTL. diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index cee1f2c5..408e17dc 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -69,11 +69,8 @@ pub struct CpuColumnsView { /// If CPU cycle: the opcode, broken up into bits in little-endian order. pub opcode_bits: [T; 8], - /// Filter. 1 iff a Keccak lookup is performed on this row. - pub is_keccak: T, - - /// Filter. 1 iff a Keccak memory lookup is performed on this row. - pub is_keccak_memory: T, + /// Filter. 1 iff a Keccak sponge lookup is performed on this row. + pub is_keccak_sponge: T, pub(crate) general: CpuGeneralColumnsView, diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 8a01ff6d..f0c85638 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -20,34 +20,28 @@ use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; -pub fn ctl_data_keccak() -> Vec> { - let keccak = COL_MAP.general.keccak(); - let mut res: Vec<_> = Column::singles(keccak.input_limbs).collect(); - res.extend(Column::singles(keccak.output_limbs)); - res -} - -pub fn ctl_data_keccak_memory() -> Vec> { +pub fn ctl_data_keccak_sponge() -> Vec> { // When executing KECCAK_GENERAL, the GP memory channels are used as follows: // GP channel 0: stack[-1] = context // GP channel 1: stack[-2] = segment - // GP channel 2: stack[-3] = virtual + // GP channel 2: stack[-3] = virt + // GP channel 3: stack[-4] = len + // GP channel 4: pushed = outputs let context = Column::single(COL_MAP.mem_channels[0].value[0]); let segment = Column::single(COL_MAP.mem_channels[1].value[0]); let virt = Column::single(COL_MAP.mem_channels[2].value[0]); + let len = Column::single(COL_MAP.mem_channels[3].value[0]); let num_channels = F::from_canonical_usize(NUM_CHANNELS); - let clock = Column::linear_combination([(COL_MAP.clock, num_channels)]); + let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); - vec![context, segment, virt, clock] + let mut cols = vec![context, segment, virt, len, timestamp]; + cols.extend(COL_MAP.mem_channels[3].value.map(Column::single)); + cols } -pub fn ctl_filter_keccak() -> Column { - Column::single(COL_MAP.is_keccak) -} - -pub fn ctl_filter_keccak_memory() -> Column { - Column::single(COL_MAP.is_keccak_memory) +pub fn ctl_filter_keccak_sponge() -> Column { + Column::single(COL_MAP.is_keccak_sponge) } pub fn ctl_data_logic() -> Vec> { @@ -122,6 +116,7 @@ pub struct CpuStark { } impl CpuStark { + // TODO: Remove? pub fn generate(&self, local_values: &mut [F; NUM_CPU_COLUMNS]) { let local_values: &mut CpuColumnsView<_> = local_values.borrow_mut(); decode::generate(local_values); diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index 57e4eb61..a0ca7ef2 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -2,19 +2,17 @@ use std::collections::HashMap; use ethereum_types::U256; use itertools::izip; +use keccak_hash::keccak; use log::debug; -use plonky2_util::ceil_div_usize; use super::ast::PushTarget; use crate::cpu::kernel::ast::Item::LocalLabelDeclaration; use crate::cpu::kernel::ast::{File, Item, StackReplacement}; -use crate::cpu::kernel::keccak_util::hash_kernel; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use crate::cpu::kernel::optimizer::optimize_asm; use crate::cpu::kernel::stack::stack_manipulation::expand_stack_manipulation; use crate::cpu::kernel::utils::u256_to_trimmed_be_bytes; use crate::generation::prover_input::ProverInputFn; -use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; /// The number of bytes to push when pushing an offset within the code (i.e. when assembling jumps). /// Ideally we would automatically use the minimal number of bytes required, but that would be @@ -41,8 +39,10 @@ impl Kernel { global_labels: HashMap, prover_inputs: HashMap, ) -> Self { - let code_hash = hash_kernel(&Self::padded_code_helper(&code)); - + let code_hash_bytes = keccak(&code).0; + let code_hash = std::array::from_fn(|i| { + u32::from_le_bytes(std::array::from_fn(|j| code_hash_bytes[i * 4 + j])) + }); Self { code, code_hash, @@ -51,18 +51,6 @@ impl Kernel { } } - /// Zero-pads the code such that its length is a multiple of the Keccak rate. - pub(crate) fn padded_code(&self) -> Vec { - Self::padded_code_helper(&self.code) - } - - fn padded_code_helper(code: &[u8]) -> Vec { - let padded_len = ceil_div_usize(code.len(), KECCAK_RATE_BYTES) * KECCAK_RATE_BYTES; - let mut padded_code = code.to_vec(); - padded_code.resize(padded_len, 0); - padded_code - } - /// Get a string representation of the current offset for debugging purposes. pub(crate) fn offset_name(&self, offset: usize) -> String { self.offset_label(offset) diff --git a/evm/src/cpu/kernel/keccak_util.rs b/evm/src/cpu/kernel/keccak_util.rs index bc6bff7a..38361389 100644 --- a/evm/src/cpu/kernel/keccak_util.rs +++ b/evm/src/cpu/kernel/keccak_util.rs @@ -1,29 +1,6 @@ use tiny_keccak::keccakf; -use crate::keccak_memory::columns::KECCAK_WIDTH_BYTES; -use crate::keccak_sponge::columns::{KECCAK_RATE_BYTES, KECCAK_RATE_U32S, KECCAK_WIDTH_U32S}; - -/// A Keccak-f based hash. -/// -/// This hash does not use standard Keccak padding, since we don't care about extra zeros at the -/// end of the code. It also uses an overwrite-mode sponge, rather than a standard sponge where -/// inputs are xor'ed in. -pub(crate) fn hash_kernel(code: &[u8]) -> [u32; 8] { - debug_assert_eq!( - code.len() % KECCAK_RATE_BYTES, - 0, - "Code should have been padded to a multiple of the Keccak rate." - ); - - let mut state = [0u32; 50]; - for chunk in code.chunks(KECCAK_RATE_BYTES) { - for i in 0..KECCAK_RATE_U32S { - state[i] = u32::from_le_bytes(std::array::from_fn(|j| chunk[i * 4 + j])); - } - keccakf_u32s(&mut state); - } - state[..8].try_into().unwrap() -} +use crate::keccak_sponge::columns::{KECCAK_WIDTH_BYTES, KECCAK_WIDTH_U32S}; /// Like tiny-keccak's `keccakf`, but deals with `u32` limbs instead of `u64` limbs. pub(crate) fn keccakf_u32s(state_u32s: &mut [u32; KECCAK_WIDTH_U32S]) { diff --git a/evm/src/cross_table_lookup.rs b/evm/src/cross_table_lookup.rs index 01e91746..4930321a 100644 --- a/evm/src/cross_table_lookup.rs +++ b/evm/src/cross_table_lookup.rs @@ -694,6 +694,7 @@ pub(crate) mod testutils { type MultiSet = HashMap, Vec<(Table, usize)>>; /// Check that the provided traces and cross-table lookups are consistent. + #[allow(unused)] // TODO: used later? pub(crate) fn check_ctls( trace_poly_values: &[Vec>], cross_table_lookups: &[CrossTableLookup], diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index 27a6bf8b..885760eb 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -22,7 +22,6 @@ impl From> for ProverInputFn { } impl GenerationState { - #[allow(unused)] // TODO: Should be used soon. pub(crate) fn prover_input(&mut self, input_fn: &ProverInputFn) -> U256 { match input_fn.0[0].as_str() { "end_of_txns" => self.run_end_of_txns(), diff --git a/evm/src/keccak_memory/columns.rs b/evm/src/keccak_memory/columns.rs deleted file mode 100644 index 92bdbf2b..00000000 --- a/evm/src/keccak_memory/columns.rs +++ /dev/null @@ -1,29 +0,0 @@ -pub(crate) const KECCAK_WIDTH_BYTES: usize = 200; - -/// 1 if this row represents a real operation; 0 if it's a padding row. -pub(crate) const COL_IS_REAL: usize = 0; - -// The address at which we will read inputs and write outputs. -pub(crate) const COL_CONTEXT: usize = 1; -pub(crate) const COL_SEGMENT: usize = 2; -pub(crate) const COL_VIRTUAL: usize = 3; - -/// The timestamp at which inputs should be read from memory. -/// Outputs will be written at the following timestamp. -pub(crate) const COL_READ_TIMESTAMP: usize = 4; - -const START_INPUT_LIMBS: usize = 5; -/// A byte of the input. -pub(crate) fn col_input_byte(i: usize) -> usize { - debug_assert!(i < KECCAK_WIDTH_BYTES); - START_INPUT_LIMBS + i -} - -const START_OUTPUT_LIMBS: usize = START_INPUT_LIMBS + KECCAK_WIDTH_BYTES; -/// A byte of the output. -pub(crate) fn col_output_byte(i: usize) -> usize { - debug_assert!(i < KECCAK_WIDTH_BYTES); - START_OUTPUT_LIMBS + i -} - -pub const NUM_COLUMNS: usize = START_OUTPUT_LIMBS + KECCAK_WIDTH_BYTES; diff --git a/evm/src/keccak_memory/keccak_memory_stark.rs b/evm/src/keccak_memory/keccak_memory_stark.rs deleted file mode 100644 index 3e41d4e1..00000000 --- a/evm/src/keccak_memory/keccak_memory_stark.rs +++ /dev/null @@ -1,222 +0,0 @@ -use std::marker::PhantomData; - -use plonky2::field::extension::{Extendable, FieldExtension}; -use plonky2::field::packed::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; -use plonky2::hash::hash_types::RichField; -use plonky2::timed; -use plonky2::util::timing::TimingTree; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::Column; -use crate::keccak::keccak_stark::NUM_INPUTS; -use crate::keccak_memory::columns::*; -use crate::stark::Stark; -use crate::util::trace_rows_to_poly_values; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; -use crate::witness::memory::MemoryAddress; - -pub(crate) fn ctl_looked_data() -> Vec> { - Column::singles([COL_CONTEXT, COL_SEGMENT, COL_VIRTUAL, COL_READ_TIMESTAMP]).collect() -} - -pub(crate) fn ctl_looking_keccak() -> Vec> { - let input_cols = (0..50).map(|i| { - Column::le_bytes((0..4).map(|j| { - let byte_index = i * 4 + j; - col_input_byte(byte_index) - })) - }); - let output_cols = (0..50).map(|i| { - Column::le_bytes((0..4).map(|j| { - let byte_index = i * 4 + j; - col_output_byte(byte_index) - })) - }); - input_cols.chain(output_cols).collect() -} - -pub(crate) fn ctl_looking_memory(i: usize, is_read: bool) -> Vec> { - let mut res = vec![Column::constant(F::from_bool(is_read))]; - res.extend(Column::singles([COL_CONTEXT, COL_SEGMENT, COL_VIRTUAL])); - - res.push(Column::single(col_input_byte(i))); - // Since we're reading or writing a single byte, the higher limbs must be zero. - res.extend((1..8).map(|_| Column::zero())); - - // Since COL_READ_TIMESTAMP is the read time, we add 1 if this is a write. - let is_write_f = F::from_bool(!is_read); - res.push(Column::linear_combination_with_constant( - [(COL_READ_TIMESTAMP, F::ONE)], - is_write_f, - )); - - assert_eq!( - res.len(), - crate::memory::memory_stark::ctl_data::().len() - ); - res -} - -/// CTL filter used for both directions (looked and looking). -pub(crate) fn ctl_filter() -> Column { - Column::single(COL_IS_REAL) -} - -/// Information about a Keccak memory operation needed for witness generation. -#[derive(Debug)] -pub(crate) struct KeccakMemoryOp { - /// The base address at which we will read inputs and write outputs. - pub(crate) address: MemoryAddress, - - /// The timestamp at which inputs should be read from memory. - /// Outputs will be written at the following timestamp. - pub(crate) read_timestamp: usize, - - /// The input that was read at that address. - pub(crate) input: [u64; NUM_INPUTS], - pub(crate) output: [u64; NUM_INPUTS], -} - -#[derive(Copy, Clone, Default)] -pub struct KeccakMemoryStark { - pub(crate) f: PhantomData, -} - -impl, const D: usize> KeccakMemoryStark { - #[allow(unused)] // TODO: Should be used soon. - pub(crate) fn generate_trace( - &self, - operations: Vec, - min_rows: usize, - timing: &mut TimingTree, - ) -> Vec> { - // Generate the witness row-wise. - let trace_rows = timed!( - timing, - "generate trace rows", - self.generate_trace_rows(operations, min_rows) - ); - - let trace_polys = timed!( - timing, - "convert to PolynomialValues", - trace_rows_to_poly_values(trace_rows) - ); - - trace_polys - } - - fn generate_trace_rows( - &self, - operations: Vec, - min_rows: usize, - ) -> Vec<[F; NUM_COLUMNS]> { - let num_rows = operations.len().max(min_rows).next_power_of_two(); - let mut rows = Vec::with_capacity(num_rows); - for op in operations { - rows.push(self.generate_row_for_op(op)); - } - - let padding_row = self.generate_padding_row(); - for _ in rows.len()..num_rows { - rows.push(padding_row); - } - rows - } - - fn generate_row_for_op(&self, op: KeccakMemoryOp) -> [F; NUM_COLUMNS] { - let mut row = [F::ZERO; NUM_COLUMNS]; - row[COL_IS_REAL] = F::ONE; - row[COL_CONTEXT] = F::from_canonical_usize(op.address.context); - row[COL_SEGMENT] = F::from_canonical_usize(op.address.segment); - row[COL_VIRTUAL] = F::from_canonical_usize(op.address.virt); - row[COL_READ_TIMESTAMP] = F::from_canonical_usize(op.read_timestamp); - for i in 0..25 { - let input_u64 = op.input[i]; - let output_u64 = op.output[i]; - for j in 0..8 { - let byte_index = i * 8 + j; - row[col_input_byte(byte_index)] = F::from_canonical_u8(input_u64.to_le_bytes()[j]); - row[col_output_byte(byte_index)] = - F::from_canonical_u8(output_u64.to_le_bytes()[j]); - } - } - row - } - - fn generate_padding_row(&self) -> [F; NUM_COLUMNS] { - // We just need COL_IS_REAL to be zero, which it is by default. - // The other fields will have no effect. - [F::ZERO; NUM_COLUMNS] - } -} - -impl, const D: usize> Stark for KeccakMemoryStark { - const COLUMNS: usize = NUM_COLUMNS; - - fn eval_packed_generic( - &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - // is_real must be 0 or 1. - let is_real = vars.local_values[COL_IS_REAL]; - yield_constr.constraint(is_real * (is_real - P::ONES)); - } - - fn eval_ext_circuit( - &self, - builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - // is_real must be 0 or 1. - let is_real = vars.local_values[COL_IS_REAL]; - let constraint = builder.mul_sub_extension(is_real, is_real, is_real); - yield_constr.constraint(builder, constraint); - } - - fn constraint_degree(&self) -> usize { - 2 - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; - - use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; - use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; - - #[test] - fn test_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = KeccakMemoryStark; - - 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 = KeccakMemoryStark; - - let stark = S { - f: Default::default(), - }; - test_stark_circuit_constraints::(stark) - } -} diff --git a/evm/src/keccak_memory/mod.rs b/evm/src/keccak_memory/mod.rs deleted file mode 100644 index 7b5e3d01..00000000 --- a/evm/src/keccak_memory/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod columns; -pub mod keccak_memory_stark; diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index a1d9a1e3..ebefce06 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -23,7 +23,6 @@ use crate::util::trace_rows_to_poly_values; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; use crate::witness::memory::MemoryAddress; -#[allow(unused)] // TODO: Should be used soon. pub(crate) fn ctl_looked_data() -> Vec> { let cols = KECCAK_SPONGE_COL_MAP; let outputs = Column::singles(&cols.updated_state_u32s[..8]); @@ -31,14 +30,13 @@ pub(crate) fn ctl_looked_data() -> Vec> { cols.context, cols.segment, cols.virt, - cols.timestamp, cols.len, + cols.timestamp, ]) .chain(outputs) .collect() } -#[allow(unused)] // TODO: Should be used soon. pub(crate) fn ctl_looking_keccak() -> Vec> { let cols = KECCAK_SPONGE_COL_MAP; Column::singles( @@ -52,7 +50,6 @@ pub(crate) fn ctl_looking_keccak() -> Vec> { .collect() } -#[allow(unused)] // TODO: Should be used soon. pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { let cols = KECCAK_SPONGE_COL_MAP; @@ -81,14 +78,18 @@ pub(crate) fn ctl_looking_memory(i: usize) -> Vec> { res } +pub(crate) fn num_logic_ctls() -> usize { + const U8S_PER_CTL: usize = 32; + ceil_div_usize(KECCAK_RATE_BYTES, U8S_PER_CTL) +} + /// CTL for performing the `i`th logic CTL. Since we need to do 136 byte XORs, and the logic CTL can /// XOR 32 bytes per CTL, there are 5 such CTLs. -#[allow(unused)] // TODO: Should be used soon. pub(crate) fn ctl_looking_logic(i: usize) -> Vec> { const U32S_PER_CTL: usize = 8; const U8S_PER_CTL: usize = 32; - debug_assert!(i < ceil_div_usize(KECCAK_RATE_BYTES, U8S_PER_CTL)); + debug_assert!(i < num_logic_ctls()); let cols = KECCAK_SPONGE_COL_MAP; let mut res = vec![ @@ -111,7 +112,7 @@ pub(crate) fn ctl_looking_logic(i: usize) -> Vec> { .chunks(size_of::()) .map(|chunk| Column::le_bytes(chunk)) .chain(repeat(Column::zero())) - .take(U8S_PER_CTL), + .take(U32S_PER_CTL), ); // The output contains the XOR'd rate part. @@ -124,14 +125,12 @@ pub(crate) fn ctl_looking_logic(i: usize) -> Vec> { res } -#[allow(unused)] // TODO: Should be used soon. pub(crate) fn ctl_looked_filter() -> Column { // The CPU table is only interested in our final-block rows, since those contain the final // sponge output. Column::single(KECCAK_SPONGE_COL_MAP.is_final_block) } -#[allow(unused)] // TODO: Should be used soon. /// CTL filter for reading the `i`th byte of input from memory. pub(crate) fn ctl_looking_memory_filter(i: usize) -> Column { // We perform the `i`th read if either @@ -141,6 +140,11 @@ pub(crate) fn ctl_looking_memory_filter(i: usize) -> Column { Column::sum(once(&cols.is_full_input_block).chain(&cols.is_final_input_len[i..])) } +pub(crate) fn ctl_looking_keccak_filter() -> Column { + let cols = KECCAK_SPONGE_COL_MAP; + Column::sum([cols.is_full_input_block, cols.is_final_block]) +} + /// Information about a Keccak sponge operation needed for witness generation. #[derive(Debug)] pub(crate) struct KeccakSpongeOp { @@ -150,15 +154,12 @@ pub(crate) struct KeccakSpongeOp { /// The timestamp at which inputs are read. pub(crate) timestamp: usize, - /// The length of the input, in bytes. - pub(crate) len: usize, - /// The input that was read. pub(crate) input: Vec, } #[derive(Copy, Clone, Default)] -pub(crate) struct KeccakSpongeStark { +pub struct KeccakSpongeStark { f: PhantomData, } @@ -259,7 +260,7 @@ impl, const D: usize> KeccakSpongeStark { sponge_state: [u32; KECCAK_WIDTH_U32S], final_inputs: &[u8], ) -> KeccakSpongeColumnsView { - assert_eq!(already_absorbed_bytes + final_inputs.len(), op.len); + assert_eq!(already_absorbed_bytes + final_inputs.len(), op.input.len()); let mut row = KeccakSpongeColumnsView { is_final_block: F::ONE, @@ -297,7 +298,7 @@ impl, const D: usize> KeccakSpongeStark { row.segment = F::from_canonical_usize(op.base_address.segment); row.virt = F::from_canonical_usize(op.base_address.virt); row.timestamp = F::from_canonical_usize(op.timestamp); - row.len = F::from_canonical_usize(op.len); + row.len = F::from_canonical_usize(op.input.len()); row.already_absorbed_bytes = F::from_canonical_usize(already_absorbed_bytes); row.original_rate_u32s = sponge_state[..KECCAK_RATE_U32S] @@ -448,7 +449,6 @@ mod tests { virt: 0, }, timestamp: 0, - len: input.len(), input, }; let stark = S::default(); diff --git a/evm/src/lib.rs b/evm/src/lib.rs index c48aef16..d311449b 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -15,7 +15,6 @@ pub mod cross_table_lookup; pub mod generation; mod get_challenges; pub mod keccak; -pub mod keccak_memory; pub mod keccak_sponge; pub mod logic; pub mod lookup; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index 6c0424c9..443b0380 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -439,97 +439,11 @@ impl, const D: usize> Stark for MemoryStark(num_ops: usize, rng: &mut R) -> Vec { - let mut memory_ops = Vec::new(); - - let mut current_memory_values: HashMap<(usize, Segment, usize), U256> = HashMap::new(); - let num_cycles = num_ops / 2; - 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; - for _ in 0..2 { - let mut channel_index = rng.gen_range(0..NUM_CHANNELS); - while used_indices.contains(&channel_index) { - channel_index = rng.gen_range(0..NUM_CHANNELS); - } - used_indices.insert(channel_index); - - let is_read = if clock == 0 { - false - } else { - !has_read && rng.gen() - }; - has_read = has_read || is_read; - - let (context, segment, virt, vals) = if is_read { - let written: Vec<_> = current_memory_values.keys().collect(); - let &(mut context, mut segment, mut virt) = - written[rng.gen_range(0..written.len())]; - while new_writes_this_cycle.contains_key(&(context, segment, virt)) { - (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 mut context = rng.gen_range(0..40); - let segments = [Segment::Code, Segment::Stack, Segment::MainMemory]; - let mut segment = *segments.choose(rng).unwrap(); - let mut virt = rng.gen_range(0..20); - while new_writes_this_cycle.contains_key(&(context, segment, virt)) { - context = rng.gen_range(0..40); - segment = *segments.choose(rng).unwrap(); - virt = rng.gen_range(0..20); - } - - let val = U256(rng.gen()); - - new_writes_this_cycle.insert((context, segment, virt), val); - - (context, segment, virt, val) - }; - - let timestamp = clock * NUM_CHANNELS + channel_index; - memory_ops.push(MemoryOp { - filter: true, - timestamp, - address: MemoryAddress { - context, - segment: segment as usize, - virt, - }, - kind: if is_read { Read } else { Write }, - value: vals, - }); - } - for (k, v) in new_writes_this_cycle { - current_memory_values.insert(k, v); - } - } - - memory_ops - } #[test] fn test_stark_degree() -> Result<()> { diff --git a/evm/src/prover.rs b/evm/src/prover.rs index 4627784d..c99e1873 100644 --- a/evm/src/prover.rs +++ b/evm/src/prover.rs @@ -24,7 +24,7 @@ use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{cross_table_lookup_data, CtlCheckVars, CtlData}; use crate::generation::{generate_traces, GenerationInputs}; use crate::keccak::keccak_stark::KeccakStark; -use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; +use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::{ @@ -49,7 +49,7 @@ where [(); C::Hasher::HASH_SIZE]:, [(); CpuStark::::COLUMNS]:, [(); KeccakStark::::COLUMNS]:, - [(); KeccakMemoryStark::::COLUMNS]:, + [(); KeccakSpongeStark::::COLUMNS]:, [(); LogicStark::::COLUMNS]:, [(); MemoryStark::::COLUMNS]:, { @@ -71,7 +71,7 @@ where [(); C::Hasher::HASH_SIZE]:, [(); CpuStark::::COLUMNS]:, [(); KeccakStark::::COLUMNS]:, - [(); KeccakMemoryStark::::COLUMNS]:, + [(); KeccakSpongeStark::::COLUMNS]:, [(); LogicStark::::COLUMNS]:, [(); MemoryStark::::COLUMNS]:, { @@ -132,12 +132,12 @@ where &mut challenger, timing, )?; - let keccak_memory_proof = prove_single_table( - &all_stark.keccak_memory_stark, + let keccak_sponge_proof = prove_single_table( + &all_stark.keccak_sponge_stark, config, - &trace_poly_values[Table::KeccakMemory as usize], - &trace_commitments[Table::KeccakMemory as usize], - &ctl_data_per_table[Table::KeccakMemory as usize], + &trace_poly_values[Table::KeccakSponge as usize], + &trace_commitments[Table::KeccakSponge as usize], + &ctl_data_per_table[Table::KeccakSponge as usize], &mut challenger, timing, )?; @@ -163,7 +163,7 @@ where let stark_proofs = [ cpu_proof, keccak_proof, - keccak_memory_proof, + keccak_sponge_proof, logic_proof, memory_proof, ]; diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index bf307699..bc1357a9 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -27,7 +27,7 @@ use crate::cross_table_lookup::{ CtlCheckVarsTarget, }; use crate::keccak::keccak_stark::KeccakStark; -use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; +use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::{ @@ -332,7 +332,7 @@ pub fn all_verifier_data_recursive_stark_proof< where [(); CpuStark::::COLUMNS]:, [(); KeccakStark::::COLUMNS]:, - [(); KeccakMemoryStark::::COLUMNS]:, + [(); KeccakSpongeStark::::COLUMNS]:, [(); LogicStark::::COLUMNS]:, [(); MemoryStark::::COLUMNS]:, [(); C::Hasher::HASH_SIZE]:, @@ -356,9 +356,9 @@ where circuit_config, ), verifier_data_recursive_stark_proof( - Table::KeccakMemory, - all_stark.keccak_memory_stark, - degree_bits[Table::KeccakMemory as usize], + Table::KeccakSponge, + all_stark.keccak_sponge_stark, + degree_bits[Table::KeccakSponge as usize], &all_stark.cross_table_lookups, inner_config, circuit_config, @@ -534,10 +534,10 @@ pub fn add_virtual_all_proof, const D: usize>( ), add_virtual_stark_proof( builder, - &all_stark.keccak_memory_stark, + &all_stark.keccak_sponge_stark, config, - degree_bits[Table::KeccakMemory as usize], - nums_ctl_zs[Table::KeccakMemory as usize], + degree_bits[Table::KeccakSponge as usize], + nums_ctl_zs[Table::KeccakSponge as usize], ), add_virtual_stark_proof( builder, @@ -853,7 +853,7 @@ pub(crate) mod tests { use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{CrossTableLookup, CtlCheckVarsTarget}; use crate::keccak::keccak_stark::KeccakStark; - use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; + use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::{GrandProductChallenge, GrandProductChallengeSet}; @@ -866,6 +866,7 @@ pub(crate) mod tests { /// Recursively verify a Stark proof. /// Outputs the recursive proof and the associated verifier data. + #[allow(unused)] // TODO: used later? fn recursively_verify_stark_proof< F: RichField + Extendable, C: GenericConfig, @@ -965,6 +966,7 @@ pub(crate) mod tests { } /// Recursively verify every Stark proof in an `AllProof`. + #[allow(unused)] // TODO: used later? pub fn recursively_verify_all_proof< F: RichField + Extendable, C: GenericConfig, @@ -978,7 +980,7 @@ pub(crate) mod tests { where [(); CpuStark::::COLUMNS]:, [(); KeccakStark::::COLUMNS]:, - [(); KeccakMemoryStark::::COLUMNS]:, + [(); KeccakSpongeStark::::COLUMNS]:, [(); LogicStark::::COLUMNS]:, [(); MemoryStark::::COLUMNS]:, [(); C::Hasher::HASH_SIZE]:, @@ -1013,9 +1015,9 @@ pub(crate) mod tests { )? .0, recursively_verify_stark_proof( - Table::KeccakMemory, - all_stark.keccak_memory_stark, - &all_proof.stark_proofs[Table::KeccakMemory as usize], + Table::KeccakSponge, + all_stark.keccak_sponge_stark, + &all_proof.stark_proofs[Table::KeccakSponge as usize], &all_stark.cross_table_lookups, &ctl_challenges, states[2], diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index ec870a0a..a0329d04 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -14,7 +14,7 @@ use crate::constraint_consumer::ConstraintConsumer; use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars}; use crate::keccak::keccak_stark::KeccakStark; -use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark; +use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeStark; use crate::logic::LogicStark; use crate::memory::memory_stark::MemoryStark; use crate::permutation::PermutationCheckVars; @@ -33,7 +33,7 @@ pub fn verify_proof, C: GenericConfig, co where [(); CpuStark::::COLUMNS]:, [(); KeccakStark::::COLUMNS]:, - [(); KeccakMemoryStark::::COLUMNS]:, + [(); KeccakSpongeStark::::COLUMNS]:, [(); LogicStark::::COLUMNS]:, [(); MemoryStark::::COLUMNS]:, [(); C::Hasher::HASH_SIZE]:, @@ -48,7 +48,7 @@ where let AllStark { cpu_stark, keccak_stark, - keccak_memory_stark, + keccak_sponge_stark, logic_stark, memory_stark, cross_table_lookups, @@ -76,10 +76,10 @@ where config, )?; verify_stark_proof_with_challenges( - keccak_memory_stark, - &all_proof.stark_proofs[Table::KeccakMemory as usize], - &stark_challenges[Table::KeccakMemory as usize], - &ctl_vars_per_table[Table::KeccakMemory as usize], + keccak_sponge_stark, + &all_proof.stark_proofs[Table::KeccakSponge as usize], + &stark_challenges[Table::KeccakSponge as usize], + &ctl_vars_per_table[Table::KeccakSponge as usize], config, )?; verify_stark_proof_with_challenges( diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 96631565..1c88ad80 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -5,20 +5,16 @@ use plonky2::field::types::Field; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::keccak_util::keccakf_u8s; use crate::cpu::membus::NUM_GP_CHANNELS; use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff; use crate::generation::state::GenerationState; -use crate::keccak_memory::columns::KECCAK_WIDTH_BYTES; -use crate::keccak_sponge::columns::KECCAK_RATE_BYTES; -use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; use crate::memory::segments::Segment; use crate::util::u256_saturating_cast_usize; use crate::witness::errors::ProgramError; use crate::witness::memory::MemoryAddress; use crate::witness::util::{ - mem_read_code_with_log_and_fill, mem_read_gp_with_log_and_fill, mem_write_gp_log_and_fill, - stack_pop_with_log_and_fill, stack_push_log_and_fill, + keccak_sponge_log, mem_read_code_with_log_and_fill, mem_read_gp_with_log_and_fill, + mem_write_gp_log_and_fill, stack_pop_with_log_and_fill, stack_push_log_and_fill, }; use crate::{arithmetic, logic}; @@ -109,6 +105,7 @@ pub(crate) fn generate_keccak_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { + row.is_keccak_sponge = F::ONE; let [(context, log_in0), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] = stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; let len = len.as_usize(); @@ -128,33 +125,7 @@ pub(crate) fn generate_keccak_general( let hash = keccak(&input); let log_push = stack_push_log_and_fill(state, &mut row, hash.into_uint())?; - let mut input_blocks = input.chunks_exact(KECCAK_RATE_BYTES); - let mut sponge_state = [0u8; KECCAK_WIDTH_BYTES]; - for block in input_blocks.by_ref() { - sponge_state[..KECCAK_RATE_BYTES].copy_from_slice(block); - state.traces.push_keccak_bytes(sponge_state); - keccakf_u8s(&mut sponge_state); - } - - let final_inputs = input_blocks.remainder(); - sponge_state[..final_inputs.len()].copy_from_slice(final_inputs); - // pad10*1 rule - sponge_state[final_inputs.len()..KECCAK_RATE_BYTES].fill(0); - if final_inputs.len() == KECCAK_RATE_BYTES - 1 { - // Both 1s are placed in the same byte. - sponge_state[final_inputs.len()] = 0b10000001; - } else { - sponge_state[final_inputs.len()] = 1; - sponge_state[KECCAK_RATE_BYTES - 1] = 0b10000000; - } - state.traces.push_keccak_bytes(sponge_state); - - state.traces.push_keccak_sponge(KeccakSpongeOp { - base_address, - timestamp: state.traces.clock(), - len, - input, - }); + keccak_sponge_log(state, base_address, input); state.traces.push_memory(log_in0); state.traces.push_memory(log_in1); diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index 9649499d..60e0d8af 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -9,8 +9,7 @@ use plonky2::util::timing::TimingTree; use crate::all_stark::{AllStark, NUM_TABLES}; use crate::config::StarkConfig; use crate::cpu::columns::CpuColumnsView; -use crate::keccak_memory::columns::KECCAK_WIDTH_BYTES; -use crate::keccak_memory::keccak_memory_stark::KeccakMemoryOp; +use crate::keccak_sponge::columns::KECCAK_WIDTH_BYTES; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; use crate::util::trace_rows_to_poly_values; use crate::witness::memory::MemoryOp; @@ -31,7 +30,6 @@ pub(crate) struct Traces { pub(crate) arithmetic: Vec, pub(crate) memory_ops: Vec, pub(crate) keccak_inputs: Vec<[u64; keccak::keccak_stark::NUM_INPUTS]>, - pub(crate) keccak_memory_inputs: Vec, pub(crate) keccak_sponge_ops: Vec, } @@ -43,7 +41,6 @@ impl Traces { arithmetic: vec![], memory_ops: vec![], keccak_inputs: vec![], - keccak_memory_inputs: vec![], keccak_sponge_ops: vec![], } } @@ -124,8 +121,7 @@ impl Traces { arithmetic: _, // TODO memory_ops, keccak_inputs, - keccak_memory_inputs, - keccak_sponge_ops: _, // TODO + keccak_sponge_ops, } = self; let cpu_rows = cpu.into_iter().map(|x| x.into()).collect(); @@ -134,11 +130,10 @@ impl Traces { all_stark .keccak_stark .generate_trace(keccak_inputs, cap_elements, timing); - let keccak_memory_trace = all_stark.keccak_memory_stark.generate_trace( - keccak_memory_inputs, - cap_elements, - timing, - ); + let keccak_sponge_trace = + all_stark + .keccak_sponge_stark + .generate_trace(keccak_sponge_ops, cap_elements, timing); let logic_trace = all_stark .logic_stark .generate_trace(logic_ops, cap_elements, timing); @@ -147,7 +142,7 @@ impl Traces { [ cpu_trace, keccak_trace, - keccak_memory_trace, + keccak_sponge_trace, logic_trace, memory_trace, ] diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index dd19cee4..74a9957f 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -225,6 +225,7 @@ fn perform_op( fn try_perform_instruction(state: &mut GenerationState) -> Result<(), ProgramError> { let mut row: CpuColumnsView = CpuColumnsView::default(); row.is_cpu_cycle = F::ONE; + row.clock = F::from_canonical_usize(state.traces.clock()); row.context = F::from_canonical_usize(state.registers.context); row.program_counter = F::from_canonical_usize(state.registers.program_counter); row.is_kernel_mode = F::from_bool(state.registers.is_kernel); diff --git a/evm/src/witness/util.rs b/evm/src/witness/util.rs index 59ae52f6..788f47e0 100644 --- a/evm/src/witness/util.rs +++ b/evm/src/witness/util.rs @@ -2,9 +2,12 @@ use ethereum_types::U256; use plonky2::field::types::Field; use crate::cpu::columns::CpuColumnsView; +use crate::cpu::kernel::keccak_util::keccakf_u8s; use crate::cpu::membus::NUM_GP_CHANNELS; use crate::cpu::stack_bounds::MAX_USER_STACK_SIZE; use crate::generation::state::GenerationState; +use crate::keccak_sponge::columns::{KECCAK_RATE_BYTES, KECCAK_WIDTH_BYTES}; +use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; use crate::memory::segments::Segment; use crate::witness::errors::ProgramError; use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; @@ -170,3 +173,40 @@ pub(crate) fn stack_push_log_and_fill( Ok(res) } + +pub(crate) fn keccak_sponge_log( + state: &mut GenerationState, + base_address: MemoryAddress, + input: Vec, +) { + let mut input_blocks = input.chunks_exact(KECCAK_RATE_BYTES); + let mut sponge_state = [0u8; KECCAK_WIDTH_BYTES]; + for block in input_blocks.by_ref() { + sponge_state[..KECCAK_RATE_BYTES].copy_from_slice(block); + state.traces.push_keccak_bytes(sponge_state); + // TODO: Also push logic rows for XORs. + // TODO: Also push memory read rows. + keccakf_u8s(&mut sponge_state); + } + + let final_inputs = input_blocks.remainder(); + sponge_state[..final_inputs.len()].copy_from_slice(final_inputs); + // pad10*1 rule + sponge_state[final_inputs.len()..KECCAK_RATE_BYTES].fill(0); + if final_inputs.len() == KECCAK_RATE_BYTES - 1 { + // Both 1s are placed in the same byte. + sponge_state[final_inputs.len()] = 0b10000001; + } else { + sponge_state[final_inputs.len()] = 1; + sponge_state[KECCAK_RATE_BYTES - 1] = 0b10000000; + } + state.traces.push_keccak_bytes(sponge_state); + // TODO: Also push logic rows for XORs. + // TODO: Also push memory read rows. + + state.traces.push_keccak_sponge(KeccakSpongeOp { + base_address, + timestamp: state.traces.clock(), + input, + }); +}