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