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