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/// Converts 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 cell reference.
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 by default if not set up 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/// Fetches 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 = FromIterator::from_iter(
1460        data.iter().flat_map(|felt| felt.to_bigint().to_u32().unwrap().to_be_bytes()),
1461    );
1462    let mut state_as_words: [u32; 8] = prev_state
1463        .iter()
1464        .map(|felt| felt.to_bigint().to_u32().unwrap())
1465        .collect_vec()
1466        .try_into()
1467        .unwrap();
1468    sha2::compress256(&mut state_as_words, &[data_as_bytes]);
1469    let next_state_ptr = alloc_memory(exec_scopes, vm.vm(), 8)?;
1470    let mut buff: MemBuffer<'_> = MemBuffer::new(vm, next_state_ptr);
1471    buff.write_data(state_as_words.into_iter().map(Felt252::from))?;
1472    Ok(SyscallResult::Success(vec![next_state_ptr.into()]))
1473}
1474
1475// --- secp256k1 ---
1476
1477/// Executes the `secp256k1_new_syscall` syscall.
1478fn secp256k1_new(
1479    gas_counter: &mut usize,
1480    x: BigUint,
1481    y: BigUint,
1482    exec_scopes: &mut ExecutionScopes,
1483) -> Result<SyscallResult, HintError> {
1484    deduct_gas!(gas_counter, SECP256K1_NEW);
1485    let modulus = <secp256k1::Fq as PrimeField>::MODULUS.into();
1486    if x >= modulus || y >= modulus {
1487        fail_syscall!(b"Coordinates out of range");
1488    }
1489    let p = if x.is_zero() && y.is_zero() {
1490        secp256k1::Affine::identity()
1491    } else {
1492        secp256k1::Affine::new_unchecked(x.into(), y.into())
1493    };
1494    Ok(SyscallResult::Success(
1495        if !(p.is_on_curve() && p.is_in_correct_subgroup_assuming_on_curve()) {
1496            vec![1.into(), 0.into()]
1497        } else {
1498            let ec = get_secp256k1_exec_scope(exec_scopes)?;
1499            let id = ec.ec_points.len();
1500            ec.ec_points.push(p);
1501            vec![0.into(), id.into()]
1502        },
1503    ))
1504}
1505
1506/// Executes the `secp256k1_add_syscall` syscall.
1507fn secp256k1_add(
1508    gas_counter: &mut usize,
1509    exec_scopes: &mut ExecutionScopes,
1510    p0_id: usize,
1511    p1_id: usize,
1512) -> Result<SyscallResult, HintError> {
1513    deduct_gas!(gas_counter, SECP256K1_ADD);
1514    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1515    let p0 = &ec.ec_points[p0_id];
1516    let p1 = &ec.ec_points[p1_id];
1517    let sum = *p0 + *p1;
1518    let id = ec.ec_points.len();
1519    ec.ec_points.push(sum.into());
1520    Ok(SyscallResult::Success(vec![id.into()]))
1521}
1522
1523/// Executes the `secp256k1_mul_syscall` syscall.
1524fn secp256k1_mul(
1525    gas_counter: &mut usize,
1526    p_id: usize,
1527    scalar: BigUint,
1528    exec_scopes: &mut ExecutionScopes,
1529) -> Result<SyscallResult, HintError> {
1530    deduct_gas!(gas_counter, SECP256K1_MUL);
1531
1532    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1533    let p = &ec.ec_points[p_id];
1534    let product = *p * secp256k1::Fr::from(scalar);
1535    let id = ec.ec_points.len();
1536    ec.ec_points.push(product.into());
1537    Ok(SyscallResult::Success(vec![id.into()]))
1538}
1539
1540/// Executes the `secp256k1_get_point_from_x_syscall` syscall.
1541fn secp256k1_get_point_from_x(
1542    gas_counter: &mut usize,
1543    x: BigUint,
1544    y_parity: bool,
1545    exec_scopes: &mut ExecutionScopes,
1546) -> Result<SyscallResult, HintError> {
1547    deduct_gas!(gas_counter, SECP256K1_GET_POINT_FROM_X);
1548    if x >= <secp256k1::Fq as PrimeField>::MODULUS.into() {
1549        fail_syscall!(b"Coordinates out of range");
1550    }
1551    let x = x.into();
1552    let maybe_p = secp256k1::Affine::get_ys_from_x_unchecked(x)
1553        .map(
1554            |(smaller, greater)|
1555            // Return the correct y coordinate based on the parity.
1556            if smaller.into_bigint().is_odd() == y_parity { smaller } else { greater },
1557        )
1558        .map(|y| secp256k1::Affine::new_unchecked(x, y))
1559        .filter(|p| p.is_in_correct_subgroup_assuming_on_curve());
1560    let Some(p) = maybe_p else {
1561        return Ok(SyscallResult::Success(vec![1.into(), 0.into()]));
1562    };
1563    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1564    let id = ec.ec_points.len();
1565    ec.ec_points.push(p);
1566    Ok(SyscallResult::Success(vec![0.into(), id.into()]))
1567}
1568
1569/// Executes the `secp256k1_get_xy_syscall` syscall.
1570fn secp256k1_get_xy(
1571    gas_counter: &mut usize,
1572    p_id: usize,
1573    exec_scopes: &mut ExecutionScopes,
1574) -> Result<SyscallResult, HintError> {
1575    deduct_gas!(gas_counter, SECP256K1_GET_XY);
1576    let ec = get_secp256k1_exec_scope(exec_scopes)?;
1577    let p = &ec.ec_points[p_id];
1578    let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1579    let (x1, x0) = BigUint::from(p.x).div_rem(&pow_2_128);
1580    let (y1, y0) = BigUint::from(p.y).div_rem(&pow_2_128);
1581    Ok(SyscallResult::Success(vec![
1582        Felt252::from(x0).into(),
1583        Felt252::from(x1).into(),
1584        Felt252::from(y0).into(),
1585        Felt252::from(y1).into(),
1586    ]))
1587}
1588
1589/// Returns the `Secp256k1ExecScope` managing the different active points.
1590/// The first call to this function will create the scope, and subsequent calls will return it.
1591/// The first call would happen from some point creation syscall.
1592fn get_secp256k1_exec_scope(
1593    exec_scopes: &mut ExecutionScopes,
1594) -> Result<&mut Secp256k1ExecutionScope, HintError> {
1595    const NAME: &str = "secp256k1_exec_scope";
1596    if exec_scopes.get_ref::<Secp256k1ExecutionScope>(NAME).is_err() {
1597        exec_scopes.assign_or_update_variable(NAME, Box::<Secp256k1ExecutionScope>::default());
1598    }
1599    exec_scopes.get_mut_ref::<Secp256k1ExecutionScope>(NAME)
1600}
1601
1602// --- secp256r1 ---
1603
1604/// Executes the `secp256r1_new_syscall` syscall.
1605fn secp256r1_new(
1606    gas_counter: &mut usize,
1607    x: BigUint,
1608    y: BigUint,
1609    exec_scopes: &mut ExecutionScopes,
1610) -> Result<SyscallResult, HintError> {
1611    deduct_gas!(gas_counter, SECP256R1_GET_POINT_FROM_X);
1612    let modulus = <secp256r1::Fq as PrimeField>::MODULUS.into();
1613    if x >= modulus || y >= modulus {
1614        fail_syscall!(b"Coordinates out of range");
1615    }
1616    let p = if x.is_zero() && y.is_zero() {
1617        secp256r1::Affine::identity()
1618    } else {
1619        secp256r1::Affine::new_unchecked(x.into(), y.into())
1620    };
1621    Ok(SyscallResult::Success(
1622        if !(p.is_on_curve() && p.is_in_correct_subgroup_assuming_on_curve()) {
1623            vec![1.into(), 0.into()]
1624        } else {
1625            let ec = get_secp256r1_exec_scope(exec_scopes)?;
1626            let id = ec.ec_points.len();
1627            ec.ec_points.push(p);
1628            vec![0.into(), id.into()]
1629        },
1630    ))
1631}
1632
1633/// Executes the `secp256r1_add_syscall` syscall.
1634fn secp256r1_add(
1635    gas_counter: &mut usize,
1636    exec_scopes: &mut ExecutionScopes,
1637    p0_id: usize,
1638    p1_id: usize,
1639) -> Result<SyscallResult, HintError> {
1640    deduct_gas!(gas_counter, SECP256R1_ADD);
1641    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1642    let p0 = &ec.ec_points[p0_id];
1643    let p1 = &ec.ec_points[p1_id];
1644    let sum = *p0 + *p1;
1645    let id = ec.ec_points.len();
1646    ec.ec_points.push(sum.into());
1647    Ok(SyscallResult::Success(vec![id.into()]))
1648}
1649
1650/// Executes the `secp256r1_mul_syscall` syscall.
1651fn secp256r1_mul(
1652    gas_counter: &mut usize,
1653    p_id: usize,
1654    scalar: BigUint,
1655    exec_scopes: &mut ExecutionScopes,
1656) -> Result<SyscallResult, HintError> {
1657    deduct_gas!(gas_counter, SECP256R1_MUL);
1658
1659    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1660    let p = &ec.ec_points[p_id];
1661    let product = *p * secp256r1::Fr::from(scalar);
1662    let id = ec.ec_points.len();
1663    ec.ec_points.push(product.into());
1664    Ok(SyscallResult::Success(vec![id.into()]))
1665}
1666
1667/// Executes the `secp256r1_get_point_from_x_syscall` syscall.
1668fn secp256r1_get_point_from_x(
1669    gas_counter: &mut usize,
1670    x: BigUint,
1671    y_parity: bool,
1672    exec_scopes: &mut ExecutionScopes,
1673) -> Result<SyscallResult, HintError> {
1674    deduct_gas!(gas_counter, SECP256R1_NEW);
1675    if x >= <secp256r1::Fq as PrimeField>::MODULUS.into() {
1676        fail_syscall!(b"Coordinates out of range");
1677    }
1678    let x = x.into();
1679    let maybe_p = secp256r1::Affine::get_ys_from_x_unchecked(x)
1680        .map(
1681            |(smaller, greater)|
1682            // Return the correct y coordinate based on the parity.
1683            if smaller.into_bigint().is_odd() == y_parity { smaller } else { greater },
1684        )
1685        .map(|y| secp256r1::Affine::new_unchecked(x, y))
1686        .filter(|p| p.is_in_correct_subgroup_assuming_on_curve());
1687    let Some(p) = maybe_p else {
1688        return Ok(SyscallResult::Success(vec![1.into(), 0.into()]));
1689    };
1690    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1691    let id = ec.ec_points.len();
1692    ec.ec_points.push(p);
1693    Ok(SyscallResult::Success(vec![0.into(), id.into()]))
1694}
1695
1696/// Executes the `secp256r1_get_xy_syscall` syscall.
1697fn secp256r1_get_xy(
1698    gas_counter: &mut usize,
1699    p_id: usize,
1700    exec_scopes: &mut ExecutionScopes,
1701) -> Result<SyscallResult, HintError> {
1702    deduct_gas!(gas_counter, SECP256R1_GET_XY);
1703    let ec = get_secp256r1_exec_scope(exec_scopes)?;
1704    let p = &ec.ec_points[p_id];
1705    let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1706    let (x1, x0) = BigUint::from(p.x).div_rem(&pow_2_128);
1707    let (y1, y0) = BigUint::from(p.y).div_rem(&pow_2_128);
1708    Ok(SyscallResult::Success(vec![
1709        Felt252::from(x0).into(),
1710        Felt252::from(x1).into(),
1711        Felt252::from(y0).into(),
1712        Felt252::from(y1).into(),
1713    ]))
1714}
1715
1716/// Returns the `Secp256r1ExecScope` managing the different active points.
1717/// The first call to this function will create the scope, and subsequent calls will return it.
1718/// The first call would happen from some point creation syscall.
1719fn get_secp256r1_exec_scope(
1720    exec_scopes: &mut ExecutionScopes,
1721) -> Result<&mut Secp256r1ExecutionScope, HintError> {
1722    const NAME: &str = "secp256r1_exec_scope";
1723    if exec_scopes.get_ref::<Secp256r1ExecutionScope>(NAME).is_err() {
1724        exec_scopes.assign_or_update_variable(NAME, Box::<Secp256r1ExecutionScope>::default());
1725    }
1726    exec_scopes.get_mut_ref::<Secp256r1ExecutionScope>(NAME)
1727}
1728
1729// ---
1730
1731pub fn execute_core_hint_base(
1732    vm: &mut VirtualMachine,
1733    exec_scopes: &mut ExecutionScopes,
1734    core_hint_base: &cairo_lang_casm::hints::CoreHintBase,
1735    no_temporary_segments: bool,
1736) -> Result<(), HintError> {
1737    match core_hint_base {
1738        cairo_lang_casm::hints::CoreHintBase::Core(core_hint) => {
1739            execute_core_hint(vm, exec_scopes, core_hint, no_temporary_segments)
1740        }
1741        cairo_lang_casm::hints::CoreHintBase::Deprecated(deprecated_hint) => {
1742            execute_deprecated_hint(vm, exec_scopes, deprecated_hint)
1743        }
1744    }
1745}
1746
1747pub fn execute_deprecated_hint(
1748    vm: &mut VirtualMachine,
1749    exec_scopes: &mut ExecutionScopes,
1750    deprecated_hint: &cairo_lang_casm::hints::DeprecatedHint,
1751) -> Result<(), HintError> {
1752    match deprecated_hint {
1753        DeprecatedHint::Felt252DictRead { dict_ptr, key, value_dst } => {
1754            let dict_address = extract_relocatable(vm, dict_ptr)?;
1755            let key = get_val(vm, key)?;
1756            let dict_manager_exec_scope = exec_scopes
1757                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
1758                .expect("Trying to read from a dict while dict manager was not initialized.");
1759            let value = dict_manager_exec_scope
1760                .get_from_tracker(dict_address, &key)
1761                .unwrap_or_else(|| DictManagerExecScope::DICT_DEFAULT_VALUE.into());
1762            insert_value_to_cellref!(vm, value_dst, value)?;
1763        }
1764        DeprecatedHint::Felt252DictWrite { dict_ptr, key, value } => {
1765            let dict_address = extract_relocatable(vm, dict_ptr)?;
1766            let key = get_val(vm, key)?;
1767            let value = get_maybe(vm, value)?;
1768            let dict_manager_exec_scope = exec_scopes
1769                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
1770                .expect("Trying to write to a dict while dict manager was not initialized.");
1771            let prev_value = dict_manager_exec_scope
1772                .get_from_tracker(dict_address, &key)
1773                .unwrap_or_else(|| DictManagerExecScope::DICT_DEFAULT_VALUE.into());
1774            vm.insert_value((dict_address + 1)?, prev_value)?;
1775            dict_manager_exec_scope.insert_to_tracker(dict_address, key, value);
1776        }
1777        DeprecatedHint::AssertCurrentAccessIndicesIsEmpty
1778        | DeprecatedHint::AssertAllAccessesUsed { .. }
1779        | DeprecatedHint::AssertAllKeysUsed
1780        | DeprecatedHint::AssertLeAssertThirdArcExcluded
1781        | DeprecatedHint::AssertLtAssertValidInput { .. } => {}
1782    }
1783    Ok(())
1784}
1785
1786/// Allocates a memory buffer of size `size` on a VM segment.
1787/// The segment will be reused between calls.
1788fn alloc_memory(
1789    exec_scopes: &mut ExecutionScopes,
1790    vm: &mut VirtualMachine,
1791    size: usize,
1792) -> Result<Relocatable, HintError> {
1793    const NAME: &str = "memory_exec_scope";
1794    if exec_scopes.get_ref::<MemoryExecScope>(NAME).is_err() {
1795        exec_scopes.assign_or_update_variable(
1796            NAME,
1797            Box::new(MemoryExecScope { next_address: vm.add_memory_segment() }),
1798        );
1799    }
1800    let scope = exec_scopes.get_mut_ref::<MemoryExecScope>(NAME)?;
1801    let updated = (scope.next_address + size)?;
1802    Ok(std::mem::replace(&mut scope.next_address, updated))
1803}
1804
1805/// Sample a random point on the elliptic curve and insert into memory.
1806pub fn random_ec_point<R: rand::RngCore>(
1807    vm: &mut VirtualMachine,
1808    x: &CellRef,
1809    y: &CellRef,
1810    rng: &mut R,
1811) -> Result<(), HintError> {
1812    // Keep sampling a random field element `X` until `X^3 + X + beta` is a quadratic
1813    // residue.
1814    let (random_x, random_y) = loop {
1815        // Randomizing 31 bytes to make sure is in range.
1816        // TODO(orizi): Use `Felt252` random implementation when exists.
1817        let x_bytes: [u8; 31] = rng.random();
1818        let random_x = Felt252::from_bytes_be_slice(&x_bytes);
1819        /// The Beta value of the Starkware elliptic curve.
1820        pub const BETA: Felt252 = Felt252::from_hex_unchecked(
1821            "0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89",
1822        );
1823        let random_y_squared = random_x * random_x * random_x + random_x + BETA;
1824        if let Some(random_y) = random_y_squared.sqrt() {
1825            break (random_x, random_y);
1826        }
1827    };
1828    insert_value_to_cellref!(vm, x, random_x)?;
1829    insert_value_to_cellref!(vm, y, random_y)?;
1830    Ok(())
1831}
1832
1833/// Executes a core hint.
1834pub fn execute_core_hint(
1835    vm: &mut VirtualMachine,
1836    exec_scopes: &mut ExecutionScopes,
1837    core_hint: &CoreHint,
1838    no_temporary_segments: bool,
1839) -> Result<(), HintError> {
1840    match core_hint {
1841        CoreHint::AllocSegment { dst } => {
1842            let segment = vm.add_memory_segment();
1843            insert_value_to_cellref!(vm, dst, segment)?;
1844        }
1845        CoreHint::TestLessThan { lhs, rhs, dst } => {
1846            let lhs_val = get_val(vm, lhs)?;
1847            let rhs_val = get_val(vm, rhs)?;
1848            insert_value_to_cellref!(
1849                vm,
1850                dst,
1851                if lhs_val < rhs_val { Felt252::from(1) } else { Felt252::from(0) }
1852            )?;
1853        }
1854        CoreHint::TestLessThanOrEqual { lhs, rhs, dst }
1855        | CoreHint::TestLessThanOrEqualAddress { lhs, rhs, dst } => {
1856            let lhs_val = get_maybe(vm, lhs)?;
1857            let rhs_val = get_maybe(vm, rhs)?;
1858            insert_value_to_cellref!(
1859                vm,
1860                dst,
1861                if lhs_val <= rhs_val { Felt252::from(1) } else { Felt252::from(0) }
1862            )?;
1863        }
1864        CoreHint::WideMul128 { lhs, rhs, high, low } => {
1865            let mask128 = BigUint::from(u128::MAX);
1866            let lhs_val = get_val(vm, lhs)?.to_biguint();
1867            let rhs_val = get_val(vm, rhs)?.to_biguint();
1868            let prod = lhs_val * rhs_val;
1869            insert_value_to_cellref!(vm, high, Felt252::from(prod.clone() >> 128))?;
1870            insert_value_to_cellref!(vm, low, Felt252::from(prod & mask128))?;
1871        }
1872        CoreHint::DivMod { lhs, rhs, quotient, remainder } => {
1873            let lhs_val = get_val(vm, lhs)?.to_biguint();
1874            let rhs_val = get_val(vm, rhs)?.to_biguint();
1875            insert_value_to_cellref!(
1876                vm,
1877                quotient,
1878                Felt252::from(lhs_val.clone() / rhs_val.clone())
1879            )?;
1880            insert_value_to_cellref!(vm, remainder, Felt252::from(lhs_val % rhs_val))?;
1881        }
1882        CoreHint::Uint256DivMod {
1883            dividend0,
1884            dividend1,
1885            divisor0,
1886            divisor1,
1887            quotient0,
1888            quotient1,
1889            remainder0,
1890            remainder1,
1891        } => {
1892            let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1893            let dividend0 = get_val(vm, dividend0)?.to_biguint();
1894            let dividend1 = get_val(vm, dividend1)?.to_biguint();
1895            let divisor0 = get_val(vm, divisor0)?.to_biguint();
1896            let divisor1 = get_val(vm, divisor1)?.to_biguint();
1897            let dividend: BigUint = dividend0 + dividend1.shl(128);
1898            let divisor = divisor0 + divisor1.shl(128);
1899            let (quotient, remainder) = dividend.div_rem(&divisor);
1900            let (limb1, limb0) = quotient.div_rem(&pow_2_128);
1901            insert_value_to_cellref!(vm, quotient0, Felt252::from(limb0))?;
1902            insert_value_to_cellref!(vm, quotient1, Felt252::from(limb1))?;
1903            let (limb1, limb0) = remainder.div_rem(&pow_2_128);
1904            insert_value_to_cellref!(vm, remainder0, Felt252::from(limb0))?;
1905            insert_value_to_cellref!(vm, remainder1, Felt252::from(limb1))?;
1906        }
1907        CoreHint::Uint512DivModByUint256 {
1908            dividend0,
1909            dividend1,
1910            dividend2,
1911            dividend3,
1912            divisor0,
1913            divisor1,
1914            quotient0,
1915            quotient1,
1916            quotient2,
1917            quotient3,
1918            remainder0,
1919            remainder1,
1920        } => {
1921            let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1922            let dividend0 = get_val(vm, dividend0)?.to_biguint();
1923            let dividend1 = get_val(vm, dividend1)?.to_biguint();
1924            let dividend2 = get_val(vm, dividend2)?.to_biguint();
1925            let dividend3 = get_val(vm, dividend3)?.to_biguint();
1926            let divisor0 = get_val(vm, divisor0)?.to_biguint();
1927            let divisor1 = get_val(vm, divisor1)?.to_biguint();
1928            let dividend: BigUint =
1929                dividend0 + dividend1.shl(128) + dividend2.shl(256) + dividend3.shl(384);
1930            let divisor = divisor0 + divisor1.shl(128);
1931            let (quotient, remainder) = dividend.div_rem(&divisor);
1932            let (quotient, limb0) = quotient.div_rem(&pow_2_128);
1933            insert_value_to_cellref!(vm, quotient0, Felt252::from(limb0))?;
1934            let (quotient, limb1) = quotient.div_rem(&pow_2_128);
1935            insert_value_to_cellref!(vm, quotient1, Felt252::from(limb1))?;
1936            let (limb3, limb2) = quotient.div_rem(&pow_2_128);
1937            insert_value_to_cellref!(vm, quotient2, Felt252::from(limb2))?;
1938            insert_value_to_cellref!(vm, quotient3, Felt252::from(limb3))?;
1939            let (limb1, limb0) = remainder.div_rem(&pow_2_128);
1940            insert_value_to_cellref!(vm, remainder0, Felt252::from(limb0))?;
1941            insert_value_to_cellref!(vm, remainder1, Felt252::from(limb1))?;
1942        }
1943        CoreHint::SquareRoot { value, dst } => {
1944            let val = get_val(vm, value)?.to_biguint();
1945            insert_value_to_cellref!(vm, dst, Felt252::from(val.sqrt()))?;
1946        }
1947        CoreHint::Uint256SquareRoot {
1948            value_low,
1949            value_high,
1950            sqrt0,
1951            sqrt1,
1952            remainder_low,
1953            remainder_high,
1954            sqrt_mul_2_minus_remainder_ge_u128,
1955        } => {
1956            let pow_2_128 = BigUint::from(u128::MAX) + 1u32;
1957            let pow_2_64 = BigUint::from(u64::MAX) + 1u32;
1958            let value_low = get_val(vm, value_low)?.to_biguint();
1959            let value_high = get_val(vm, value_high)?.to_biguint();
1960            let value = value_low + value_high * pow_2_128.clone();
1961            let sqrt = value.sqrt();
1962            let remainder = value - sqrt.clone() * sqrt.clone();
1963            let sqrt_mul_2_minus_remainder_ge_u128_val =
1964                sqrt.clone() * 2u32 - remainder.clone() >= pow_2_128;
1965
1966            // Guess sqrt limbs.
1967            let (sqrt1_val, sqrt0_val) = sqrt.div_rem(&pow_2_64);
1968            insert_value_to_cellref!(vm, sqrt0, Felt252::from(sqrt0_val))?;
1969            insert_value_to_cellref!(vm, sqrt1, Felt252::from(sqrt1_val))?;
1970
1971            let (remainder_high_val, remainder_low_val) = remainder.div_rem(&pow_2_128);
1972            // Guess remainder limbs.
1973            insert_value_to_cellref!(vm, remainder_low, Felt252::from(remainder_low_val))?;
1974            insert_value_to_cellref!(vm, remainder_high, Felt252::from(remainder_high_val))?;
1975            insert_value_to_cellref!(
1976                vm,
1977                sqrt_mul_2_minus_remainder_ge_u128,
1978                Felt252::from(usize::from(sqrt_mul_2_minus_remainder_ge_u128_val))
1979            )?;
1980        }
1981        CoreHint::LinearSplit { value, scalar, max_x, x, y } => {
1982            let value = get_val(vm, value)?;
1983            let scalar = get_val(vm, scalar)?;
1984            let max_x = get_val(vm, max_x)?;
1985            let x_value = value.floor_div(&NonZeroFelt::from_felt_unchecked(scalar)).min(max_x);
1986            let y_value = value - x_value * scalar;
1987            insert_value_to_cellref!(vm, x, x_value)?;
1988            insert_value_to_cellref!(vm, y, y_value)?;
1989        }
1990        CoreHint::RandomEcPoint { x, y } => {
1991            let mut rng = rand::rng();
1992            random_ec_point(vm, x, y, &mut rng)?;
1993        }
1994        CoreHint::FieldSqrt { val, sqrt } => {
1995            let val = get_val(vm, val)?;
1996            let res = val.sqrt().unwrap_or_else(|| (val * Felt252::THREE).sqrt().unwrap());
1997            insert_value_to_cellref!(vm, sqrt, std::cmp::min(res, -res))?;
1998        }
1999        CoreHint::AllocFelt252Dict { segment_arena_ptr } => {
2000            let dict_manager_address = extract_relocatable(vm, segment_arena_ptr)?;
2001            let n_dicts = vm
2002                .get_integer((dict_manager_address - 2)?)?
2003                .into_owned()
2004                .to_usize()
2005                .expect("Number of dictionaries too large.");
2006            let dict_infos_base = vm.get_relocatable((dict_manager_address - 3)?)?;
2007
2008            let dict_manager_exec_scope = match exec_scopes
2009                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2010            {
2011                Ok(dict_manager_exec_scope) => dict_manager_exec_scope,
2012                Err(_) => {
2013                    exec_scopes.assign_or_update_variable(
2014                        "dict_manager_exec_scope",
2015                        Box::<DictManagerExecScope>::default(),
2016                    );
2017                    exec_scopes.get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")?
2018                }
2019            };
2020            let new_dict_segment =
2021                dict_manager_exec_scope.new_default_dict(vm, no_temporary_segments);
2022            vm.insert_value((dict_infos_base + 3 * n_dicts)?, new_dict_segment)?;
2023        }
2024        CoreHint::Felt252DictEntryInit { dict_ptr, key } => {
2025            let dict_address = extract_relocatable(vm, dict_ptr)?;
2026            let key = get_val(vm, key)?;
2027            let dict_manager_exec_scope = exec_scopes
2028                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2029                .expect("Trying to write to a dict while dict manager was not initialized.");
2030            let prev_value = dict_manager_exec_scope
2031                .get_from_tracker(dict_address, &key)
2032                .unwrap_or_else(|| DictManagerExecScope::DICT_DEFAULT_VALUE.into());
2033            vm.insert_value((dict_address + 1)?, prev_value)?;
2034        }
2035        CoreHint::Felt252DictEntryUpdate { dict_ptr, value } => {
2036            let (dict_base, dict_offset) = extract_buffer(dict_ptr);
2037            let dict_address = get_ptr(vm, dict_base, &dict_offset)?;
2038            let key = get_double_deref_val(vm, dict_base, &(dict_offset + Felt252::from(-3)))?;
2039            let value = get_maybe(vm, value)?;
2040            let dict_manager_exec_scope = exec_scopes
2041                .get_mut_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2042                .expect("Trying to write to a dict while dict manager was not initialized.");
2043            dict_manager_exec_scope.insert_to_tracker(dict_address, key, value);
2044        }
2045        CoreHint::GetSegmentArenaIndex { dict_end_ptr, dict_index, .. } => {
2046            let dict_address = extract_relocatable(vm, dict_end_ptr)?;
2047            let dict_manager_exec_scope = exec_scopes
2048                .get_ref::<DictManagerExecScope>("dict_manager_exec_scope")
2049                .expect("Trying to read from a dict while dict manager was not initialized.");
2050            let dict_infos_index = dict_manager_exec_scope.get_dict_infos_index(dict_address);
2051            insert_value_to_cellref!(vm, dict_index, Felt252::from(dict_infos_index))?;
2052        }
2053        CoreHint::InitSquashData { dict_accesses, n_accesses, first_key, big_keys, .. } => {
2054            let dict_access_size = 3;
2055            let rangecheck_bound = Felt252::from(BigInt::from(1).shl(128));
2056
2057            exec_scopes.assign_or_update_variable(
2058                "dict_squash_exec_scope",
2059                Box::<DictSquashExecScope>::default(),
2060            );
2061            let dict_squash_exec_scope =
2062                exec_scopes.get_mut_ref::<DictSquashExecScope>("dict_squash_exec_scope")?;
2063            let dict_accesses_address = extract_relocatable(vm, dict_accesses)?;
2064            let n_accesses = get_val(vm, n_accesses)?
2065                .to_usize()
2066                .expect("Number of accesses is too large or negative.");
2067            for i in 0..n_accesses {
2068                let current_key =
2069                    vm.get_integer((dict_accesses_address + i * dict_access_size)?)?;
2070                dict_squash_exec_scope
2071                    .access_indices
2072                    .entry(current_key.into_owned())
2073                    .and_modify(|indices| indices.push(Felt252::from(i)))
2074                    .or_insert_with(|| vec![Felt252::from(i)]);
2075            }
2076            // Reverse the accesses in order to pop them in order later.
2077            for (_, accesses) in dict_squash_exec_scope.access_indices.iter_mut() {
2078                accesses.reverse();
2079            }
2080            dict_squash_exec_scope.keys =
2081                dict_squash_exec_scope.access_indices.keys().cloned().collect();
2082            dict_squash_exec_scope.keys.sort_by(|a, b| b.cmp(a));
2083            // big_keys indicates if the keys are greater than rangecheck_bound. If they are not
2084            // a simple range check is used instead of assert_le_felt252.
2085            insert_value_to_cellref!(
2086                vm,
2087                big_keys,
2088                if dict_squash_exec_scope.keys[0] < rangecheck_bound {
2089                    Felt252::from(0)
2090                } else {
2091                    Felt252::from(1)
2092                }
2093            )?;
2094            insert_value_to_cellref!(vm, first_key, dict_squash_exec_scope.current_key().unwrap())?;
2095        }
2096        CoreHint::GetCurrentAccessIndex { range_check_ptr } => {
2097            let dict_squash_exec_scope: &mut DictSquashExecScope =
2098                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2099            let range_check_ptr = extract_relocatable(vm, range_check_ptr)?;
2100            let current_access_index = dict_squash_exec_scope.current_access_index().unwrap();
2101            vm.insert_value(range_check_ptr, current_access_index)?;
2102        }
2103        CoreHint::ShouldSkipSquashLoop { should_skip_loop } => {
2104            let dict_squash_exec_scope: &mut DictSquashExecScope =
2105                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2106            insert_value_to_cellref!(
2107                vm,
2108                should_skip_loop,
2109                // The loop verifies that each two consecutive accesses are valid, thus we
2110                // break when there is only one remaining access.
2111                if dict_squash_exec_scope.current_access_indices().unwrap().len() > 1 {
2112                    Felt252::from(0)
2113                } else {
2114                    Felt252::from(1)
2115                }
2116            )?;
2117        }
2118        CoreHint::GetCurrentAccessDelta { index_delta_minus1 } => {
2119            let dict_squash_exec_scope: &mut DictSquashExecScope =
2120                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2121            let prev_access_index = dict_squash_exec_scope.pop_current_access_index().unwrap();
2122            let index_delta_minus_1_val = (*dict_squash_exec_scope.current_access_index().unwrap()
2123                - prev_access_index)
2124                .sub(1);
2125
2126            insert_value_to_cellref!(vm, index_delta_minus1, index_delta_minus_1_val)?;
2127        }
2128        CoreHint::ShouldContinueSquashLoop { should_continue } => {
2129            let dict_squash_exec_scope: &mut DictSquashExecScope =
2130                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2131            insert_value_to_cellref!(
2132                vm,
2133                should_continue,
2134                // The loop verifies that each two consecutive accesses are valid, thus we
2135                // break when there is only one remaining access.
2136                if dict_squash_exec_scope.current_access_indices().unwrap().len() > 1 {
2137                    Felt252::from(1)
2138                } else {
2139                    Felt252::from(0)
2140                }
2141            )?;
2142        }
2143        CoreHint::GetNextDictKey { next_key } => {
2144            let dict_squash_exec_scope: &mut DictSquashExecScope =
2145                exec_scopes.get_mut_ref("dict_squash_exec_scope")?;
2146            dict_squash_exec_scope.pop_current_key();
2147            insert_value_to_cellref!(vm, next_key, dict_squash_exec_scope.current_key().unwrap())?;
2148        }
2149        CoreHint::AssertLeFindSmallArcs { a, b, range_check_ptr } => {
2150            let a_val = get_val(vm, a)?;
2151            let b_val = get_val(vm, b)?;
2152            let mut lengths_and_indices =
2153                [(a_val, 0), (b_val - a_val, 1), (Felt252::from(-1) - b_val, 2)];
2154            lengths_and_indices.sort();
2155            exec_scopes
2156                .assign_or_update_variable("excluded_arc", Box::new(lengths_and_indices[2].1));
2157            // ceil((PRIME / 3) / 2 ** 128).
2158            let prime_over_3_high = 3544607988759775765608368578435044694_u128;
2159            // ceil((PRIME / 2) / 2 ** 128).
2160            let prime_over_2_high = 5316911983139663648412552867652567041_u128;
2161            let range_check_ptr = extract_relocatable(vm, range_check_ptr)?;
2162            vm.insert_value(
2163                range_check_ptr,
2164                Felt252::from(lengths_and_indices[0].0.to_biguint() % prime_over_3_high),
2165            )?;
2166            vm.insert_value(
2167                (range_check_ptr + 1)?,
2168                Felt252::from(lengths_and_indices[0].0.to_biguint() / prime_over_3_high),
2169            )?;
2170            vm.insert_value(
2171                (range_check_ptr + 2)?,
2172                Felt252::from(lengths_and_indices[1].0.to_biguint() % prime_over_2_high),
2173            )?;
2174            vm.insert_value(
2175                (range_check_ptr + 3)?,
2176                Felt252::from(lengths_and_indices[1].0.to_biguint() / prime_over_2_high),
2177            )?;
2178        }
2179        CoreHint::AssertLeIsFirstArcExcluded { skip_exclude_a_flag } => {
2180            let excluded_arc: i32 = exec_scopes.get("excluded_arc")?;
2181            insert_value_to_cellref!(
2182                vm,
2183                skip_exclude_a_flag,
2184                if excluded_arc != 0 { Felt252::from(1) } else { Felt252::from(0) }
2185            )?;
2186        }
2187        CoreHint::AssertLeIsSecondArcExcluded { skip_exclude_b_minus_a } => {
2188            let excluded_arc: i32 = exec_scopes.get("excluded_arc")?;
2189            insert_value_to_cellref!(
2190                vm,
2191                skip_exclude_b_minus_a,
2192                if excluded_arc != 1 { Felt252::from(1) } else { Felt252::from(0) }
2193            )?;
2194        }
2195        CoreHint::DebugPrint { start, end } => {
2196            print!("{}", format_for_debug(read_felts(vm, start, end)?.into_iter()));
2197        }
2198        CoreHint::AllocConstantSize { size, dst } => {
2199            let object_size = get_val(vm, size)?.to_usize().expect("Object size too large.");
2200            let ptr = alloc_memory(exec_scopes, vm, object_size)?;
2201            insert_value_to_cellref!(vm, dst, ptr)?;
2202        }
2203        CoreHint::U256InvModN {
2204            b0,
2205            b1,
2206            n0,
2207            n1,
2208            g0_or_no_inv,
2209            g1_option,
2210            s_or_r0,
2211            s_or_r1,
2212            t_or_k0,
2213            t_or_k1,
2214        } => {
2215            let pow_2_128 = BigInt::from(u128::MAX) + 1u32;
2216            let b0 = get_val(vm, b0)?.to_bigint();
2217            let b1 = get_val(vm, b1)?.to_bigint();
2218            let n0 = get_val(vm, n0)?.to_bigint();
2219            let n1 = get_val(vm, n1)?.to_bigint();
2220            let b: BigInt = b0.clone() + b1.clone().shl(128);
2221            let n: BigInt = n0 + n1.shl(128);
2222            let ExtendedGcd { gcd: mut g, x: _, y: mut r } = n.extended_gcd(&b);
2223            if n == 1.into() {
2224                insert_value_to_cellref!(vm, s_or_r0, Felt252::from(b0))?;
2225                insert_value_to_cellref!(vm, s_or_r1, Felt252::from(b1))?;
2226                insert_value_to_cellref!(vm, t_or_k0, Felt252::from(1))?;
2227                insert_value_to_cellref!(vm, t_or_k1, Felt252::from(0))?;
2228                insert_value_to_cellref!(vm, g0_or_no_inv, Felt252::from(1))?;
2229                insert_value_to_cellref!(vm, g1_option, Felt252::from(0))?;
2230            } else if g != 1.into() {
2231                // This makes sure `g0_or_no_inv` is always non-zero in the no inverse case.
2232                if g.is_even() {
2233                    g = 2u32.into();
2234                }
2235                let (limb1, limb0) = (&b / &g).div_rem(&pow_2_128);
2236                insert_value_to_cellref!(vm, s_or_r0, Felt252::from(limb0))?;
2237                insert_value_to_cellref!(vm, s_or_r1, Felt252::from(limb1))?;
2238                let (limb1, limb0) = (&n / &g).div_rem(&pow_2_128);
2239                insert_value_to_cellref!(vm, t_or_k0, Felt252::from(limb0))?;
2240                insert_value_to_cellref!(vm, t_or_k1, Felt252::from(limb1))?;
2241                let (limb1, limb0) = g.div_rem(&pow_2_128);
2242                insert_value_to_cellref!(vm, g0_or_no_inv, Felt252::from(limb0))?;
2243                insert_value_to_cellref!(vm, g1_option, Felt252::from(limb1))?;
2244            } else {
2245                r %= &n;
2246                if r.is_negative() {
2247                    r += &n;
2248                }
2249                let k: BigInt = (&r * b - 1) / n;
2250                let (limb1, limb0) = r.div_rem(&pow_2_128);
2251                insert_value_to_cellref!(vm, s_or_r0, Felt252::from(limb0))?;
2252                insert_value_to_cellref!(vm, s_or_r1, Felt252::from(limb1))?;
2253                let (limb1, limb0) = k.div_rem(&pow_2_128);
2254                insert_value_to_cellref!(vm, t_or_k0, Felt252::from(limb0))?;
2255                insert_value_to_cellref!(vm, t_or_k1, Felt252::from(limb1))?;
2256                insert_value_to_cellref!(vm, g0_or_no_inv, Felt252::from(0))?;
2257            }
2258        }
2259        CoreHint::EvalCircuit {
2260            n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin, ..
2261        } => {
2262            let add_mod_builtin = extract_relocatable(vm, add_mod_builtin)?;
2263            let n_add_mods = get_val(vm, n_add_mods)?.to_usize().unwrap();
2264            let mul_mod_builtin = extract_relocatable(vm, mul_mod_builtin)?;
2265            let n_mul_mods = get_val(vm, n_mul_mods)?.to_usize().unwrap();
2266
2267            circuit::eval_circuit(vm, add_mod_builtin, n_add_mods, mul_mod_builtin, n_mul_mods)?;
2268        }
2269    };
2270    Ok(())
2271}
2272
2273/// Reads a range of `Felt252`s from the VM.
2274fn read_felts(
2275    vm: &mut VirtualMachine,
2276    start: &ResOperand,
2277    end: &ResOperand,
2278) -> Result<Vec<Felt252>, HintError> {
2279    let mut curr = extract_relocatable(vm, start)?;
2280    let end = extract_relocatable(vm, end)?;
2281
2282    let mut felts = Vec::new();
2283    while curr != end {
2284        let value = *vm.get_integer(curr)?;
2285        felts.push(value);
2286        curr = (curr + 1)?;
2287    }
2288
2289    Ok(felts)
2290}
2291
2292/// Reads the result of a function call that returns `Array<felt252>`.
2293fn read_array_result_as_vec(memory: &[Option<Felt252>], value: &[Felt252]) -> Vec<Felt252> {
2294    // TODO(spapini): Handle failures.
2295    let [res_start, res_end] = value else {
2296        panic!("Unexpected return value from contract call");
2297    };
2298    let res_start: usize = res_start.to_bigint().try_into().unwrap();
2299    let res_end: usize = res_end.to_bigint().try_into().unwrap();
2300    (res_start..res_end).map(|i| memory[i].unwrap()).collect()
2301}
2302
2303/// Loads a range of values from the VM memory.
2304pub fn vm_get_range(
2305    vm: &mut VirtualMachine,
2306    mut calldata_start_ptr: Relocatable,
2307    calldata_end_ptr: Relocatable,
2308) -> Result<Vec<Felt252>, HintError> {
2309    let mut values = vec![];
2310    while calldata_start_ptr != calldata_end_ptr {
2311        let val = *vm.get_integer(calldata_start_ptr)?;
2312        values.push(val);
2313        calldata_start_ptr.offset += 1;
2314    }
2315    Ok(values)
2316}
2317
2318/// Extracts a parameter assumed to be a buffer.
2319pub fn extract_buffer(buffer: &ResOperand) -> (&CellRef, Felt252) {
2320    let (cell, base_offset) = match buffer {
2321        ResOperand::Deref(cell) => (cell, 0.into()),
2322        ResOperand::BinOp(BinOpOperand { op: Operation::Add, a, b }) => {
2323            (a, extract_matches!(b, DerefOrImmediate::Immediate).clone().value.into())
2324        }
2325        _ => panic!("Illegal argument for a buffer."),
2326    };
2327    (cell, base_offset)
2328}
2329
2330/// Runs CairoRunner on layout with prime.
2331/// Allows injecting custom CairoRunner.
2332pub fn run_function_with_runner(
2333    additional_initialization: impl FnOnce(&mut VirtualMachine) -> Result<(), Box<CairoRunError>>,
2334    hint_processor: &mut dyn HintProcessor,
2335    runner: &mut CairoRunner,
2336) -> Result<(), Box<CairoRunError>> {
2337    let end = runner.initialize(true).map_err(CairoRunError::from)?;
2338
2339    additional_initialization(&mut runner.vm)?;
2340
2341    runner.run_until_pc(end, hint_processor).map_err(CairoRunError::from)?;
2342    runner.end_run(true, false, hint_processor).map_err(CairoRunError::from)?;
2343    runner.relocate(true).map_err(CairoRunError::from)?;
2344    Ok(())
2345}
2346
2347/// Creates CairoRunner for `program`.
2348pub fn build_cairo_runner(
2349    data: Vec<MaybeRelocatable>,
2350    builtins: Vec<BuiltinName>,
2351    hints_dict: HashMap<usize, Vec<HintParams>>,
2352) -> Result<CairoRunner, Box<CairoRunError>> {
2353    let program = Program::new(
2354        builtins,
2355        data,
2356        Some(0),
2357        hints_dict,
2358        ReferenceManager { references: Vec::new() },
2359        HashMap::new(),
2360        vec![],
2361        None,
2362    )
2363    .map_err(CairoRunError::from)?;
2364    let dynamic_layout_params = None;
2365    let proof_mode = false;
2366    let trace_enabled = true;
2367    let disable_trace_padding = false;
2368    CairoRunner::new(
2369        &program,
2370        LayoutName::all_cairo,
2371        dynamic_layout_params,
2372        proof_mode,
2373        trace_enabled,
2374        disable_trace_padding,
2375    )
2376    .map_err(CairoRunError::from)
2377    .map_err(Box::new)
2378}
2379
2380/// The result of [run_function].
2381pub struct RunFunctionResult {
2382    /// The ap value after the run.
2383    pub ap: usize,
2384    /// The used resources after the run.
2385    pub used_resources: ExecutionResources,
2386    /// The relocated memory after the run.
2387    pub memory: Vec<Option<Felt252>>,
2388    /// The relocated trace.
2389    pub relocated_trace: Vec<RelocatedTraceEntry>,
2390}
2391
2392/// Runs `bytecode` on layout with prime, and returns the matching [RunFunctionResult].
2393/// Allows injecting custom HintProcessor.
2394pub fn run_function<'a, 'b: 'a>(
2395    bytecode: impl Iterator<Item = &'a BigInt> + Clone,
2396    builtins: Vec<BuiltinName>,
2397    additional_initialization: impl FnOnce(&mut VirtualMachine) -> Result<(), Box<CairoRunError>>,
2398    hint_processor: &mut dyn HintProcessor,
2399    hints_dict: HashMap<usize, Vec<HintParams>>,
2400) -> Result<RunFunctionResult, Box<CairoRunError>> {
2401    let data: Vec<MaybeRelocatable> =
2402        bytecode.map(Felt252::from).map(MaybeRelocatable::from).collect();
2403    let mut runner = build_cairo_runner(data, builtins, hints_dict)?;
2404
2405    run_function_with_runner(additional_initialization, hint_processor, &mut runner)?;
2406
2407    let used_resources = runner
2408        .get_execution_resources()
2409        .expect("Failed to get execution resources, but the run was successful.");
2410
2411    let relocated_trace = runner.relocated_trace.unwrap();
2412    let memory = runner.relocated_memory;
2413
2414    Ok(RunFunctionResult {
2415        ap: relocated_trace.last().unwrap().ap,
2416        used_resources,
2417        memory,
2418        relocated_trace,
2419    })
2420}
2421
2422/// Formats the given felts as a debug string.
2423fn format_for_debug(mut felts: IntoIter<Felt252>) -> String {
2424    let mut items = Vec::new();
2425    while let Some(item) = format_next_item(&mut felts) {
2426        items.push(item);
2427    }
2428    if let [item] = &items[..]
2429        && item.is_string
2430    {
2431        return item.item.clone();
2432    }
2433    items
2434        .into_iter()
2435        .map(|item| {
2436            if item.is_string {
2437                format!("{}\n", item.item)
2438            } else {
2439                format!("[DEBUG]\t{}\n", item.item)
2440            }
2441        })
2442        .join("")
2443}
2444
2445/// A formatted string representation of anything formattable (e.g. ByteArray, felt, short-string).
2446pub struct FormattedItem {
2447    /// The formatted string representing the item.
2448    item: String,
2449    /// Whether the item is a string.
2450    is_string: bool,
2451}
2452impl FormattedItem {
2453    /// Returns the formatted item as is.
2454    pub fn get(self) -> String {
2455        self.item
2456    }
2457    /// Wraps the formatted item with quote, if it's a string. Otherwise returns it as is.
2458    pub fn quote_if_string(self) -> String {
2459        if self.is_string { format!("\"{}\"", self.item) } else { self.item }
2460    }
2461}
2462
2463/// Formats a string or a short string / `felt252`. Returns the formatted string and a boolean
2464/// indicating whether it's a string. If can't format the item, returns None.
2465pub fn format_next_item<T>(values: &mut T) -> Option<FormattedItem>
2466where
2467    T: Iterator<Item = Felt252> + Clone,
2468{
2469    let first_felt = values.next()?;
2470
2471    if first_felt == Felt252::from_hex(BYTE_ARRAY_MAGIC).unwrap()
2472        && let Some(string) = try_format_string(values)
2473    {
2474        return Some(FormattedItem { item: string, is_string: true });
2475    }
2476    Some(FormattedItem { item: format_short_string(&first_felt), is_string: false })
2477}
2478
2479/// Formats the given felts as a panic string.
2480pub fn format_for_panic<T>(mut felts: T) -> String
2481where
2482    T: Iterator<Item = Felt252> + Clone,
2483{
2484    let mut items = Vec::new();
2485    while let Some(item) = format_next_item(&mut felts) {
2486        items.push(item.quote_if_string());
2487    }
2488    let panic_values_string =
2489        if let [item] = &items[..] { item.clone() } else { format!("({})", items.join(", ")) };
2490    format!("Panicked with {panic_values_string}.")
2491}
2492
2493/// Formats a `Felt252`, as a short string if possible.
2494fn format_short_string(value: &Felt252) -> String {
2495    let hex_value = value.to_biguint();
2496    match as_cairo_short_string(value) {
2497        Some(as_string) => format!("{hex_value:#x} ('{as_string}')"),
2498        None => format!("{hex_value:#x}"),
2499    }
2500}
2501
2502/// Tries to format a string, represented as a sequence of `Felt252`s.
2503/// If the sequence is not a valid serialization of a ByteArray, returns None and doesn't change the
2504/// given iterator (`values`).
2505fn try_format_string<T>(values: &mut T) -> Option<String>
2506where
2507    T: Iterator<Item = Felt252> + Clone,
2508{
2509    // Clone the iterator and work with the clone. If the extraction of the string is successful,
2510    // change the original iterator to the one we worked with. If not, continue with the
2511    // original iterator at the original point.
2512    let mut cloned_values_iter = values.clone();
2513
2514    let num_full_words = cloned_values_iter.next()?.to_usize()?;
2515    let full_words = cloned_values_iter.by_ref().take(num_full_words).collect_vec();
2516    let pending_word = cloned_values_iter.next()?;
2517    let pending_word_len = cloned_values_iter.next()?.to_usize()?;
2518
2519    let full_words_string = full_words
2520        .into_iter()
2521        .map(|word| as_cairo_short_string_ex(&word, BYTES_IN_WORD))
2522        .collect::<Option<Vec<String>>>()?
2523        .join("");
2524    let pending_word_string = as_cairo_short_string_ex(&pending_word, pending_word_len)?;
2525
2526    // Extraction was successful, change the original iterator to the one we worked with.
2527    *values = cloned_values_iter;
2528
2529    Some(format!("{full_words_string}{pending_word_string}"))
2530}