cairo_lang_runner/casm_run/
mod.rs

1use std::any::Any;
2use std::borrow::Cow;
3use std::collections::{HashMap, VecDeque};
4use std::ops::{Shl, Sub};
5use std::rc::Rc;
6use std::vec::IntoIter;
7
8use ark_ff::{BigInteger, PrimeField};
9use cairo_lang_casm::hints::{CoreHint, DeprecatedHint, ExternalHint, Hint, StarknetHint};
10use cairo_lang_casm::operand::{
11    BinOpOperand, CellRef, DerefOrImmediate, Operation, Register, ResOperand,
12};
13use cairo_lang_sierra::ids::FunctionId;
14use cairo_lang_utils::bigint::BigIntAsHex;
15use cairo_lang_utils::byte_array::{BYTE_ARRAY_MAGIC, BYTES_IN_WORD};
16use cairo_lang_utils::extract_matches;
17use cairo_vm::hint_processor::hint_processor_definition::{
18    HintProcessor, HintProcessorLogic, HintReference,
19};
20use cairo_vm::serde::deserialize_program::{
21    ApTracking, FlowTrackingData, HintParams, ReferenceManager,
22};
23use cairo_vm::types::builtin_name::BuiltinName;
24use cairo_vm::types::exec_scope::ExecutionScopes;
25use cairo_vm::types::layout_name::LayoutName;
26use cairo_vm::types::program::Program;
27use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
28use cairo_vm::vm::errors::cairo_run_errors::CairoRunError;
29use cairo_vm::vm::errors::hint_errors::HintError;
30use cairo_vm::vm::errors::memory_errors::MemoryError;
31use cairo_vm::vm::errors::vm_errors::VirtualMachineError;
32use cairo_vm::vm::runners::cairo_runner::{
33    CairoRunner, ExecutionResources, ResourceTracker, RunResources,
34};
35use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry;
36use cairo_vm::vm::vm_core::VirtualMachine;
37use dict_manager::DictManagerExecScope;
38use itertools::Itertools;
39use num_bigint::{BigInt, BigUint};
40use num_integer::{ExtendedGcd, Integer};
41use num_traits::{Signed, ToPrimitive, Zero};
42use rand::Rng;
43use starknet_types_core::felt::{Felt as Felt252, NonZeroFelt};
44use {ark_secp256k1 as secp256k1, ark_secp256r1 as secp256r1};
45
46use self::contract_address::calculate_contract_address;
47use self::dict_manager::DictSquashExecScope;
48use crate::short_string::{as_cairo_short_string, as_cairo_short_string_ex};
49use crate::{Arg, RunResultValue, SierraCasmRunner, StarknetExecutionResources, args_size};
50
51#[cfg(test)]
52mod test;
53
54mod circuit;
55mod contract_address;
56mod dict_manager;
57
58/// Convert a Hint to the cairo-vm class HintParams by canonically serializing it to a string.
59pub fn hint_to_hint_params(hint: &Hint) -> HintParams {
60    HintParams {
61        code: hint.representing_string(),
62        accessible_scopes: vec![],
63        flow_tracking_data: FlowTrackingData {
64            ap_tracking: ApTracking::new(),
65            reference_ids: HashMap::new(),
66        },
67    }
68}
69
70/// Helper object to allocate and track Secp256k1 elliptic curve points.
71#[derive(Default)]
72struct Secp256k1ExecutionScope {
73    /// All elliptic curve points provided by the secp256k1 syscalls.
74    /// The id of a point is the index in the vector.
75    ec_points: Vec<secp256k1::Affine>,
76}
77
78/// Helper object to allocate and track Secp256r1 elliptic curve points.
79#[derive(Default)]
80struct Secp256r1ExecutionScope {
81    /// All elliptic curve points provided by the secp256r1 syscalls.
82    /// The id of a point is the index in the vector.
83    ec_points: Vec<secp256r1::Affine>,
84}
85
86/// HintProcessor for Cairo compiler hints.
87pub struct CairoHintProcessor<'a> {
88    /// The Cairo runner.
89    pub runner: Option<&'a SierraCasmRunner>,
90    /// The user arguments for the run.
91    ///
92    /// We have a vector of the arguments per parameter, as a parameter type may be composed of
93    /// several user args.
94    pub user_args: Vec<Vec<Arg>>,
95    /// A mapping from a string that represents a hint to the hint object.
96    pub string_to_hint: HashMap<String, Hint>,
97    /// The starknet state.
98    pub starknet_state: StarknetState,
99    /// Maintains the resources of the run.
100    pub run_resources: RunResources,
101    /// Resources used during syscalls - does not include resources used during the current VM run.
102    /// At the end of the run - adding both would result in the actual expected resource usage.
103    pub syscalls_used_resources: StarknetExecutionResources,
104    /// Avoid allocating memory segments so finalization of segment arena may not occur.
105    pub no_temporary_segments: bool,
106    /// A set of markers created by the run.
107    pub markers: Vec<Vec<Felt252>>,
108    /// The traceback set by a panic trace hint call.
109    pub panic_traceback: Vec<(Relocatable, Relocatable)>,
110}
111
112pub fn cell_ref_to_relocatable(cell_ref: &CellRef, vm: &VirtualMachine) -> Relocatable {
113    let base = match cell_ref.register {
114        Register::AP => vm.get_ap(),
115        Register::FP => vm.get_fp(),
116    };
117    (base + (cell_ref.offset as i32)).unwrap()
118}
119
120/// Inserts a value into the vm memory cell represented by the cellref.
121#[macro_export]
122macro_rules! insert_value_to_cellref {
123    ($vm:ident, $cell_ref:ident, $value:expr) => {
124        $vm.insert_value(cell_ref_to_relocatable($cell_ref, $vm), $value)
125    };
126}
127
128// Log type signature
129type Log = (Vec<Felt252>, Vec<Felt252>);
130
131// L2 to L1 message type signature
132type L2ToL1Message = (Felt252, Vec<Felt252>);
133
134/// Execution scope for starknet related data.
135/// All values will be 0 and by default if not setup by the test.
136#[derive(Clone, Default)]
137pub struct StarknetState {
138    /// The values of addresses in the simulated storage per contract.
139    storage: HashMap<Felt252, HashMap<Felt252, Felt252>>,
140    /// A mapping from contract address to class hash.
141    deployed_contracts: HashMap<Felt252, Felt252>,
142    /// A mapping from contract address to logs.
143    logs: HashMap<Felt252, ContractLogs>,
144    /// The simulated execution info.
145    exec_info: ExecutionInfo,
146    /// A mock history, mapping block number to the class hash.
147    block_hash: HashMap<u64, Felt252>,
148}
149impl StarknetState {
150    /// Replaces the addresses in the context.
151    pub fn open_caller_context(
152        &mut self,
153        (new_contract_address, new_caller_address): (Felt252, Felt252),
154    ) -> (Felt252, Felt252) {
155        let old_contract_address =
156            std::mem::replace(&mut self.exec_info.contract_address, new_contract_address);
157        let old_caller_address =
158            std::mem::replace(&mut self.exec_info.caller_address, new_caller_address);
159        (old_contract_address, old_caller_address)
160    }
161
162    /// Restores the addresses in the context.
163    pub fn close_caller_context(
164        &mut self,
165        (old_contract_address, old_caller_address): (Felt252, Felt252),
166    ) {
167        self.exec_info.contract_address = old_contract_address;
168        self.exec_info.caller_address = old_caller_address;
169    }
170}
171
172/// Object storing logs for a contract.
173#[derive(Clone, Default)]
174struct ContractLogs {
175    /// Events.
176    events: VecDeque<Log>,
177    /// Messages sent to L1.
178    l2_to_l1_messages: VecDeque<L2ToL1Message>,
179}
180
181/// Copy of the cairo `ExecutionInfo` struct.
182#[derive(Clone, Default)]
183struct ExecutionInfo {
184    block_info: BlockInfo,
185    tx_info: TxInfo,
186    caller_address: Felt252,
187    contract_address: Felt252,
188    entry_point_selector: Felt252,
189}
190
191/// Copy of the cairo `BlockInfo` struct.
192#[derive(Clone, Default)]
193struct BlockInfo {
194    block_number: Felt252,
195    block_timestamp: Felt252,
196    sequencer_address: Felt252,
197}
198
199/// Copy of the cairo `TxInfo` struct.
200#[derive(Clone, Default)]
201struct TxInfo {
202    version: Felt252,
203    account_contract_address: Felt252,
204    max_fee: Felt252,
205    signature: Vec<Felt252>,
206    transaction_hash: Felt252,
207    chain_id: Felt252,
208    nonce: Felt252,
209    resource_bounds: Vec<ResourceBounds>,
210    tip: Felt252,
211    paymaster_data: Vec<Felt252>,
212    nonce_data_availability_mode: Felt252,
213    fee_data_availability_mode: Felt252,
214    account_deployment_data: Vec<Felt252>,
215}
216
217/// Copy of the cairo `ResourceBounds` struct.
218#[derive(Clone, Default)]
219struct ResourceBounds {
220    resource: Felt252,
221    max_amount: Felt252,
222    max_price_per_unit: Felt252,
223}
224
225/// Execution scope for constant memory allocation.
226struct MemoryExecScope {
227    /// The first free address in the segment.
228    next_address: Relocatable,
229}
230
231/// Fetches the value of a cell from the vm.
232fn get_cell_val(vm: &VirtualMachine, cell: &CellRef) -> Result<Felt252, VirtualMachineError> {
233    Ok(*vm.get_integer(cell_ref_to_relocatable(cell, vm))?)
234}
235
236/// Fetch the `MaybeRelocatable` value from an address.
237fn get_maybe_from_addr(
238    vm: &VirtualMachine,
239    addr: Relocatable,
240) -> Result<MaybeRelocatable, VirtualMachineError> {
241    vm.get_maybe(&addr)
242        .ok_or_else(|| VirtualMachineError::InvalidMemoryValueTemporaryAddress(Box::new(addr)))
243}
244
245/// Fetches the maybe relocatable value of a cell from the vm.
246fn get_cell_maybe(
247    vm: &VirtualMachine,
248    cell: &CellRef,
249) -> Result<MaybeRelocatable, VirtualMachineError> {
250    get_maybe_from_addr(vm, cell_ref_to_relocatable(cell, vm))
251}
252
253/// Fetches the value of a cell plus an offset from the vm, useful for pointers.
254pub fn get_ptr(
255    vm: &VirtualMachine,
256    cell: &CellRef,
257    offset: &Felt252,
258) -> Result<Relocatable, VirtualMachineError> {
259    Ok((vm.get_relocatable(cell_ref_to_relocatable(cell, vm))? + offset)?)
260}
261
262/// Fetches the value of a pointer described by the value at `cell` plus an offset from the vm.
263fn get_double_deref_val(
264    vm: &VirtualMachine,
265    cell: &CellRef,
266    offset: &Felt252,
267) -> Result<Felt252, VirtualMachineError> {
268    Ok(*vm.get_integer(get_ptr(vm, cell, offset)?)?)
269}
270
271/// Fetches the maybe relocatable value of a pointer described by the value at `cell` plus an offset
272/// from the vm.
273fn get_double_deref_maybe(
274    vm: &VirtualMachine,
275    cell: &CellRef,
276    offset: &Felt252,
277) -> Result<MaybeRelocatable, VirtualMachineError> {
278    get_maybe_from_addr(vm, get_ptr(vm, cell, offset)?)
279}
280
281/// Extracts a parameter assumed to be a buffer, and converts it into a relocatable.
282pub fn extract_relocatable(
283    vm: &VirtualMachine,
284    buffer: &ResOperand,
285) -> Result<Relocatable, VirtualMachineError> {
286    let (base, offset) = extract_buffer(buffer);
287    get_ptr(vm, base, &offset)
288}
289
290/// Fetches the value of `res_operand` from the vm.
291pub fn get_val(
292    vm: &VirtualMachine,
293    res_operand: &ResOperand,
294) -> Result<Felt252, VirtualMachineError> {
295    match res_operand {
296        ResOperand::Deref(cell) => get_cell_val(vm, cell),
297        ResOperand::DoubleDeref(cell, offset) => get_double_deref_val(vm, cell, &(*offset).into()),
298        ResOperand::Immediate(x) => Ok(Felt252::from(x.value.clone())),
299        ResOperand::BinOp(op) => {
300            let a = get_cell_val(vm, &op.a)?;
301            let b = match &op.b {
302                DerefOrImmediate::Deref(cell) => get_cell_val(vm, cell)?,
303                DerefOrImmediate::Immediate(x) => Felt252::from(x.value.clone()),
304            };
305            match op.op {
306                Operation::Add => Ok(a + b),
307                Operation::Mul => Ok(a * b),
308            }
309        }
310    }
311}
312
313/// Resulting options from a syscall.
314enum SyscallResult {
315    /// The syscall was successful.
316    Success(Vec<MaybeRelocatable>),
317    /// The syscall failed, with the revert reason.
318    Failure(Vec<Felt252>),
319}
320
321macro_rules! fail_syscall {
322    ([$reason1:expr, $reason2:expr]) => {
323        return Ok(SyscallResult::Failure(vec![
324            Felt252::from_bytes_be_slice($reason1),
325            Felt252::from_bytes_be_slice($reason2),
326        ]))
327    };
328    ($reason:expr) => {
329        return Ok(SyscallResult::Failure(vec![Felt252::from_bytes_be_slice($reason)]))
330    };
331    ($existing:ident, $reason:expr) => {
332        $existing.push(Felt252::from_bytes_be_slice($reason));
333        return Ok(SyscallResult::Failure($existing))
334    };
335}
336
337/// Gas Costs for syscalls.
338/// Mostly duplication of:
339/// `https://github.com/starkware-libs/blockifier/blob/main/crates/blockifier/src/abi/constants.rs`.
340mod gas_costs {
341    const STEP: usize = 100;
342    const RANGE_CHECK: usize = 70;
343    const BITWISE: usize = 594;
344
345    /// Entry point initial gas cost enforced by the compiler.
346    /// Should match `ENTRY_POINT_COST` at `crates/cairo-lang-starknet/src/casm_contract_class.rs`.
347    pub const ENTRY_POINT_INITIAL_BUDGET: usize = 100 * STEP;
348    /// OS gas costs.
349    const ENTRY_POINT: usize = ENTRY_POINT_INITIAL_BUDGET + 500 * STEP;
350    // The required gas for each syscall minus the base amount that was pre-charged (by the
351    // compiler).
352    pub const CALL_CONTRACT: usize = 10 * STEP + ENTRY_POINT;
353    pub const DEPLOY: usize = 200 * STEP + ENTRY_POINT;
354    pub const EMIT_EVENT: usize = 10 * STEP;
355    pub const GET_BLOCK_HASH: usize = 50 * STEP;
356    pub const GET_EXECUTION_INFO: usize = 10 * STEP;
357    pub const GET_CLASS_HASH_AT: usize = 50 * STEP;
358    pub const KECCAK: usize = 0;
359    pub const KECCAK_ROUND_COST: usize = 180000;
360    pub const SHA256_PROCESS_BLOCK: usize = 1852 * STEP + 65 * RANGE_CHECK + 1115 * BITWISE;
361    pub const LIBRARY_CALL: usize = CALL_CONTRACT;
362    pub const REPLACE_CLASS: usize = 50 * STEP;
363    pub const SECP256K1_ADD: usize = 254 * STEP + 29 * RANGE_CHECK;
364    pub const SECP256K1_GET_POINT_FROM_X: usize = 260 * STEP + 29 * RANGE_CHECK;
365    pub const SECP256K1_GET_XY: usize = 24 * STEP + 9 * RANGE_CHECK;
366    pub const SECP256K1_MUL: usize = 121810 * STEP + 10739 * RANGE_CHECK;
367    pub const SECP256K1_NEW: usize = 340 * STEP + 36 * RANGE_CHECK;
368    pub const SECP256R1_ADD: usize = 254 * STEP + 29 * RANGE_CHECK;
369    pub const SECP256R1_GET_POINT_FROM_X: usize = 260 * STEP + 29 * RANGE_CHECK;
370    pub const SECP256R1_GET_XY: usize = 24 * STEP + 9 * RANGE_CHECK;
371    pub const SECP256R1_MUL: usize = 121810 * STEP + 10739 * RANGE_CHECK;
372    pub const SECP256R1_NEW: usize = 340 * STEP + 36 * RANGE_CHECK;
373    pub const SEND_MESSAGE_TO_L1: usize = 50 * STEP;
374    pub const STORAGE_READ: usize = 50 * STEP;
375    pub const STORAGE_WRITE: usize = 50 * STEP;
376}
377
378/// Deducts gas from the given gas counter, or fails the syscall if there is not enough gas.
379macro_rules! deduct_gas {
380    ($gas:ident, $amount:ident) => {
381        if *$gas < gas_costs::$amount {
382            fail_syscall!(b"Syscall out of gas");
383        }
384        *$gas -= gas_costs::$amount;
385    };
386}
387
388/// Fetches the maybe relocatable value of `res_operand` from the vm.
389fn get_maybe(
390    vm: &VirtualMachine,
391    res_operand: &ResOperand,
392) -> Result<MaybeRelocatable, VirtualMachineError> {
393    match res_operand {
394        ResOperand::Deref(cell) => get_cell_maybe(vm, cell),
395        ResOperand::DoubleDeref(cell, offset) => {
396            get_double_deref_maybe(vm, cell, &(*offset).into())
397        }
398        ResOperand::Immediate(x) => Ok(Felt252::from(x.value.clone()).into()),
399        ResOperand::BinOp(op) => {
400            let a = get_cell_maybe(vm, &op.a)?;
401            let b = match &op.b {
402                DerefOrImmediate::Deref(cell) => get_cell_val(vm, cell)?,
403                DerefOrImmediate::Immediate(x) => Felt252::from(x.value.clone()),
404            };
405            Ok(match op.op {
406                Operation::Add => a.add_int(&b)?,
407                Operation::Mul => match a {
408                    MaybeRelocatable::RelocatableValue(_) => {
409                        panic!("mul not implemented for relocatable values")
410                    }
411                    MaybeRelocatable::Int(a) => (a * b).into(),
412                },
413            })
414        }
415    }
416}
417
418impl HintProcessorLogic for CairoHintProcessor<'_> {
419    /// Trait function to execute a given hint in the hint processor.
420    fn execute_hint(
421        &mut self,
422        vm: &mut VirtualMachine,
423        exec_scopes: &mut ExecutionScopes,
424        hint_data: &Box<dyn Any>,
425    ) -> Result<(), HintError> {
426        let hint = hint_data.downcast_ref::<Hint>().ok_or(HintError::WrongHintData)?;
427        let hint = match hint {
428            Hint::Starknet(hint) => hint,
429            Hint::Core(core_hint_base) => {
430                return execute_core_hint_base(
431                    vm,
432                    exec_scopes,
433                    core_hint_base,
434                    self.no_temporary_segments,
435                );
436            }
437            Hint::External(hint) => {
438                return self.execute_external_hint(vm, hint);
439            }
440        };
441        match hint {
442            StarknetHint::SystemCall { system } => {
443                self.execute_syscall(system, vm, exec_scopes)?;
444            }
445            StarknetHint::Cheatcode {
446                selector,
447                input_start,
448                input_end,
449                output_start,
450                output_end,
451            } => {
452                self.execute_cheatcode(
453                    selector,
454                    [input_start, input_end],
455                    [output_start, output_end],
456                    vm,
457                    exec_scopes,
458                )?;
459            }
460        };
461        Ok(())
462    }
463
464    /// Trait function to store hint in the hint processor by string.
465    fn compile_hint(
466        &self,
467        hint_code: &str,
468        _ap_tracking_data: &ApTracking,
469        _reference_ids: &HashMap<String, usize>,
470        _references: &[HintReference],
471        _constants: Rc<HashMap<String, Felt252>>,
472    ) -> Result<Box<dyn Any>, VirtualMachineError> {
473        Ok(Box::new(self.string_to_hint[hint_code].clone()))
474    }
475}
476
477impl ResourceTracker for CairoHintProcessor<'_> {
478    fn consumed(&self) -> bool {
479        self.run_resources.consumed()
480    }
481
482    fn consume_step(&mut self) {
483        self.run_resources.consume_step()
484    }
485
486    fn get_n_steps(&self) -> Option<usize> {
487        self.run_resources.get_n_steps()
488    }
489
490    fn run_resources(&self) -> &RunResources {
491        self.run_resources.run_resources()
492    }
493}
494
495pub trait StarknetHintProcessor: HintProcessor {
496    /// Take [`StarknetState`] out of this hint processor, resetting own state.
497    fn take_starknet_state(&mut self) -> StarknetState;
498    /// Take [`StarknetExecutionResources`] out of this hint processor, resetting own state.
499    fn take_syscalls_used_resources(&mut self) -> StarknetExecutionResources;
500}
501
502impl StarknetHintProcessor for CairoHintProcessor<'_> {
503    fn take_starknet_state(&mut self) -> StarknetState {
504        std::mem::take(&mut self.starknet_state)
505    }
506
507    fn take_syscalls_used_resources(&mut self) -> StarknetExecutionResources {
508        std::mem::take(&mut self.syscalls_used_resources)
509    }
510}
511
512/// Wrapper trait for a VM owner.
513pub trait VMWrapper {
514    fn vm(&mut self) -> &mut VirtualMachine;
515}
516impl VMWrapper for VirtualMachine {
517    fn vm(&mut self) -> &mut VirtualMachine {
518        self
519    }
520}
521
522/// Creates a new segment in the VM memory and writes data to it, returning the start and end
523/// pointers of the segment.
524fn segment_with_data<T: Into<MaybeRelocatable>, Data: Iterator<Item = T>>(
525    vm: &mut dyn VMWrapper,
526    data: Data,
527) -> Result<(Relocatable, Relocatable), MemoryError> {
528    let mut segment = MemBuffer::new_segment(vm);
529    let start = segment.ptr;
530    segment.write_data(data)?;
531    Ok((start, segment.ptr))
532}
533
534/// A helper struct to continuously write and read from a buffer in the VM memory.
535pub struct MemBuffer<'a> {
536    /// The VM to write to.
537    /// This is a trait so that we would borrow the actual VM only once.
538    vm: &'a mut dyn VMWrapper,
539    /// The current location of the buffer.
540    pub ptr: Relocatable,
541}
542impl<'a> MemBuffer<'a> {
543    /// Creates a new buffer.
544    pub fn new(vm: &'a mut dyn VMWrapper, ptr: Relocatable) -> Self {
545        Self { vm, ptr }
546    }
547
548    /// Creates a new segment and returns a buffer wrapping it.
549    pub fn new_segment(vm: &'a mut dyn VMWrapper) -> Self {
550        let ptr = vm.vm().add_memory_segment();
551        Self::new(vm, ptr)
552    }
553
554    /// Returns the current position of the buffer and advances it by one.
555    fn next(&mut self) -> Relocatable {
556        let ptr = self.ptr;
557        self.ptr += 1;
558        ptr
559    }
560
561    /// Returns the felt252 value in the current position of the buffer and advances it by one.
562    /// Fails if the value is not a felt252.
563    /// Borrows the buffer since a reference is returned.
564    pub fn next_felt252(&mut self) -> Result<Cow<'_, Felt252>, MemoryError> {
565        let ptr = self.next();
566        self.vm.vm().get_integer(ptr)
567    }
568
569    /// Returns the bool value in the current position of the buffer and advances it by one.
570    /// Fails with `MemoryError` if the value is not a felt252.
571    /// Panics if the value is not a bool.
572    fn next_bool(&mut self) -> Result<bool, MemoryError> {
573        let ptr = self.next();
574        Ok(!(self.vm.vm().get_integer(ptr)?.is_zero()))
575    }
576
577    /// Returns the usize value in the current position of the buffer and advances it by one.
578    /// Fails with `MemoryError` if the value is not a felt252.
579    /// Panics if the value is not a usize.
580    pub fn next_usize(&mut self) -> Result<usize, MemoryError> {
581        Ok(self.next_felt252()?.to_usize().unwrap())
582    }
583
584    /// Returns the u128 value in the current position of the buffer and advances it by one.
585    /// Fails with `MemoryError` if the value is not a felt252.
586    /// Panics if the value is not a u128.
587    pub fn next_u128(&mut self) -> Result<u128, MemoryError> {
588        Ok(self.next_felt252()?.to_u128().unwrap())
589    }
590
591    /// Returns the u64 value in the current position of the buffer and advances it by one.
592    /// Fails with `MemoryError` if the value is not a felt252.
593    /// Panics if the value is not a u64.
594    pub fn next_u64(&mut self) -> Result<u64, MemoryError> {
595        Ok(self.next_felt252()?.to_u64().unwrap())
596    }
597
598    /// Returns the u256 value encoded starting from the current position of the buffer and advances
599    /// it by two.
600    /// Fails with `MemoryError` if any of the next two values are not felt252s.
601    /// Panics if any of the next two values are not u128.
602    pub fn next_u256(&mut self) -> Result<BigUint, MemoryError> {
603        Ok(self.next_u128()? + BigUint::from(self.next_u128()?).shl(128))
604    }
605
606    /// Returns the address value in the current position of the buffer and advances it by one.
607    /// Fails if the value is not an address.
608    pub fn next_addr(&mut self) -> Result<Relocatable, MemoryError> {
609        let ptr = self.next();
610        self.vm.vm().get_relocatable(ptr)
611    }
612
613    /// Returns the array of integer values pointed to by the two next addresses in the buffer and
614    /// advances it by two. Will fail if the two values are not addresses or if the addresses do
615    /// not point to an array of integers.
616    pub fn next_arr(&mut self) -> Result<Vec<Felt252>, HintError> {
617        let start = self.next_addr()?;
618        let end = self.next_addr()?;
619        vm_get_range(self.vm.vm(), start, end)
620    }
621
622    /// Returns the array of integer values pointed to by the next address in the buffer and
623    /// with a fixed size and advances the buffer by one. Will fail if the next value is not
624    /// an address or if the address does not point to an array of integers.
625    pub fn next_fixed_size_arr_pointer(&mut self, size: usize) -> Result<Vec<Felt252>, HintError> {
626        let start = self.next_addr()?;
627        let end = (start + size)?;
628        vm_get_range(self.vm.vm(), start, end)
629    }
630
631    /// Writes a value to the current position of the buffer and advances it by one.
632    pub fn write<T: Into<MaybeRelocatable>>(&mut self, value: T) -> Result<(), MemoryError> {
633        let ptr = self.next();
634        self.vm.vm().insert_value(ptr, value)
635    }
636    /// Writes an iterator of values starting from the current position of the buffer and advances
637    /// it to after the end of the written value.
638    pub fn write_data<T: Into<MaybeRelocatable>, Data: Iterator<Item = T>>(
639        &mut self,
640        data: Data,
641    ) -> Result<(), MemoryError> {
642        for value in data {
643            self.write(value)?;
644        }
645        Ok(())
646    }
647
648    /// Writes an array into a new segment and writes the start and end pointers to the current
649    /// position of the buffer. Advances the buffer by two.
650    pub fn write_arr<T: Into<MaybeRelocatable>, Data: Iterator<Item = T>>(
651        &mut self,
652        data: Data,
653    ) -> Result<(), MemoryError> {
654        let (start, end) = segment_with_data(self, data)?;
655        self.write(start)?;
656        self.write(end)
657    }
658}
659
660impl VMWrapper for MemBuffer<'_> {
661    fn vm(&mut self) -> &mut VirtualMachine {
662        self.vm.vm()
663    }
664}
665
666impl CairoHintProcessor<'_> {
667    /// Executes a syscall.
668    fn execute_syscall(
669        &mut self,
670        system: &ResOperand,
671        vm: &mut VirtualMachine,
672        exec_scopes: &mut ExecutionScopes,
673    ) -> Result<(), HintError> {
674        let system_ptr = extract_relocatable(vm, system)?;
675        let mut system_buffer = MemBuffer::new(vm, system_ptr);
676        let selector = system_buffer.next_felt252()?.to_bytes_be();
677        let mut gas_counter = system_buffer.next_usize()?;
678        let mut execute_handle_helper =
679            |handler: &mut dyn FnMut(
680                // The syscall buffer.
681                &mut MemBuffer<'_>,
682                // The gas counter.
683                &mut usize,
684            ) -> Result<SyscallResult, HintError>| {
685                match handler(&mut system_buffer, &mut gas_counter)? {
686                    SyscallResult::Success(values) => {
687                        system_buffer.write(gas_counter)?;
688                        system_buffer.write(Felt252::from(0))?;
689                        system_buffer.write_data(values.into_iter())?;
690                    }
691                    SyscallResult::Failure(revert_reason) => {
692                        system_buffer.write(gas_counter)?;
693                        system_buffer.write(Felt252::from(1))?;
694                        system_buffer.write_arr(revert_reason.into_iter())?;
695                    }
696                }
697                Ok(())
698            };
699        let selector = std::str::from_utf8(&selector).unwrap().trim_start_matches('\0');
700        *self.syscalls_used_resources.syscalls.entry(selector.into()).or_default() += 1;
701        match selector {
702            "StorageWrite" => execute_handle_helper(&mut |system_buffer, gas_counter| {
703                self.storage_write(
704                    gas_counter,
705                    system_buffer.next_felt252()?.into_owned(),
706                    system_buffer.next_felt252()?.into_owned(),
707                    system_buffer.next_felt252()?.into_owned(),
708                )
709            }),
710            "StorageRead" => execute_handle_helper(&mut |system_buffer, gas_counter| {
711                self.storage_read(
712                    gas_counter,
713                    system_buffer.next_felt252()?.into_owned(),
714                    system_buffer.next_felt252()?.into_owned(),
715                )
716            }),
717            "GetBlockHash" => execute_handle_helper(&mut |system_buffer, gas_counter| {
718                self.get_block_hash(gas_counter, system_buffer.next_u64()?)
719            }),
720            "GetExecutionInfo" => execute_handle_helper(&mut |system_buffer, gas_counter| {
721                self.get_execution_info(gas_counter, system_buffer)
722            }),
723            "EmitEvent" => execute_handle_helper(&mut |system_buffer, gas_counter| {
724                self.emit_event(gas_counter, system_buffer.next_arr()?, system_buffer.next_arr()?)
725            }),
726            "SendMessageToL1" => execute_handle_helper(&mut |system_buffer, gas_counter| {
727                self.send_message_to_l1(
728                    gas_counter,
729                    system_buffer.next_felt252()?.into_owned(),
730                    system_buffer.next_arr()?,
731                )
732            }),
733            "Keccak" => execute_handle_helper(&mut |system_buffer, gas_counter| {
734                keccak(gas_counter, system_buffer.next_arr()?)
735            }),
736            "Sha256ProcessBlock" => execute_handle_helper(&mut |system_buffer, gas_counter| {
737                sha_256_process_block(
738                    gas_counter,
739                    system_buffer.next_fixed_size_arr_pointer(8)?,
740                    system_buffer.next_fixed_size_arr_pointer(16)?,
741                    exec_scopes,
742                    system_buffer,
743                )
744            }),
745            "Secp256k1New" => execute_handle_helper(&mut |system_buffer, gas_counter| {
746                secp256k1_new(
747                    gas_counter,
748                    system_buffer.next_u256()?,
749                    system_buffer.next_u256()?,
750                    exec_scopes,
751                )
752            }),
753            "Secp256k1Add" => execute_handle_helper(&mut |system_buffer, gas_counter| {
754                secp256k1_add(
755                    gas_counter,
756                    exec_scopes,
757                    system_buffer.next_usize()?,
758                    system_buffer.next_usize()?,
759                )
760            }),
761            "Secp256k1Mul" => execute_handle_helper(&mut |system_buffer, gas_counter| {
762                secp256k1_mul(
763                    gas_counter,
764                    system_buffer.next_usize()?,
765                    system_buffer.next_u256()?,
766                    exec_scopes,
767                )
768            }),
769            "Secp256k1GetPointFromX" => execute_handle_helper(&mut |system_buffer, gas_counter| {
770                secp256k1_get_point_from_x(
771                    gas_counter,
772                    system_buffer.next_u256()?,
773                    system_buffer.next_bool()?,
774                    exec_scopes,
775                )
776            }),
777            "Secp256k1GetXy" => execute_handle_helper(&mut |system_buffer, gas_counter| {
778                secp256k1_get_xy(gas_counter, system_buffer.next_usize()?, exec_scopes)
779            }),
780            "Secp256r1New" => execute_handle_helper(&mut |system_buffer, gas_counter| {
781                secp256r1_new(
782                    gas_counter,
783                    system_buffer.next_u256()?,
784                    system_buffer.next_u256()?,
785                    exec_scopes,
786                )
787            }),
788            "Secp256r1Add" => execute_handle_helper(&mut |system_buffer, gas_counter| {
789                secp256r1_add(
790                    gas_counter,
791                    exec_scopes,
792                    system_buffer.next_usize()?,
793                    system_buffer.next_usize()?,
794                )
795            }),
796            "Secp256r1Mul" => execute_handle_helper(&mut |system_buffer, gas_counter| {
797                secp256r1_mul(
798                    gas_counter,
799                    system_buffer.next_usize()?,
800                    system_buffer.next_u256()?,
801                    exec_scopes,
802                )
803            }),
804            "Secp256r1GetPointFromX" => execute_handle_helper(&mut |system_buffer, gas_counter| {
805                secp256r1_get_point_from_x(
806                    gas_counter,
807                    system_buffer.next_u256()?,
808                    system_buffer.next_bool()?,
809                    exec_scopes,
810                )
811            }),
812            "Secp256r1GetXy" => execute_handle_helper(&mut |system_buffer, gas_counter| {
813                secp256r1_get_xy(gas_counter, system_buffer.next_usize()?, exec_scopes)
814            }),
815            "Deploy" => execute_handle_helper(&mut |system_buffer, gas_counter| {
816                self.deploy(
817                    gas_counter,
818                    system_buffer.next_felt252()?.into_owned(),
819                    system_buffer.next_felt252()?.into_owned(),
820                    system_buffer.next_arr()?,
821                    system_buffer.next_bool()?,
822                    system_buffer,
823                )
824            }),
825            "CallContract" => execute_handle_helper(&mut |system_buffer, gas_counter| {
826                self.call_contract(
827                    gas_counter,
828                    system_buffer.next_felt252()?.into_owned(),
829                    system_buffer.next_felt252()?.into_owned(),
830                    system_buffer.next_arr()?,
831                    system_buffer,
832                )
833            }),
834            "LibraryCall" => execute_handle_helper(&mut |system_buffer, gas_counter| {
835                self.library_call(
836                    gas_counter,
837                    system_buffer.next_felt252()?.into_owned(),
838                    system_buffer.next_felt252()?.into_owned(),
839                    system_buffer.next_arr()?,
840                    system_buffer,
841                )
842            }),
843            "ReplaceClass" => execute_handle_helper(&mut |system_buffer, gas_counter| {
844                self.replace_class(gas_counter, system_buffer.next_felt252()?.into_owned())
845            }),
846            "GetClassHashAt" => execute_handle_helper(&mut |system_buffer, gas_counter| {
847                self.get_class_hash_at(gas_counter, system_buffer.next_felt252()?.into_owned())
848            }),
849            "MetaTxV0" => execute_handle_helper(&mut |_system_buffer, _gas_counter| {
850                panic!("Meta transaction is not supported.")
851            }),
852            _ => panic!("Unknown selector for system call!"),
853        }
854    }
855
856    /// Executes the `storage_write_syscall` syscall.
857    fn storage_write(
858        &mut self,
859        gas_counter: &mut usize,
860        addr_domain: Felt252,
861        addr: Felt252,
862        value: Felt252,
863    ) -> Result<SyscallResult, HintError> {
864        deduct_gas!(gas_counter, STORAGE_WRITE);
865        if !addr_domain.is_zero() {
866            // Only address_domain 0 is currently supported.
867            fail_syscall!(b"Unsupported address domain");
868        }
869        let contract = self.starknet_state.exec_info.contract_address;
870        self.starknet_state.storage.entry(contract).or_default().insert(addr, value);
871        Ok(SyscallResult::Success(vec![]))
872    }
873
874    /// Executes the `storage_read_syscall` syscall.
875    fn storage_read(
876        &mut self,
877        gas_counter: &mut usize,
878        addr_domain: Felt252,
879        addr: Felt252,
880    ) -> Result<SyscallResult, HintError> {
881        deduct_gas!(gas_counter, STORAGE_READ);
882        if !addr_domain.is_zero() {
883            // Only address_domain 0 is currently supported.
884            fail_syscall!(b"Unsupported address domain");
885        }
886        let value = self
887            .starknet_state
888            .storage
889            .get(&self.starknet_state.exec_info.contract_address)
890            .and_then(|contract_storage| contract_storage.get(&addr))
891            .cloned()
892            .unwrap_or_else(|| Felt252::from(0));
893        Ok(SyscallResult::Success(vec![value.into()]))
894    }
895
896    /// Executes the `get_block_hash_syscall` syscall.
897    fn get_block_hash(
898        &mut self,
899        gas_counter: &mut usize,
900        block_number: u64,
901    ) -> Result<SyscallResult, HintError> {
902        deduct_gas!(gas_counter, GET_BLOCK_HASH);
903        if let Some(block_hash) = self.starknet_state.block_hash.get(&block_number) {
904            Ok(SyscallResult::Success(vec![block_hash.into()]))
905        } else {
906            fail_syscall!(b"GET_BLOCK_HASH_NOT_SET");
907        }
908    }
909
910    /// Executes the `get_execution_info_syscall` syscall.
911    fn get_execution_info(
912        &mut self,
913        gas_counter: &mut usize,
914        vm: &mut dyn VMWrapper,
915    ) -> Result<SyscallResult, HintError> {
916        deduct_gas!(gas_counter, GET_EXECUTION_INFO);
917        let exec_info = &self.starknet_state.exec_info;
918        let block_info = &exec_info.block_info;
919        let tx_info = &exec_info.tx_info;
920        let mut res_segment = MemBuffer::new_segment(vm);
921        let signature_start = res_segment.ptr;
922        res_segment.write_data(tx_info.signature.iter().cloned())?;
923        let signature_end = res_segment.ptr;
924        let resource_bounds_start = res_segment.ptr;
925        for value in &tx_info.resource_bounds {
926            res_segment.write(value.resource)?;
927            res_segment.write(value.max_amount)?;
928            res_segment.write(value.max_price_per_unit)?;
929        }
930        let resource_bounds_end = res_segment.ptr;
931        let paymaster_data_start = res_segment.ptr;
932        res_segment.write_data(tx_info.paymaster_data.iter().cloned())?;
933        let paymaster_data_end = res_segment.ptr;
934        let account_deployment_data_start = res_segment.ptr;
935        res_segment.write_data(tx_info.account_deployment_data.iter().cloned())?;
936        let account_deployment_data_end = res_segment.ptr;
937        let tx_info_ptr = res_segment.ptr;
938        res_segment.write(tx_info.version)?;
939        res_segment.write(tx_info.account_contract_address)?;
940        res_segment.write(tx_info.max_fee)?;
941        res_segment.write(signature_start)?;
942        res_segment.write(signature_end)?;
943        res_segment.write(tx_info.transaction_hash)?;
944        res_segment.write(tx_info.chain_id)?;
945        res_segment.write(tx_info.nonce)?;
946        res_segment.write(resource_bounds_start)?;
947        res_segment.write(resource_bounds_end)?;
948        res_segment.write(tx_info.tip)?;
949        res_segment.write(paymaster_data_start)?;
950        res_segment.write(paymaster_data_end)?;
951        res_segment.write(tx_info.nonce_data_availability_mode)?;
952        res_segment.write(tx_info.fee_data_availability_mode)?;
953        res_segment.write(account_deployment_data_start)?;
954        res_segment.write(account_deployment_data_end)?;
955        let block_info_ptr = res_segment.ptr;
956        res_segment.write(block_info.block_number)?;
957        res_segment.write(block_info.block_timestamp)?;
958        res_segment.write(block_info.sequencer_address)?;
959        let exec_info_ptr = res_segment.ptr;
960        res_segment.write(block_info_ptr)?;
961        res_segment.write(tx_info_ptr)?;
962        res_segment.write(exec_info.caller_address)?;
963        res_segment.write(exec_info.contract_address)?;
964        res_segment.write(exec_info.entry_point_selector)?;
965        Ok(SyscallResult::Success(vec![exec_info_ptr.into()]))
966    }
967
968    /// Executes the `emit_event_syscall` syscall.
969    fn emit_event(
970        &mut self,
971        gas_counter: &mut usize,
972        keys: Vec<Felt252>,
973        data: Vec<Felt252>,
974    ) -> Result<SyscallResult, HintError> {
975        deduct_gas!(gas_counter, EMIT_EVENT);
976        let contract = self.starknet_state.exec_info.contract_address;
977        self.starknet_state.logs.entry(contract).or_default().events.push_back((keys, data));
978        Ok(SyscallResult::Success(vec![]))
979    }
980
981    /// Executes the `send_message_to_l1_event_syscall` syscall.
982    fn send_message_to_l1(
983        &mut self,
984        gas_counter: &mut usize,
985        to_address: Felt252,
986        payload: Vec<Felt252>,
987    ) -> Result<SyscallResult, HintError> {
988        deduct_gas!(gas_counter, SEND_MESSAGE_TO_L1);
989        let contract = self.starknet_state.exec_info.contract_address;
990        self.starknet_state
991            .logs
992            .entry(contract)
993            .or_default()
994            .l2_to_l1_messages
995            .push_back((to_address, payload));
996        Ok(SyscallResult::Success(vec![]))
997    }
998
999    /// Executes the `deploy_syscall` syscall.
1000    fn deploy(
1001        &mut self,
1002        gas_counter: &mut usize,
1003        class_hash: Felt252,
1004        _contract_address_salt: Felt252,
1005        calldata: Vec<Felt252>,
1006        deploy_from_zero: bool,
1007        vm: &mut dyn VMWrapper,
1008    ) -> Result<SyscallResult, HintError> {
1009        deduct_gas!(gas_counter, DEPLOY);
1010
1011        // Assign the starknet address of the contract.
1012        let deployer_address = if deploy_from_zero {
1013            Felt252::zero()
1014        } else {
1015            self.starknet_state.exec_info.contract_address
1016        };
1017        let deployed_contract_address = calculate_contract_address(
1018            &_contract_address_salt,
1019            &class_hash,
1020            &calldata,
1021            &deployer_address,
1022        );
1023
1024        // Prepare runner for running the constructor.
1025        let runner = self.runner.expect("Runner is needed for starknet.");
1026        let Some(contract_info) = runner.starknet_contracts_info.get(&class_hash) else {
1027            fail_syscall!(b"CLASS_HASH_NOT_FOUND");
1028        };
1029
1030        // Set the class hash of the deployed contract before executing the constructor,
1031        // as the constructor could make an external call to this address.
1032        if self
1033            .starknet_state
1034            .deployed_contracts
1035            .insert(deployed_contract_address, class_hash)
1036            .is_some()
1037        {
1038            fail_syscall!(b"CONTRACT_ALREADY_DEPLOYED");
1039        }
1040
1041        // Call constructor if it exists.
1042        let (res_data_start, res_data_end) = if let Some(constructor) = &contract_info.constructor {
1043            let old_addrs = self
1044                .starknet_state
1045                .open_caller_context((deployed_contract_address, deployer_address));
1046            let res = self.call_entry_point(gas_counter, runner, constructor, calldata, vm);
1047            self.starknet_state.close_caller_context(old_addrs);
1048            match res {
1049                Ok(value) => value,
1050                Err(mut revert_reason) => {
1051                    self.starknet_state.deployed_contracts.remove(&deployed_contract_address);
1052                    fail_syscall!(revert_reason, b"CONSTRUCTOR_FAILED");
1053                }
1054            }
1055        } else if calldata.is_empty() {
1056            (Relocatable::from((0, 0)), Relocatable::from((0, 0)))
1057        } else {
1058            // Remove the contract from the deployed contracts,
1059            // since it failed to deploy.
1060            self.starknet_state.deployed_contracts.remove(&deployed_contract_address);
1061            fail_syscall!(b"INVALID_CALLDATA_LEN");
1062        };
1063
1064        Ok(SyscallResult::Success(vec![
1065            deployed_contract_address.into(),
1066            res_data_start.into(),
1067            res_data_end.into(),
1068        ]))
1069    }
1070
1071    /// Executes the `call_contract_syscall` syscall.
1072    fn call_contract(
1073        &mut self,
1074        gas_counter: &mut usize,
1075        contract_address: Felt252,
1076        selector: Felt252,
1077        calldata: Vec<Felt252>,
1078        vm: &mut dyn VMWrapper,
1079    ) -> Result<SyscallResult, HintError> {
1080        deduct_gas!(gas_counter, CALL_CONTRACT);
1081
1082        // Get the class hash of the contract.
1083        let Some(class_hash) = self.starknet_state.deployed_contracts.get(&contract_address) else {
1084            fail_syscall!([b"CONTRACT_NOT_DEPLOYED", b"ENTRYPOINT_FAILED"]);
1085        };
1086
1087        // Prepare runner for running the ctor.
1088        let runner = self.runner.expect("Runner is needed for starknet.");
1089        let contract_info = runner
1090            .starknet_contracts_info
1091            .get(class_hash)
1092            .expect("Deployed contract not found in registry.");
1093
1094        // Call the function.
1095        let Some(entry_point) = contract_info.externals.get(&selector) else {
1096            fail_syscall!([b"ENTRYPOINT_NOT_FOUND", b"ENTRYPOINT_FAILED"]);
1097        };
1098
1099        let old_addrs = self.starknet_state.open_caller_context((
1100            contract_address,
1101            self.starknet_state.exec_info.contract_address,
1102        ));
1103        let res = self.call_entry_point(gas_counter, runner, entry_point, calldata, vm);
1104        self.starknet_state.close_caller_context(old_addrs);
1105
1106        match res {
1107            Ok((res_data_start, res_data_end)) => {
1108                Ok(SyscallResult::Success(vec![res_data_start.into(), res_data_end.into()]))
1109            }
1110            Err(mut revert_reason) => {
1111                fail_syscall!(revert_reason, b"ENTRYPOINT_FAILED");
1112            }
1113        }
1114    }
1115
1116    /// Executes the `library_call_syscall` syscall.
1117    fn library_call(
1118        &mut self,
1119        gas_counter: &mut usize,
1120        class_hash: Felt252,
1121        selector: Felt252,
1122        calldata: Vec<Felt252>,
1123        vm: &mut dyn VMWrapper,
1124    ) -> Result<SyscallResult, HintError> {
1125        deduct_gas!(gas_counter, LIBRARY_CALL);
1126        // Prepare runner for running the call.
1127        let runner = self.runner.expect("Runner is needed for starknet.");
1128        let Some(contract_info) = runner.starknet_contracts_info.get(&class_hash) else {
1129            fail_syscall!(b"CLASS_HASH_NOT_DECLARED")
1130        };
1131
1132        // Call the function.
1133        let Some(entry_point) = contract_info.externals.get(&selector) else {
1134            fail_syscall!([b"ENTRYPOINT_NOT_FOUND", b"ENTRYPOINT_FAILED"]);
1135        };
1136        match self.call_entry_point(gas_counter, runner, entry_point, calldata, vm) {
1137            Ok((res_data_start, res_data_end)) => {
1138                Ok(SyscallResult::Success(vec![res_data_start.into(), res_data_end.into()]))
1139            }
1140            Err(mut revert_reason) => {
1141                fail_syscall!(revert_reason, b"ENTRYPOINT_FAILED");
1142            }
1143        }
1144    }
1145
1146    /// Executes the `replace_class_syscall` syscall.
1147    fn replace_class(
1148        &mut self,
1149        gas_counter: &mut usize,
1150        new_class: Felt252,
1151    ) -> Result<SyscallResult, HintError> {
1152        deduct_gas!(gas_counter, REPLACE_CLASS);
1153        // Validating the class hash was declared as one of the starknet contracts.
1154        if !self
1155            .runner
1156            .expect("Runner is needed for starknet.")
1157            .starknet_contracts_info
1158            .contains_key(&new_class)
1159        {
1160            fail_syscall!(b"CLASS_HASH_NOT_FOUND");
1161        };
1162        let address = self.starknet_state.exec_info.contract_address;
1163        self.starknet_state.deployed_contracts.insert(address, new_class);
1164        Ok(SyscallResult::Success(vec![]))
1165    }
1166
1167    /// Executes the `get_class_hash_at_syscall` syscall.
1168    fn get_class_hash_at(
1169        &mut self,
1170        gas_counter: &mut usize,
1171        contract_address: Felt252,
1172    ) -> Result<SyscallResult, HintError> {
1173        deduct_gas!(gas_counter, GET_CLASS_HASH_AT);
1174        // Look up the class hash of the deployed contract at the given address.
1175        let class_hash = self
1176            .starknet_state
1177            .deployed_contracts
1178            .get(&contract_address)
1179            .cloned()
1180            .unwrap_or_else(Felt252::zero);
1181        Ok(SyscallResult::Success(vec![MaybeRelocatable::Int(class_hash)]))
1182    }
1183
1184    /// Executes the entry point with the given calldata.
1185    fn call_entry_point(
1186        &mut self,
1187        gas_counter: &mut usize,
1188        runner: &SierraCasmRunner,
1189        entry_point: &FunctionId,
1190        calldata: Vec<Felt252>,
1191        vm: &mut dyn VMWrapper,
1192    ) -> Result<(Relocatable, Relocatable), Vec<Felt252>> {
1193        let function = runner
1194            .builder
1195            .registry()
1196            .get_function(entry_point)
1197            .expect("Entrypoint exists, but not found.");
1198        let res = runner
1199            .run_function_with_starknet_context(
1200                function,
1201                vec![Arg::Array(calldata.into_iter().map(Arg::Value).collect())],
1202                // The costs of the relevant syscall include `ENTRY_POINT_INITIAL_BUDGET` so we
1203                // need to refund it here before running the entry point to avoid double charging.
1204                Some(*gas_counter + gas_costs::ENTRY_POINT_INITIAL_BUDGET),
1205                self.starknet_state.clone(),
1206            )
1207            .expect("Internal runner error.");
1208        self.syscalls_used_resources += res.used_resources;
1209        *gas_counter = res.gas_counter.unwrap().to_usize().unwrap();
1210        match res.value {
1211            RunResultValue::Success(value) => {
1212                self.starknet_state = res.starknet_state;
1213                Ok(segment_with_data(vm, read_array_result_as_vec(&res.memory, &value).into_iter())
1214                    .expect("failed to allocate segment"))
1215            }
1216            RunResultValue::Panic(panic_data) => Err(panic_data),
1217        }
1218    }
1219
1220    /// Executes a cheatcode.
1221    fn execute_cheatcode(
1222        &mut self,
1223        selector: &BigIntAsHex,
1224        [input_start, input_end]: [&ResOperand; 2],
1225        [output_start, output_end]: [&CellRef; 2],
1226        vm: &mut VirtualMachine,
1227        _exec_scopes: &mut ExecutionScopes,
1228    ) -> Result<(), HintError> {
1229        // Parse the selector.
1230        let selector = &selector.value.to_bytes_be().1;
1231        let selector = std::str::from_utf8(selector).map_err(|_| {
1232            HintError::CustomHint(Box::from("failed to parse selector".to_string()))
1233        })?;
1234
1235        // Extract the inputs.
1236        let input_start = extract_relocatable(vm, input_start)?;
1237        let input_end = extract_relocatable(vm, input_end)?;
1238        let inputs = vm_get_range(vm, input_start, input_end)?;
1239
1240        // Helper for all the instances requiring only a single input.
1241        let as_single_input = |inputs| {
1242            vec_as_array(inputs, || {
1243                format!(
1244                    "`{selector}` cheatcode invalid args: pass span of an array with exactly one \
1245                     element",
1246                )
1247            })
1248            .map(|[value]| value)
1249        };
1250
1251        let mut res_segment = MemBuffer::new_segment(vm);
1252        let res_segment_start = res_segment.ptr;
1253        match selector {
1254            "set_sequencer_address" => {
1255                self.starknet_state.exec_info.block_info.sequencer_address =
1256                    as_single_input(inputs)?;
1257            }
1258            "set_block_number" => {
1259                self.starknet_state.exec_info.block_info.block_number = as_single_input(inputs)?;
1260            }
1261            "set_block_timestamp" => {
1262                self.starknet_state.exec_info.block_info.block_timestamp = as_single_input(inputs)?;
1263            }
1264            "set_caller_address" => {
1265                self.starknet_state.exec_info.caller_address = as_single_input(inputs)?;
1266            }
1267            "set_contract_address" => {
1268                self.starknet_state.exec_info.contract_address = as_single_input(inputs)?;
1269            }
1270            "set_version" => {
1271                self.starknet_state.exec_info.tx_info.version = as_single_input(inputs)?;
1272            }
1273            "set_account_contract_address" => {
1274                self.starknet_state.exec_info.tx_info.account_contract_address =
1275                    as_single_input(inputs)?;
1276            }
1277            "set_max_fee" => {
1278                self.starknet_state.exec_info.tx_info.max_fee = as_single_input(inputs)?;
1279            }
1280            "set_transaction_hash" => {
1281                self.starknet_state.exec_info.tx_info.transaction_hash = as_single_input(inputs)?;
1282            }
1283            "set_chain_id" => {
1284                self.starknet_state.exec_info.tx_info.chain_id = as_single_input(inputs)?;
1285            }
1286            "set_nonce" => {
1287                self.starknet_state.exec_info.tx_info.nonce = as_single_input(inputs)?;
1288            }
1289            "set_signature" => {
1290                self.starknet_state.exec_info.tx_info.signature = inputs;
1291            }
1292            "set_block_hash" => {
1293                let [block_number, block_hash] = vec_as_array(inputs, || {
1294                    format!(
1295                        "`{selector}` cheatcode invalid args: pass span of an array with exactly \
1296                         two elements",
1297                    )
1298                })?;
1299                self.starknet_state.block_hash.insert(block_number.to_u64().unwrap(), block_hash);
1300            }
1301            "pop_log" => {
1302                let contract_logs = self.starknet_state.logs.get_mut(&as_single_input(inputs)?);
1303                if let Some((keys, data)) =
1304                    contract_logs.and_then(|contract_logs| contract_logs.events.pop_front())
1305                {
1306                    res_segment.write(keys.len())?;
1307                    res_segment.write_data(keys.iter())?;
1308                    res_segment.write(data.len())?;
1309                    res_segment.write_data(data.iter())?;
1310                }
1311            }
1312            "pop_l2_to_l1_message" => {
1313                let contract_logs = self.starknet_state.logs.get_mut(&as_single_input(inputs)?);
1314                if let Some((to_address, payload)) = contract_logs
1315                    .and_then(|contract_logs| contract_logs.l2_to_l1_messages.pop_front())
1316                {
1317                    res_segment.write(to_address)?;
1318                    res_segment.write(payload.len())?;
1319                    res_segment.write_data(payload.iter())?;
1320                }
1321            }
1322            _ => Err(HintError::CustomHint(Box::from(format!(
1323                "Unknown cheatcode selector: {selector}"
1324            ))))?,
1325        }
1326        let res_segment_end = res_segment.ptr;
1327        insert_value_to_cellref!(vm, output_start, res_segment_start)?;
1328        insert_value_to_cellref!(vm, output_end, res_segment_end)?;
1329        Ok(())
1330    }
1331
1332    /// Executes an external hint.
1333    fn execute_external_hint(
1334        &mut self,
1335        vm: &mut VirtualMachine,
1336        core_hint: &ExternalHint,
1337    ) -> Result<(), HintError> {
1338        match core_hint {
1339            ExternalHint::AddRelocationRule { src, dst } => vm.add_relocation_rule(
1340                extract_relocatable(vm, src)?,
1341                // The following is needed for when the `extensive_hints` feature is used in the
1342                // VM, in which case `dst_ptr` is a `MaybeRelocatable` type.
1343                #[allow(clippy::useless_conversion)]
1344                {
1345                    extract_relocatable(vm, dst)?.into()
1346                },
1347            )?,
1348            ExternalHint::WriteRunParam { index, dst } => {
1349                let index = get_val(vm, index)?.to_usize().expect("Got a bad index.");
1350                let mut stack = vec![(cell_ref_to_relocatable(dst, vm), &self.user_args[index])];
1351                while let Some((mut buffer, values)) = stack.pop() {
1352                    for value in values {
1353                        match value {
1354                            Arg::Value(v) => {
1355                                vm.insert_value(buffer, v)?;
1356                                buffer += 1;
1357                            }
1358                            Arg::Array(arr) => {
1359                                let arr_buffer = vm.add_memory_segment();
1360                                stack.push((arr_buffer, arr));
1361                                vm.insert_value(buffer, arr_buffer)?;
1362                                buffer += 1;
1363                                vm.insert_value(buffer, (arr_buffer + args_size(arr))?)?;
1364                                buffer += 1;
1365                            }
1366                        }
1367                    }
1368                }
1369            }
1370            ExternalHint::AddMarker { start, end } => {
1371                self.markers.push(read_felts(vm, start, end)?);
1372            }
1373            ExternalHint::AddTrace { flag } => {
1374                let flag = get_val(vm, flag)?;
1375                // Setting the panic backtrace if the given flag is panic.
1376                if flag == 0x70616e6963u64.into() {
1377                    let mut fp = vm.get_fp();
1378                    self.panic_traceback = vec![(vm.get_pc(), fp)];
1379                    // Fetch the fp and pc traceback entries
1380                    loop {
1381                        let ptr_at_offset = |offset: usize| {
1382                            (fp - offset).ok().and_then(|r| vm.get_relocatable(r).ok())
1383                        };
1384                        // Get return pc.
1385                        let Some(ret_pc) = ptr_at_offset(1) else {
1386                            break;
1387                        };
1388                        println!("ret_pc: {ret_pc}");
1389                        // Get fp traceback.
1390                        let Some(ret_fp) = ptr_at_offset(2) else {
1391                            break;
1392                        };
1393                        println!("ret_fp: {ret_fp}");
1394                        if ret_fp == fp {
1395                            break;
1396                        }
1397                        fp = ret_fp;
1398
1399                        let call_instruction = |offset: usize| -> Option<Relocatable> {
1400                            let ptr = (ret_pc - offset).ok()?;
1401                            println!("ptr: {ptr}");
1402                            let inst = vm.get_integer(ptr).ok()?;
1403                            println!("inst: {inst}");
1404                            let inst_short = inst.to_u64()?;
1405                            (inst_short & 0x7000_0000_0000_0000 == 0x1000_0000_0000_0000)
1406                                .then_some(ptr)
1407                        };
1408                        if let Some(call_pc) = call_instruction(1).or_else(|| call_instruction(2)) {
1409                            self.panic_traceback.push((call_pc, fp));
1410                        } else {
1411                            break;
1412                        }
1413                    }
1414                    self.panic_traceback.reverse();
1415                }
1416            }
1417        }
1418        Ok(())
1419    }
1420}
1421
1422/// Extracts an array of felt252s from a vector of such.
1423fn vec_as_array<const COUNT: usize>(
1424    inputs: Vec<Felt252>,
1425    err_msg: impl FnOnce() -> String,
1426) -> Result<[Felt252; COUNT], HintError> {
1427    inputs.try_into().map_err(|_| HintError::CustomHint(Box::from(err_msg())))
1428}
1429
1430/// Executes the `keccak_syscall` syscall.
1431fn keccak(gas_counter: &mut usize, data: Vec<Felt252>) -> Result<SyscallResult, HintError> {
1432    deduct_gas!(gas_counter, KECCAK);
1433    if !data.len().is_multiple_of(17) {
1434        fail_syscall!(b"Invalid keccak input size");
1435    }
1436    let mut state = [0u64; 25];
1437    for chunk in data.chunks(17) {
1438        deduct_gas!(gas_counter, KECCAK_ROUND_COST);
1439        for (i, val) in chunk.iter().enumerate() {
1440            state[i] ^= val.to_u64().unwrap();
1441        }
1442        keccak::f1600(&mut state)
1443    }
1444    Ok(SyscallResult::Success(vec![
1445        ((Felt252::from((state[1] as u128) << 64u32)) + Felt252::from(state[0])).into(),
1446        ((Felt252::from((state[3] as u128) << 64u32)) + Felt252::from(state[2])).into(),
1447    ]))
1448}
1449
1450/// Executes the `sha256_process_block` syscall.
1451fn sha_256_process_block(
1452    gas_counter: &mut usize,
1453    prev_state: Vec<Felt252>,
1454    data: Vec<Felt252>,
1455    exec_scopes: &mut ExecutionScopes,
1456    vm: &mut dyn VMWrapper,
1457) -> Result<SyscallResult, HintError> {
1458    deduct_gas!(gas_counter, SHA256_PROCESS_BLOCK);
1459    let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter(
1460        data.iter().flat_map(|felt| felt.to_bigint().to_u32().unwrap().to_be_bytes()),
1461    )
1462    .unwrap();
1463    let mut state_as_words: [u32; 8] = prev_state
1464        .iter()
1465        .map(|felt| felt.to_bigint().to_u32().unwrap())
1466        .collect_vec()
1467        .try_into()
1468        .unwrap();
1469    sha2::compress256(&mut state_as_words, &[data_as_bytes]);
1470    let next_state_ptr = alloc_memory(exec_scopes, vm.vm(), 8)?;
1471    let mut buff: MemBuffer<'_> = MemBuffer::new(vm, next_state_ptr);
1472    buff.write_data(state_as_words.into_iter().map(Felt252::from))?;
1473    Ok(SyscallResult::Success(vec![next_state_ptr.into()]))
1474}
1475
1476// --- secp256k1 ---
1477
1478/// Executes the `secp256k1_new_syscall` syscall.
1479fn secp256k1_new(
1480    gas_counter: &mut usize,
1481    x: BigUint,
1482    y: BigUint,
1483    exec_scopes: &mut ExecutionScopes,
1484) -> Result<SyscallResult, HintError> {
1485    deduct_gas!(gas_counter, SECP256K1_NEW);
1486    let modulus = <secp256k1::Fq as PrimeField>::MODULUS.into();
1487    if x >= modulus || y >= modulus {
1488        fail_syscall!(b"Coordinates out of range");
1489    }
1490    let p = if x.is_zero() && y.is_zero() {
1491        secp256k1::Affine::identity()
1492    } else {
1493        secp256k1::Affine::new_unchecked(x.into(), y.into())
1494    };
1495    Ok(SyscallResult::Success(
1496        if !(p.is_on_curve() && p.is_in_correct_subgroup_assuming_on_curve()) {
1497            vec![1.into(), 0.into()]
1498        } else {
1499            let ec = get_secp256k1_exec_scope(exec_scopes)?;
1500            let id = ec.ec_points.len();
1501            ec.ec_points.push(p);
1502            vec![0.into(), id.into()]
1503        },
1504    ))
1505}
1506
1507/// Executes the `secp256k1_add_syscall` syscall.
1508fn secp256k1_add(
1509    gas_counter: &mut usize,
1510    exec_scopes: &mut ExecutionScopes,
1511    p0_id: usize,
1512    p1_id: usize,
1513) -> Result<SyscallResult, HintError> {
1514    deduct_gas!(gas_counter, SECP256K1_ADD);
1515    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1516    let p0 = &ec.ec_points[p0_id];
1517    let p1 = &ec.ec_points[p1_id];
1518    let sum = *p0 + *p1;
1519    let id = ec.ec_points.len();
1520    ec.ec_points.push(sum.into());
1521    Ok(SyscallResult::Success(vec![id.into()]))
1522}
1523
1524/// Executes the `secp256k1_mul_syscall` syscall.
1525fn secp256k1_mul(
1526    gas_counter: &mut usize,
1527    p_id: usize,
1528    scalar: BigUint,
1529    exec_scopes: &mut ExecutionScopes,
1530) -> Result<SyscallResult, HintError> {
1531    deduct_gas!(gas_counter, SECP256K1_MUL);
1532
1533    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1534    let p = &ec.ec_points[p_id];
1535    let product = *p * secp256k1::Fr::from(scalar);
1536    let id = ec.ec_points.len();
1537    ec.ec_points.push(product.into());
1538    Ok(SyscallResult::Success(vec![id.into()]))
1539}
1540
1541/// Executes the `secp256k1_get_point_from_x_syscall` syscall.
1542fn secp256k1_get_point_from_x(
1543    gas_counter: &mut usize,
1544    x: BigUint,
1545    y_parity: bool,
1546    exec_scopes: &mut ExecutionScopes,
1547) -> Result<SyscallResult, HintError> {
1548    deduct_gas!(gas_counter, SECP256K1_GET_POINT_FROM_X);
1549    if x >= <secp256k1::Fq as PrimeField>::MODULUS.into() {
1550        fail_syscall!(b"Coordinates out of range");
1551    }
1552    let x = x.into();
1553    let maybe_p = secp256k1::Affine::get_ys_from_x_unchecked(x)
1554        .map(
1555            |(smaller, greater)|
1556            // Return the correct y coordinate based on the parity.
1557            if smaller.into_bigint().is_odd() == y_parity { smaller } else { greater },
1558        )
1559        .map(|y| secp256k1::Affine::new_unchecked(x, y))
1560        .filter(|p| p.is_in_correct_subgroup_assuming_on_curve());
1561    let Some(p) = maybe_p else {
1562        return Ok(SyscallResult::Success(vec![1.into(), 0.into()]));
1563    };
1564    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1565    let id = ec.ec_points.len();
1566    ec.ec_points.push(p);
1567    Ok(SyscallResult::Success(vec![0.into(), id.into()]))
1568}
1569
1570/// Executes the `secp256k1_get_xy_syscall` syscall.
1571fn secp256k1_get_xy(
1572    gas_counter: &mut usize,
1573    p_id: usize,
1574    exec_scopes: &mut ExecutionScopes,
1575) -> Result<SyscallResult, HintError> {
1576    deduct_gas!(gas_counter, SECP256K1_GET_XY);
1577    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1578    let p = &ec.ec_points[p_id];
1579    let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1580    let (x1, x0) = BigUint::from(p.x).div_rem(&pow_2_128);
1581    let (y1, y0) = BigUint::from(p.y).div_rem(&pow_2_128);
1582    Ok(SyscallResult::Success(vec![
1583        Felt252::from(x0).into(),
1584        Felt252::from(x1).into(),
1585        Felt252::from(y0).into(),
1586        Felt252::from(y1).into(),
1587    ]))
1588}
1589
1590/// Returns the `Secp256k1ExecScope` managing the different active points.
1591/// The first call to this function will create the scope, and subsequent calls will return it.
1592/// The first call would happen from some point creation syscall.
1593fn get_secp256k1_exec_scope(
1594    exec_scopes: &mut ExecutionScopes,
1595) -> Result<&mut Secp256k1ExecutionScope, HintError> {
1596    const NAME: &str = "secp256k1_exec_scope";
1597    if exec_scopes.get_ref::<Secp256k1ExecutionScope>(NAME).is_err() {
1598        exec_scopes.assign_or_update_variable(NAME, Box::<Secp256k1ExecutionScope>::default());
1599    }
1600    exec_scopes.get_mut_ref::<Secp256k1ExecutionScope>(NAME)
1601}
1602
1603// --- secp256r1 ---
1604
1605/// Executes the `secp256k1_new_syscall` syscall.
1606fn secp256r1_new(
1607    gas_counter: &mut usize,
1608    x: BigUint,
1609    y: BigUint,
1610    exec_scopes: &mut ExecutionScopes,
1611) -> Result<SyscallResult, HintError> {
1612    deduct_gas!(gas_counter, SECP256R1_GET_POINT_FROM_X);
1613    let modulus = <secp256r1::Fq as PrimeField>::MODULUS.into();
1614    if x >= modulus || y >= modulus {
1615        fail_syscall!(b"Coordinates out of range");
1616    }
1617    let p = if x.is_zero() && y.is_zero() {
1618        secp256r1::Affine::identity()
1619    } else {
1620        secp256r1::Affine::new_unchecked(x.into(), y.into())
1621    };
1622    Ok(SyscallResult::Success(
1623        if !(p.is_on_curve() && p.is_in_correct_subgroup_assuming_on_curve()) {
1624            vec![1.into(), 0.into()]
1625        } else {
1626            let ec = get_secp256r1_exec_scope(exec_scopes)?;
1627            let id = ec.ec_points.len();
1628            ec.ec_points.push(p);
1629            vec![0.into(), id.into()]
1630        },
1631    ))
1632}
1633
1634/// Executes the `secp256r1_add_syscall` syscall.
1635fn secp256r1_add(
1636    gas_counter: &mut usize,
1637    exec_scopes: &mut ExecutionScopes,
1638    p0_id: usize,
1639    p1_id: usize,
1640) -> Result<SyscallResult, HintError> {
1641    deduct_gas!(gas_counter, SECP256R1_ADD);
1642    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1643    let p0 = &ec.ec_points[p0_id];
1644    let p1 = &ec.ec_points[p1_id];
1645    let sum = *p0 + *p1;
1646    let id = ec.ec_points.len();
1647    ec.ec_points.push(sum.into());
1648    Ok(SyscallResult::Success(vec![id.into()]))
1649}
1650
1651/// Executes the `secp256r1_mul_syscall` syscall.
1652fn secp256r1_mul(
1653    gas_counter: &mut usize,
1654    p_id: usize,
1655    scalar: BigUint,
1656    exec_scopes: &mut ExecutionScopes,
1657) -> Result<SyscallResult, HintError> {
1658    deduct_gas!(gas_counter, SECP256R1_MUL);
1659
1660    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1661    let p = &ec.ec_points[p_id];
1662    let product = *p * secp256r1::Fr::from(scalar);
1663    let id = ec.ec_points.len();
1664    ec.ec_points.push(product.into());
1665    Ok(SyscallResult::Success(vec![id.into()]))
1666}
1667
1668/// Executes the `secp256r1_get_point_from_x_syscall` syscall.
1669fn secp256r1_get_point_from_x(
1670    gas_counter: &mut usize,
1671    x: BigUint,
1672    y_parity: bool,
1673    exec_scopes: &mut ExecutionScopes,
1674) -> Result<SyscallResult, HintError> {
1675    deduct_gas!(gas_counter, SECP256R1_NEW);
1676    if x >= <secp256r1::Fq as PrimeField>::MODULUS.into() {
1677        fail_syscall!(b"Coordinates out of range");
1678    }
1679    let x = x.into();
1680    let maybe_p = secp256r1::Affine::get_ys_from_x_unchecked(x)
1681        .map(
1682            |(smaller, greater)|
1683            // Return the correct y coordinate based on the parity.
1684            if smaller.into_bigint().is_odd() == y_parity { smaller } else { greater },
1685        )
1686        .map(|y| secp256r1::Affine::new_unchecked(x, y))
1687        .filter(|p| p.is_in_correct_subgroup_assuming_on_curve());
1688    let Some(p) = maybe_p else {
1689        return Ok(SyscallResult::Success(vec![1.into(), 0.into()]));
1690    };
1691    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1692    let id = ec.ec_points.len();
1693    ec.ec_points.push(p);
1694    Ok(SyscallResult::Success(vec![0.into(), id.into()]))
1695}
1696
1697/// Executes the `secp256r1_get_xy_syscall` syscall.
1698fn secp256r1_get_xy(
1699    gas_counter: &mut usize,
1700    p_id: usize,
1701    exec_scopes: &mut ExecutionScopes,
1702) -> Result<SyscallResult, HintError> {
1703    deduct_gas!(gas_counter, SECP256R1_GET_XY);
1704    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1705    let p = &ec.ec_points[p_id];
1706    let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1707    let (x1, x0) = BigUint::from(p.x).div_rem(&pow_2_128);
1708    let (y1, y0) = BigUint::from(p.y).div_rem(&pow_2_128);
1709    Ok(SyscallResult::Success(vec![
1710        Felt252::from(x0).into(),
1711        Felt252::from(x1).into(),
1712        Felt252::from(y0).into(),
1713        Felt252::from(y1).into(),
1714    ]))
1715}
1716
1717/// Returns the `Secp256r1ExecScope` managing the different active points.
1718/// The first call to this function will create the scope, and subsequent calls will return it.
1719/// The first call would happen from some point creation syscall.
1720fn get_secp256r1_exec_scope(
1721    exec_scopes: &mut ExecutionScopes,
1722) -> Result<&mut Secp256r1ExecutionScope, HintError> {
1723    const NAME: &str = "secp256r1_exec_scope";
1724    if exec_scopes.get_ref::<Secp256r1ExecutionScope>(NAME).is_err() {
1725        exec_scopes.assign_or_update_variable(NAME, Box::<Secp256r1ExecutionScope>::default());
1726    }
1727    exec_scopes.get_mut_ref::<Secp256r1ExecutionScope>(NAME)
1728}
1729
1730// ---
1731
1732pub fn execute_core_hint_base(
1733    vm: &mut VirtualMachine,
1734    exec_scopes: &mut ExecutionScopes,
1735    core_hint_base: &cairo_lang_casm::hints::CoreHintBase,
1736    no_temporary_segments: bool,
1737) -> Result<(), HintError> {
1738    match core_hint_base {
1739        cairo_lang_casm::hints::CoreHintBase::Core(core_hint) => {
1740            execute_core_hint(vm, exec_scopes, core_hint, no_temporary_segments)
1741        }
1742        cairo_lang_casm::hints::CoreHintBase::Deprecated(deprecated_hint) => {
1743            execute_deprecated_hint(vm, exec_scopes, deprecated_hint)
1744        }
1745    }
1746}
1747
1748pub fn execute_deprecated_hint(
1749    vm: &mut VirtualMachine,
1750    exec_scopes: &mut ExecutionScopes,
1751    deprecated_hint: &cairo_lang_casm::hints::DeprecatedHint,
1752) -> Result<(), HintError> {
1753    match deprecated_hint {
1754        DeprecatedHint::Felt252DictRead { dict_ptr, key, value_dst } => {
1755            let dict_address = extract_relocatable(vm, dict_ptr)?;
1756            let key = get_val(vm, key)?;
1757            let dict_manager_exec_scope = exec_scopes
1758                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
1759                .expect("Trying to read from a dict while dict manager was not initialized.");
1760            let value = dict_manager_exec_scope
1761                .get_from_tracker(dict_address, &key)
1762                .unwrap_or_else(|| DictManagerExecScope::DICT_DEFAULT_VALUE.into());
1763            insert_value_to_cellref!(vm, value_dst, value)?;
1764        }
1765        DeprecatedHint::Felt252DictWrite { dict_ptr, key, value } => {
1766            let dict_address = extract_relocatable(vm, dict_ptr)?;
1767            let key = get_val(vm, key)?;
1768            let value = get_maybe(vm, value)?;
1769            let dict_manager_exec_scope = exec_scopes
1770                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
1771                .expect("Trying to write to a dict while dict manager was not initialized.");
1772            let prev_value = dict_manager_exec_scope
1773                .get_from_tracker(dict_address, &key)
1774                .unwrap_or_else(|| DictManagerExecScope::DICT_DEFAULT_VALUE.into());
1775            vm.insert_value((dict_address + 1)?, prev_value)?;
1776            dict_manager_exec_scope.insert_to_tracker(dict_address, key, value);
1777        }
1778        DeprecatedHint::AssertCurrentAccessIndicesIsEmpty
1779        | DeprecatedHint::AssertAllAccessesUsed { .. }
1780        | DeprecatedHint::AssertAllKeysUsed
1781        | DeprecatedHint::AssertLeAssertThirdArcExcluded
1782        | DeprecatedHint::AssertLtAssertValidInput { .. } => {}
1783    }
1784    Ok(())
1785}
1786
1787/// Allocates a memory buffer of size `size` on a vm segment.
1788/// Segment will be reused between calls.
1789fn alloc_memory(
1790    exec_scopes: &mut ExecutionScopes,
1791    vm: &mut VirtualMachine,
1792    size: usize,
1793) -> Result<Relocatable, HintError> {
1794    const NAME: &str = "memory_exec_scope";
1795    if exec_scopes.get_ref::<MemoryExecScope>(NAME).is_err() {
1796        exec_scopes.assign_or_update_variable(
1797            NAME,
1798            Box::new(MemoryExecScope { next_address: vm.add_memory_segment() }),
1799        );
1800    }
1801    let scope = exec_scopes.get_mut_ref::<MemoryExecScope>(NAME)?;
1802    let updated = (scope.next_address + size)?;
1803    Ok(std::mem::replace(&mut scope.next_address, updated))
1804}
1805
1806/// Sample a random point on the elliptic curve and insert into memory.
1807pub fn random_ec_point<R: rand::RngCore>(
1808    vm: &mut VirtualMachine,
1809    x: &CellRef,
1810    y: &CellRef,
1811    rng: &mut R,
1812) -> Result<(), HintError> {
1813    // Keep sampling a random field element `X` until `X^3 + X + beta` is a quadratic
1814    // residue.
1815    let (random_x, random_y) = loop {
1816        // Randomizing 31 bytes to make sure is in range.
1817        // TODO(orizi): Use `Felt252` random implementation when exists.
1818        let x_bytes: [u8; 31] = rng.random();
1819        let random_x = Felt252::from_bytes_be_slice(&x_bytes);
1820        /// The Beta value of the Starkware elliptic curve.
1821        pub const BETA: Felt252 = Felt252::from_hex_unchecked(
1822            "0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89",
1823        );
1824        let random_y_squared = random_x * random_x * random_x + random_x + BETA;
1825        if let Some(random_y) = random_y_squared.sqrt() {
1826            break (random_x, random_y);
1827        }
1828    };
1829    insert_value_to_cellref!(vm, x, random_x)?;
1830    insert_value_to_cellref!(vm, y, random_y)?;
1831    Ok(())
1832}
1833
1834/// Executes a core hint.
1835pub fn execute_core_hint(
1836    vm: &mut VirtualMachine,
1837    exec_scopes: &mut ExecutionScopes,
1838    core_hint: &CoreHint,
1839    no_temporary_segments: bool,
1840) -> Result<(), HintError> {
1841    match core_hint {
1842        CoreHint::AllocSegment { dst } => {
1843            let segment = vm.add_memory_segment();
1844            insert_value_to_cellref!(vm, dst, segment)?;
1845        }
1846        CoreHint::TestLessThan { lhs, rhs, dst } => {
1847            let lhs_val = get_val(vm, lhs)?;
1848            let rhs_val = get_val(vm, rhs)?;
1849            insert_value_to_cellref!(
1850                vm,
1851                dst,
1852                if lhs_val < rhs_val { Felt252::from(1) } else { Felt252::from(0) }
1853            )?;
1854        }
1855        CoreHint::TestLessThanOrEqual { lhs, rhs, dst }
1856        | CoreHint::TestLessThanOrEqualAddress { lhs, rhs, dst } => {
1857            let lhs_val = get_maybe(vm, lhs)?;
1858            let rhs_val = get_maybe(vm, rhs)?;
1859            insert_value_to_cellref!(
1860                vm,
1861                dst,
1862                if lhs_val <= rhs_val { Felt252::from(1) } else { Felt252::from(0) }
1863            )?;
1864        }
1865        CoreHint::WideMul128 { lhs, rhs, high, low } => {
1866            let mask128 = BigUint::from(u128::MAX);
1867            let lhs_val = get_val(vm, lhs)?.to_biguint();
1868            let rhs_val = get_val(vm, rhs)?.to_biguint();
1869            let prod = lhs_val * rhs_val;
1870            insert_value_to_cellref!(vm, high, Felt252::from(prod.clone() >> 128))?;
1871            insert_value_to_cellref!(vm, low, Felt252::from(prod & mask128))?;
1872        }
1873        CoreHint::DivMod { lhs, rhs, quotient, remainder } => {
1874            let lhs_val = get_val(vm, lhs)?.to_biguint();
1875            let rhs_val = get_val(vm, rhs)?.to_biguint();
1876            insert_value_to_cellref!(
1877                vm,
1878                quotient,
1879                Felt252::from(lhs_val.clone() / rhs_val.clone())
1880            )?;
1881            insert_value_to_cellref!(vm, remainder, Felt252::from(lhs_val % rhs_val))?;
1882        }
1883        CoreHint::Uint256DivMod {
1884            dividend0,
1885            dividend1,
1886            divisor0,
1887            divisor1,
1888            quotient0,
1889            quotient1,
1890            remainder0,
1891            remainder1,
1892        } => {
1893            let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1894            let dividend0 = get_val(vm, dividend0)?.to_biguint();
1895            let dividend1 = get_val(vm, dividend1)?.to_biguint();
1896            let divisor0 = get_val(vm, divisor0)?.to_biguint();
1897            let divisor1 = get_val(vm, divisor1)?.to_biguint();
1898            let dividend: BigUint = dividend0 + dividend1.shl(128);
1899            let divisor = divisor0 + divisor1.shl(128);
1900            let (quotient, remainder) = dividend.div_rem(&divisor);
1901            let (limb1, limb0) = quotient.div_rem(&pow_2_128);
1902            insert_value_to_cellref!(vm, quotient0, Felt252::from(limb0))?;
1903            insert_value_to_cellref!(vm, quotient1, Felt252::from(limb1))?;
1904            let (limb1, limb0) = remainder.div_rem(&pow_2_128);
1905            insert_value_to_cellref!(vm, remainder0, Felt252::from(limb0))?;
1906            insert_value_to_cellref!(vm, remainder1, Felt252::from(limb1))?;
1907        }
1908        CoreHint::Uint512DivModByUint256 {
1909            dividend0,
1910            dividend1,
1911            dividend2,
1912            dividend3,
1913            divisor0,
1914            divisor1,
1915            quotient0,
1916            quotient1,
1917            quotient2,
1918            quotient3,
1919            remainder0,
1920            remainder1,
1921        } => {
1922            let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1923            let dividend0 = get_val(vm, dividend0)?.to_biguint();
1924            let dividend1 = get_val(vm, dividend1)?.to_biguint();
1925            let dividend2 = get_val(vm, dividend2)?.to_biguint();
1926            let dividend3 = get_val(vm, dividend3)?.to_biguint();
1927            let divisor0 = get_val(vm, divisor0)?.to_biguint();
1928            let divisor1 = get_val(vm, divisor1)?.to_biguint();
1929            let dividend: BigUint =
1930                dividend0 + dividend1.shl(128) + dividend2.shl(256) + dividend3.shl(384);
1931            let divisor = divisor0 + divisor1.shl(128);
1932            let (quotient, remainder) = dividend.div_rem(&divisor);
1933            let (quotient, limb0) = quotient.div_rem(&pow_2_128);
1934            insert_value_to_cellref!(vm, quotient0, Felt252::from(limb0))?;
1935            let (quotient, limb1) = quotient.div_rem(&pow_2_128);
1936            insert_value_to_cellref!(vm, quotient1, Felt252::from(limb1))?;
1937            let (limb3, limb2) = quotient.div_rem(&pow_2_128);
1938            insert_value_to_cellref!(vm, quotient2, Felt252::from(limb2))?;
1939            insert_value_to_cellref!(vm, quotient3, Felt252::from(limb3))?;
1940            let (limb1, limb0) = remainder.div_rem(&pow_2_128);
1941            insert_value_to_cellref!(vm, remainder0, Felt252::from(limb0))?;
1942            insert_value_to_cellref!(vm, remainder1, Felt252::from(limb1))?;
1943        }
1944        CoreHint::SquareRoot { value, dst } => {
1945            let val = get_val(vm, value)?.to_biguint();
1946            insert_value_to_cellref!(vm, dst, Felt252::from(val.sqrt()))?;
1947        }
1948        CoreHint::Uint256SquareRoot {
1949            value_low,
1950            value_high,
1951            sqrt0,
1952            sqrt1,
1953            remainder_low,
1954            remainder_high,
1955            sqrt_mul_2_minus_remainder_ge_u128,
1956        } => {
1957            let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1958            let pow_2_64 = BigUint::from(u64::MAX) + 1u32;
1959            let value_low = get_val(vm, value_low)?.to_biguint();
1960            let value_high = get_val(vm, value_high)?.to_biguint();
1961            let value = value_low + value_high * pow_2_128.clone();
1962            let sqrt = value.sqrt();
1963            let remainder = value - sqrt.clone() * sqrt.clone();
1964            let sqrt_mul_2_minus_remainder_ge_u128_val =
1965                sqrt.clone() * 2u32 - remainder.clone() >= pow_2_128;
1966
1967            // Guess sqrt limbs.
1968            let (sqrt1_val, sqrt0_val) = sqrt.div_rem(&pow_2_64);
1969            insert_value_to_cellref!(vm, sqrt0, Felt252::from(sqrt0_val))?;
1970            insert_value_to_cellref!(vm, sqrt1, Felt252::from(sqrt1_val))?;
1971
1972            let (remainder_high_val, remainder_low_val) = remainder.div_rem(&pow_2_128);
1973            // Guess remainder limbs.
1974            insert_value_to_cellref!(vm, remainder_low, Felt252::from(remainder_low_val))?;
1975            insert_value_to_cellref!(vm, remainder_high, Felt252::from(remainder_high_val))?;
1976            insert_value_to_cellref!(
1977                vm,
1978                sqrt_mul_2_minus_remainder_ge_u128,
1979                Felt252::from(usize::from(sqrt_mul_2_minus_remainder_ge_u128_val))
1980            )?;
1981        }
1982        CoreHint::LinearSplit { value, scalar, max_x, x, y } => {
1983            let value = get_val(vm, value)?;
1984            let scalar = get_val(vm, scalar)?;
1985            let max_x = get_val(vm, max_x)?;
1986            let x_value = value.floor_div(&NonZeroFelt::from_felt_unchecked(scalar)).min(max_x);
1987            let y_value = value - x_value * scalar;
1988            insert_value_to_cellref!(vm, x, x_value)?;
1989            insert_value_to_cellref!(vm, y, y_value)?;
1990        }
1991        CoreHint::RandomEcPoint { x, y } => {
1992            let mut rng = rand::rng();
1993            random_ec_point(vm, x, y, &mut rng)?;
1994        }
1995        CoreHint::FieldSqrt { val, sqrt } => {
1996            let val = get_val(vm, val)?;
1997            let res = val.sqrt().unwrap_or_else(|| (val * Felt252::THREE).sqrt().unwrap());
1998            insert_value_to_cellref!(vm, sqrt, std::cmp::min(res, -res))?;
1999        }
2000        CoreHint::AllocFelt252Dict { segment_arena_ptr } => {
2001            let dict_manager_address = extract_relocatable(vm, segment_arena_ptr)?;
2002            let n_dicts = vm
2003                .get_integer((dict_manager_address - 2)?)?
2004                .into_owned()
2005                .to_usize()
2006                .expect("Number of dictionaries too large.");
2007            let dict_infos_base = vm.get_relocatable((dict_manager_address - 3)?)?;
2008
2009            let dict_manager_exec_scope = match exec_scopes
2010                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2011            {
2012                Ok(dict_manager_exec_scope) => dict_manager_exec_scope,
2013                Err(_) => {
2014                    exec_scopes.assign_or_update_variable(
2015                        "dict_manager_exec_scope",
2016                        Box::<DictManagerExecScope>::default(),
2017                    );
2018                    exec_scopes.get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")?
2019                }
2020            };
2021            let new_dict_segment =
2022                dict_manager_exec_scope.new_default_dict(vm, no_temporary_segments);
2023            vm.insert_value((dict_infos_base + 3 * n_dicts)?, new_dict_segment)?;
2024        }
2025        CoreHint::Felt252DictEntryInit { dict_ptr, key } => {
2026            let dict_address = extract_relocatable(vm, dict_ptr)?;
2027            let key = get_val(vm, key)?;
2028            let dict_manager_exec_scope = exec_scopes
2029                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2030                .expect("Trying to write to a dict while dict manager was not initialized.");
2031            let prev_value = dict_manager_exec_scope
2032                .get_from_tracker(dict_address, &key)
2033                .unwrap_or_else(|| DictManagerExecScope::DICT_DEFAULT_VALUE.into());
2034            vm.insert_value((dict_address + 1)?, prev_value)?;
2035        }
2036        CoreHint::Felt252DictEntryUpdate { dict_ptr, value } => {
2037            let (dict_base, dict_offset) = extract_buffer(dict_ptr);
2038            let dict_address = get_ptr(vm, dict_base, &dict_offset)?;
2039            let key = get_double_deref_val(vm, dict_base, &(dict_offset + Felt252::from(-3)))?;
2040            let value = get_maybe(vm, value)?;
2041            let dict_manager_exec_scope = exec_scopes
2042                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2043                .expect("Trying to write to a dict while dict manager was not initialized.");
2044            dict_manager_exec_scope.insert_to_tracker(dict_address, key, value);
2045        }
2046        CoreHint::GetSegmentArenaIndex { dict_end_ptr, dict_index, .. } => {
2047            let dict_address = extract_relocatable(vm, dict_end_ptr)?;
2048            let dict_manager_exec_scope = exec_scopes
2049                .get_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2050                .expect("Trying to read from a dict while dict manager was not initialized.");
2051            let dict_infos_index = dict_manager_exec_scope.get_dict_infos_index(dict_address);
2052            insert_value_to_cellref!(vm, dict_index, Felt252::from(dict_infos_index))?;
2053        }
2054        CoreHint::InitSquashData { dict_accesses, n_accesses, first_key, big_keys, .. } => {
2055            let dict_access_size = 3;
2056            let rangecheck_bound = Felt252::from(BigInt::from(1).shl(128));
2057
2058            exec_scopes.assign_or_update_variable(
2059                "dict_squash_exec_scope",
2060                Box::<DictSquashExecScope>::default(),
2061            );
2062            let dict_squash_exec_scope =
2063                exec_scopes.get_mut_ref::<DictSquashExecScope>("dict_squash_exec_scope")?;
2064            let dict_accesses_address = extract_relocatable(vm, dict_accesses)?;
2065            let n_accesses = get_val(vm, n_accesses)?
2066                .to_usize()
2067                .expect("Number of accesses is too large or negative.");
2068            for i in 0..n_accesses {
2069                let current_key =
2070                    vm.get_integer((dict_accesses_address + i * dict_access_size)?)?;
2071                dict_squash_exec_scope
2072                    .access_indices
2073                    .entry(current_key.into_owned())
2074                    .and_modify(|indices| indices.push(Felt252::from(i)))
2075                    .or_insert_with(|| vec![Felt252::from(i)]);
2076            }
2077            // Reverse the accesses in order to pop them in order later.
2078            for (_, accesses) in dict_squash_exec_scope.access_indices.iter_mut() {
2079                accesses.reverse();
2080            }
2081            dict_squash_exec_scope.keys =
2082                dict_squash_exec_scope.access_indices.keys().cloned().collect();
2083            dict_squash_exec_scope.keys.sort_by(|a, b| b.cmp(a));
2084            // big_keys indicates if the keys are greater than rangecheck_bound. If they are not
2085            // a simple range check is used instead of assert_le_felt252.
2086            insert_value_to_cellref!(
2087                vm,
2088                big_keys,
2089                if dict_squash_exec_scope.keys[0] < rangecheck_bound {
2090                    Felt252::from(0)
2091                } else {
2092                    Felt252::from(1)
2093                }
2094            )?;
2095            insert_value_to_cellref!(vm, first_key, dict_squash_exec_scope.current_key().unwrap())?;
2096        }
2097        CoreHint::GetCurrentAccessIndex { range_check_ptr } => {
2098            let dict_squash_exec_scope: &mut DictSquashExecScope =
2099                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2100            let range_check_ptr = extract_relocatable(vm, range_check_ptr)?;
2101            let current_access_index = dict_squash_exec_scope.current_access_index().unwrap();
2102            vm.insert_value(range_check_ptr, current_access_index)?;
2103        }
2104        CoreHint::ShouldSkipSquashLoop { should_skip_loop } => {
2105            let dict_squash_exec_scope: &mut DictSquashExecScope =
2106                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2107            insert_value_to_cellref!(
2108                vm,
2109                should_skip_loop,
2110                // The loop verifies that each two consecutive accesses are valid, thus we
2111                // break when there is only one remaining access.
2112                if dict_squash_exec_scope.current_access_indices().unwrap().len() > 1 {
2113                    Felt252::from(0)
2114                } else {
2115                    Felt252::from(1)
2116                }
2117            )?;
2118        }
2119        CoreHint::GetCurrentAccessDelta { index_delta_minus1 } => {
2120            let dict_squash_exec_scope: &mut DictSquashExecScope =
2121                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2122            let prev_access_index = dict_squash_exec_scope.pop_current_access_index().unwrap();
2123            let index_delta_minus_1_val = (*dict_squash_exec_scope.current_access_index().unwrap()
2124                - prev_access_index)
2125                .sub(1);
2126
2127            insert_value_to_cellref!(vm, index_delta_minus1, index_delta_minus_1_val)?;
2128        }
2129        CoreHint::ShouldContinueSquashLoop { should_continue } => {
2130            let dict_squash_exec_scope: &mut DictSquashExecScope =
2131                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2132            insert_value_to_cellref!(
2133                vm,
2134                should_continue,
2135                // The loop verifies that each two consecutive accesses are valid, thus we
2136                // break when there is only one remaining access.
2137                if dict_squash_exec_scope.current_access_indices().unwrap().len() > 1 {
2138                    Felt252::from(1)
2139                } else {
2140                    Felt252::from(0)
2141                }
2142            )?;
2143        }
2144        CoreHint::GetNextDictKey { next_key } => {
2145            let dict_squash_exec_scope: &mut DictSquashExecScope =
2146                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2147            dict_squash_exec_scope.pop_current_key();
2148            insert_value_to_cellref!(vm, next_key, dict_squash_exec_scope.current_key().unwrap())?;
2149        }
2150        CoreHint::AssertLeFindSmallArcs { a, b, range_check_ptr } => {
2151            let a_val = get_val(vm, a)?;
2152            let b_val = get_val(vm, b)?;
2153            let mut lengths_and_indices =
2154                [(a_val, 0), (b_val - a_val, 1), (Felt252::from(-1) - b_val, 2)];
2155            lengths_and_indices.sort();
2156            exec_scopes
2157                .assign_or_update_variable("excluded_arc", Box::new(lengths_and_indices[2].1));
2158            // ceil((PRIME / 3) / 2 ** 128).
2159            let prime_over_3_high = 3544607988759775765608368578435044694_u128;
2160            // ceil((PRIME / 2) / 2 ** 128).
2161            let prime_over_2_high = 5316911983139663648412552867652567041_u128;
2162            let range_check_ptr = extract_relocatable(vm, range_check_ptr)?;
2163            vm.insert_value(
2164                range_check_ptr,
2165                Felt252::from(lengths_and_indices[0].0.to_biguint() % prime_over_3_high),
2166            )?;
2167            vm.insert_value(
2168                (range_check_ptr + 1)?,
2169                Felt252::from(lengths_and_indices[0].0.to_biguint() / prime_over_3_high),
2170            )?;
2171            vm.insert_value(
2172                (range_check_ptr + 2)?,
2173                Felt252::from(lengths_and_indices[1].0.to_biguint() % prime_over_2_high),
2174            )?;
2175            vm.insert_value(
2176                (range_check_ptr + 3)?,
2177                Felt252::from(lengths_and_indices[1].0.to_biguint() / prime_over_2_high),
2178            )?;
2179        }
2180        CoreHint::AssertLeIsFirstArcExcluded { skip_exclude_a_flag } => {
2181            let excluded_arc: i32 = exec_scopes.get("excluded_arc")?;
2182            insert_value_to_cellref!(
2183                vm,
2184                skip_exclude_a_flag,
2185                if excluded_arc != 0 { Felt252::from(1) } else { Felt252::from(0) }
2186            )?;
2187        }
2188        CoreHint::AssertLeIsSecondArcExcluded { skip_exclude_b_minus_a } => {
2189            let excluded_arc: i32 = exec_scopes.get("excluded_arc")?;
2190            insert_value_to_cellref!(
2191                vm,
2192                skip_exclude_b_minus_a,
2193                if excluded_arc != 1 { Felt252::from(1) } else { Felt252::from(0) }
2194            )?;
2195        }
2196        CoreHint::DebugPrint { start, end } => {
2197            print!("{}", format_for_debug(read_felts(vm, start, end)?.into_iter()));
2198        }
2199        CoreHint::AllocConstantSize { size, dst } => {
2200            let object_size = get_val(vm, size)?.to_usize().expect("Object size too large.");
2201            let ptr = alloc_memory(exec_scopes, vm, object_size)?;
2202            insert_value_to_cellref!(vm, dst, ptr)?;
2203        }
2204        CoreHint::U256InvModN {
2205            b0,
2206            b1,
2207            n0,
2208            n1,
2209            g0_or_no_inv,
2210            g1_option,
2211            s_or_r0,
2212            s_or_r1,
2213            t_or_k0,
2214            t_or_k1,
2215        } => {
2216            let pow_2_128 = BigInt::from(u128::MAX) + 1u32;
2217            let b0 = get_val(vm, b0)?.to_bigint();
2218            let b1 = get_val(vm, b1)?.to_bigint();
2219            let n0 = get_val(vm, n0)?.to_bigint();
2220            let n1 = get_val(vm, n1)?.to_bigint();
2221            let b: BigInt = b0.clone() + b1.clone().shl(128);
2222            let n: BigInt = n0 + n1.shl(128);
2223            let ExtendedGcd { gcd: mut g, x: _, y: mut r } = n.extended_gcd(&b);
2224            if n == 1.into() {
2225                insert_value_to_cellref!(vm, s_or_r0, Felt252::from(b0))?;
2226                insert_value_to_cellref!(vm, s_or_r1, Felt252::from(b1))?;
2227                insert_value_to_cellref!(vm, t_or_k0, Felt252::from(1))?;
2228                insert_value_to_cellref!(vm, t_or_k1, Felt252::from(0))?;
2229                insert_value_to_cellref!(vm, g0_or_no_inv, Felt252::from(1))?;
2230                insert_value_to_cellref!(vm, g1_option, Felt252::from(0))?;
2231            } else if g != 1.into() {
2232                // This makes sure `g0_or_no_inv` is always non-zero in the no inverse case.
2233                if g.is_even() {
2234                    g = 2u32.into();
2235                }
2236                let (limb1, limb0) = (&b / &g).div_rem(&pow_2_128);
2237                insert_value_to_cellref!(vm, s_or_r0, Felt252::from(limb0))?;
2238                insert_value_to_cellref!(vm, s_or_r1, Felt252::from(limb1))?;
2239                let (limb1, limb0) = (&n / &g).div_rem(&pow_2_128);
2240                insert_value_to_cellref!(vm, t_or_k0, Felt252::from(limb0))?;
2241                insert_value_to_cellref!(vm, t_or_k1, Felt252::from(limb1))?;
2242                let (limb1, limb0) = g.div_rem(&pow_2_128);
2243                insert_value_to_cellref!(vm, g0_or_no_inv, Felt252::from(limb0))?;
2244                insert_value_to_cellref!(vm, g1_option, Felt252::from(limb1))?;
2245            } else {
2246                r %= &n;
2247                if r.is_negative() {
2248                    r += &n;
2249                }
2250                let k: BigInt = (&r * b - 1) / n;
2251                let (limb1, limb0) = r.div_rem(&pow_2_128);
2252                insert_value_to_cellref!(vm, s_or_r0, Felt252::from(limb0))?;
2253                insert_value_to_cellref!(vm, s_or_r1, Felt252::from(limb1))?;
2254                let (limb1, limb0) = k.div_rem(&pow_2_128);
2255                insert_value_to_cellref!(vm, t_or_k0, Felt252::from(limb0))?;
2256                insert_value_to_cellref!(vm, t_or_k1, Felt252::from(limb1))?;
2257                insert_value_to_cellref!(vm, g0_or_no_inv, Felt252::from(0))?;
2258            }
2259        }
2260        CoreHint::EvalCircuit {
2261            n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin, ..
2262        } => {
2263            let add_mod_builtin = extract_relocatable(vm, add_mod_builtin)?;
2264            let n_add_mods = get_val(vm, n_add_mods)?.to_usize().unwrap();
2265            let mul_mod_builtin = extract_relocatable(vm, mul_mod_builtin)?;
2266            let n_mul_mods = get_val(vm, n_mul_mods)?.to_usize().unwrap();
2267
2268            circuit::eval_circuit(vm, add_mod_builtin, n_add_mods, mul_mod_builtin, n_mul_mods)?;
2269        }
2270    };
2271    Ok(())
2272}
2273
2274/// Reads a range of `Felt252`s from the VM.
2275fn read_felts(
2276    vm: &mut VirtualMachine,
2277    start: &ResOperand,
2278    end: &ResOperand,
2279) -> Result<Vec<Felt252>, HintError> {
2280    let mut curr = extract_relocatable(vm, start)?;
2281    let end = extract_relocatable(vm, end)?;
2282
2283    let mut felts = Vec::new();
2284    while curr != end {
2285        let value = *vm.get_integer(curr)?;
2286        felts.push(value);
2287        curr = (curr + 1)?;
2288    }
2289
2290    Ok(felts)
2291}
2292
2293/// Reads the result of a function call that returns `Array<felt252>`.
2294fn read_array_result_as_vec(memory: &[Option<Felt252>], value: &[Felt252]) -> Vec<Felt252> {
2295    // TODO(spapini): Handle failures.
2296    let [res_start, res_end] = value else {
2297        panic!("Unexpected return value from contract call");
2298    };
2299    let res_start: usize = res_start.clone().to_bigint().try_into().unwrap();
2300    let res_end: usize = res_end.clone().to_bigint().try_into().unwrap();
2301    (res_start..res_end).map(|i| memory[i].unwrap()).collect()
2302}
2303
2304/// Loads a range of values from the VM memory.
2305pub fn vm_get_range(
2306    vm: &mut VirtualMachine,
2307    mut calldata_start_ptr: Relocatable,
2308    calldata_end_ptr: Relocatable,
2309) -> Result<Vec<Felt252>, HintError> {
2310    let mut values = vec![];
2311    while calldata_start_ptr != calldata_end_ptr {
2312        let val = *vm.get_integer(calldata_start_ptr)?;
2313        values.push(val);
2314        calldata_start_ptr.offset += 1;
2315    }
2316    Ok(values)
2317}
2318
2319/// Extracts a parameter assumed to be a buffer.
2320pub fn extract_buffer(buffer: &ResOperand) -> (&CellRef, Felt252) {
2321    let (cell, base_offset) = match buffer {
2322        ResOperand::Deref(cell) => (cell, 0.into()),
2323        ResOperand::BinOp(BinOpOperand { op: Operation::Add, a, b }) => {
2324            (a, extract_matches!(b, DerefOrImmediate::Immediate).clone().value.into())
2325        }
2326        _ => panic!("Illegal argument for a buffer."),
2327    };
2328    (cell, base_offset)
2329}
2330
2331/// Runs CairoRunner on layout with prime.
2332/// Allows injecting custom CairoRunner.
2333pub fn run_function_with_runner(
2334    additional_initialization: impl FnOnce(&mut VirtualMachine) -> Result<(), Box<CairoRunError>>,
2335    hint_processor: &mut dyn HintProcessor,
2336    runner: &mut CairoRunner,
2337) -> Result<(), Box<CairoRunError>> {
2338    let end = runner.initialize(true).map_err(CairoRunError::from)?;
2339
2340    additional_initialization(&mut runner.vm)?;
2341
2342    runner.run_until_pc(end, hint_processor).map_err(CairoRunError::from)?;
2343    runner.end_run(true, false, hint_processor).map_err(CairoRunError::from)?;
2344    runner.relocate(true).map_err(CairoRunError::from)?;
2345    Ok(())
2346}
2347
2348/// Creates CairoRunner for `program`.
2349pub fn build_cairo_runner(
2350    data: Vec<MaybeRelocatable>,
2351    builtins: Vec<BuiltinName>,
2352    hints_dict: HashMap<usize, Vec<HintParams>>,
2353) -> Result<CairoRunner, Box<CairoRunError>> {
2354    let program = Program::new(
2355        builtins,
2356        data,
2357        Some(0),
2358        hints_dict,
2359        ReferenceManager { references: Vec::new() },
2360        HashMap::new(),
2361        vec![],
2362        None,
2363    )
2364    .map_err(CairoRunError::from)?;
2365    let dynamic_layout_params = None;
2366    let proof_mode = false;
2367    let trace_enabled = true;
2368    let disable_trace_padding = false;
2369    CairoRunner::new(
2370        &program,
2371        LayoutName::all_cairo,
2372        dynamic_layout_params,
2373        proof_mode,
2374        trace_enabled,
2375        disable_trace_padding,
2376    )
2377    .map_err(CairoRunError::from)
2378    .map_err(Box::new)
2379}
2380
2381/// The result of [run_function].
2382pub struct RunFunctionResult {
2383    /// The ap value after the run.
2384    pub ap: usize,
2385    /// The used resources after the run.
2386    pub used_resources: ExecutionResources,
2387    /// The relocated memory after the run.
2388    pub memory: Vec<Option<Felt252>>,
2389    /// The relocated trace.
2390    pub relocated_trace: Vec<RelocatedTraceEntry>,
2391}
2392
2393/// Runs `bytecode` on layout with prime, and returns the matching [RunFunctionResult].
2394/// Allows injecting custom HintProcessor.
2395pub fn run_function<'a, 'b: 'a>(
2396    bytecode: impl Iterator<Item = &'a BigInt> + Clone,
2397    builtins: Vec<BuiltinName>,
2398    additional_initialization: impl FnOnce(&mut VirtualMachine) -> Result<(), Box<CairoRunError>>,
2399    hint_processor: &mut dyn HintProcessor,
2400    hints_dict: HashMap<usize, Vec<HintParams>>,
2401) -> Result<RunFunctionResult, Box<CairoRunError>> {
2402    let data: Vec<MaybeRelocatable> =
2403        bytecode.map(Felt252::from).map(MaybeRelocatable::from).collect();
2404    let mut runner = build_cairo_runner(data, builtins, hints_dict)?;
2405
2406    run_function_with_runner(additional_initialization, hint_processor, &mut runner)?;
2407
2408    let used_resources = runner
2409        .get_execution_resources()
2410        .expect("Failed to get execution resources, but the run was successful.");
2411
2412    let relocated_trace = runner.relocated_trace.unwrap();
2413    let memory = runner.relocated_memory;
2414
2415    Ok(RunFunctionResult {
2416        ap: relocated_trace.last().unwrap().ap,
2417        used_resources,
2418        memory,
2419        relocated_trace,
2420    })
2421}
2422
2423/// Formats the given felts as a debug string.
2424fn format_for_debug(mut felts: IntoIter<Felt252>) -> String {
2425    let mut items = Vec::new();
2426    while let Some(item) = format_next_item(&mut felts) {
2427        items.push(item);
2428    }
2429    if let [item] = &items[..]
2430        && item.is_string
2431    {
2432        return item.item.clone();
2433    }
2434    items
2435        .into_iter()
2436        .map(|item| {
2437            if item.is_string {
2438                format!("{}\n", item.item)
2439            } else {
2440                format!("[DEBUG]\t{}\n", item.item)
2441            }
2442        })
2443        .join("")
2444}
2445
2446/// A formatted string representation of anything formattable (e.g. ByteArray, felt, short-string).
2447pub struct FormattedItem {
2448    /// The formatted string representing the item.
2449    item: String,
2450    /// Whether the item is a string.
2451    is_string: bool,
2452}
2453impl FormattedItem {
2454    /// Returns the formatted item as is.
2455    pub fn get(self) -> String {
2456        self.item
2457    }
2458    /// Wraps the formatted item with quote, if it's a string. Otherwise returns it as is.
2459    pub fn quote_if_string(self) -> String {
2460        if self.is_string { format!("\"{}\"", self.item) } else { self.item }
2461    }
2462}
2463
2464/// Formats a string or a short string / `felt252`. Returns the formatted string and a boolean
2465/// indicating whether it's a string. If can't format the item, returns None.
2466pub fn format_next_item<T>(values: &mut T) -> Option<FormattedItem>
2467where
2468    T: Iterator<Item = Felt252> + Clone,
2469{
2470    let first_felt = values.next()?;
2471
2472    if first_felt == Felt252::from_hex(BYTE_ARRAY_MAGIC).unwrap()
2473        && let Some(string) = try_format_string(values)
2474    {
2475        return Some(FormattedItem { item: string, is_string: true });
2476    }
2477    Some(FormattedItem { item: format_short_string(&first_felt), is_string: false })
2478}
2479
2480/// Formats the given felts as a panic string.
2481pub fn format_for_panic<T>(mut felts: T) -> String
2482where
2483    T: Iterator<Item = Felt252> + Clone,
2484{
2485    let mut items = Vec::new();
2486    while let Some(item) = format_next_item(&mut felts) {
2487        items.push(item.quote_if_string());
2488    }
2489    let panic_values_string =
2490        if let [item] = &items[..] { item.clone() } else { format!("({})", items.join(", ")) };
2491    format!("Panicked with {panic_values_string}.")
2492}
2493
2494/// Formats a `Felt252`, as a short string if possible.
2495fn format_short_string(value: &Felt252) -> String {
2496    let hex_value = value.to_biguint();
2497    match as_cairo_short_string(value) {
2498        Some(as_string) => format!("{hex_value:#x} ('{as_string}')"),
2499        None => format!("{hex_value:#x}"),
2500    }
2501}
2502
2503/// Tries to format a string, represented as a sequence of `Felt252`s.
2504/// If the sequence is not a valid serialization of a ByteArray, returns None and doesn't change the
2505/// given iterator (`values`).
2506fn try_format_string<T>(values: &mut T) -> Option<String>
2507where
2508    T: Iterator<Item = Felt252> + Clone,
2509{
2510    // Clone the iterator and work with the clone. If the extraction of the string is successful,
2511    // change the original iterator to the one we worked with. If not, continue with the
2512    // original iterator at the original point.
2513    let mut cloned_values_iter = values.clone();
2514
2515    let num_full_words = cloned_values_iter.next()?.to_usize()?;
2516    let full_words = cloned_values_iter.by_ref().take(num_full_words).collect_vec();
2517    let pending_word = cloned_values_iter.next()?;
2518    let pending_word_len = cloned_values_iter.next()?.to_usize()?;
2519
2520    let full_words_string = full_words
2521        .into_iter()
2522        .map(|word| as_cairo_short_string_ex(&word, BYTES_IN_WORD))
2523        .collect::<Option<Vec<String>>>()?
2524        .join("");
2525    let pending_word_string = as_cairo_short_string_ex(&pending_word, pending_word_len)?;
2526
2527    // Extraction was successful, change the original iterator to the one we worked with.
2528    *values = cloned_values_iter;
2529
2530    Some(format!("{full_words_string}{pending_word_string}"))
2531}