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