Compiler errors + refactor

This commit is contained in:
Jacqueline Nabaglo 2022-11-28 13:19:40 -08:00
parent 205bd58f98
commit 87a9c002c9
17 changed files with 687 additions and 318 deletions

View File

@ -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<T: Copy> {
keccak: CpuKeccakView<T>,
arithmetic: CpuArithmeticView<T>,

View File

@ -31,7 +31,7 @@ pub struct MemoryChannelView<T: Copy> {
}
#[repr(C)]
#[derive(Eq, PartialEq, Debug)]
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct CpuColumnsView<T: Copy> {
/// Filter. 1 if the row is part of bootstrapping the kernel code, 0 otherwise.
pub is_bootstrap_kernel: T,

View File

@ -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<T> {
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct OpsColumnsView<T: Copy> {
// 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<T> {
// `u8` is guaranteed to have a `size_of` of 1.
pub const NUM_OPS_COLUMNS: usize = size_of::<OpsColumnsView<u8>>();
impl<T> From<[T; NUM_OPS_COLUMNS]> for OpsColumnsView<T> {
impl<T: Copy> From<[T; NUM_OPS_COLUMNS]> for OpsColumnsView<T> {
fn from(value: [T; NUM_OPS_COLUMNS]) -> Self {
unsafe { transmute_no_compile_time_size_checks(value) }
}
}
impl<T> From<OpsColumnsView<T>> for [T; NUM_OPS_COLUMNS] {
impl<T: Copy> From<OpsColumnsView<T>> for [T; NUM_OPS_COLUMNS] {
fn from(value: OpsColumnsView<T>) -> Self {
unsafe { transmute_no_compile_time_size_checks(value) }
}
}
impl<T> Borrow<OpsColumnsView<T>> for [T; NUM_OPS_COLUMNS] {
impl<T: Copy> Borrow<OpsColumnsView<T>> for [T; NUM_OPS_COLUMNS] {
fn borrow(&self) -> &OpsColumnsView<T> {
unsafe { transmute(self) }
}
}
impl<T> BorrowMut<OpsColumnsView<T>> for [T; NUM_OPS_COLUMNS] {
impl<T: Copy> BorrowMut<OpsColumnsView<T>> for [T; NUM_OPS_COLUMNS] {
fn borrow_mut(&mut self) -> &mut OpsColumnsView<T> {
unsafe { transmute(self) }
}
}
impl<T> Deref for OpsColumnsView<T> {
impl<T: Copy> Deref for OpsColumnsView<T> {
type Target = [T; NUM_OPS_COLUMNS];
fn deref(&self) -> &Self::Target {
unsafe { transmute(self) }
}
}
impl<T> DerefMut for OpsColumnsView<T> {
impl<T: Copy> DerefMut for OpsColumnsView<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { transmute(self) }
}

View File

@ -126,7 +126,6 @@ impl<F: RichField, const D: usize> CpuStark<F, D> {
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`.
}
}

View File

@ -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;

View File

@ -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<F: RichField>(val0: U256, val1: U256, lv: &mut CpuColumnsView<F>) {
let input0 = lv.mem_channels[0].value;
pub fn generate_pinv_diff<F: Field>(val0: U256, val1: U256, lv: &mut CpuColumnsView<F>) {
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<F: RichField>(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<F: RichField>(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;
}
}

View File

@ -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<F: RichField>(lv: &mut CpuColumnsView<F>) {
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<P: PackedField>(
lv: &CpuColumnsView<P>,
yield_constr: &mut ConstraintConsumer<P>,

View File

@ -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;

View File

@ -29,3 +29,4 @@ pub mod util;
pub mod vanishing_poly;
pub mod vars;
pub mod verifier;
pub mod witness;

View File

@ -1,4 +1,5 @@
enum ProgramError {
#[allow(dead_code)]
pub enum ProgramError {
OutOfGas,
InvalidOpcode,
StackUnderflow,

View File

@ -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<MemoryAddress, U256>,
@ -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) {

View File

@ -1,4 +1,7 @@
mod mem_tx;
mod errors;
mod memory;
mod operation;
mod state;
mod traces;
pub mod transition;
mod util;

View File

@ -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<F>(op: BinaryLogicOp, in0: U256, in1: U256, result: U256) -> [F; logic::columns::NUM_COLUMNS] {
fn make_logic_row<F: Field>(
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<F>(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<F>(op: BinaryLogicOp, state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> Result<(), ProgramError> {
let ([in0, in1], logs_in) = state.pop_stack_with_log::<2>()?;
pub fn generate_binary_logic_op<F: Field>(
op: BinaryLogicOp,
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F>(n: u8, state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> 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<F: Field>(
n: u8,
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F>(n: u8, state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> 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<F: Field>(
n: u8,
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F>(state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> Result<(), ProgramError> {
let ([x], [log_in]) = state.pop_stack_with_log::<1>()?;
pub fn generate_not<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F>(state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> Result<(), ProgramError> {
let ([x], [log_in]) = state.pop_stack_with_log::<1>()?;
let is_zero = state.is_zero();
let result = is_zero.into::<u64>().into::<U256>();
let log_out = state.push_stack_with_log(result)?;
pub fn generate_iszero<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F>(op: JumpOp, state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> Result<(), ProgramError> {
todo!();
}
fn generate_syscall<F>(opcode: u8, state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> Result<(), ProgramError> {
pub fn generate_syscall<F: Field>(
opcode: u8,
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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::<U256>() + (state.is_kernel_mode.into::<u64>.into::<U256> << 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<F>(state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> Result<(), ProgramError> {
let ([x0, x1], logs_in) = state.pop_stack_with_log::<1>()?;
let equal = x0 == x1;
let result = equal.into::<u64>().into::<U256>();
let log_out = state.push_stack_with_log(result)?;
pub fn generate_eq<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F>(state: &mut State, row: &mut CpuRow, traces: &mut Traces<T>) -> 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<F: Field>(
mut registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
mut row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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)
}

View File

@ -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<const N: usize>(&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<MemoryOp, ProgramError> {
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)
}
}

View File

@ -6,11 +6,20 @@ use crate::witness::memory::MemoryOp;
type LogicRow<T> = [T; logic::columns::NUM_COLUMNS];
type ArithmeticRow<T> = [T; NUM_ARITH_COLUMNS];
struct Traces<T: Copy> {
pub cpu: Vec<CpuColumnsView<T>>,
pub logic: Vec<LogicRow<T>>,
pub arithmetic: Vec<ArithmeticRow<T>>,
pub memory: Vec<MemoryOp>,
#[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<T: Copy> {
cpu: Vec<CpuColumnsView<T>>,
logic: Vec<LogicRow<T>>,
arithmetic: Vec<ArithmeticRow<T>>,
memory: Vec<MemoryOp>,
}
impl<T: Copy> Traces<T> {
@ -23,10 +32,45 @@ impl<T: Copy> Traces<T> {
}
}
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<T>) {
self.cpu.push(val);
}
pub fn push_logic(&mut self, val: LogicRow<T>) {
self.logic.push(val);
}
pub fn push_arithmetic(&mut self, val: ArithmeticRow<T>) {
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<T: Copy> Default for Traces<T> {
fn default() -> Self {
Self::new()
}
}

View File

@ -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<F: Field>(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<F: Field>(
registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
row: &mut CpuColumnsView<F>,
) -> 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<Operation, ProgramError> {
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<F: Field>(op: Operation, row: &mut CpuColumnsView<F>) {
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<F: Field>(
op: Operation,
registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
row: CpuColumnsView<F>,
) -> Result<RegistersState, ProgramError> {
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<F: Field>(
registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
) -> Result<RegistersState, ProgramError> {
let mut row: CpuColumnsView<F> = [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<F: Field>(
_registers_state: RegistersState,
_memory_state: &MemoryState,
_traces: &mut Traces<F>,
) -> RegistersState {
todo!("constraints for exception handling are not implemented");
}
pub fn transition<F: Field>(
registers_state: RegistersState,
memory_state: &MemoryState,
traces: &mut Traces<F>,
) -> 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<F: Field>(state: State) -> (State, Traces<T>) {
let mut current_row: CpuColumnsView<F> = [F::ZERO; NUM_CPU_COLUMNS].into();
current_row.is_cpu_cycle = F::ONE;
let (op, code_mem_log) = decode(&state, &mut current_row);
}

162
evm/src/witness/util.rs Normal file
View File

@ -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<F: Field>(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<T: Copy>(
channel: MemoryChannel,
address: MemoryAddress,
memory_state: &MemoryState,
traces: &Traces<T>,
) -> (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<T: Copy>(
channel: MemoryChannel,
address: MemoryAddress,
traces: &Traces<T>,
val: U256,
) -> MemoryOp {
MemoryOp::new(channel, traces.clock(), address, MemoryOpKind::Write(val))
}
pub fn mem_read_code_with_log_and_fill<F: Field>(
address: MemoryAddress,
memory_state: &MemoryState,
traces: &Traces<F>,
row: &mut CpuColumnsView<F>,
) -> (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<F: Field>(
n: usize,
address: MemoryAddress,
memory_state: &MemoryState,
traces: &Traces<F>,
row: &mut CpuColumnsView<F>,
) -> (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<F: Field>(
n: usize,
address: MemoryAddress,
traces: &Traces<F>,
row: &mut CpuColumnsView<F>,
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<const N: usize, F: Field>(
registers_state: &mut RegistersState,
memory_state: &MemoryState,
traces: &Traces<F>,
row: &mut CpuColumnsView<F>,
) -> 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<F: Field>(
registers_state: &mut RegistersState,
traces: &Traces<F>,
row: &mut CpuColumnsView<F>,
val: U256,
) -> Result<MemoryOp, ProgramError> {
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)
}