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