use super::{ExecutionError, Felt, FieldElement, SysTrace, Word, EMPTY_WORD, ONE, ZERO};
use alloc::vec::Vec;
use core::fmt::{self, Display};
#[cfg(test)]
mod tests;
pub const FMP_MIN: u64 = 2_u64.pow(30);
pub const SYSCALL_FMP_MIN: u32 = 2_u32.pow(31);
pub const FMP_MAX: u64 = 3 * 2_u64.pow(30) - 1;
pub struct System {
clk: u32,
ctx: ContextId,
fmp: Felt,
in_syscall: bool,
fn_hash: Word,
ctx_trace: Vec<Felt>,
clk_trace: Vec<Felt>,
fmp_trace: Vec<Felt>,
in_syscall_trace: Vec<Felt>,
fn_hash_trace: [Vec<Felt>; 4],
}
impl System {
pub fn new(init_trace_capacity: usize) -> Self {
let fmp = Felt::new(FMP_MIN);
let mut fmp_trace = Felt::zeroed_vector(init_trace_capacity);
fmp_trace[0] = fmp;
Self {
clk: 0,
ctx: ContextId::root(),
fmp,
in_syscall: false,
fn_hash: EMPTY_WORD,
clk_trace: Felt::zeroed_vector(init_trace_capacity),
ctx_trace: Felt::zeroed_vector(init_trace_capacity),
fmp_trace,
in_syscall_trace: Felt::zeroed_vector(init_trace_capacity),
fn_hash_trace: [
Felt::zeroed_vector(init_trace_capacity),
Felt::zeroed_vector(init_trace_capacity),
Felt::zeroed_vector(init_trace_capacity),
Felt::zeroed_vector(init_trace_capacity),
],
}
}
#[inline(always)]
pub fn clk(&self) -> u32 {
self.clk
}
#[inline(always)]
pub fn ctx(&self) -> ContextId {
self.ctx
}
#[inline(always)]
pub fn fmp(&self) -> Felt {
self.fmp
}
pub fn in_syscall(&self) -> bool {
self.in_syscall
}
#[inline(always)]
pub fn fn_hash(&self) -> Word {
self.fn_hash
}
#[inline(always)]
pub fn trace_len(&self) -> usize {
self.clk as usize
}
#[inline(always)]
pub fn get_ctx_at(&self, clk: u32) -> ContextId {
(self.ctx_trace[clk as usize].as_int() as u32).into()
}
#[inline(always)]
pub fn get_fmp_at(&self, clk: u32) -> Felt {
self.fmp_trace[clk as usize]
}
pub fn advance_clock(&mut self, max_cycles: u32) -> Result<(), ExecutionError> {
self.clk += 1;
if self.clk > max_cycles {
return Err(ExecutionError::CycleLimitExceeded(max_cycles));
}
let clk = self.clk as usize;
self.clk_trace[clk] = Felt::from(self.clk);
self.fmp_trace[clk] = self.fmp;
self.ctx_trace[clk] = Felt::from(self.ctx);
self.in_syscall_trace[clk] = if self.in_syscall { ONE } else { ZERO };
self.fn_hash_trace[0][clk] = self.fn_hash[0];
self.fn_hash_trace[1][clk] = self.fn_hash[1];
self.fn_hash_trace[2][clk] = self.fn_hash[2];
self.fn_hash_trace[3][clk] = self.fn_hash[3];
Ok(())
}
pub fn set_fmp(&mut self, fmp: Felt) {
self.fmp = fmp;
}
pub fn start_call(&mut self, fn_hash: Word) {
debug_assert!(!self.in_syscall, "call in syscall");
self.ctx = (self.clk + 1).into();
self.fmp = Felt::new(FMP_MIN);
self.fn_hash = fn_hash;
}
pub fn start_syscall(&mut self) {
debug_assert!(!self.in_syscall, "already in syscall");
self.ctx = ContextId::root();
self.fmp = Felt::from(SYSCALL_FMP_MIN);
self.in_syscall = true;
}
pub fn restore_context(&mut self, ctx: ContextId, fmp: Felt, fn_hash: Word) {
self.ctx = ctx;
self.fmp = fmp;
self.in_syscall = false;
self.fn_hash = fn_hash;
}
pub fn into_trace(mut self, trace_len: usize, num_rand_rows: usize) -> SysTrace {
let clk = self.clk() as usize;
assert!(clk + num_rand_rows <= trace_len, "target trace length too small");
self.clk_trace.resize(trace_len, ZERO);
for (i, clk) in self.clk_trace.iter_mut().enumerate().skip(clk) {
*clk = Felt::from(i as u32);
}
debug_assert!(self.ctx.is_root());
self.ctx_trace.resize(trace_len, ZERO);
let last_value = self.fmp_trace[clk];
self.fmp_trace[clk..].fill(last_value);
self.fmp_trace.resize(trace_len, last_value);
debug_assert!(!self.in_syscall);
self.in_syscall_trace.resize(trace_len, ZERO);
let mut trace = vec![self.clk_trace, self.fmp_trace, self.ctx_trace, self.in_syscall_trace];
debug_assert_eq!(self.fn_hash, EMPTY_WORD);
for mut column in self.fn_hash_trace.into_iter() {
column.resize(trace_len, ZERO);
trace.push(column);
}
trace.try_into().expect("failed to convert vector to array")
}
pub fn ensure_trace_capacity(&mut self) {
let current_capacity = self.clk_trace.len();
if self.clk + 1 >= current_capacity as u32 {
let new_length = current_capacity * 2;
self.clk_trace.resize(new_length, ZERO);
self.ctx_trace.resize(new_length, ZERO);
self.fmp_trace.resize(new_length, ZERO);
self.in_syscall_trace.resize(new_length, ZERO);
for column in self.fn_hash_trace.iter_mut() {
column.resize(new_length, ZERO);
}
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct ContextId(u32);
impl ContextId {
pub fn root() -> Self {
Self(0)
}
pub fn is_root(&self) -> bool {
self.0 == 0
}
}
impl From<u32> for ContextId {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<ContextId> for u32 {
fn from(context_id: ContextId) -> Self {
context_id.0
}
}
impl From<ContextId> for u64 {
fn from(context_id: ContextId) -> Self {
context_id.0.into()
}
}
impl From<ContextId> for Felt {
fn from(context_id: ContextId) -> Self {
context_id.0.into()
}
}
impl Display for ContextId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}