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