Skip to main content

cairo_lang_runner/casm_run/
mod.rs

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