1#![allow(non_snake_case)]
2
3use cairo_lang_sierra_gas::core_libfunc_cost::{
4 DICT_SQUASH_REPEATED_ACCESS_COST, DICT_SQUASH_UNIQUE_KEY_COST,
5};
6use itertools::Itertools;
7use lazy_static::lazy_static;
8use num_traits::{ToPrimitive, Zero};
9use rand::Rng;
10use starknet_curve::curve_params::BETA;
11use starknet_types_core::{
12 curve::{AffinePoint, ProjectivePoint},
13 felt::Felt,
14 hash::StarkHash,
15};
16use std::{
17 alloc::{alloc, dealloc, realloc, Layout},
18 cell::Cell,
19 collections::{hash_map::Entry, HashMap},
20 ffi::{c_int, c_void},
21 fs::File,
22 io::Write,
23 mem::{forget, ManuallyDrop},
24 os::fd::FromRawFd,
25 ptr::{self, null, null_mut},
26 rc::Rc,
27 slice,
28};
29use std::{ops::Mul, vec::IntoIter};
30
31lazy_static! {
32 pub static ref HALF_PRIME: Felt = Felt::from_dec_str(
33 "1809251394333065606848661391547535052811553607665798349986546028067936010240"
34 )
35 .unwrap();
36 pub static ref DICT_GAS_REFUND_PER_ACCESS: u64 =
37 (DICT_SQUASH_UNIQUE_KEY_COST.cost() - DICT_SQUASH_REPEATED_ACCESS_COST.cost()) as u64;
38}
39
40#[no_mangle]
41#[allow(clippy::missing_safety_doc)]
42pub unsafe extern "C" fn cairo_native__get_version(target: *mut u8, length: usize) -> usize {
43 let version = env!("CARGO_PKG_VERSION");
44 assert!(length > version.len(), "version buffer not big enough");
45
46 let target = slice::from_raw_parts_mut(target, length);
47
48 target[..version.len()].copy_from_slice(version.as_bytes());
49 target[version.len()] = b'\0';
50
51 version.len()
52}
53
54#[no_mangle]
63pub unsafe extern "C" fn cairo_native__libfunc__debug__print(
64 target_fd: i32,
65 data: *const [u8; 32],
66 len: u32,
67) -> i32 {
68 let mut target = ManuallyDrop::new(File::from_raw_fd(target_fd));
70
71 let mut items = Vec::with_capacity(len as usize);
72
73 for i in 0..len as usize {
74 let mut data = *data.add(i);
75 data[31] &= 0x0F; let value = Felt::from_bytes_le(&data);
78 items.push(value);
79 }
80
81 let value = format_for_debug(items.into_iter());
82
83 if write!(target, "{}", value).is_err() {
84 return 1;
85 };
86
87 0
88}
89
90#[no_mangle]
103pub unsafe extern "C" fn cairo_native__libfunc__pedersen(
104 dst: &mut [u8; 32],
105 lhs: &[u8; 32],
106 rhs: &[u8; 32],
107) {
108 let mut lhs = *lhs;
110 let mut rhs = *rhs;
111
112 lhs[31] &= 0x0F; rhs[31] &= 0x0F; let lhs = Felt::from_bytes_le(&lhs);
117 let rhs = Felt::from_bytes_le(&rhs);
118
119 let res = starknet_types_core::hash::Pedersen::hash(&lhs, &rhs);
121 *dst = res.to_bytes_le();
122}
123
124#[no_mangle]
137pub unsafe extern "C" fn cairo_native__libfunc__hades_permutation(
138 op0: &mut [u8; 32],
139 op1: &mut [u8; 32],
140 op2: &mut [u8; 32],
141) {
142 op0[31] &= 0x0F; op1[31] &= 0x0F; op2[31] &= 0x0F; let mut state = [
148 Felt::from_bytes_le(op0),
149 Felt::from_bytes_le(op1),
150 Felt::from_bytes_le(op2),
151 ];
152
153 starknet_types_core::hash::Poseidon::hades_permutation(&mut state);
155
156 *op0 = state[0].to_bytes_le();
158 *op1 = state[1].to_bytes_le();
159 *op2 = state[2].to_bytes_le();
160}
161
162#[derive(Debug)]
164pub struct FeltDict {
165 pub mappings: HashMap<[u8; 32], usize>,
166
167 pub layout: Layout,
168 pub elements: *mut (),
169
170 pub dup_fn: Option<extern "C" fn(*mut c_void, *mut c_void)>,
171 pub drop_fn: Option<extern "C" fn(*mut c_void)>,
172
173 pub count: u64,
174}
175
176impl Clone for FeltDict {
177 fn clone(&self) -> Self {
178 let mut new_dict = FeltDict {
179 mappings: HashMap::with_capacity(self.mappings.len()),
180
181 layout: self.layout,
182 elements: if self.mappings.is_empty() {
183 null_mut()
184 } else {
185 unsafe {
186 alloc(Layout::from_size_align_unchecked(
187 self.layout.pad_to_align().size() * self.mappings.len(),
188 self.layout.align(),
189 ))
190 .cast()
191 }
192 },
193
194 dup_fn: self.dup_fn,
195 drop_fn: self.drop_fn,
196
197 count: 0,
199 };
200
201 for (&key, &old_index) in self.mappings.iter() {
202 let old_value_ptr = unsafe {
203 self.elements
204 .byte_add(self.layout.pad_to_align().size() * old_index)
205 };
206
207 let new_index = new_dict.mappings.len();
208 let new_value_ptr = unsafe {
209 new_dict
210 .elements
211 .byte_add(new_dict.layout.pad_to_align().size() * new_index)
212 };
213
214 new_dict.mappings.insert(key, new_index);
215 match self.dup_fn {
216 Some(dup_fn) => dup_fn(old_value_ptr.cast(), new_value_ptr.cast()),
217 None => unsafe {
218 ptr::copy_nonoverlapping::<u8>(
219 old_value_ptr.cast(),
220 new_value_ptr.cast(),
221 self.layout.size(),
222 )
223 },
224 }
225 }
226
227 new_dict
228 }
229}
230
231impl Drop for FeltDict {
232 fn drop(&mut self) {
233 if let Some(drop_fn) = self.drop_fn {
235 for (_, &index) in self.mappings.iter() {
236 let value_ptr = unsafe {
237 self.elements
238 .byte_add(self.layout.pad_to_align().size() * index)
239 };
240
241 drop_fn(value_ptr.cast());
242 }
243 }
244
245 if !self.elements.is_null() {
247 unsafe {
248 dealloc(
249 self.elements.cast(),
250 Layout::from_size_align_unchecked(
251 self.layout.pad_to_align().size() * self.mappings.capacity(),
252 self.layout.align(),
253 ),
254 )
255 };
256 }
257 }
258}
259
260#[no_mangle]
267pub unsafe extern "C" fn cairo_native__dict_new(
268 size: u64,
269 align: u64,
270 dup_fn: Option<extern "C" fn(*mut c_void, *mut c_void)>,
271 drop_fn: Option<extern "C" fn(*mut c_void)>,
272) -> *const FeltDict {
273 Rc::into_raw(Rc::new(FeltDict {
274 mappings: HashMap::default(),
275
276 layout: Layout::from_size_align_unchecked(size as usize, align as usize),
277 elements: null_mut(),
278
279 dup_fn,
280 drop_fn,
281
282 count: 0,
283 }))
284}
285
286#[no_mangle]
296pub unsafe extern "C" fn cairo_native__dict_drop(ptr: *const FeltDict) {
297 drop(Rc::from_raw(ptr));
298}
299
300#[no_mangle]
307pub unsafe extern "C" fn cairo_native__dict_dup(dict_ptr: *const FeltDict) -> *const FeltDict {
308 let old_dict = Rc::from_raw(dict_ptr);
309 let new_dict = Rc::clone(&old_dict);
310
311 forget(old_dict);
312 Rc::into_raw(new_dict)
313}
314
315#[no_mangle]
326pub unsafe extern "C" fn cairo_native__dict_get(
327 dict: *const FeltDict,
328 key: &[u8; 32],
329 value_ptr: *mut *mut c_void,
330) -> c_int {
331 let mut dict_rc = Rc::from_raw(dict);
332 let dict = Rc::make_mut(&mut dict_rc);
333
334 let num_mappings = dict.mappings.len();
335 let has_capacity = num_mappings != dict.mappings.capacity();
336
337 let (is_present, index) = match dict.mappings.entry(*key) {
338 Entry::Occupied(entry) => (true, *entry.get()),
339 Entry::Vacant(entry) => {
340 entry.insert(num_mappings);
341 (false, num_mappings)
342 }
343 };
344
345 if !has_capacity && !is_present {
347 dict.elements = realloc(
348 dict.elements.cast(),
349 Layout::from_size_align_unchecked(
350 dict.layout.pad_to_align().size() * dict.mappings.len(),
351 dict.layout.align(),
352 ),
353 dict.layout.pad_to_align().size() * dict.mappings.capacity(),
354 )
355 .cast();
356 }
357
358 *value_ptr = dict
359 .elements
360 .byte_add(dict.layout.pad_to_align().size() * index)
361 .cast();
362
363 dict.count += 1;
364 forget(dict_rc);
365
366 is_present as c_int
367}
368
369#[no_mangle]
376pub unsafe extern "C" fn cairo_native__dict_gas_refund(ptr: *const FeltDict) -> u64 {
377 let dict = Rc::from_raw(ptr);
378 let amount =
379 (dict.count.saturating_sub(dict.mappings.len() as u64)) * *DICT_GAS_REFUND_PER_ACCESS;
380
381 forget(dict);
382 amount
383}
384
385#[no_mangle]
396pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_from_x_nz(
397 point_ptr: &mut [[u8; 32]; 2],
398) -> bool {
399 point_ptr[0][31] &= 0x0F; let x = Felt::from_bytes_le(&point_ptr[0]);
401
402 let x2 = x * x;
405 let x3 = x2 * x;
406 let alpha_x_plus_beta = x + BETA;
407 let rhs = x3 + alpha_x_plus_beta;
408 let y = rhs
410 .sqrt()
411 .unwrap_or_else(|| (Felt::THREE * rhs).sqrt().unwrap());
412 let y = y.min(-y);
413
414 match AffinePoint::new(x, y) {
415 Ok(point) => {
416 point_ptr[1] = point.y().to_bytes_le();
417 true
418 }
419 Err(_) => false,
420 }
421}
422
423#[no_mangle]
434pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_point_try_new_nz(
435 point_ptr: &mut [[u8; 32]; 2],
436) -> bool {
437 point_ptr[0][31] &= 0x0F; point_ptr[1][31] &= 0x0F; let x = Felt::from_bytes_le(&point_ptr[0]);
441 let y = Felt::from_bytes_le(&point_ptr[1]);
442
443 match AffinePoint::new(x, y) {
444 Ok(point) => {
445 point_ptr[0] = point.x().to_bytes_le();
446 point_ptr[1] = point.y().to_bytes_le();
447 true
448 }
449 Err(_) => false,
450 }
451}
452
453#[no_mangle]
460pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_init(state_ptr: &mut [[u8; 32]; 4]) {
461 let mut rng = rand::thread_rng();
463 let (random_x, random_y) = loop {
464 let x_bytes: [u8; 31] = rng.gen();
466 let random_x = Felt::from_bytes_be_slice(&x_bytes);
467 let random_y_squared = random_x * random_x * random_x + random_x + BETA;
468 if let Some(random_y) = random_y_squared.sqrt() {
469 break (random_x, random_y);
470 }
471 };
472
473 let state = AffinePoint::new_unchecked(random_x, random_y);
475
476 state_ptr[0] = state.x().to_bytes_le();
477 state_ptr[1] = state.y().to_bytes_le();
478 state_ptr[2] = state_ptr[0];
479 state_ptr[3] = state_ptr[1];
480}
481
482#[no_mangle]
493pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_add(
494 state_ptr: &mut [[u8; 32]; 4],
495 point_ptr: &[[u8; 32]; 2],
496) {
497 state_ptr[0][31] &= 0x0F; state_ptr[1][31] &= 0x0F; let mut point_ptr = *point_ptr;
501 point_ptr[0][31] &= 0x0F; point_ptr[1][31] &= 0x0F; let mut state = ProjectivePoint::from_affine_unchecked(
506 Felt::from_bytes_le(&state_ptr[0]),
507 Felt::from_bytes_le(&state_ptr[1]),
508 );
509 let point = AffinePoint::new_unchecked(
510 Felt::from_bytes_le(&point_ptr[0]),
511 Felt::from_bytes_le(&point_ptr[1]),
512 );
513
514 state += &point;
515 let state = state.to_affine().unwrap();
516
517 state_ptr[0] = state.x().to_bytes_le();
518 state_ptr[1] = state.y().to_bytes_le();
519}
520
521#[no_mangle]
532pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_add_mul(
533 state_ptr: &mut [[u8; 32]; 4],
534 scalar_ptr: &[u8; 32],
535 point_ptr: &[[u8; 32]; 2],
536) {
537 state_ptr[0][31] &= 0x0F; state_ptr[1][31] &= 0x0F; let mut point_ptr = *point_ptr;
541 point_ptr[0][31] &= 0x0F; point_ptr[1][31] &= 0x0F; let mut scalar_ptr = *scalar_ptr;
545 scalar_ptr[31] &= 0x0F; let mut state = ProjectivePoint::from_affine_unchecked(
549 Felt::from_bytes_le(&state_ptr[0]),
550 Felt::from_bytes_le(&state_ptr[1]),
551 );
552 let point = ProjectivePoint::from_affine_unchecked(
553 Felt::from_bytes_le(&point_ptr[0]),
554 Felt::from_bytes_le(&point_ptr[1]),
555 );
556 let scalar = Felt::from_bytes_le(&scalar_ptr);
557
558 state += &point.mul(scalar);
559 let state = state.to_affine().unwrap();
560
561 state_ptr[0] = state.x().to_bytes_le();
562 state_ptr[1] = state.y().to_bytes_le();
563}
564
565#[no_mangle]
576pub unsafe extern "C" fn cairo_native__libfunc__ec__ec_state_try_finalize_nz(
577 point_ptr: &mut [[u8; 32]; 2],
578 state_ptr: &[[u8; 32]; 4],
579) -> bool {
580 let mut state_ptr = *state_ptr;
581 state_ptr[0][31] &= 0x0F; state_ptr[1][31] &= 0x0F; state_ptr[2][31] &= 0x0F; state_ptr[3][31] &= 0x0F; let state = ProjectivePoint::from_affine_unchecked(
588 Felt::from_bytes_le(&state_ptr[0]),
589 Felt::from_bytes_le(&state_ptr[1]),
590 );
591 let random = ProjectivePoint::from_affine_unchecked(
592 Felt::from_bytes_le(&state_ptr[2]),
593 Felt::from_bytes_le(&state_ptr[3]),
594 );
595
596 if state.x() == random.x() && state.y() == random.y() {
597 false
598 } else {
599 let point = &state - &random;
600 let point = point.to_affine().unwrap();
601
602 point_ptr[0] = point.x().to_bytes_le();
603 point_ptr[1] = point.y().to_bytes_le();
604
605 true
606 }
607}
608
609thread_local! {
610 static BUILTIN_COSTS: Cell<*const u64> = const {
612 Cell::new(null())
613 };
614}
615
616#[no_mangle]
619pub extern "C" fn cairo_native__set_costs_builtin(ptr: *const u64) -> *const u64 {
620 let old = BUILTIN_COSTS.get();
621 BUILTIN_COSTS.set(ptr);
622 old
623}
624
625#[no_mangle]
627pub extern "C" fn cairo_native__get_costs_builtin() -> *const u64 {
628 if BUILTIN_COSTS.get().is_null() {
629 eprintln!("BUILTIN_COSTS POINTER IS NULL!");
631 }
632 BUILTIN_COSTS.get()
633}
634
635fn format_for_debug(mut felts: IntoIter<Felt>) -> String {
639 let mut items = Vec::new();
640 while let Some(item) = format_next_item(&mut felts) {
641 items.push(item);
642 }
643 if let [item] = &items[..] {
644 if item.is_string {
645 return item.item.clone();
646 }
647 }
648 items
649 .into_iter()
650 .map(|item| {
651 if item.is_string {
652 format!("{}\n", item.item)
653 } else {
654 format!("[DEBUG]\t{}\n", item.item)
655 }
656 })
657 .join("")
658}
659
660pub struct FormattedItem {
662 item: String,
664 is_string: bool,
666}
667impl FormattedItem {
668 #[must_use]
670 pub fn get(self) -> String {
671 self.item
672 }
673 #[must_use]
675 pub fn quote_if_string(self) -> String {
676 if self.is_string {
677 format!("\"{}\"", self.item)
678 } else {
679 self.item
680 }
681 }
682}
683
684pub const BYTE_ARRAY_MAGIC: &str =
685 "46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3";
686pub const BYTES_IN_WORD: usize = 31;
687
688pub fn format_next_item<T>(values: &mut T) -> Option<FormattedItem>
691where
692 T: Iterator<Item = Felt> + Clone,
693{
694 let first_felt = values.next()?;
695
696 if first_felt == Felt::from_hex(BYTE_ARRAY_MAGIC).unwrap() {
697 if let Some(string) = try_format_string(values) {
698 return Some(FormattedItem {
699 item: string,
700 is_string: true,
701 });
702 }
703 }
704 Some(FormattedItem {
705 item: format_short_string(&first_felt),
706 is_string: false,
707 })
708}
709
710fn format_short_string(value: &Felt) -> String {
712 let hex_value = value.to_biguint();
713 match as_cairo_short_string(value) {
714 Some(as_string) => format!("{hex_value:#x} ('{as_string}')"),
715 None => format!("{hex_value:#x}"),
716 }
717}
718
719fn try_format_string<T>(values: &mut T) -> Option<String>
723where
724 T: Iterator<Item = Felt> + Clone,
725{
726 let mut cloned_values_iter = values.clone();
730
731 let num_full_words = cloned_values_iter.next()?.to_usize()?;
732 let full_words = cloned_values_iter
733 .by_ref()
734 .take(num_full_words)
735 .collect_vec();
736 let pending_word = cloned_values_iter.next()?;
737 let pending_word_len = cloned_values_iter.next()?.to_usize()?;
738
739 let full_words_string = full_words
740 .into_iter()
741 .map(|word| as_cairo_short_string_ex(&word, BYTES_IN_WORD))
742 .collect::<Option<Vec<String>>>()?
743 .join("");
744 let pending_word_string = as_cairo_short_string_ex(&pending_word, pending_word_len)?;
745
746 *values = cloned_values_iter;
748
749 Some(format!("{full_words_string}{pending_word_string}"))
750}
751
752#[must_use]
754pub fn as_cairo_short_string(value: &Felt) -> Option<String> {
755 let mut as_string = String::default();
756 let mut is_end = false;
757 for byte in value.to_biguint().to_bytes_be() {
758 if byte == 0 {
759 is_end = true;
760 } else if is_end {
761 return None;
762 } else if byte.is_ascii_graphic() || byte.is_ascii_whitespace() {
763 as_string.push(byte as char);
764 } else {
765 return None;
766 }
767 }
768 Some(as_string)
769}
770
771#[must_use]
774pub fn as_cairo_short_string_ex(value: &Felt, length: usize) -> Option<String> {
775 if length == 0 {
776 return if value.is_zero() {
777 Some(String::new())
778 } else {
779 None
780 };
781 }
782 if length > 31 {
783 return None;
785 }
786
787 let bytes = value.to_biguint().to_bytes_be();
789 let bytes_len = bytes.len();
790 if bytes_len > length {
791 return None;
793 }
794
795 let mut as_string = String::new();
796 for byte in bytes {
797 if byte == 0 {
798 as_string.push_str(r"\0");
799 } else if byte.is_ascii_graphic() || byte.is_ascii_whitespace() {
800 as_string.push(byte as char);
801 } else {
802 as_string.push_str(format!(r"\x{:02x}", byte).as_str());
803 }
804 }
805
806 let missing_nulls = length - bytes_len;
808 as_string.insert_str(0, &r"\0".repeat(missing_nulls));
809
810 Some(as_string)
811}
812
813#[cfg(test)]
814mod tests {
815 use super::*;
816 use std::{
817 env, fs,
818 io::{Read, Seek},
819 os::fd::AsRawFd,
820 };
821
822 pub fn felt252_short_str(value: &str) -> Felt {
823 let values: Vec<_> = value
824 .chars()
825 .filter_map(|c| c.is_ascii().then_some(c as u8))
826 .collect();
827
828 assert!(values.len() < 32);
829 Felt::from_bytes_be_slice(&values)
830 }
831
832 #[test]
833 fn test_debug_print() {
834 let dir = env::temp_dir();
835 fs::remove_file(dir.join("print.txt")).ok();
836 let mut file = File::create_new(dir.join("print.txt")).unwrap();
837 {
838 let fd = file.as_raw_fd();
839 let data = felt252_short_str("hello world");
840 let data = data.to_bytes_le();
841 unsafe { cairo_native__libfunc__debug__print(fd, &data, 1) };
842 }
843 file.seek(std::io::SeekFrom::Start(0)).unwrap();
844
845 let mut result = String::new();
846 file.read_to_string(&mut result).unwrap();
847
848 assert_eq!(
849 result,
850 "[DEBUG]\t0x68656c6c6f20776f726c64 ('hello world')\n"
851 );
852 }
853
854 #[test]
855 fn test_pederesen() {
856 let mut dst = [0; 32];
857 let lhs = Felt::from(1).to_bytes_le();
858 let rhs = Felt::from(3).to_bytes_le();
859
860 unsafe {
861 cairo_native__libfunc__pedersen(&mut dst, &lhs, &rhs);
862 }
863
864 assert_eq!(
865 dst,
866 [
867 84, 98, 174, 134, 3, 124, 237, 179, 166, 110, 159, 98, 170, 35, 83, 237, 130, 154,
868 236, 0, 205, 134, 200, 185, 39, 92, 0, 228, 132, 217, 130, 5
869 ]
870 )
871 }
872
873 #[test]
874 fn test_hades_permutation() {
875 let mut op0 = Felt::from(1).to_bytes_le();
876 let mut op1 = Felt::from(1).to_bytes_le();
877 let mut op2 = Felt::from(1).to_bytes_le();
878
879 unsafe {
880 cairo_native__libfunc__hades_permutation(&mut op0, &mut op1, &mut op2);
881 }
882
883 assert_eq!(
884 Felt::from_bytes_le(&op0),
885 Felt::from_hex("0x4ebdde1149fcacbb41e4fc342432a48c97994fd045f432ad234ae9279269779")
886 .unwrap()
887 );
888 assert_eq!(
889 Felt::from_bytes_le(&op1),
890 Felt::from_hex("0x7f4cec57dd08b69414f7de7dffa230fc90fa3993673c422408af05831e0cc98")
891 .unwrap()
892 );
893 assert_eq!(
894 Felt::from_bytes_le(&op2),
895 Felt::from_hex("0x5b5d00fd09caade43caffe70527fa84d5d9cd51e22c2ce115693ecbb5854d6a")
896 .unwrap()
897 );
898 }
899
900 #[test]
901 fn test_dict() {
902 let dict = unsafe {
903 cairo_native__dict_new(
904 size_of::<u64>() as u64,
905 align_of::<u64>() as u64,
906 None,
907 None,
908 )
909 };
910
911 let key = Felt::ONE.to_bytes_le();
912 let mut ptr = null_mut::<u64>();
913
914 assert_eq!(
915 unsafe { cairo_native__dict_get(dict, &key, (&raw mut ptr).cast()) },
916 0,
917 );
918 assert!(!ptr.is_null());
919 unsafe { *ptr = 24 };
920
921 assert_eq!(
922 unsafe { cairo_native__dict_get(dict, &key, (&raw mut ptr).cast()) },
923 1,
924 );
925 assert!(!ptr.is_null());
926 assert_eq!(unsafe { *ptr }, 24);
927 unsafe { *ptr = 42 };
928
929 let refund = unsafe { cairo_native__dict_gas_refund(dict) };
930 assert_eq!(refund, 4050);
931
932 let cloned_dict = unsafe { cairo_native__dict_dup(&*dict) };
933 unsafe { cairo_native__dict_drop(dict) };
934
935 assert_eq!(
936 unsafe { cairo_native__dict_get(cloned_dict, &key, (&raw mut ptr).cast()) },
937 1,
938 );
939 assert!(!ptr.is_null());
940 assert_eq!(unsafe { *ptr }, 42);
941
942 unsafe { cairo_native__dict_drop(cloned_dict) };
943 }
944
945 #[test]
946 fn test_ec__ec_point() {
947 let mut state = [
948 Felt::ZERO.to_bytes_le(),
949 Felt::ZERO.to_bytes_le(),
950 Felt::ZERO.to_bytes_le(),
951 Felt::ZERO.to_bytes_le(),
952 ];
953
954 unsafe { cairo_native__libfunc__ec__ec_state_init(&mut state) };
955
956 let points: &mut [[u8; 32]; 2] = (&mut state[..2]).try_into().unwrap();
957
958 let result = unsafe { cairo_native__libfunc__ec__ec_point_try_new_nz(points) };
959
960 assert!(result);
962 }
963
964 #[test]
965 fn test_ec__ec_point_add() {
966 let mut state = [
968 Felt::from_dec_str(
969 "874739451078007766457464989774322083649278607533249481151382481072868806602",
970 )
971 .unwrap()
972 .to_bytes_le(),
973 Felt::from_dec_str(
974 "152666792071518830868575557812948353041420400780739481342941381225525861407",
975 )
976 .unwrap()
977 .to_bytes_le(),
978 Felt::from_dec_str(
979 "874739451078007766457464989774322083649278607533249481151382481072868806602",
980 )
981 .unwrap()
982 .to_bytes_le(),
983 Felt::from_dec_str(
984 "152666792071518830868575557812948353041420400780739481342941381225525861407",
985 )
986 .unwrap()
987 .to_bytes_le(),
988 ];
989
990 let point = [
991 Felt::from_dec_str(
992 "874739451078007766457464989774322083649278607533249481151382481072868806602",
993 )
994 .unwrap()
995 .to_bytes_le(),
996 Felt::from_dec_str(
997 "152666792071518830868575557812948353041420400780739481342941381225525861407",
998 )
999 .unwrap()
1000 .to_bytes_le(),
1001 ];
1002
1003 unsafe {
1004 cairo_native__libfunc__ec__ec_state_add(&mut state, &point);
1005 };
1006
1007 assert_eq!(
1008 state[0],
1009 Felt::from_dec_str(
1010 "3324833730090626974525872402899302150520188025637965566623476530814354734325",
1011 )
1012 .unwrap()
1013 .to_bytes_le()
1014 );
1015 assert_eq!(
1016 state[1],
1017 Felt::from_dec_str(
1018 "3147007486456030910661996439995670279305852583596209647900952752170983517249",
1019 )
1020 .unwrap()
1021 .to_bytes_le()
1022 );
1023 }
1024}