mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-24 16:43:21 +00:00
Compiler errors + refactor
This commit is contained in:
parent
205bd58f98
commit
87a9c002c9
@ -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>,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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) }
|
||||
}
|
||||
|
||||
@ -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`.
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -29,3 +29,4 @@ pub mod util;
|
||||
pub mod vanishing_poly;
|
||||
pub mod vars;
|
||||
pub mod verifier;
|
||||
pub mod witness;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
enum ProgramError {
|
||||
#[allow(dead_code)]
|
||||
pub enum ProgramError {
|
||||
OutOfGas,
|
||||
InvalidOpcode,
|
||||
StackUnderflow,
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
mod mem_tx;
|
||||
mod errors;
|
||||
mod memory;
|
||||
mod operation;
|
||||
mod state;
|
||||
mod traces;
|
||||
pub mod transition;
|
||||
mod util;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
162
evm/src/witness/util.rs
Normal 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)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user