use super::business_logic_syscall_handler::BusinessLogicSyscallHandler;
use crate::state::state_api::StateReader;
use crate::transaction::error::TransactionError;
use cairo_lang_casm::{
hints::{Hint, StarknetHint},
operand::{CellRef, DerefOrImmediate, Register, ResOperand},
};
use cairo_vm::vm::runners::cairo_runner::{ResourceTracker, RunResources};
use cairo_vm::{
felt::Felt252,
hint_processor::{
cairo_1_hint_processor::hint_processor::Cairo1HintProcessor,
hint_processor_definition::{HintProcessorLogic, HintReference},
},
types::{
errors::math_errors::MathError, exec_scope::ExecutionScopes, relocatable::Relocatable,
},
vm::{
errors::{hint_errors::HintError, vm_errors::VirtualMachineError},
vm_core::VirtualMachine,
},
};
use std::{any::Any, boxed::Box, collections::HashMap};
pub(crate) trait HintProcessorPostRun {
fn post_run(
&self,
_runner: &mut VirtualMachine,
_syscall_stop_ptr: Relocatable,
) -> Result<(), TransactionError>;
}
#[allow(unused)]
pub(crate) struct SyscallHintProcessor<'a, S: StateReader> {
pub(crate) cairo1_hint_processor: Cairo1HintProcessor,
pub(crate) syscall_handler: BusinessLogicSyscallHandler<'a, S>,
pub(crate) run_resources: RunResources,
}
impl<'a, S: StateReader> SyscallHintProcessor<'a, S> {
pub fn new(
syscall_handler: BusinessLogicSyscallHandler<'a, S>,
hints: &[(usize, Vec<Hint>)],
run_resources: RunResources,
) -> Self {
SyscallHintProcessor {
cairo1_hint_processor: Cairo1HintProcessor::new(hints, run_resources.clone()),
syscall_handler,
run_resources,
}
}
}
impl<'a, S: StateReader> HintProcessorLogic for SyscallHintProcessor<'a, S> {
fn execute_hint(
&mut self,
vm: &mut VirtualMachine,
exec_scopes: &mut ExecutionScopes,
hint_data: &Box<dyn Any>,
_constants: &HashMap<String, Felt252>,
) -> Result<(), HintError> {
let hints: &Vec<Hint> = hint_data.downcast_ref().ok_or(HintError::WrongHintData)?;
for hint in hints {
match hint {
Hint::Core(_core_hint) => {
self.cairo1_hint_processor.execute(vm, exec_scopes, hint)?
}
Hint::Starknet(starknet_hint) => match starknet_hint {
StarknetHint::SystemCall { system } => {
let syscall_ptr = as_relocatable(vm, system)?;
self.syscall_handler
.syscall(vm, syscall_ptr)
.map_err(|err| {
HintError::CustomHint(
format!("Syscall handler invocation error: {err}")
.into_boxed_str(),
)
})?;
}
other => {
return Err(HintError::UnknownHint(
format!("{:?}", other).into_boxed_str(),
))
}
},
};
}
Ok(())
}
fn compile_hint(
&self,
hint_code: &str,
ap_tracking_data: &cairo_vm::serde::deserialize_program::ApTracking,
reference_ids: &HashMap<String, usize>,
references: &[HintReference],
) -> Result<Box<dyn Any>, VirtualMachineError> {
self.cairo1_hint_processor.compile_hint(
hint_code,
ap_tracking_data,
reference_ids,
references,
)
}
}
impl<'a, S: StateReader> ResourceTracker for SyscallHintProcessor<'a, S> {
fn consumed(&self) -> bool {
self.run_resources.consumed()
}
fn consume_step(&mut self) {
self.run_resources.consume_step()
}
fn get_n_steps(&self) -> Option<usize> {
self.run_resources.get_n_steps()
}
fn run_resources(&self) -> &RunResources {
&self.run_resources
}
}
impl<'a, S: StateReader> HintProcessorPostRun for SyscallHintProcessor<'a, S> {
fn post_run(
&self,
runner: &mut VirtualMachine,
syscall_stop_ptr: Relocatable,
) -> Result<(), crate::transaction::error::TransactionError> {
self.syscall_handler.post_run(runner, syscall_stop_ptr)
}
}
fn as_relocatable(vm: &mut VirtualMachine, value: &ResOperand) -> Result<Relocatable, HintError> {
let (base, offset) = extract_buffer(value)?;
get_ptr(vm, base, &offset).map_err(HintError::from)
}
fn extract_buffer(buffer: &ResOperand) -> Result<(&CellRef, Felt252), HintError> {
let (cell, base_offset) = match buffer {
ResOperand::Deref(cell) => (cell, 0.into()),
ResOperand::BinOp(bin_op) => {
if let DerefOrImmediate::Immediate(val) = &bin_op.b {
(&bin_op.a, val.clone().value.into())
} else {
return Err(HintError::CustomHint("Failed to extract buffer, expected ResOperand of BinOp type to have Inmediate b value".to_owned().into_boxed_str()));
}
}
_ => {
return Err(HintError::CustomHint(
"Illegal argument for a buffer."
.to_string()
.into_boxed_str(),
))
}
};
Ok((cell, base_offset))
}
fn get_ptr(
vm: &VirtualMachine,
cell: &CellRef,
offset: &Felt252,
) -> Result<Relocatable, VirtualMachineError> {
Ok((vm.get_relocatable(cell_ref_to_relocatable(cell, vm)?)? + offset)?)
}
fn cell_ref_to_relocatable(
cell_ref: &CellRef,
vm: &VirtualMachine,
) -> Result<Relocatable, MathError> {
let base = match cell_ref.register {
Register::AP => vm.get_ap(),
Register::FP => vm.get_fp(),
};
base + (cell_ref.offset as i32)
}