use vm_core::{
Felt, Operation,
mast::{MastForest, MastNodeExt},
sys_events::SystemEvent,
};
use super::{
super::{
ONE,
system::{FMP_MAX, FMP_MIN},
},
ExecutionError, Process,
};
use crate::{Host, ProcessState, errors::ErrorContext};
pub(crate) mod sys_event_handlers;
impl Process {
pub(super) fn op_assert<H>(
&mut self,
err_code: Felt,
program: &MastForest,
host: &mut H,
err_ctx: &ErrorContext<'_, impl MastNodeExt>,
) -> Result<(), ExecutionError>
where
H: Host,
{
if self.stack.get(0) != ONE {
let state = ProcessState::from(self);
host.on_assert_failed(state, err_code);
let err_msg = program.resolve_error_message(err_code);
return Err(ExecutionError::failed_assertion(state.clk(), err_code, err_msg, err_ctx));
}
self.stack.shift_left(1);
Ok(())
}
pub(super) fn op_fmpadd(&mut self) -> Result<(), ExecutionError> {
let offset = self.stack.get(0);
let fmp = self.system.fmp();
self.stack.set(0, fmp + offset);
self.stack.copy_state(1);
Ok(())
}
pub(super) fn op_fmpupdate(&mut self) -> Result<(), ExecutionError> {
let offset = self.stack.get(0);
let fmp = self.system.fmp();
let new_fmp = fmp + offset;
if new_fmp.as_int() < FMP_MIN || new_fmp.as_int() > FMP_MAX {
return Err(ExecutionError::InvalidFmpValue(fmp, new_fmp));
}
self.system.set_fmp(new_fmp);
self.stack.shift_left(1);
Ok(())
}
pub(super) fn op_sdepth(&mut self) -> Result<(), ExecutionError> {
let stack_depth = self.stack.depth();
self.stack.set(0, Felt::new(stack_depth as u64));
self.stack.shift_right(0);
Ok(())
}
pub(super) fn op_caller(&mut self) -> Result<(), ExecutionError> {
if !self.system.in_syscall() {
return Err(ExecutionError::CallerNotInSyscall);
}
let fn_hash = self.system.fn_hash();
self.stack.set(0, fn_hash[3]);
self.stack.set(1, fn_hash[2]);
self.stack.set(2, fn_hash[1]);
self.stack.set(3, fn_hash[0]);
self.stack.copy_state(4);
Ok(())
}
pub(super) fn op_clk(&mut self) -> Result<(), ExecutionError> {
let clk = self.system.clk();
self.stack.set(0, Felt::from(clk));
self.stack.shift_right(0);
Ok(())
}
pub(super) fn op_emit<H>(
&mut self,
event_id: u32,
host: &mut H,
err_ctx: &ErrorContext<'_, impl MastNodeExt>,
) -> Result<(), ExecutionError>
where
H: Host,
{
self.stack.copy_state(0);
self.decoder.set_user_op_helpers(Operation::Emit(event_id), &[event_id.into()]);
if let Some(system_event) = SystemEvent::from_event_id(event_id) {
self.handle_system_event(system_event, host, err_ctx)
} else {
host.on_event(self.into(), event_id, err_ctx)
}
}
}
#[cfg(test)]
mod tests {
use vm_core::mast::MastForest;
use super::{
super::{MIN_STACK_DEPTH, Operation},
FMP_MAX, FMP_MIN, Felt, Process,
};
use crate::{DefaultHost, ONE, StackInputs, ZERO};
const MAX_PROC_LOCALS: u64 = 2_u64.pow(31) - 1;
#[test]
fn op_assert() {
let mut host = DefaultHost::default();
let mut process = Process::new_dummy_with_empty_stack();
let program = &MastForest::default();
process.execute_op(Operation::Push(ONE), program, &mut host).unwrap();
process.execute_op(Operation::Swap, program, &mut host).unwrap();
process.execute_op(Operation::Drop, program, &mut host).unwrap();
assert!(process.execute_op(Operation::Assert(ZERO), program, &mut host).is_ok());
}
#[test]
fn op_fmpupdate() {
let mut host = DefaultHost::default();
let mut process = Process::new_dummy_with_empty_stack();
let program = &MastForest::default();
assert_eq!(Felt::new(2_u64.pow(30)), process.system.fmp());
process.execute_op(Operation::Push(Felt::new(2)), program, &mut host).unwrap();
process.execute_op(Operation::FmpUpdate, program, &mut host).unwrap();
assert_eq!(Felt::new(FMP_MIN + 2), process.system.fmp());
process.execute_op(Operation::Push(Felt::new(3)), program, &mut host).unwrap();
process.execute_op(Operation::FmpUpdate, program, &mut host).unwrap();
assert_eq!(Felt::new(FMP_MIN + 5), process.system.fmp());
process.execute_op(Operation::Push(-Felt::new(3)), program, &mut host).unwrap();
process.execute_op(Operation::FmpUpdate, program, &mut host).unwrap();
assert_eq!(Felt::new(FMP_MIN + 2), process.system.fmp());
process.execute_op(Operation::Push(-Felt::new(3)), program, &mut host).unwrap();
assert!(process.execute_op(Operation::FmpUpdate, program, &mut host).is_err());
let stack = StackInputs::try_from_ints([MAX_PROC_LOCALS]).unwrap();
let mut process = Process::new_dummy(stack);
process.execute_op(Operation::FmpUpdate, program, &mut host).unwrap();
assert_eq!(Felt::new(FMP_MAX), process.system.fmp());
let stack = StackInputs::try_from_ints([MAX_PROC_LOCALS + 1]).unwrap();
let mut process = Process::new_dummy(stack);
assert!(process.execute_op(Operation::FmpUpdate, program, &mut host).is_err());
let stack = StackInputs::try_from_ints([2, 3]).unwrap();
let mut process = Process::new_dummy(stack);
process.execute_op(Operation::FmpUpdate, program, &mut host).unwrap();
let expected = build_expected_stack(&[2]);
assert_eq!(expected, process.stack.trace_state());
let mut process = Process::new_dummy_with_empty_stack();
assert!(process.execute_op(Operation::FmpUpdate, program, &mut host).is_ok());
}
#[test]
fn op_fmpadd() {
let mut host = DefaultHost::default();
let mut process = Process::new_dummy_with_empty_stack();
let program = &MastForest::default();
process.execute_op(Operation::Push(Felt::new(2)), program, &mut host).unwrap();
process.execute_op(Operation::FmpUpdate, program, &mut host).unwrap();
process.execute_op(Operation::Push(-ONE), program, &mut host).unwrap();
process.execute_op(Operation::FmpAdd, program, &mut host).unwrap();
let expected = build_expected_stack(&[FMP_MIN + 1]);
assert_eq!(expected, process.stack.trace_state());
process.execute_op(Operation::Push(-Felt::new(2)), program, &mut host).unwrap();
process.execute_op(Operation::FmpAdd, program, &mut host).unwrap();
let expected = build_expected_stack(&[FMP_MIN, FMP_MIN + 1]);
assert_eq!(expected, process.stack.trace_state());
}
#[test]
fn op_sdepth() {
let mut host = DefaultHost::default();
let mut process = Process::new_dummy_with_empty_stack();
let program = &MastForest::default();
process.execute_op(Operation::SDepth, program, &mut host).unwrap();
let expected = build_expected_stack(&[MIN_STACK_DEPTH as u64]);
assert_eq!(expected, process.stack.trace_state());
assert_eq!(MIN_STACK_DEPTH + 1, process.stack.depth());
process.execute_op(Operation::SDepth, program, &mut host).unwrap();
let expected = build_expected_stack(&[MIN_STACK_DEPTH as u64 + 1, MIN_STACK_DEPTH as u64]);
assert_eq!(expected, process.stack.trace_state());
assert_eq!(MIN_STACK_DEPTH + 2, process.stack.depth());
process.execute_op(Operation::Pad, program, &mut host).unwrap();
process.execute_op(Operation::SDepth, program, &mut host).unwrap();
let expected = build_expected_stack(&[
MIN_STACK_DEPTH as u64 + 3,
0,
MIN_STACK_DEPTH as u64 + 1,
MIN_STACK_DEPTH as u64,
]);
assert_eq!(expected, process.stack.trace_state());
assert_eq!(MIN_STACK_DEPTH + 4, process.stack.depth());
}
#[test]
fn op_clk() {
let mut host = DefaultHost::default();
let mut process = Process::new_dummy_with_empty_stack();
let program = &MastForest::default();
process.execute_op(Operation::Clk, program, &mut host).unwrap();
let expected = build_expected_stack(&[1]);
assert_eq!(expected, process.stack.trace_state());
process.execute_op(Operation::Push(Felt::new(2)), program, &mut host).unwrap();
process.execute_op(Operation::Clk, program, &mut host).unwrap();
let expected = build_expected_stack(&[3, 2, 1]);
assert_eq!(expected, process.stack.trace_state());
process.execute_op(Operation::Push(Felt::new(3)), program, &mut host).unwrap();
process.execute_op(Operation::Clk, program, &mut host).unwrap();
let expected = build_expected_stack(&[5, 3, 3, 2, 1]);
assert_eq!(expected, process.stack.trace_state());
}
fn build_expected_stack(values: &[u64]) -> [Felt; 16] {
let mut expected = [ZERO; 16];
for (&value, result) in values.iter().zip(expected.iter_mut()) {
*result = Felt::new(value);
}
expected
}
}