cairo_vm/vm/runners/builtin_runner/
ec_op.rs

1use crate::air_private_input::{PrivateInput, PrivateInputEcOp};
2use crate::stdlib::prelude::*;
3use crate::stdlib::{cell::RefCell, collections::HashMap};
4use crate::types::instance_definitions::ec_op_instance_def::{
5    CELLS_PER_EC_OP, INPUT_CELLS_PER_EC_OP, SCALAR_HEIGHT,
6};
7use crate::types::relocatable::{MaybeRelocatable, Relocatable};
8use crate::vm::errors::memory_errors::MemoryError;
9use crate::vm::errors::runner_errors::RunnerError;
10use crate::vm::vm_memory::memory::Memory;
11use crate::vm::vm_memory::memory_segments::MemorySegmentManager;
12use crate::Felt252;
13use num_integer::{div_ceil, Integer};
14use starknet_types_core::curve::ProjectivePoint;
15
16#[derive(Debug, Clone)]
17pub struct EcOpBuiltinRunner {
18    ratio: Option<u32>,
19    pub base: usize,
20    pub(crate) stop_ptr: Option<usize>,
21    pub(crate) included: bool,
22    cache: RefCell<HashMap<Relocatable, Felt252>>,
23}
24
25impl EcOpBuiltinRunner {
26    pub(crate) fn new(ratio: Option<u32>, included: bool) -> Self {
27        EcOpBuiltinRunner {
28            base: 0,
29            ratio,
30            stop_ptr: None,
31            included,
32            cache: RefCell::new(HashMap::new()),
33        }
34    }
35    ///Returns True if the point (x, y) is on the elliptic curve defined as
36    ///y^2 = x^3 + alpha * x + beta (mod p)
37    ///or False otherwise.
38    fn point_on_curve(x: &Felt252, y: &Felt252, alpha: &Felt252, beta: &Felt252) -> bool {
39        y.pow(2_u32) == (x.pow(3_u32) + alpha * x) + beta
40    }
41
42    ///Returns the result of the EC operation P + m * Q.
43    /// where P = (p_x, p_y), Q = (q_x, q_y) are points on the elliptic curve defined as
44    /// y^2 = x^3 + alpha * x + beta (mod prime).
45    /// Mimics the operation of the AIR, so that this function fails whenever the builtin AIR
46    /// would not yield a correct result, i.e. when any part of the computation attempts to add
47    /// two points with the same x coordinate.
48    fn ec_op_impl(
49        partial_sum: (Felt252, Felt252),
50        doubled_point: (Felt252, Felt252),
51        m: &Felt252,
52        height: u32,
53    ) -> Result<(Felt252, Felt252), RunnerError> {
54        let slope = m.to_biguint();
55        let mut partial_sum_b = ProjectivePoint::from_affine(partial_sum.0, partial_sum.1)
56            .map_err(|_| RunnerError::PointNotOnCurve(Box::new(partial_sum)))?;
57        let mut doubled_point_b = ProjectivePoint::from_affine(doubled_point.0, doubled_point.1)
58            .map_err(|_| RunnerError::PointNotOnCurve(Box::new(doubled_point)))?;
59        for i in 0..(height as u64).min(slope.bits()) {
60            if partial_sum_b.x() * doubled_point_b.z() == partial_sum_b.z() * doubled_point_b.x() {
61                return Err(RunnerError::EcOpSameXCoordinate(
62                    Self::format_ec_op_error(partial_sum_b, slope, doubled_point_b)
63                        .into_boxed_str(),
64                ));
65            };
66            if slope.bit(i) {
67                partial_sum_b += &doubled_point_b;
68            }
69            doubled_point_b = doubled_point_b.double();
70        }
71        partial_sum_b
72            .to_affine()
73            .map(|p| (p.x(), p.y()))
74            .map_err(|_| RunnerError::InvalidPoint)
75    }
76
77    pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
78        self.base = segments.add().segment_index as usize // segments.add() always returns a positive index
79    }
80
81    pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
82        if self.included {
83            vec![MaybeRelocatable::from((self.base as isize, 0))]
84        } else {
85            vec![]
86        }
87    }
88
89    pub fn base(&self) -> usize {
90        self.base
91    }
92
93    pub fn ratio(&self) -> Option<u32> {
94        self.ratio
95    }
96
97    pub fn deduce_memory_cell(
98        &self,
99        address: Relocatable,
100        memory: &Memory,
101    ) -> Result<Option<MaybeRelocatable>, RunnerError> {
102        //Constant values declared here
103        const EC_POINT_INDICES: [(usize, usize); 3] = [(0, 1), (2, 3), (5, 6)];
104        const OUTPUT_INDICES: (usize, usize) = EC_POINT_INDICES[2];
105        let alpha: Felt252 = Felt252::ONE;
106        let beta_low: Felt252 = Felt252::from(0x609ad26c15c915c1f4cdfcb99cee9e89_u128);
107        let beta_high: Felt252 = Felt252::from(0x6f21413efbe40de150e596d72f7a8c5_u128);
108        let beta: Felt252 = (beta_high * (Felt252::ONE + Felt252::from(u128::MAX))) + beta_low;
109
110        let index = address.offset.mod_floor(&(CELLS_PER_EC_OP as usize));
111        //Index should be an output cell
112        if index != OUTPUT_INDICES.0 && index != OUTPUT_INDICES.1 {
113            return Ok(None);
114        }
115        let instance = Relocatable::from((address.segment_index, address.offset - index));
116        let x_addr = (instance + (&Felt252::from(INPUT_CELLS_PER_EC_OP)))
117            .map_err(|_| RunnerError::Memory(MemoryError::ExpectedInteger(Box::new(instance))))?;
118
119        if let Some(number) = self.cache.borrow().get(&address).cloned() {
120            return Ok(Some(MaybeRelocatable::Int(number)));
121        }
122
123        //All input cells should be filled, and be integer values
124        //If an input cell is not filled, return None
125        let mut input_cells = Vec::<Felt252>::with_capacity(INPUT_CELLS_PER_EC_OP as usize);
126        for i in 0..INPUT_CELLS_PER_EC_OP as usize {
127            match memory.get(&(instance + i)?) {
128                None => return Ok(None),
129                Some(addr) => {
130                    input_cells.push(match addr.as_ref() {
131                        MaybeRelocatable::Int(num) => *num,
132                        _ => {
133                            return Err(RunnerError::Memory(MemoryError::ExpectedInteger(
134                                Box::new((instance + i)?),
135                            )))
136                        }
137                    });
138                }
139            };
140        }
141        //Assert that m is under the limit defined by scalar_limit.
142        /*if input_cells[M_INDEX].as_ref() >= &self.ec_op_builtin.scalar_limit {
143            return Err(RunnerError::EcOpBuiltinScalarLimit(
144                self.ec_op_builtin.scalar_limit.clone(),
145            ));
146        }*/
147
148        // Assert that if the current address is part of a point, the point is on the curve
149        for pair in &EC_POINT_INDICES[0..2] {
150            if !EcOpBuiltinRunner::point_on_curve(
151                &input_cells[pair.0],
152                &input_cells[pair.1],
153                &alpha,
154                &beta,
155            ) {
156                return Err(RunnerError::PointNotOnCurve(Box::new((
157                    input_cells[pair.0],
158                    input_cells[pair.1],
159                ))));
160            };
161        }
162        let result = EcOpBuiltinRunner::ec_op_impl(
163            (input_cells[0].to_owned(), input_cells[1].to_owned()),
164            (input_cells[2].to_owned(), input_cells[3].to_owned()),
165            &input_cells[4],
166            SCALAR_HEIGHT,
167        )?;
168        self.cache.borrow_mut().insert(x_addr, result.0);
169        self.cache.borrow_mut().insert(
170            (x_addr + 1usize)
171                .map_err(|_| RunnerError::Memory(MemoryError::ExpectedInteger(Box::new(x_addr))))?,
172            result.1,
173        );
174        match index - INPUT_CELLS_PER_EC_OP as usize {
175            0 => Ok(Some(MaybeRelocatable::Int(result.0))),
176            _ => Ok(Some(MaybeRelocatable::Int(result.1))),
177            //Default case corresponds to 1, as there are no other possible cases
178        }
179    }
180
181    pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
182        segments
183            .get_segment_used_size(self.base())
184            .ok_or(MemoryError::MissingSegmentUsedSizes)
185    }
186
187    pub fn get_used_instances(
188        &self,
189        segments: &MemorySegmentManager,
190    ) -> Result<usize, MemoryError> {
191        let used_cells = self.get_used_cells(segments)?;
192        Ok(div_ceil(used_cells, CELLS_PER_EC_OP as usize))
193    }
194
195    pub fn format_ec_op_error(
196        p: ProjectivePoint,
197        m: num_bigint::BigUint,
198        q: ProjectivePoint,
199    ) -> String {
200        let p = p.to_affine().map(|p| (p.x(), p.y())).unwrap_or_default();
201        let q = q.to_affine().map(|q| (q.x(), q.y())).unwrap_or_default();
202        format!("Cannot apply EC operation: computation reached two points with the same x coordinate. \n
203    Attempting to compute P + m * Q where:\n
204    P = {p:?} \n
205    m = {m:?}\n
206    Q = {q:?}.")
207    }
208
209    pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
210        let mut private_inputs = vec![];
211        if let Some(segment) = memory.data.get(self.base) {
212            let segment_len = segment.len();
213            for (index, off) in (0..segment_len)
214                .step_by(CELLS_PER_EC_OP as usize)
215                .enumerate()
216            {
217                // Add the input cells of each ec_op instance to the private inputs
218                if let (Ok(p_x), Ok(p_y), Ok(q_x), Ok(q_y), Ok(m)) = (
219                    memory.get_integer((self.base as isize, off).into()),
220                    memory.get_integer((self.base as isize, off + 1).into()),
221                    memory.get_integer((self.base as isize, off + 2).into()),
222                    memory.get_integer((self.base as isize, off + 3).into()),
223                    memory.get_integer((self.base as isize, off + 4).into()),
224                ) {
225                    private_inputs.push(PrivateInput::EcOp(PrivateInputEcOp {
226                        index,
227                        p_x: *p_x,
228                        p_y: *p_y,
229                        m: *m,
230                        q_x: *q_x,
231                        q_y: *q_y,
232                    }))
233                }
234            }
235        }
236        private_inputs
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243    use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
244    use crate::types::builtin_name::BuiltinName;
245    use crate::types::layout_name::LayoutName;
246    use crate::types::program::Program;
247    use crate::utils::test_utils::*;
248    use crate::vm::errors::cairo_run_errors::CairoRunError;
249    use crate::vm::errors::vm_errors::VirtualMachineError;
250    use crate::{felt_hex, felt_str, relocatable};
251
252    use crate::vm::{
253        errors::{memory_errors::MemoryError, runner_errors::RunnerError},
254        runners::builtin_runner::BuiltinRunner,
255    };
256    use EcOpBuiltinRunner;
257
258    #[cfg(target_arch = "wasm32")]
259    use wasm_bindgen_test::*;
260
261    #[test]
262    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
263    fn get_used_instances() {
264        let builtin = EcOpBuiltinRunner::new(Some(10), true);
265
266        let mut vm = vm!();
267        vm.segments.segment_used_sizes = Some(vec![1]);
268
269        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
270    }
271
272    #[test]
273    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
274    fn final_stack() {
275        let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into();
276
277        let mut vm = vm!();
278
279        vm.segments = segments![
280            ((0, 0), (0, 0)),
281            ((0, 1), (0, 1)),
282            ((2, 0), (0, 0)),
283            ((2, 1), (0, 0))
284        ];
285
286        vm.segments.segment_used_sizes = Some(vec![0]);
287
288        let pointer = Relocatable::from((2, 2));
289
290        assert_eq!(
291            builtin.final_stack(&vm.segments, pointer).unwrap(),
292            Relocatable::from((2, 1))
293        );
294    }
295
296    #[test]
297    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
298    fn final_stack_error_stop_pointer() {
299        let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into();
300
301        let mut vm = vm!();
302
303        vm.segments = segments![
304            ((0, 0), (0, 0)),
305            ((0, 1), (0, 1)),
306            ((2, 0), (0, 0)),
307            ((2, 1), (0, 0))
308        ];
309
310        vm.segments.segment_used_sizes = Some(vec![994]);
311
312        let pointer = Relocatable::from((2, 2));
313
314        assert_eq!(
315            builtin.final_stack(&vm.segments, pointer),
316            Err(RunnerError::InvalidStopPointer(Box::new((
317                BuiltinName::ec_op,
318                relocatable!(0, 994),
319                relocatable!(0, 0)
320            ))))
321        );
322    }
323
324    #[test]
325    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
326    fn final_stack_error_when_notincluded() {
327        let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), false).into();
328
329        let mut vm = vm!();
330
331        vm.segments = segments![
332            ((0, 0), (0, 0)),
333            ((0, 1), (0, 1)),
334            ((2, 0), (0, 0)),
335            ((2, 1), (0, 0))
336        ];
337
338        vm.segments.segment_used_sizes = Some(vec![0]);
339
340        let pointer = Relocatable::from((2, 2));
341
342        assert_eq!(
343            builtin.final_stack(&vm.segments, pointer).unwrap(),
344            Relocatable::from((2, 2))
345        );
346    }
347
348    #[test]
349    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
350    fn final_stack_error_non_relocatable() {
351        let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into();
352
353        let mut vm = vm!();
354
355        vm.segments = segments![
356            ((0, 0), (0, 0)),
357            ((0, 1), (0, 1)),
358            ((2, 0), (0, 0)),
359            ((2, 1), 2)
360        ];
361
362        vm.segments.segment_used_sizes = Some(vec![0]);
363
364        let pointer = Relocatable::from((2, 2));
365
366        assert_eq!(
367            builtin.final_stack(&vm.segments, pointer),
368            Err(RunnerError::NoStopPointer(Box::new(BuiltinName::ec_op)))
369        );
370    }
371
372    #[test]
373    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
374    fn get_used_cells_and_allocated_size_test() {
375        let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into();
376
377        let program = program!(
378            builtins = vec![BuiltinName::pedersen],
379            data = vec_data!(
380                (4612671182993129469_i64),
381                (5189976364521848832_i64),
382                (18446744073709551615_i128),
383                (5199546496550207487_i64),
384                (4612389712311386111_i64),
385                (5198983563776393216_i64),
386                (2),
387                (2345108766317314046_i64),
388                (5191102247248822272_i64),
389                (5189976364521848832_i64),
390                (7),
391                (1226245742482522112_i64),
392                ((
393                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
394                    10
395                )),
396                (2345108766317314046_i64)
397            ),
398            main = Some(8),
399        );
400        let mut cairo_runner = cairo_runner!(program);
401
402        cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
403
404        let mut hint_processor = BuiltinHintProcessor::new_empty();
405
406        let address = cairo_runner.initialize(false).unwrap();
407
408        cairo_runner
409            .run_until_pc(address, &mut hint_processor)
410            .unwrap();
411
412        assert_eq!(
413            builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
414            Ok((0, 7))
415        );
416    }
417
418    #[test]
419    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
420    fn get_allocated_memory_units() {
421        let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into();
422
423        let program = program!(
424            builtins = vec![BuiltinName::ec_op],
425            data = vec_data!(
426                (4612671182993129469_i64),
427                (5189976364521848832_i64),
428                (18446744073709551615_i128),
429                (5199546496550207487_i64),
430                (4612389712311386111_i64),
431                (5198983563776393216_i64),
432                (2),
433                (2345108766317314046_i64),
434                (5191102247248822272_i64),
435                (5189976364521848832_i64),
436                (7),
437                (1226245742482522112_i64),
438                ((
439                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
440                    10
441                )),
442                (2345108766317314046_i64)
443            ),
444            main = Some(8),
445        );
446
447        let mut cairo_runner = cairo_runner!(program);
448
449        let mut hint_processor = BuiltinHintProcessor::new_empty();
450
451        let address = cairo_runner.initialize(false).unwrap();
452
453        cairo_runner
454            .run_until_pc(address, &mut hint_processor)
455            .unwrap();
456
457        assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(7));
458    }
459
460    #[test]
461    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
462    fn point_is_on_curve_a() {
463        let x = felt_hex!("0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca");
464        let y = felt_hex!("0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f");
465        let alpha = Felt252::ONE;
466        let beta = felt_hex!("0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89");
467        assert!(EcOpBuiltinRunner::point_on_curve(&x, &y, &alpha, &beta));
468    }
469
470    #[test]
471    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
472    fn point_is_on_curve_b() {
473        let x = felt_hex!("0x6f0a1ddaf19c44781c8946db396f494a10ffab183c2d8cf6c4cd321a8d87fd9");
474        let y = felt_hex!("0x4afa52a9ef8c023d3385fddb6e1d78d57b0693b9b02d45d0f939b526d474c39");
475        let alpha = Felt252::ONE;
476        let beta = felt_hex!("0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89");
477        assert!(EcOpBuiltinRunner::point_on_curve(&x, &y, &alpha, &beta));
478    }
479
480    #[test]
481    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
482    fn point_is_not_on_curve_a() {
483        let x = felt_hex!("0x1ef15c1a2162fb0d2e5d83196a6fb0509632fab5d746f0c3d723d8bc943cfca");
484        let y = felt_hex!("0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f");
485        let alpha = Felt252::ONE;
486        let beta = felt_hex!("0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89");
487        assert!(!EcOpBuiltinRunner::point_on_curve(&x, &y, &alpha, &beta));
488    }
489
490    #[test]
491    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
492    fn point_is_not_on_curve_b() {
493        let x = felt_hex!("0x6f0a1ddaeb88837dcc8ac9a48f894deed706bc3e8998e63535e2c91a8d87fd9");
494        let y = felt_hex!("0x4afa52a9ef8c023d33ea3865fb4e0e49abfc50dd50ccea867539b526d474c39");
495        let alpha = Felt252::ONE;
496        let beta = felt_hex!("0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89");
497        assert!(!EcOpBuiltinRunner::point_on_curve(&x, &y, &alpha, &beta));
498    }
499
500    #[test]
501    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
502    fn compute_ec_op_impl_valid_a() {
503        let partial_sum = (
504            felt_hex!("0x6f0a1ddaf19c44781c8946db396f494a10ffab183c2d8cf6c4cd321a8d87fd9"),
505            felt_hex!("0x4afa52a9ef8c023d3385fddb6e1d78d57b0693b9b02d45d0f939b526d474c39"),
506        );
507        let doubled_point = (
508            felt_hex!("0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca"),
509            felt_hex!("0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f"),
510        );
511        let m = Felt252::from(34);
512        let height = 256;
513        let result = EcOpBuiltinRunner::ec_op_impl(partial_sum, doubled_point, &m, height);
514        assert_eq!(
515            result,
516            Ok((
517                felt_str!(
518                    "1977874238339000383330315148209250828062304908491266318460063803060754089297"
519                ),
520                felt_str!(
521                    "2969386888251099938335087541720168257053975603483053253007176033556822156706"
522                )
523            ))
524        );
525    }
526
527    #[test]
528    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
529    fn compute_ec_op_impl_valid_b() {
530        let partial_sum = (
531            felt_hex!("0x68caa9509b7c2e90b4d92661cbf7c465471c1e8598c5f989691eef6653e0f38"),
532            felt_hex!("0x79a8673f498531002fc549e06ff2010ffc0c191cceb7da5532acb95cdcb591"),
533        );
534        let doubled_point = (
535            felt_hex!("0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca"),
536            felt_hex!("0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f"),
537        );
538        let m = Felt252::from(34);
539        let height = 256;
540        let result = EcOpBuiltinRunner::ec_op_impl(partial_sum, doubled_point, &m, height);
541        assert_eq!(
542            result,
543            Ok((
544                felt_str!(
545                    "2778063437308421278851140253538604815869848682781135193774472480292420096757"
546                ),
547                felt_str!(
548                    "3598390311618116577316045819420613574162151407434885460365915347732568210029"
549                )
550            ))
551        );
552    }
553
554    #[test]
555    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
556    fn compute_ec_op_invalid_same_x_coordinate() {
557        let partial_sum = (
558            felt_hex!("0x6f0a1ddaf19c44781c8946db396f494a10ffab183c2d8cf6c4cd321a8d87fd9"),
559            felt_hex!("0x4afa52a9ef8c023d3385fddb6e1d78d57b0693b9b02d45d0f939b526d474c39"),
560        );
561        let doubled_point = (
562            felt_hex!("0x6f0a1ddaf19c44781c8946db396f494a10ffab183c2d8cf6c4cd321a8d87fd9"),
563            felt_hex!("0x4afa52a9ef8c023d3385fddb6e1d78d57b0693b9b02d45d0f939b526d474c39"),
564        );
565        let m = Felt252::from(34);
566        let height = 256;
567        let result = EcOpBuiltinRunner::ec_op_impl(partial_sum, doubled_point, &m, height);
568        assert_eq!(
569            result,
570            Err(RunnerError::EcOpSameXCoordinate(
571                EcOpBuiltinRunner::format_ec_op_error(
572                    ProjectivePoint::from_affine(partial_sum.0, partial_sum.1).unwrap(),
573                    m.to_biguint(),
574                    ProjectivePoint::from_affine(doubled_point.0, doubled_point.1).unwrap(),
575                )
576                .into_boxed_str()
577            ))
578        );
579    }
580
581    #[test]
582    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
583    /* Data taken from this program execution:
584       %builtins output ec_op
585       from starkware.cairo.common.cairo_builtins import EcOpBuiltin
586       from starkware.cairo.common.serialize import serialize_word
587       from starkware.cairo.common.ec_point import EcPoint
588       from starkware.cairo.common.ec import ec_op
589
590       func main{output_ptr: felt*, ec_op_ptr: EcOpBuiltin*}():
591           let x: EcPoint = EcPoint(2089986280348253421170679821480865132823066470938446095505822317253594081284, 1713931329540660377023406109199410414810705867260802078187082345529207694986)
592
593           let y: EcPoint = EcPoint(874739451078007766457464989774322083649278607533249481151382481072868806602,152666792071518830868575557812948353041420400780739481342941381225525861407)
594           let z: EcPoint = ec_op(x,34, y)
595           serialize_word(z.x)
596           return()
597           end
598    */
599    fn deduce_memory_cell_ec_op_for_preset_memory_valid() {
600        let memory = memory![
601            (
602                (3, 0),
603                (
604                    "0x68caa9509b7c2e90b4d92661cbf7c465471c1e8598c5f989691eef6653e0f38",
605                    16
606                )
607            ),
608            (
609                (3, 1),
610                (
611                    "0x79a8673f498531002fc549e06ff2010ffc0c191cceb7da5532acb95cdcb591",
612                    16
613                )
614            ),
615            (
616                (3, 2),
617                (
618                    "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
619                    16
620                )
621            ),
622            (
623                (3, 3),
624                (
625                    "0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f",
626                    16
627                )
628            ),
629            ((3, 4), 34),
630            (
631                (3, 5),
632                (
633                    "2778063437308421278851140253538604815869848682781135193774472480292420096757",
634                    10
635                )
636            )
637        ];
638        let builtin = EcOpBuiltinRunner::new(Some(256), true);
639
640        let result = builtin.deduce_memory_cell(Relocatable::from((3, 6)), &memory);
641        assert_eq!(
642            result,
643            Ok(Some(MaybeRelocatable::from(felt_str!(
644                "3598390311618116577316045819420613574162151407434885460365915347732568210029"
645            ))))
646        );
647    }
648
649    #[test]
650    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
651    fn deduce_memory_cell_ec_op_for_preset_memory_unfilled_input_cells() {
652        let memory = memory![
653            (
654                (3, 1),
655                (
656                    "0x79a8673f498531002fc549e06ff2010ffc0c191cceb7da5532acb95cdcb591",
657                    16
658                )
659            ),
660            (
661                (3, 2),
662                (
663                    "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
664                    16
665                )
666            ),
667            (
668                (3, 3),
669                (
670                    "0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f",
671                    16
672                )
673            ),
674            ((3, 4), 34),
675            (
676                (3, 5),
677                (
678                    "2778063437308421278851140253538604815869848682781135193774472480292420096757",
679                    10
680                )
681            )
682        ];
683
684        let builtin = EcOpBuiltinRunner::new(Some(256), true);
685        let result = builtin.deduce_memory_cell(Relocatable::from((3, 6)), &memory);
686        assert_eq!(result, Ok(None));
687    }
688
689    #[test]
690    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
691    fn deduce_memory_cell_ec_op_for_preset_memory_addr_not_an_output_cell() {
692        let memory = memory![
693            (
694                (3, 0),
695                (
696                    "0x68caa9509b7c2e90b4d92661cbf7c465471c1e8598c5f989691eef6653e0f38",
697                    16
698                )
699            ),
700            (
701                (3, 1),
702                (
703                    "0x79a8673f498531002fc549e06ff2010ffc0c191cceb7da5532acb95cdcb591",
704                    16
705                )
706            ),
707            (
708                (3, 2),
709                (
710                    "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
711                    16
712                )
713            ),
714            (
715                (3, 3),
716                (
717                    "0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f",
718                    16
719                )
720            ),
721            ((3, 4), 34),
722            (
723                (3, 5),
724                (
725                    "2778063437308421278851140253538604815869848682781135193774472480292420096757",
726                    10
727                )
728            )
729        ];
730        let builtin = EcOpBuiltinRunner::new(Some(256), true);
731
732        let result = builtin.deduce_memory_cell(Relocatable::from((3, 3)), &memory);
733        assert_eq!(result, Ok(None));
734    }
735
736    #[test]
737    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
738    fn deduce_memory_cell_ec_op_for_preset_memory_non_integer_input() {
739        let memory = memory![
740            (
741                (3, 0),
742                (
743                    "0x68caa9509b7c2e90b4d92661cbf7c465471c1e8598c5f989691eef6653e0f38",
744                    16
745                )
746            ),
747            (
748                (3, 1),
749                (
750                    "0x79a8673f498531002fc549e06ff2010ffc0c191cceb7da5532acb95cdcb591",
751                    16
752                )
753            ),
754            (
755                (3, 2),
756                (
757                    "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca",
758                    16
759                )
760            ),
761            ((3, 3), (1, 2)),
762            ((3, 4), 34),
763            (
764                (3, 5),
765                (
766                    "2778063437308421278851140253538604815869848682781135193774472480292420096757",
767                    10
768                )
769            )
770        ];
771        let builtin = EcOpBuiltinRunner::new(Some(256), true);
772
773        assert_eq!(
774            builtin.deduce_memory_cell(Relocatable::from((3, 6)), &memory),
775            Err(RunnerError::Memory(MemoryError::ExpectedInteger(Box::new(
776                Relocatable::from((3, 3))
777            ))))
778        );
779    }
780
781    #[test]
782    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
783    fn get_used_cells_missing_segment_used_sizes() {
784        let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true));
785        let vm = vm!();
786
787        assert_eq!(
788            builtin.get_used_cells(&vm.segments),
789            Err(MemoryError::MissingSegmentUsedSizes)
790        );
791    }
792
793    #[test]
794    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
795    fn get_used_cells_empty() {
796        let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true));
797        let mut vm = vm!();
798
799        vm.segments.segment_used_sizes = Some(vec![0]);
800        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
801    }
802
803    #[test]
804    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
805    fn get_used_cells() {
806        let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true));
807        let mut vm = vm!();
808
809        vm.segments.segment_used_sizes = Some(vec![4]);
810        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
811    }
812
813    #[test]
814    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
815    fn initial_stackincluded_test() {
816        let ec_op_builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into();
817        assert_eq!(ec_op_builtin.initial_stack(), vec![mayberelocatable!(0, 0)])
818    }
819
820    #[test]
821    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
822    fn initial_stack_notincluded_test() {
823        let ec_op_builtin = EcOpBuiltinRunner::new(Some(256), false);
824        assert_eq!(ec_op_builtin.initial_stack(), Vec::new())
825    }
826
827    #[test]
828    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
829    fn catch_point_same_x() {
830        let program =
831            include_bytes!("../../../../../cairo_programs/bad_programs/ec_op_same_x.json");
832        let cairo_run_config = crate::cairo_run::CairoRunConfig {
833            layout: LayoutName::all_cairo,
834            ..crate::cairo_run::CairoRunConfig::default()
835        };
836        let result = crate::cairo_run::cairo_run(
837            program,
838            &cairo_run_config,
839            &mut BuiltinHintProcessor::new_empty(),
840        );
841        assert!(result.is_err());
842        // We need to check this way because CairoRunError doens't implement PartialEq
843        match result {
844            Err(CairoRunError::VirtualMachine(VirtualMachineError::RunnerError(
845                RunnerError::EcOpSameXCoordinate(_),
846            ))) => {}
847            Err(_) => panic!("Wrong error returned, expected RunnerError::EcOpSameXCoordinate"),
848            Ok(_) => panic!("Expected run to fail"),
849        }
850    }
851
852    #[test]
853    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
854    fn catch_point_not_in_curve() {
855        let program =
856            include_bytes!("../../../../../cairo_programs/bad_programs/ec_op_not_in_curve.json");
857        let cairo_run_config = crate::cairo_run::CairoRunConfig {
858            layout: LayoutName::all_cairo,
859            ..crate::cairo_run::CairoRunConfig::default()
860        };
861        let result = crate::cairo_run::cairo_run(
862            program,
863            &cairo_run_config,
864            &mut BuiltinHintProcessor::new_empty(),
865        );
866        assert!(result.is_err());
867
868        // We need to check this way because CairoRunError doens't implement PartialEq
869        match result {
870            Err(CairoRunError::VirtualMachine(VirtualMachineError::RunnerError(
871                RunnerError::PointNotOnCurve(_),
872            ))) => {}
873            Err(_) => panic!("Wrong error returned, expected RunnerError::EcOpSameXCoordinate"),
874            Ok(_) => panic!("Expected run to fail"),
875        }
876    }
877
878    #[test]
879    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
880    fn get_air_private_input() {
881        let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into();
882
883        let segments = segments![
884            ((0, 0), 0),
885            ((0, 1), 1),
886            ((0, 2), 2),
887            ((0, 3), 3),
888            ((0, 4), 4)
889        ];
890        assert_eq!(
891            builtin.air_private_input(&segments),
892            (vec![PrivateInput::EcOp(PrivateInputEcOp {
893                index: 0,
894                p_x: 0.into(),
895                p_y: 1.into(),
896                m: 4.into(),
897                q_x: 2.into(),
898                q_y: 3.into(),
899            })])
900        );
901    }
902}