Skip to main content

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