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