use super::{Felt, FieldElement, StarkField, SysTrace, Vec, Word, ONE, ZERO};
pub const FMP_MIN: u64 = 2_u64.pow(30);
pub const SYSCALL_FMP_MIN: u64 = 2_u64.pow(31);
pub const FMP_MAX: u64 = 3 * 2_u64.pow(30) - 1;
pub struct System {
clk: u32,
ctx: u32,
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::from(FMP_MIN);
let mut fmp_trace = Felt::zeroed_vector(init_trace_capacity);
fmp_trace[0] = fmp;
Self {
clk: 0,
ctx: 0,
fmp,
in_syscall: false,
fn_hash: [ZERO; 4],
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) -> u32 {
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) -> u32 {
self.ctx_trace[clk as usize].as_int() as u32
}
#[inline(always)]
pub fn get_fmp_at(&self, clk: u32) -> Felt {
self.fmp_trace[clk as usize]
}
pub fn advance_clock(&mut self) {
self.clk += 1;
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];
}
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;
self.fmp = Felt::from(FMP_MIN);
self.fn_hash = fn_hash;
}
pub fn start_syscall(&mut self) {
debug_assert!(!self.in_syscall, "already in syscall");
self.ctx = 0;
self.fmp = Felt::from(SYSCALL_FMP_MIN);
self.in_syscall = true;
}
pub fn restore_context(&mut self, ctx: u32, 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_eq!(0, self.ctx);
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, [ZERO; 4]);
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);
}
}
}
}