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