From d3aa33975ae9d3bc12dd1bd64b96c89f10fc4266 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Thu, 1 Dec 2022 12:06:29 -0800 Subject: [PATCH] generate_keccak_general, generate_byte --- evm/src/cpu/kernel/assembler.rs | 12 ++++ evm/src/cpu/kernel/interpreter.rs | 10 +-- evm/src/keccak_memory/keccak_memory_stark.rs | 14 ++-- evm/src/keccak_sponge/columns.rs | 2 +- evm/src/keccak_sponge/keccak_sponge_stark.rs | 14 ++-- evm/src/witness/errors.rs | 1 + evm/src/witness/memory.rs | 9 +++ evm/src/witness/operation.rs | 69 ++++++++++++++++---- evm/src/witness/traces.rs | 8 ++- evm/src/witness/transition.rs | 24 +++++-- 10 files changed, 118 insertions(+), 45 deletions(-) diff --git a/evm/src/cpu/kernel/assembler.rs b/evm/src/cpu/kernel/assembler.rs index eddc3272..57e4eb61 100644 --- a/evm/src/cpu/kernel/assembler.rs +++ b/evm/src/cpu/kernel/assembler.rs @@ -62,6 +62,18 @@ impl Kernel { 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) + .unwrap_or_else(|| offset.to_string()) + } + + pub(crate) fn offset_label(&self, offset: usize) -> Option { + self.global_labels + .iter() + .find_map(|(k, v)| (*v == offset).then(|| k.clone())) + } } #[derive(Eq, PartialEq, Hash, Clone, Debug)] diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 2fbb75ac..9c3d2938 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -355,18 +355,12 @@ impl<'a> Interpreter<'a> { Ok(()) } - /// Get a string representation of the current offset for debugging purposes. fn offset_name(&self) -> String { - self.offset_label() - .unwrap_or_else(|| self.offset.to_string()) + KERNEL.offset_name(self.offset) } fn offset_label(&self) -> Option { - // TODO: Not sure we should use KERNEL? Interpreter is more general in other places. - KERNEL - .global_labels - .iter() - .find_map(|(k, v)| (*v == self.offset).then(|| k.clone())) + KERNEL.offset_label(self.offset) } fn run_stop(&mut self) { diff --git a/evm/src/keccak_memory/keccak_memory_stark.rs b/evm/src/keccak_memory/keccak_memory_stark.rs index 3719fc8e..69d9ed6e 100644 --- a/evm/src/keccak_memory/keccak_memory_stark.rs +++ b/evm/src/keccak_memory/keccak_memory_stark.rs @@ -12,10 +12,10 @@ 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::memory::segments::Segment; 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() @@ -67,10 +67,8 @@ pub(crate) fn ctl_filter() -> Column { /// Information about a Keccak memory operation needed for witness generation. #[derive(Debug)] pub(crate) struct KeccakMemoryOp { - // The address at which we will read inputs and write outputs. - pub(crate) context: usize, - pub(crate) segment: Segment, - pub(crate) virt: usize, + /// 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. @@ -131,9 +129,9 @@ impl, const D: usize> KeccakMemoryStark { 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.context); - row[COL_SEGMENT] = F::from_canonical_usize(op.segment as usize); - row[COL_VIRTUAL] = F::from_canonical_usize(op.virt); + row[COL_CONTEXT] = F::from_canonical_usize(op.address.context); + row[COL_SEGMENT] = F::from_canonical_usize(op.address.segment as usize); + 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]; diff --git a/evm/src/keccak_sponge/columns.rs b/evm/src/keccak_sponge/columns.rs index 08194e87..440c59ab 100644 --- a/evm/src/keccak_sponge/columns.rs +++ b/evm/src/keccak_sponge/columns.rs @@ -21,7 +21,7 @@ pub(crate) struct KeccakSpongeColumnsView { /// in the block will be padding bytes; 0 otherwise. pub is_final_block: T, - // The address at which we will read the input block. + // The base address at which we will read the input block. pub context: T, pub segment: T, pub virt: T, diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index f2af8895..b492d594 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -18,10 +18,10 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::cpu::kernel::keccak_util::keccakf_u32s; use crate::cross_table_lookup::Column; use crate::keccak_sponge::columns::*; -use crate::memory::segments::Segment; use crate::stark::Stark; 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> { @@ -144,10 +144,8 @@ pub(crate) fn ctl_looking_memory_filter(i: usize) -> Column { /// Information about a Keccak sponge operation needed for witness generation. #[derive(Debug)] pub(crate) struct KeccakSpongeOp { - // The address at which inputs are read. - pub(crate) context: usize, - pub(crate) segment: Segment, - pub(crate) virt: usize, + /// The base address at which inputs are read. + pub(crate) base_address: MemoryAddress, /// The timestamp at which inputs are read. pub(crate) timestamp: usize, @@ -295,9 +293,9 @@ impl, const D: usize> KeccakSpongeStark { already_absorbed_bytes: usize, mut sponge_state: [u32; KECCAK_WIDTH_U32S], ) { - row.context = F::from_canonical_usize(op.context); - row.segment = F::from_canonical_usize(op.segment as usize); - row.virt = F::from_canonical_usize(op.virt); + row.context = F::from_canonical_usize(op.base_address.context); + row.segment = F::from_canonical_usize(op.base_address.segment as usize); + 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.already_absorbed_bytes = F::from_canonical_usize(already_absorbed_bytes); diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 1bc8417d..bd4b03c9 100644 --- a/evm/src/witness/errors.rs +++ b/evm/src/witness/errors.rs @@ -1,4 +1,5 @@ #[allow(dead_code)] +#[derive(Debug)] pub enum ProgramError { OutOfGas, InvalidOpcode, diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index f80c089b..24eec867 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -13,6 +13,7 @@ pub enum MemoryChannel { use MemoryChannel::{Code, GeneralPurpose}; use crate::memory::segments::Segment; +use crate::util::u256_saturating_cast_usize; impl MemoryChannel { pub fn index(&self) -> usize { @@ -41,6 +42,14 @@ impl MemoryAddress { virt, } } + + pub(crate) fn new_u256s(context: U256, segment: U256, virt: U256) -> Self { + Self { + context: u256_saturating_cast_usize(context), + segment: u256_saturating_cast_usize(segment), + virt: u256_saturating_cast_usize(virt), + } + } } #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 56bca279..cf9f69b3 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -7,6 +7,7 @@ use crate::cpu::kernel::aggregator::KERNEL; 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_sponge::keccak_sponge_stark::KeccakSpongeOp; use crate::memory::segments::Segment; use crate::util::u256_saturating_cast_usize; use crate::witness::errors::ProgramError; @@ -100,6 +101,40 @@ pub(crate) fn generate_ternary_arithmetic_op( Ok(()) } +pub(crate) fn generate_keccak_general( + state: &mut GenerationState, + mut row: CpuColumnsView, +) -> Result<(), ProgramError> { + 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(); + + let mut base_address = MemoryAddress::new_u256s(context, segment, base_virt); + let input = (0..len) + .map(|i| { + let address = MemoryAddress { + virt: base_address.virt.saturating_add(i), + ..base_address + }; + let val = state.memory.get(address); + val.as_u32() as u8 + }) + .collect(); + + state.traces.push_keccak_sponge(KeccakSpongeOp { + base_address, + timestamp: state.traces.clock(), + len, + input, + }); + state.traces.push_memory(log_in0); + state.traces.push_memory(log_in1); + state.traces.push_memory(log_in2); + state.traces.push_memory(log_in3); + state.traces.push_cpu(row); + Ok(()) +} + pub(crate) fn generate_prover_input( state: &mut GenerationState, mut row: CpuColumnsView, @@ -285,6 +320,26 @@ pub(crate) fn generate_not( Ok(()) } +pub(crate) fn generate_byte( + state: &mut GenerationState, + mut row: CpuColumnsView, +) -> Result<(), ProgramError> { + let [(i, log_in0), (x, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; + + let byte = if i < 32.into() { + x.byte(i.as_usize()) + } else { + 0 + }; + let log_out = stack_push_log_and_fill(state, &mut row, byte.into())?; + + state.traces.push_memory(log_in0); + state.traces.push_memory(log_in1); + state.traces.push_memory(log_out); + state.traces.push_cpu(row); + Ok(()) +} + pub(crate) fn generate_iszero( state: &mut GenerationState, mut row: CpuColumnsView, @@ -395,17 +450,9 @@ pub(crate) fn generate_mload_general( let [(context, log_in0), (segment, log_in1), (virt, log_in2)] = stack_pop_with_log_and_fill::<3, _>(state, &mut row)?; - // If virt won't fit in a usize, don't try to convert it, just return 0. - let val = if virt > usize::MAX.into() { - U256::zero() - } else { - state.memory.get(MemoryAddress { - context: context.as_usize(), - segment: segment.as_usize(), - virt: virt.as_usize(), - }) - }; - + let val = state + .memory + .get(MemoryAddress::new_u256s(context, segment, virt)); let log_out = stack_push_log_and_fill(state, &mut row, val)?; state.traces.push_memory(log_in0); diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index b4de5378..05540ed7 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -78,8 +78,12 @@ impl Traces { self.arithmetic.push(op); } - pub fn push_memory(&mut self, val: MemoryOp) { - self.memory_ops.push(val); + pub fn push_memory(&mut self, op: MemoryOp) { + self.memory_ops.push(op); + } + + pub fn push_keccak_sponge(&mut self, op: KeccakSpongeOp) { + self.keccak_sponge_ops.push(op); } pub fn clock(&self) -> usize { diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index 76f2cebc..2712163c 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -1,13 +1,15 @@ +use itertools::Itertools; use plonky2::field::types::Field; use crate::cpu::columns::CpuColumnsView; +use crate::cpu::kernel::aggregator::KERNEL; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::witness::errors::ProgramError; use crate::witness::memory::MemoryAddress; use crate::witness::operation::*; use crate::witness::state::RegistersState; -use crate::witness::util::mem_read_code_with_log_and_fill; +use crate::witness::util::{mem_read_code_with_log_and_fill, stack_peek}; use crate::{arithmetic, logic}; fn read_code_memory(state: &mut GenerationState, row: &mut CpuColumnsView) -> u8 { @@ -186,7 +188,7 @@ fn perform_op( Operation::Swap(n) => generate_swap(n, state, row)?, Operation::Iszero => generate_iszero(state, row)?, Operation::Not => generate_not(state, row)?, - Operation::Byte => todo!(), + Operation::Byte => generate_byte(state, row)?, Operation::Syscall(opcode) => generate_syscall(opcode, state, row)?, Operation::Eq => generate_eq(state, row)?, Operation::BinaryLogic(binary_logic_op) => { @@ -194,7 +196,7 @@ fn perform_op( } Operation::BinaryArithmetic(op) => generate_binary_arithmetic_op(op, state, row)?, Operation::TernaryArithmetic(op) => generate_ternary_arithmetic_op(op, state, row)?, - Operation::KeccakGeneral => todo!(), + Operation::KeccakGeneral => generate_keccak_general(state, row)?, Operation::ProverInput => generate_prover_input(state, row)?, Operation::Pop => generate_pop(state, row)?, Operation::Jump => generate_jump(state, row)?, @@ -226,7 +228,15 @@ fn try_perform_instruction(state: &mut GenerationState) -> Result<( let opcode = read_code_memory(state, &mut row); let op = decode(state.registers, opcode)?; - log::trace!("Executing {:?} at {}", op, state.registers.program_counter); + let pc = state.registers.program_counter; + + log::trace!("Executing {:?} at {}", op, KERNEL.offset_name(pc)); + log::trace!( + "Stack: {:?}", + (0..state.registers.stack_len) + .map(|i| stack_peek(state, i).unwrap()) + .collect_vec() + ); fill_op_flag(op, &mut row); perform_op(state, op, row) @@ -246,11 +256,11 @@ pub(crate) fn transition(state: &mut GenerationState) { .memory .apply_ops(state.traces.mem_ops_since(checkpoint.traces)); } - Err(_) => { - state.rollback(checkpoint); + Err(e) => { if state.registers.is_kernel { - panic!("exception in kernel mode"); + panic!("exception in kernel mode: {:?}", e); } + state.rollback(checkpoint); handle_error(state) } }