From 87a9c002c98d6bb518559343a5281ad3a84250fa Mon Sep 17 00:00:00 2001 From: Jacqueline Nabaglo Date: Mon, 28 Nov 2022 13:19:40 -0800 Subject: [PATCH] Compiler errors + refactor --- evm/src/cpu/columns/general.rs | 1 + evm/src/cpu/columns/mod.rs | 2 +- evm/src/cpu/columns/ops.rs | 16 +- evm/src/cpu/cpu_stark.rs | 1 - evm/src/cpu/mod.rs | 2 +- evm/src/cpu/simple_logic/eq_iszero.rs | 33 +-- evm/src/cpu/simple_logic/mod.rs | 13 +- evm/src/cpu/simple_logic/not.rs | 1 + evm/src/lib.rs | 1 + evm/src/witness/errors.rs | 3 +- evm/src/witness/memory.rs | 17 +- evm/src/witness/mod.rs | 5 +- evm/src/witness/operation.rs | 287 ++++++++++++++++++------- evm/src/witness/state.rs | 105 +-------- evm/src/witness/traces.rs | 64 +++++- evm/src/witness/transition.rs | 292 +++++++++++++++++++------- evm/src/witness/util.rs | 162 ++++++++++++++ 17 files changed, 687 insertions(+), 318 deletions(-) create mode 100644 evm/src/witness/util.rs diff --git a/evm/src/cpu/columns/general.rs b/evm/src/cpu/columns/general.rs index 134788dc..eea5551c 100644 --- a/evm/src/cpu/columns/general.rs +++ b/evm/src/cpu/columns/general.rs @@ -4,6 +4,7 @@ use std::mem::{size_of, transmute}; /// General purpose columns, which can have different meanings depending on what CTL or other /// operation is occurring at this row. +#[derive(Clone, Copy)] pub(crate) union CpuGeneralColumnsView { keccak: CpuKeccakView, arithmetic: CpuArithmeticView, diff --git a/evm/src/cpu/columns/mod.rs b/evm/src/cpu/columns/mod.rs index d0ef3f28..c485eca9 100644 --- a/evm/src/cpu/columns/mod.rs +++ b/evm/src/cpu/columns/mod.rs @@ -31,7 +31,7 @@ pub struct MemoryChannelView { } #[repr(C)] -#[derive(Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub struct CpuColumnsView { /// Filter. 1 if the row is part of bootstrapping the kernel code, 0 otherwise. pub is_bootstrap_kernel: T, diff --git a/evm/src/cpu/columns/ops.rs b/evm/src/cpu/columns/ops.rs index c265be44..ed254bb7 100644 --- a/evm/src/cpu/columns/ops.rs +++ b/evm/src/cpu/columns/ops.rs @@ -5,8 +5,8 @@ use std::ops::{Deref, DerefMut}; use crate::util::{indices_arr, transmute_no_compile_time_size_checks}; #[repr(C)] -#[derive(Eq, PartialEq, Debug)] -pub struct OpsColumnsView { +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct OpsColumnsView { // TODO: combine ADD, MUL, SUB, DIV, MOD, ADDFP254, MULFP254, SUBFP254, LT, and GT into one flag pub add: T, pub mul: T, @@ -65,38 +65,38 @@ pub struct OpsColumnsView { // `u8` is guaranteed to have a `size_of` of 1. pub const NUM_OPS_COLUMNS: usize = size_of::>(); -impl From<[T; NUM_OPS_COLUMNS]> for OpsColumnsView { +impl From<[T; NUM_OPS_COLUMNS]> for OpsColumnsView { fn from(value: [T; NUM_OPS_COLUMNS]) -> Self { unsafe { transmute_no_compile_time_size_checks(value) } } } -impl From> for [T; NUM_OPS_COLUMNS] { +impl From> for [T; NUM_OPS_COLUMNS] { fn from(value: OpsColumnsView) -> Self { unsafe { transmute_no_compile_time_size_checks(value) } } } -impl Borrow> for [T; NUM_OPS_COLUMNS] { +impl Borrow> for [T; NUM_OPS_COLUMNS] { fn borrow(&self) -> &OpsColumnsView { unsafe { transmute(self) } } } -impl BorrowMut> for [T; NUM_OPS_COLUMNS] { +impl BorrowMut> for [T; NUM_OPS_COLUMNS] { fn borrow_mut(&mut self) -> &mut OpsColumnsView { unsafe { transmute(self) } } } -impl Deref for OpsColumnsView { +impl Deref for OpsColumnsView { type Target = [T; NUM_OPS_COLUMNS]; fn deref(&self) -> &Self::Target { unsafe { transmute(self) } } } -impl DerefMut for OpsColumnsView { +impl DerefMut for OpsColumnsView { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { transmute(self) } } diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index 7b34cc4f..24c7064e 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -126,7 +126,6 @@ impl CpuStark { let local_values: &mut CpuColumnsView<_> = local_values.borrow_mut(); decode::generate(local_values); membus::generate(local_values); - simple_logic::generate(local_values); stack_bounds::generate(local_values); // Must come after `decode`. } } diff --git a/evm/src/cpu/mod.rs b/evm/src/cpu/mod.rs index 563361cf..f679e3ae 100644 --- a/evm/src/cpu/mod.rs +++ b/evm/src/cpu/mod.rs @@ -8,7 +8,7 @@ mod jumps; pub mod kernel; pub(crate) mod membus; mod modfp254; -mod simple_logic; +pub(crate) mod simple_logic; mod stack; mod stack_bounds; mod syscalls; diff --git a/evm/src/cpu/simple_logic/eq_iszero.rs b/evm/src/cpu/simple_logic/eq_iszero.rs index 36c5372b..8a084c14 100644 --- a/evm/src/cpu/simple_logic/eq_iszero.rs +++ b/evm/src/cpu/simple_logic/eq_iszero.rs @@ -1,6 +1,8 @@ +use ethereum_types::U256; use itertools::izip; use plonky2::field::extension::Extendable; use plonky2::field::packed::PackedField; +use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; @@ -9,7 +11,7 @@ use crate::cpu::columns::CpuColumnsView; fn limbs(x: U256) -> [u32; 8] { let mut res = [0; 8]; - let x_u64: &[u64; 4] = x.as_ref(); + let x_u64: [u64; 4] = x.0; for i in 0..4 { res[2 * i] = x_u64[i] as u32; res[2 * i + 1] = (x_u64[i] >> 32) as u32; @@ -17,28 +19,11 @@ fn limbs(x: U256) -> [u32; 8] { res } -pub fn generate_pinv_diff(val0: U256, val1: U256, lv: &mut CpuColumnsView) { - let input0 = lv.mem_channels[0].value; +pub fn generate_pinv_diff(val0: U256, val1: U256, lv: &mut CpuColumnsView) { + let val0_limbs = limbs(val0).map(F::from_canonical_u32); + let val1_limbs = limbs(val1).map(F::from_canonical_u32); - let eq_filter = lv.op.eq.to_canonical_u64(); - let iszero_filter = lv.op.iszero.to_canonical_u64(); - assert!(eq_filter <= 1); - assert!(iszero_filter <= 1); - assert!(eq_filter + iszero_filter <= 1); - - if eq_filter + iszero_filter == 0 { - return; - } - - let input1 = &mut lv.mem_channels[1].value; - if iszero_filter != 0 { - for limb in input1.iter_mut() { - *limb = F::ZERO; - } - } - - let input1 = lv.mem_channels[1].value; - let num_unequal_limbs = izip!(input0, input1) + let num_unequal_limbs = izip!(val0_limbs, val1_limbs) .map(|(limb0, limb1)| (limb0 != limb1) as usize) .sum(); let equal = num_unequal_limbs == 0; @@ -50,7 +35,7 @@ pub fn generate_pinv_diff(val0: U256, val1: U256, lv: &mut CpuColu } // Form `diff_pinv`. - // Let `diff = input0 - input1`. Consider `x[i] = diff[i]^-1` if `diff[i] != 0` and 0 otherwise. + // Let `diff = val0 - val1`. Consider `x[i] = diff[i]^-1` if `diff[i] != 0` and 0 otherwise. // Then `diff @ x = num_unequal_limbs`, where `@` denotes the dot product. We set // `diff_pinv = num_unequal_limbs^-1 * x` if `num_unequal_limbs != 0` and 0 otherwise. We have // `diff @ diff_pinv = 1 - equal` as desired. @@ -58,7 +43,7 @@ pub fn generate_pinv_diff(val0: U256, val1: U256, lv: &mut CpuColu let num_unequal_limbs_inv = F::from_canonical_usize(num_unequal_limbs) .try_inverse() .unwrap_or(F::ZERO); - for (limb_pinv, limb0, limb1) in izip!(logic.diff_pinv.iter_mut(), input0, input1) { + for (limb_pinv, limb0, limb1) in izip!(logic.diff_pinv.iter_mut(), val0_limbs, val1_limbs) { *limb_pinv = (limb0 - limb1).try_inverse().unwrap_or(F::ZERO) * num_unequal_limbs_inv; } } diff --git a/evm/src/cpu/simple_logic/mod.rs b/evm/src/cpu/simple_logic/mod.rs index 963b11b2..03d2dd15 100644 --- a/evm/src/cpu/simple_logic/mod.rs +++ b/evm/src/cpu/simple_logic/mod.rs @@ -1,4 +1,4 @@ -mod eq_iszero; +pub(crate) mod eq_iszero; mod not; use plonky2::field::extension::Extendable; @@ -9,17 +9,6 @@ use plonky2::iop::ext_target::ExtensionTarget; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; -pub fn generate(lv: &mut CpuColumnsView) { - let cycle_filter = lv.is_cpu_cycle.to_canonical_u64(); - if cycle_filter == 0 { - return; - } - assert_eq!(cycle_filter, 1); - - not::generate(lv); - eq_iszero::generate(lv); -} - pub fn eval_packed( lv: &CpuColumnsView

, yield_constr: &mut ConstraintConsumer

, diff --git a/evm/src/cpu/simple_logic/not.rs b/evm/src/cpu/simple_logic/not.rs index 0a29e068..16572e9c 100644 --- a/evm/src/cpu/simple_logic/not.rs +++ b/evm/src/cpu/simple_logic/not.rs @@ -6,6 +6,7 @@ use plonky2::iop::ext_target::ExtensionTarget; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; +use crate::cpu::membus::NUM_GP_CHANNELS; const LIMB_SIZE: usize = 32; const ALL_1_LIMB: u64 = (1 << LIMB_SIZE) - 1; diff --git a/evm/src/lib.rs b/evm/src/lib.rs index 6f332b59..da186409 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -29,3 +29,4 @@ pub mod util; pub mod vanishing_poly; pub mod vars; pub mod verifier; +pub mod witness; diff --git a/evm/src/witness/errors.rs b/evm/src/witness/errors.rs index 7d9dd6dc..1bc8417d 100644 --- a/evm/src/witness/errors.rs +++ b/evm/src/witness/errors.rs @@ -1,4 +1,5 @@ -enum ProgramError { +#[allow(dead_code)] +pub enum ProgramError { OutOfGas, InvalidOpcode, StackUnderflow, diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index 7a068489..e23ee62a 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -4,6 +4,7 @@ use ethereum_types::U256; use crate::cpu::membus::{NUM_CHANNELS, NUM_GP_CHANNELS}; +#[derive(Clone, Copy, Debug)] pub enum MemoryChannel { Code, GeneralPurpose(usize), @@ -25,12 +26,14 @@ impl MemoryChannel { pub type MemoryAddress = (u32, u32, u32); +#[derive(Clone, Copy, Debug)] pub enum MemoryOpKind { Read, Write(U256), } -pub struct MemoryOp { +#[derive(Clone, Copy, Debug)] +pub struct MemoryOp { pub timestamp: u64, pub address: MemoryAddress, pub op: MemoryOpKind, @@ -44,11 +47,14 @@ impl MemoryOp { op: MemoryOpKind, ) -> Self { let timestamp = (clock * NUM_CHANNELS + channel.index()) as u64; - MemoryOp { timestamp, address, op } + MemoryOp { + timestamp, + address, + op, + } } } - #[derive(Clone)] pub struct MemoryState { contents: HashMap, @@ -70,7 +76,10 @@ impl MemoryState { } pub fn get(&self, address: MemoryAddress) -> U256 { - self.contents.get(&address).copied().unwrap_or_else(U256::zero) + self.contents + .get(&address) + .copied() + .unwrap_or_else(U256::zero) } pub fn set(&mut self, address: MemoryAddress, val: U256) { diff --git a/evm/src/witness/mod.rs b/evm/src/witness/mod.rs index 409c0da7..1eb6d36a 100644 --- a/evm/src/witness/mod.rs +++ b/evm/src/witness/mod.rs @@ -1,4 +1,7 @@ -mod mem_tx; +mod errors; mod memory; +mod operation; mod state; mod traces; +pub mod transition; +mod util; diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 3d4026eb..d52136da 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -1,11 +1,27 @@ -use crate::cpu::kernel::aggregator::KERNEL; +use ethereum_types::U256; +use plonky2::field::types::Field; -enum Operation { +use crate::cpu::columns::CpuColumnsView; +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::logic; +use crate::memory::segments::Segment; +use crate::witness::errors::ProgramError; +use crate::witness::memory::MemoryState; +use crate::witness::state::RegistersState; +use crate::witness::traces::Traces; +use crate::witness::util::{ + mem_read_gp_with_log_and_fill, mem_write_gp_log_and_fill, stack_pop_with_log_and_fill, + stack_push_log_and_fill, +}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Operation { Dup(u8), Swap(u8), Iszero, Not, - Jump(JumpOp), Syscall(u8), Eq, ExitKernel, @@ -13,20 +29,15 @@ enum Operation { NotImplemented, } - -enum JumpOp { - Jump, - Jumpi, -} - -enum BinaryLogicOp { +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BinaryLogicOp { And, Or, Xor, } impl BinaryLogicOp { - fn result(&self, a: U256, b: U256) -> U256 { + pub(self) fn result(&self, a: U256, b: U256) -> U256 { match self { BinaryLogicOp::And => a & b, BinaryLogicOp::Or => a | b, @@ -35,9 +46,12 @@ impl BinaryLogicOp { } } - - -fn make_logic_row(op: BinaryLogicOp, in0: U256, in1: U256, result: U256) -> [F; logic::columns::NUM_COLUMNS] { +fn make_logic_row( + op: BinaryLogicOp, + in0: U256, + in1: U256, + result: U256, +) -> [F; logic::columns::NUM_COLUMNS] { let mut row = [F::ZERO; logic::columns::NUM_COLUMNS]; row[match op { BinaryLogicOp::And => logic::columns::IS_AND, @@ -45,112 +59,227 @@ fn make_logic_row(op: BinaryLogicOp, in0: U256, in1: U256, result: U256) -> [ BinaryLogicOp::Xor => logic::columns::IS_XOR, }] = F::ONE; for i in 0..256 { - row[logic::columns::INPUT0[i]] = F::from_bool(in0.bit(i)); - row[logic::columns::INPUT1[i]] = F::from_bool(in1.bit(i)); + row[logic::columns::INPUT0.start + i] = F::from_bool(in0.bit(i)); + row[logic::columns::INPUT1.start + i] = F::from_bool(in1.bit(i)); } let result_limbs: &[u64] = result.as_ref(); for (i, &limb) in result_limbs.iter().enumerate() { - row[logic::columns::RESULT[2 * i]] = F::from_canonical_u32(limb as u32); - row[logic::columns::RESULT[2 * i + 1]] = F::from_canonical_u32((limb >> 32) as u32); + row[logic::columns::RESULT.start + 2 * i] = F::from_canonical_u32(limb as u32); + row[logic::columns::RESULT.start + 2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); } row } - -fn generate_binary_logic_op(op: BinaryLogicOp, state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let ([in0, in1], logs_in) = state.pop_stack_with_log::<2>()?; +pub fn generate_binary_logic_op( + op: BinaryLogicOp, + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let [(in0, log_in0), (in1, log_in1)] = + stack_pop_with_log_and_fill::<2, _>(&mut registers_state, memory_state, traces, &mut row)?; let result = op.result(in0, in1); - let log_out = state.push_stack_with_log(result)?; + let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, result)?; - traces.logic.append(make_logic_row(op, in0, in1, result)); - traces.memory.extend(logs_in); - traces.memory.append(log_out); + traces.push_logic(make_logic_row(op, in0, in1, result)); + traces.push_memory(log_in0); + traces.push_memory(log_in1); + traces.push_memory(log_out); + traces.push_cpu(row); + Ok(registers_state) } -fn generate_dup(n: u8, state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let other_addr_lo = state.stack_len.sub_checked(1 + (n as usize)).ok_or(ProgramError::StackUnderflow)?; - let other_addr = (state.context, Segment::Stack as u32, other_addr_lo); +pub fn generate_dup( + n: u8, + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let other_addr_lo = registers_state + .stack_len + .checked_sub(1 + (n as u32)) + .ok_or(ProgramError::StackUnderflow)?; + let other_addr = ( + registers_state.context, + Segment::Stack as u32, + other_addr_lo, + ); - let (val, log_in) = state.mem_read_with_log(MemoryChannel::GeneralPurpose(0), other_addr); - let log_out = state.push_stack_with_log(val)?; + let (val, log_in) = + mem_read_gp_with_log_and_fill(0, other_addr, memory_state, traces, &mut row); + let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, val)?; - traces.memory.extend([log_in, log_out]); + traces.push_memory(log_in); + traces.push_memory(log_out); + traces.push_cpu(row); + Ok(registers_state) } -fn generate_swap(n: u8, state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let other_addr_lo = state.stack_len.sub_checked(2 + (n as usize)).ok_or(ProgramError::StackUnderflow)?; - let other_addr = (state.context, Segment::Stack as u32, other_addr_lo); +pub fn generate_swap( + n: u8, + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let other_addr_lo = registers_state + .stack_len + .checked_sub(2 + (n as u32)) + .ok_or(ProgramError::StackUnderflow)?; + let other_addr = ( + registers_state.context, + Segment::Stack as u32, + other_addr_lo, + ); - let ([in0], [log_in0]) = state.pop_stack_with_log::<1>()?; - let (in1, log_in1) = state.mem_read_with_log(MemoryChannel::GeneralPurpose(1), other_addr); - let log_out0 = state.mem_write_with_log(MemoryChannel::GeneralPurpose(NUM_GP_CHANNELS - 2), other_addr, in0); - let log_out1 = state.push_stack_with_log(in1)?; + let [(in0, log_in0)] = + stack_pop_with_log_and_fill::<1, _>(&mut registers_state, memory_state, traces, &mut row)?; + let (in1, log_in1) = + mem_read_gp_with_log_and_fill(1, other_addr, memory_state, traces, &mut row); + let log_out0 = + mem_write_gp_log_and_fill(NUM_GP_CHANNELS - 2, other_addr, traces, &mut row, in0); + let log_out1 = stack_push_log_and_fill(&mut registers_state, traces, &mut row, in1)?; - traces.memory.extend([log_in0, log_in1, log_out0, log_out1]); + traces.push_memory(log_in0); + traces.push_memory(log_in1); + traces.push_memory(log_out0); + traces.push_memory(log_out1); + traces.push_cpu(row); + Ok(registers_state) } -fn generate_not(state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let ([x], [log_in]) = state.pop_stack_with_log::<1>()?; +pub fn generate_not( + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let [(x, log_in)] = + stack_pop_with_log_and_fill::<1, _>(&mut registers_state, memory_state, traces, &mut row)?; let result = !x; - let log_out = state.push_stack_with_log(result)?; - - traces.memory.append(log_in); - traces.memory.append(log_out); + let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, result)?; + + traces.push_memory(log_in); + traces.push_memory(log_out); + traces.push_cpu(row); + Ok(registers_state) } -fn generate_iszero(state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let ([x], [log_in]) = state.pop_stack_with_log::<1>()?; - let is_zero = state.is_zero(); - let result = is_zero.into::().into::(); - let log_out = state.push_stack_with_log(result)?; +pub fn generate_iszero( + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let [(x, log_in)] = + stack_pop_with_log_and_fill::<1, _>(&mut registers_state, memory_state, traces, &mut row)?; + let is_zero = x.is_zero(); + let result = { + let t: u64 = is_zero.into(); + t.into() + }; + let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, result)?; - generate_pinv_diff(x, U256::zero(), row); + generate_pinv_diff(x, U256::zero(), &mut row); - traces.memory.append(log_in); - traces.memory.append(log_out); + traces.push_memory(log_in); + traces.push_memory(log_out); + traces.push_cpu(row); + Ok(registers_state) } -fn generate_jump(op: JumpOp, state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - todo!(); -} - -fn generate_syscall(opcode: u8, state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { +pub fn generate_syscall( + opcode: u8, + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"] as u32; let handler_addr_addr = handler_jumptable_addr + (opcode as u32); - let (handler_addr0, in_log0) = state.mem_read_with_log(MemoryChannel::GeneralPurpose(0), (0, Segment::Code as u32, handler_addr_addr)); - let (handler_addr1, in_log1) = state.mem_read_with_log(MemoryChannel::GeneralPurpose(1), (0, Segment::Code as u32, handler_addr_addr + 1)); - let (handler_addr2, in_log2) = state.mem_read_with_log(MemoryChannel::GeneralPurpose(2), (0, Segment::Code as u32, handler_addr_addr + 2)); + let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill( + 0, + (0, Segment::Code as u32, handler_addr_addr), + memory_state, + traces, + &mut row, + ); + let (handler_addr1, log_in1) = mem_read_gp_with_log_and_fill( + 1, + (0, Segment::Code as u32, handler_addr_addr + 1), + memory_state, + traces, + &mut row, + ); + let (handler_addr2, log_in2) = mem_read_gp_with_log_and_fill( + 2, + (0, Segment::Code as u32, handler_addr_addr + 2), + memory_state, + traces, + &mut row, + ); let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2; let new_program_counter = handler_addr.as_u32(); - let syscall_info = state.program_counter.into::() + (state.is_kernel_mode.into::.into:: << 32); - let log_out = state.push_stack_with_log(syscall_info)?; + let syscall_info = U256::from(registers_state.program_counter) + + (U256::from(u64::from(registers_state.is_kernel)) << 32); + let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, syscall_info)?; - state.program_counter = new_program_counter; - state.is_kernel = true; + registers_state.program_counter = new_program_counter; + registers_state.is_kernel = true; + + traces.push_memory(log_in0); + traces.push_memory(log_in1); + traces.push_memory(log_in2); + traces.push_memory(log_out); + traces.push_cpu(row); + + Ok(registers_state) } -fn generate_eq(state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let ([x0, x1], logs_in) = state.pop_stack_with_log::<1>()?; - let equal = x0 == x1; - let result = equal.into::().into::(); - let log_out = state.push_stack_with_log(result)?; +pub fn generate_eq( + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let [(in0, log_in0), (in1, log_in1)] = + stack_pop_with_log_and_fill::<2, _>(&mut registers_state, memory_state, traces, &mut row)?; + let eq = in0 == in1; + let result = U256::from(u64::from(eq)); + let log_out = stack_push_log_and_fill(&mut registers_state, traces, &mut row, result)?; - generate_pinv_diff(x0, x1, row); + generate_pinv_diff(in0, in1, &mut row); - traces.memory.extend(logs_in); - traces.memory.append(log_out); + traces.push_memory(log_in0); + traces.push_memory(log_in1); + traces.push_memory(log_out); + traces.push_cpu(row); + Ok(registers_state) } -fn generate_exit_kernel(state: &mut State, row: &mut CpuRow, traces: &mut Traces) -> Result<(), ProgramError> { - let ([kexit_info], [log_in]) = state.pop_stack_with_log::<1>()?; - let kexit_info_u64: &[u64; 4] = kexit_info.as_ref(); +pub fn generate_exit_kernel( + mut registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + mut row: CpuColumnsView, +) -> Result { + let [(kexit_info, log_in)] = + stack_pop_with_log_and_fill::<1, _>(&mut registers_state, memory_state, traces, &mut row)?; + let kexit_info_u64: [u64; 4] = kexit_info.0; let program_counter = kexit_info_u64[0] as u32; - let is_kernel_mode_val = (kexit_info_u64[1] >> 32) as u32 + let is_kernel_mode_val = (kexit_info_u64[1] >> 32) as u32; assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1); let is_kernel_mode = is_kernel_mode_val != 0; - state.program_counter = program_counter; - state.is_kernel = is_kernel_mode; + registers_state.program_counter = program_counter; + registers_state.is_kernel = is_kernel_mode; + + traces.push_memory(log_in); + traces.push_cpu(row); + + Ok(registers_state) } diff --git a/evm/src/witness/state.rs b/evm/src/witness/state.rs index dae9ac91..230d1287 100644 --- a/evm/src/witness/state.rs +++ b/evm/src/witness/state.rs @@ -1,108 +1,7 @@ -use ethereum_types::U256; - -use crate::cpu::kernel::aggregator::KERNEL; -use crate::witness::errors::ProgramError; -use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind, MemoryState}; - -pub const KERNEL_CONTEXT: u32 = 0; -pub const MAX_USER_STACK_SIZE: u32 = crate::cpu::stack_bounds::MAX_USER_STACK_SIZE as u32; - -#[derive(Clone)] -pub struct State { - pub clock: usize, - +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct RegistersState { pub program_counter: u32, - pub is_kernel: bool, pub stack_len: u32, pub context: u32, - - pub memory: MemoryState, -} - -impl State { - pub fn initial(clock: usize, memory: MemoryState) -> Self { - Self { - clock, - program_counter: KERNEL.global_labels["main"] as u32, - is_kernel: true, - stack_len: 0, - context: KERNEL_CONTEXT, - memory: memory, - } - } - - pub fn is_terminal(&self) -> bool { - self.is_kernel && [ - KERNEL.global_labels["halt_pc0"] as u32, - KERNEL.global_labels["halt_pc1"] as u32, - ].contains(&self.program_counter) - } - - pub fn mem_write_log( - &self, - channel: MemoryChannel, - address: MemoryAddress, - val: U256, - ) -> MemoryOp { - MemoryOp::new(channel, self.clock, address, MemoryOpKind::Write(val)) - } - - pub fn mem_write_with_log( - &mut self, - channel: MemoryChannel, - address: MemoryAddress, - val: U256, - ) -> MemoryOp { - (self.memory.set(address, val), self.mem_write_log(channel, address, val)) - } - - pub fn mem_read_log( - &self, - channel: MemoryChannel, - address: MemoryAddress, - ) -> MemoryOp { - MemoryOp::new(channel, self.clock, address, MemoryOpKind::Read) - } - - pub fn mem_read_with_log( - &self, - channel: MemoryChannel, - address: MemoryAddress, - ) -> (U256, MemoryOp) { - (self.memory.get(address), self.mem_read_log(channel, address)) - } - - pub fn pop_stack_with_log(&mut self) -> Result<[(U256, MemoryOp); N], ProgramError> { - if stack_len < N { - return Err(ProgramError::StackUnderflow); - } - - let mut result = [U256::default(); N]; - for i in 0..N { - let channel = GeneralPurpose(i); - let address = (self.context, Segment::Stack as u32, self.stack_len - 1 - i); - result[i] = self.mem_read_with_log(channel, address); - } - - self.stack_len -= N; - Ok(result) - } - - pub fn push_stack_with_log(&mut self, val: U256) -> Result { - if !self.is_kernel_mode { - assert!(self.stack_len <= MAX_USER_STACK_SIZE); - if self.stack_len == MAX_USER_STACK_SIZE { - return Err(ProgramError::StackOverflow); - } - } - - let channel = GeneralPurpose(NUM_GP_CHANNELS - 1); - let address = (self.context, Segment::Stack as u32, self.stack_len); - let result = self.mem_write_with_log(channel, address, val); - - self.stack_len += 1; - Ok(result) - } - } diff --git a/evm/src/witness/traces.rs b/evm/src/witness/traces.rs index d3fdf2be..7049572f 100644 --- a/evm/src/witness/traces.rs +++ b/evm/src/witness/traces.rs @@ -6,11 +6,20 @@ use crate::witness::memory::MemoryOp; type LogicRow = [T; logic::columns::NUM_COLUMNS]; type ArithmeticRow = [T; NUM_ARITH_COLUMNS]; -struct Traces { - pub cpu: Vec>, - pub logic: Vec>, - pub arithmetic: Vec>, - pub memory: Vec, +#[derive(Clone, Copy, Debug)] +pub struct TraceCheckpoint { + pub(self) cpu_len: usize, + pub(self) logic_len: usize, + pub(self) arithmetic_len: usize, + pub(self) memory_len: usize, +} + +#[derive(Clone, Debug)] +pub struct Traces { + cpu: Vec>, + logic: Vec>, + arithmetic: Vec>, + memory: Vec, } impl Traces { @@ -23,10 +32,45 @@ impl Traces { } } - pub fn append(&mut self, other: &mut Self) { - self.cpu.append(&mut other.cpu); - self.logic.append(&mut other.logic); - self.arithmetic.append(&mut other.arithmetic); - self.memory.append(&mut other.memory); + pub fn checkpoint(&self) -> TraceCheckpoint { + TraceCheckpoint { + cpu_len: self.cpu.len(), + logic_len: self.logic.len(), + arithmetic_len: self.arithmetic.len(), + memory_len: self.memory.len(), + } + } + + pub fn rollback(&mut self, checkpoint: TraceCheckpoint) { + self.cpu.truncate(checkpoint.cpu_len); + self.logic.truncate(checkpoint.logic_len); + self.arithmetic.truncate(checkpoint.arithmetic_len); + self.memory.truncate(checkpoint.memory_len); + } + + pub fn push_cpu(&mut self, val: CpuColumnsView) { + self.cpu.push(val); + } + + pub fn push_logic(&mut self, val: LogicRow) { + self.logic.push(val); + } + + pub fn push_arithmetic(&mut self, val: ArithmeticRow) { + self.arithmetic.push(val); + } + + pub fn push_memory(&mut self, val: MemoryOp) { + self.memory.push(val); + } + + pub fn clock(&self) -> usize { + self.cpu.len() + } +} + +impl Default for Traces { + fn default() -> Self { + Self::new() } } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index b53e6b29..0d9af5ce 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -1,87 +1,233 @@ -fn to_byte_checked(n: U256) -> u8 { - let res = n.byte(0); - assert_eq!(n, res.into()); - res -} +use plonky2::field::types::Field; -fn to_bits(n: u8) -> [F; 8] { - let mut res = [F::ZERO; 8]; - for (i, bit) in res.iter_mut().enumerate() { - *bit = F::from_bool(n & (1 << i) != 0); - } - res -} +use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; +use crate::memory::segments::Segment; +use crate::witness::errors::ProgramError; +use crate::witness::memory::MemoryState; +use crate::witness::operation::{ + generate_binary_logic_op, generate_dup, generate_eq, generate_exit_kernel, generate_iszero, + generate_not, generate_swap, generate_syscall, BinaryLogicOp, Operation, +}; +use crate::witness::state::RegistersState; +use crate::witness::traces::Traces; +use crate::witness::util::mem_read_code_with_log_and_fill; -fn decode(state: &State, row: &mut CpuRow) -> (Operation, MemoryLog) { - let code_context = if state.is_kernel { +const KERNEL_CONTEXT: u32 = 0; + +fn read_code_memory( + registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + row: &mut CpuColumnsView, +) -> u8 { + let code_context = if registers_state.is_kernel { KERNEL_CONTEXT } else { - state.context + registers_state.context }; row.code_context = F::from_canonical_u32(code_context); - let address = (context, Segment::Code as u32, state.program_counter); - let mem_contents, mem_log = state.mem_read_with_log(address); - let opcode = to_byte_checked(mem_contents); - row.opcode_bits = to_bits(address); + let address = ( + code_context, + Segment::Code as u32, + registers_state.program_counter, + ); + let (opcode, mem_log) = mem_read_code_with_log_and_fill(address, memory_state, traces, row); - let operation = match opcode { - 0x01 => Operation::NotImplemented, - 0x02 => Operation::NotImplemented, - 0x03 => Operation::NotImplemented, - 0x04 => Operation::NotImplemented, - 0x06 => Operation::NotImplemented, - 0x08 => Operation::NotImplemented, - 0x09 => Operation::NotImplemented, - 0x0c => Operation::NotImplemented, - 0x0d => Operation::NotImplemented, - 0x0e => Operation::NotImplemented, - 0x10 => Operation::NotImplemented, - 0x11 => Operation::NotImplemented, - 0x14 => Operation::Eq, - 0x15 => Operation::Iszero, - 0x16 => Operation::BinaryLogic(BinaryLogicOp::And), - 0x17 => Operation::BinaryLogic(BinaryLogicOp::Or), - 0x18 => Operation::BinaryLogic(BinaryLogicOp::Xor), - 0x19 => Operation::Not, - 0x1a => Operation::Byte, - 0x1b => Operation::NotImplemented, - 0x1c => Operation::NotImplemented, - 0x21 => Operation::NotImplemented, - 0x49 => Operation::NotImplemented, - 0x50 => Operation::NotImplemented, - 0x56 => Operation::Jump, - 0x57 => Operation::Jumpi, - 0x58 => Operation::NotImplemented, - 0x5a => Operation::NotImplemented, - 0x5b => Operation::NotImplemented, - 0x5c => Operation::NotImplemented, - 0x5d => Operation::NotImplemented, - 0x5e => Operation::NotImplemented, - 0x5f => Operation::NotImplemented, - 0x60..0x7f => Operation::NotImplemented, - 0x80..0x8f => Operation::Dup(opcode & 0xf), - 0x90..0x9f => Operation::Swap(opcode & 0xf), - 0xf6 => Operation::NotImplemented, - 0xf7 => Operation::NotImplemented, - 0xf8 => Operation::NotImplemented, - 0xf9 => Operation::ExitKernel, - 0xfb => Operation::NotImplemented, - 0xfc => Operation::NotImplemented, - _ => Operation::Syscall, + traces.push_memory(mem_log); + + opcode +} + +fn decode(registers_state: RegistersState, opcode: u8) -> Result { + match (opcode, registers_state.is_kernel) { + (0x00, _) => Ok(Operation::Syscall(opcode)), + (0x01, _) => Ok(Operation::NotImplemented), + (0x02, _) => Ok(Operation::NotImplemented), + (0x03, _) => Ok(Operation::NotImplemented), + (0x04, _) => Ok(Operation::NotImplemented), + (0x05, _) => Ok(Operation::Syscall(opcode)), + (0x06, _) => Ok(Operation::NotImplemented), + (0x07, _) => Ok(Operation::Syscall(opcode)), + (0x08, _) => Ok(Operation::NotImplemented), + (0x09, _) => Ok(Operation::NotImplemented), + (0x0a, _) => Ok(Operation::Syscall(opcode)), + (0x0b, _) => Ok(Operation::Syscall(opcode)), + (0x0c, true) => Ok(Operation::NotImplemented), + (0x0d, true) => Ok(Operation::NotImplemented), + (0x0e, true) => Ok(Operation::NotImplemented), + (0x10, _) => Ok(Operation::NotImplemented), + (0x11, _) => Ok(Operation::NotImplemented), + (0x12, _) => Ok(Operation::Syscall(opcode)), + (0x13, _) => Ok(Operation::Syscall(opcode)), + (0x14, _) => Ok(Operation::Eq), + (0x15, _) => Ok(Operation::Iszero), + (0x16, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::And)), + (0x17, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::Or)), + (0x18, _) => Ok(Operation::BinaryLogic(BinaryLogicOp::Xor)), + (0x19, _) => Ok(Operation::Not), + (0x1a, _) => Ok(Operation::NotImplemented), + (0x1b, _) => Ok(Operation::NotImplemented), + (0x1c, _) => Ok(Operation::NotImplemented), + (0x1d, _) => Ok(Operation::Syscall(opcode)), + (0x20, _) => Ok(Operation::Syscall(opcode)), + (0x21, _) => Ok(Operation::NotImplemented), + (0x30, _) => Ok(Operation::Syscall(opcode)), + (0x31, _) => Ok(Operation::Syscall(opcode)), + (0x32, _) => Ok(Operation::Syscall(opcode)), + (0x33, _) => Ok(Operation::Syscall(opcode)), + (0x34, _) => Ok(Operation::Syscall(opcode)), + (0x35, _) => Ok(Operation::Syscall(opcode)), + (0x36, _) => Ok(Operation::Syscall(opcode)), + (0x37, _) => Ok(Operation::Syscall(opcode)), + (0x38, _) => Ok(Operation::Syscall(opcode)), + (0x39, _) => Ok(Operation::Syscall(opcode)), + (0x3a, _) => Ok(Operation::Syscall(opcode)), + (0x3b, _) => Ok(Operation::Syscall(opcode)), + (0x3c, _) => Ok(Operation::Syscall(opcode)), + (0x3d, _) => Ok(Operation::Syscall(opcode)), + (0x3e, _) => Ok(Operation::Syscall(opcode)), + (0x3f, _) => Ok(Operation::Syscall(opcode)), + (0x40, _) => Ok(Operation::Syscall(opcode)), + (0x41, _) => Ok(Operation::Syscall(opcode)), + (0x42, _) => Ok(Operation::Syscall(opcode)), + (0x43, _) => Ok(Operation::Syscall(opcode)), + (0x44, _) => Ok(Operation::Syscall(opcode)), + (0x45, _) => Ok(Operation::Syscall(opcode)), + (0x46, _) => Ok(Operation::Syscall(opcode)), + (0x47, _) => Ok(Operation::Syscall(opcode)), + (0x48, _) => Ok(Operation::Syscall(opcode)), + (0x49, _) => Ok(Operation::NotImplemented), + (0x50, _) => Ok(Operation::NotImplemented), + (0x51, _) => Ok(Operation::Syscall(opcode)), + (0x52, _) => Ok(Operation::Syscall(opcode)), + (0x53, _) => Ok(Operation::Syscall(opcode)), + (0x54, _) => Ok(Operation::Syscall(opcode)), + (0x55, _) => Ok(Operation::Syscall(opcode)), + (0x56, _) => Ok(Operation::NotImplemented), + (0x57, _) => Ok(Operation::NotImplemented), + (0x58, _) => Ok(Operation::NotImplemented), + (0x59, _) => Ok(Operation::Syscall(opcode)), + (0x5a, _) => Ok(Operation::NotImplemented), + (0x5b, _) => Ok(Operation::NotImplemented), + (0x5c, true) => Ok(Operation::NotImplemented), + (0x5d, true) => Ok(Operation::NotImplemented), + (0x5e, true) => Ok(Operation::NotImplemented), + (0x5f, true) => Ok(Operation::NotImplemented), + (0x60..=0x7f, _) => Ok(Operation::NotImplemented), + (0x80..=0x8f, _) => Ok(Operation::Dup(opcode & 0xf)), + (0x90..=0x9f, _) => Ok(Operation::Swap(opcode & 0xf)), + (0xa0, _) => Ok(Operation::Syscall(opcode)), + (0xa1, _) => Ok(Operation::Syscall(opcode)), + (0xa2, _) => Ok(Operation::Syscall(opcode)), + (0xa3, _) => Ok(Operation::Syscall(opcode)), + (0xa4, _) => Ok(Operation::Syscall(opcode)), + (0xf0, _) => Ok(Operation::Syscall(opcode)), + (0xf1, _) => Ok(Operation::Syscall(opcode)), + (0xf2, _) => Ok(Operation::Syscall(opcode)), + (0xf3, _) => Ok(Operation::Syscall(opcode)), + (0xf4, _) => Ok(Operation::Syscall(opcode)), + (0xf5, _) => Ok(Operation::Syscall(opcode)), + (0xf6, true) => Ok(Operation::NotImplemented), + (0xf7, true) => Ok(Operation::NotImplemented), + (0xf8, true) => Ok(Operation::NotImplemented), + (0xf9, true) => Ok(Operation::ExitKernel), + (0xfa, _) => Ok(Operation::Syscall(opcode)), + (0xfb, true) => Ok(Operation::NotImplemented), + (0xfc, true) => Ok(Operation::NotImplemented), + (0xfd, _) => Ok(Operation::Syscall(opcode)), + (0xff, _) => Ok(Operation::Syscall(opcode)), + _ => Err(ProgramError::InvalidOpcode), } } -fn op_result() { - match op { +fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { + let flags = &mut row.op; + *match op { + Operation::Dup(_) => &mut flags.dup, + Operation::Swap(_) => &mut flags.swap, + Operation::Iszero => &mut flags.iszero, + Operation::Not => &mut flags.not, + Operation::Syscall(_) => &mut flags.syscall, + Operation::Eq => &mut flags.eq, + Operation::ExitKernel => &mut flags.exit_kernel, + Operation::BinaryLogic(BinaryLogicOp::And) => &mut flags.and, + Operation::BinaryLogic(BinaryLogicOp::Or) => &mut flags.or, + Operation::BinaryLogic(BinaryLogicOp::Xor) => &mut flags.xor, + Operation::NotImplemented => panic!("operation not implemented"), + } = F::ONE; +} +fn perform_op( + op: Operation, + registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, + row: CpuColumnsView, +) -> Result { + let mut new_registers_state = match op { + Operation::Dup(n) => generate_dup(n, registers_state, memory_state, traces, row)?, + Operation::Swap(n) => generate_swap(n, registers_state, memory_state, traces, row)?, + Operation::Iszero => generate_iszero(registers_state, memory_state, traces, row)?, + Operation::Not => generate_not(registers_state, memory_state, traces, row)?, + Operation::Syscall(opcode) => { + generate_syscall(opcode, registers_state, memory_state, traces, row)? + } + Operation::Eq => generate_eq(registers_state, memory_state, traces, row)?, + Operation::ExitKernel => generate_exit_kernel(registers_state, memory_state, traces, row)?, + Operation::BinaryLogic(binary_logic_op) => { + generate_binary_logic_op(binary_logic_op, registers_state, memory_state, traces, row)? + } + Operation::NotImplemented => panic!("operation not implemented"), + }; + + new_registers_state.program_counter += match op { + Operation::Syscall(_) | Operation::ExitKernel => 0, + _ => 1, + }; + + Ok(new_registers_state) +} + +fn try_perform_instruction( + registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, +) -> Result { + let mut row: CpuColumnsView = [F::ZERO; NUM_CPU_COLUMNS].into(); + row.is_cpu_cycle = F::ONE; + + let opcode = read_code_memory(registers_state, memory_state, traces, &mut row); + let op = decode(registers_state, opcode)?; + fill_op_flag(op, &mut row); + + perform_op(op, registers_state, memory_state, traces, row) +} + +fn handle_error( + _registers_state: RegistersState, + _memory_state: &MemoryState, + _traces: &mut Traces, +) -> RegistersState { + todo!("constraints for exception handling are not implemented"); +} + +pub fn transition( + registers_state: RegistersState, + memory_state: &MemoryState, + traces: &mut Traces, +) -> RegistersState { + let checkpoint = traces.checkpoint(); + let result = try_perform_instruction(registers_state, memory_state, traces); + + match result { + Ok(new_registers_state) => new_registers_state, + Err(_) => { + traces.rollback(checkpoint); + if registers_state.is_kernel { + panic!("exception in kernel mode"); + } + handle_error(registers_state, memory_state, traces) + } } } - -pub fn transition(state: State) -> (State, Traces) { - let mut current_row: CpuColumnsView = [F::ZERO; NUM_CPU_COLUMNS].into(); - - current_row.is_cpu_cycle = F::ONE; - - let (op, code_mem_log) = decode(&state, &mut current_row); -} diff --git a/evm/src/witness/util.rs b/evm/src/witness/util.rs new file mode 100644 index 00000000..94a4259f --- /dev/null +++ b/evm/src/witness/util.rs @@ -0,0 +1,162 @@ +use ethereum_types::U256; +use plonky2::field::types::Field; + +use crate::cpu::columns::CpuColumnsView; +use crate::cpu::membus::NUM_GP_CHANNELS; +use crate::memory::segments::Segment; +use crate::witness::errors::ProgramError; +use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind, MemoryState}; +use crate::witness::state::RegistersState; +use crate::witness::traces::Traces; + +fn to_byte_checked(n: U256) -> u8 { + let res = n.byte(0); + assert_eq!(n, res.into()); + res +} + +fn to_bits_le(n: u8) -> [F; 8] { + let mut res = [F::ZERO; 8]; + for (i, bit) in res.iter_mut().enumerate() { + *bit = F::from_bool(n & (1 << i) != 0); + } + res +} + +pub fn mem_read_with_log( + channel: MemoryChannel, + address: MemoryAddress, + memory_state: &MemoryState, + traces: &Traces, +) -> (U256, MemoryOp) { + let val = memory_state.get(address); + let op = MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Read); + (val, op) +} + +pub fn mem_write_log( + channel: MemoryChannel, + address: MemoryAddress, + traces: &Traces, + val: U256, +) -> MemoryOp { + MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Write(val)) +} + +pub fn mem_read_code_with_log_and_fill( + address: MemoryAddress, + memory_state: &MemoryState, + traces: &Traces, + row: &mut CpuColumnsView, +) -> (u8, MemoryOp) { + let (val, op) = mem_read_with_log(MemoryChannel::Code, address, memory_state, traces); + + let val_u8 = to_byte_checked(val); + row.opcode_bits = to_bits_le(val_u8); + + (val_u8, op) +} + +pub fn mem_read_gp_with_log_and_fill( + n: usize, + address: MemoryAddress, + memory_state: &MemoryState, + traces: &Traces, + row: &mut CpuColumnsView, +) -> (U256, MemoryOp) { + let (val, op) = mem_read_with_log( + MemoryChannel::GeneralPurpose(n), + address, + memory_state, + traces, + ); + let val_limbs: [u64; 4] = val.0; + + let channel = &mut row.mem_channels[n]; + channel.used = F::ONE; + channel.is_read = F::ONE; + channel.addr_context = F::from_canonical_u32(address.0); + channel.addr_segment = F::from_canonical_u32(address.1); + channel.addr_virtual = F::from_canonical_u32(address.2); + for (i, limb) in val_limbs.into_iter().enumerate() { + channel.value[2 * i] = F::from_canonical_u32(limb as u32); + channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); + } + + (val, op) +} + +pub fn mem_write_gp_log_and_fill( + n: usize, + address: MemoryAddress, + traces: &Traces, + row: &mut CpuColumnsView, + val: U256, +) -> MemoryOp { + let op = mem_write_log(MemoryChannel::GeneralPurpose(n), address, traces, val); + let val_limbs: [u64; 4] = val.0; + + let channel = &mut row.mem_channels[n]; + channel.used = F::ONE; + channel.is_read = F::ZERO; + channel.addr_context = F::from_canonical_u32(address.0); + channel.addr_segment = F::from_canonical_u32(address.1); + channel.addr_virtual = F::from_canonical_u32(address.2); + for (i, limb) in val_limbs.into_iter().enumerate() { + channel.value[2 * i] = F::from_canonical_u32(limb as u32); + channel.value[2 * i + 1] = F::from_canonical_u32((limb >> 32) as u32); + } + + op +} + +pub fn stack_pop_with_log_and_fill( + registers_state: &mut RegistersState, + memory_state: &MemoryState, + traces: &Traces, + row: &mut CpuColumnsView, +) -> Result<[(U256, MemoryOp); N], ProgramError> { + if (registers_state.stack_len as usize) < N { + return Err(ProgramError::StackUnderflow); + } + + let result = { + let mut i = 0usize; + [(); N].map(|_| { + let address = ( + registers_state.context, + Segment::Stack as u32, + registers_state.stack_len - 1 - (i as u32), + ); + let res = mem_read_gp_with_log_and_fill(i, address, memory_state, traces, row); + i += 1; + res + }) + }; + + registers_state.stack_len -= N as u32; + + Ok(result) +} + +pub fn stack_push_log_and_fill( + registers_state: &mut RegistersState, + traces: &Traces, + row: &mut CpuColumnsView, + val: U256, +) -> Result { + if !registers_state.is_kernel && registers_state.stack_len >= 1024 { + return Err(ProgramError::StackOverflow); + } + + let address = ( + registers_state.context, + Segment::Stack as u32, + registers_state.stack_len, + ); + let res = mem_write_gp_log_and_fill(NUM_GP_CHANNELS - 1, address, traces, row, val); + + registers_state.stack_len += 1; + + Ok(res) +}