Skip to main content

cairo_lang_runner/casm_run/
mod.rs

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