cairo_lang_runner/casm_run/
mod.rs

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