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