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