cairo_vm/vm/runners/builtin_runner/
signature.rs

1use crate::air_private_input::{PrivateInput, PrivateInputSignature, SignatureInput};
2use crate::math_utils::div_mod;
3use crate::stdlib::{
4    cell::RefCell,
5    collections::{BTreeMap, HashMap},
6    prelude::*,
7    rc::Rc,
8};
9
10use crate::types::builtin_name::BuiltinName;
11use crate::types::instance_definitions::ecdsa_instance_def::CELLS_PER_SIGNATURE;
12use crate::vm::errors::runner_errors::RunnerError;
13use crate::vm::runners::cairo_pie::BuiltinAdditionalData;
14use crate::Felt252;
15use crate::{
16    types::relocatable::{MaybeRelocatable, Relocatable},
17    vm::{
18        errors::memory_errors::MemoryError,
19        vm_memory::{
20            memory::{Memory, ValidationRule},
21            memory_segments::MemorySegmentManager,
22        },
23    },
24};
25use lazy_static::lazy_static;
26use num_bigint::{BigInt, Sign};
27use num_integer::div_ceil;
28use num_traits::{Num, One};
29use starknet_crypto::{verify, Signature};
30
31lazy_static! {
32    static ref EC_ORDER: BigInt = BigInt::from_str_radix(
33        "3618502788666131213697322783095070105526743751716087489154079457884512865583",
34        10
35    )
36    .unwrap();
37}
38
39#[derive(Debug, Clone)]
40pub struct SignatureBuiltinRunner {
41    pub(crate) included: bool,
42    ratio: Option<u32>,
43    pub base: usize,
44    pub(crate) stop_ptr: Option<usize>,
45    pub signatures: Rc<RefCell<HashMap<Relocatable, Signature>>>,
46}
47
48impl SignatureBuiltinRunner {
49    pub fn new(ratio: Option<u32>, included: bool) -> Self {
50        SignatureBuiltinRunner {
51            base: 0,
52            included,
53            ratio,
54            stop_ptr: None,
55            signatures: Rc::new(RefCell::new(HashMap::new())),
56        }
57    }
58
59    pub fn add_signature(
60        &mut self,
61        relocatable: Relocatable,
62        (r, s): &(Felt252, Felt252),
63    ) -> Result<(), MemoryError> {
64        let r_be_bytes = r.to_bytes_be();
65        let s_be_bytes = s.to_bytes_be();
66        let (r_felt, s_felt) = (
67            Felt252::from_bytes_be(&r_be_bytes),
68            Felt252::from_bytes_be(&s_be_bytes),
69        );
70
71        let signature = Signature {
72            r: r_felt,
73            s: s_felt,
74        };
75
76        self.signatures
77            .borrow_mut()
78            .entry(relocatable)
79            .or_insert(signature);
80
81        Ok(())
82    }
83}
84
85impl SignatureBuiltinRunner {
86    pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
87        self.base = segments.add().segment_index as usize // segments.add() always returns a positive index
88    }
89
90    pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
91        if self.included {
92            vec![MaybeRelocatable::from((self.base as isize, 0))]
93        } else {
94            vec![]
95        }
96    }
97
98    pub fn base(&self) -> usize {
99        self.base
100    }
101    pub fn add_validation_rule(&self, memory: &mut Memory) {
102        let cells_per_instance = CELLS_PER_SIGNATURE;
103        let signatures = Rc::clone(&self.signatures);
104        let rule: ValidationRule = ValidationRule(Box::new(
105            move |memory: &Memory, addr: Relocatable| -> Result<Vec<Relocatable>, MemoryError> {
106                let cell_index = addr.offset % cells_per_instance as usize;
107
108                let (pubkey_addr, message_addr) = match cell_index {
109                    0 => (addr, (addr + 1)?),
110                    1 => match addr - 1 {
111                        Ok(prev_addr) => (prev_addr, addr),
112                        Err(_) => return Ok(vec![]),
113                    },
114                    _ => return Ok(vec![]),
115                };
116
117                let pubkey = match memory.get_integer(pubkey_addr) {
118                    Ok(num) => num,
119                    Err(_) if cell_index == 1 => return Ok(vec![]),
120                    _ => return Err(MemoryError::PubKeyNonInt(Box::new(pubkey_addr))),
121                };
122
123                let msg = match memory.get_integer(message_addr) {
124                    Ok(num) => num,
125                    Err(_) if cell_index == 0 => return Ok(vec![]),
126                    _ => return Err(MemoryError::MsgNonInt(Box::new(message_addr))),
127                };
128
129                let signatures_map = signatures.borrow();
130                let signature = signatures_map
131                    .get(&pubkey_addr)
132                    .ok_or_else(|| MemoryError::SignatureNotFound(Box::new(pubkey_addr)))?;
133
134                let public_key = Felt252::from_bytes_be(&pubkey.to_bytes_be());
135                let (r, s) = (signature.r, signature.s);
136                let message = Felt252::from_bytes_be(&msg.to_bytes_be());
137                match verify(&public_key, &message, &r, &s) {
138                    Ok(true) => Ok(vec![]),
139                    _ => Err(MemoryError::InvalidSignature(Box::new((
140                        format!("({}, {})", signature.r, signature.s),
141                        pubkey.into_owned(),
142                        msg.into_owned(),
143                    )))),
144                }
145            },
146        ));
147        memory.add_validation_rule(self.base, rule);
148    }
149
150    pub fn ratio(&self) -> Option<u32> {
151        self.ratio
152    }
153
154    pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
155        segments
156            .get_segment_used_size(self.base)
157            .ok_or(MemoryError::MissingSegmentUsedSizes)
158    }
159
160    pub fn get_used_instances(
161        &self,
162        segments: &MemorySegmentManager,
163    ) -> Result<usize, MemoryError> {
164        let used_cells = self.get_used_cells(segments)?;
165        Ok(div_ceil(used_cells, CELLS_PER_SIGNATURE as usize))
166    }
167
168    pub fn get_additional_data(&self) -> BuiltinAdditionalData {
169        // Convert signatures to Felt tuple
170        let signatures: BTreeMap<Relocatable, (Felt252, Felt252)> = self
171            .signatures
172            .borrow()
173            .iter()
174            .map(|(k, v)| {
175                (
176                    *k,
177                    (
178                        Felt252::from_bytes_be(&v.r.to_bytes_be()),
179                        Felt252::from_bytes_be(&v.s.to_bytes_be()),
180                    ),
181                )
182            })
183            .collect();
184        BuiltinAdditionalData::Signature(signatures)
185    }
186
187    pub fn extend_additional_data(
188        &mut self,
189        additional_data: &BuiltinAdditionalData,
190    ) -> Result<(), RunnerError> {
191        let additional_data = match additional_data {
192            BuiltinAdditionalData::Signature(d) => d,
193            BuiltinAdditionalData::Empty(_) => return Ok(()),
194            _ => return Err(RunnerError::InvalidAdditionalData(BuiltinName::ecdsa)),
195        };
196        for (addr, (r, s)) in additional_data {
197            if addr.segment_index != self.base as isize {
198                return Err(RunnerError::InvalidAdditionalData(BuiltinName::ecdsa));
199            }
200            self.signatures.borrow_mut().insert(
201                *addr,
202                Signature {
203                    r: Felt252::from_bytes_be(&r.to_bytes_be()),
204                    s: Felt252::from_bytes_be(&s.to_bytes_be()),
205                },
206            );
207        }
208        Ok(())
209    }
210
211    pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
212        let mut private_inputs = vec![];
213
214        // Collect and sort the signatures by their index before the loop
215        let binding = self.signatures.borrow();
216        let mut sorted_signatures: Vec<_> = binding.iter().collect();
217        sorted_signatures.sort_by_key(|(addr, _)| {
218            addr.offset
219                .checked_div(CELLS_PER_SIGNATURE as usize)
220                .unwrap_or_default()
221        });
222
223        for (addr, signature) in sorted_signatures {
224            if let (Ok(pubkey), Some(msg)) = (
225                memory.get_integer(*addr),
226                (*addr + 1_usize)
227                    .ok()
228                    .and_then(|addr| memory.get_integer(addr).ok()),
229            ) {
230                private_inputs.push(PrivateInput::Signature(PrivateInputSignature {
231                    index: addr
232                        .offset
233                        .checked_div(CELLS_PER_SIGNATURE as usize)
234                        .unwrap_or_default(),
235                    pubkey: *pubkey,
236                    msg: *msg,
237                    signature_input: SignatureInput {
238                        r: Felt252::from_bytes_be(&signature.r.to_bytes_be()),
239                        w: Felt252::from(
240                            &div_mod(
241                                &BigInt::one(),
242                                &BigInt::from_bytes_be(Sign::Plus, &signature.s.to_bytes_be()),
243                                &EC_ORDER,
244                            )
245                            .unwrap_or_default(),
246                        ),
247                    },
248                }))
249            }
250        }
251        private_inputs
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use crate::{
259        relocatable,
260        types::builtin_name::BuiltinName,
261        utils::test_utils::*,
262        vm::{
263            errors::{
264                memory_errors::{InsufficientAllocatedCellsError, MemoryError},
265                runner_errors::RunnerError,
266            },
267            runners::builtin_runner::BuiltinRunner,
268            vm_memory::{memory::Memory, memory_segments::MemorySegmentManager},
269        },
270    };
271
272    use crate::felt_str;
273    #[cfg(target_arch = "wasm32")]
274    use wasm_bindgen_test::*;
275
276    #[test]
277    fn get_used_cells_and_allocated_size_valid() {
278        let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(10), true).into();
279        let mut vm = vm!();
280        vm.current_step = 110;
281        vm.segments.segment_used_sizes = Some(vec![1]);
282        assert_eq!(builtin.get_used_cells_and_allocated_size(&vm), Ok((1, 22)));
283    }
284
285    #[test]
286    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
287    fn initialize_segments_for_ecdsa() {
288        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
289        let mut segments = MemorySegmentManager::new();
290        builtin.initialize_segments(&mut segments);
291        assert_eq!(builtin.base, 0);
292    }
293
294    #[test]
295    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
296    fn get_used_instances() {
297        let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
298
299        let mut vm = vm!();
300        vm.segments.segment_used_sizes = Some(vec![1]);
301
302        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
303    }
304
305    #[test]
306    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
307    fn final_stack() {
308        let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
309
310        let mut vm = vm!();
311
312        vm.segments = segments![
313            ((0, 0), (0, 0)),
314            ((0, 1), (0, 1)),
315            ((2, 0), (0, 0)),
316            ((2, 1), (0, 0))
317        ];
318
319        vm.segments.segment_used_sizes = Some(vec![0]);
320
321        let pointer = Relocatable::from((2, 2));
322
323        assert_eq!(
324            builtin.final_stack(&vm.segments, pointer).unwrap(),
325            Relocatable::from((2, 1))
326        );
327    }
328
329    #[test]
330    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
331    fn final_stack_error_stop_pointer() {
332        let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
333
334        let mut vm = vm!();
335
336        vm.segments = segments![
337            ((0, 0), (0, 0)),
338            ((0, 1), (0, 1)),
339            ((2, 0), (0, 0)),
340            ((2, 1), (0, 0))
341        ];
342
343        vm.segments.segment_used_sizes = Some(vec![998]);
344
345        let pointer = Relocatable::from((2, 2));
346
347        assert_eq!(
348            builtin.final_stack(&vm.segments, pointer),
349            Err(RunnerError::InvalidStopPointer(Box::new((
350                BuiltinName::ecdsa,
351                relocatable!(0, 998),
352                relocatable!(0, 0)
353            ))))
354        );
355    }
356
357    #[test]
358    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
359    fn final_stack_error_non_relocatable() {
360        let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
361
362        let mut vm = vm!();
363
364        vm.segments = segments![
365            ((0, 0), (0, 0)),
366            ((0, 1), (0, 1)),
367            ((2, 0), (0, 0)),
368            ((2, 1), 2)
369        ];
370
371        vm.segments.segment_used_sizes = Some(vec![0]);
372
373        let pointer = Relocatable::from((2, 2));
374
375        assert_eq!(
376            builtin.final_stack(&vm.segments, pointer),
377            Err(RunnerError::NoStopPointer(Box::new(BuiltinName::ecdsa)))
378        );
379    }
380
381    #[test]
382    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
383    fn get_used_cells_missing_segment_used_sizes() {
384        let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), true));
385        let vm = vm!();
386
387        assert_eq!(
388            builtin.get_used_cells(&vm.segments),
389            Err(MemoryError::MissingSegmentUsedSizes)
390        );
391    }
392
393    #[test]
394    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
395    fn get_used_cells_empty() {
396        let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), true));
397        let mut vm = vm!();
398
399        vm.segments.segment_used_sizes = Some(vec![0]);
400        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
401    }
402
403    #[test]
404    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
405    fn get_used_cells() {
406        let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), true));
407        let mut vm = vm!();
408
409        vm.segments.segment_used_sizes = Some(vec![4]);
410        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
411    }
412
413    #[test]
414    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
415    fn get_initial_stack_for_range_check_with_base() {
416        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
417        builtin.base = 1;
418        let initial_stack = builtin.initial_stack();
419        assert_eq!(
420            initial_stack[0].clone(),
421            MaybeRelocatable::RelocatableValue((builtin.base() as isize, 0).into())
422        );
423        assert_eq!(initial_stack.len(), 1);
424    }
425
426    #[test]
427    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
428    fn initial_stack_not_included_test() {
429        let ecdsa_builtin = SignatureBuiltinRunner::new(Some(512), false);
430        assert_eq!(ecdsa_builtin.initial_stack(), Vec::new())
431    }
432
433    #[test]
434    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
435    fn deduce_memory_cell_test() {
436        let memory = Memory::new();
437        let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
438        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
439        assert_eq!(result, Ok(None));
440    }
441
442    #[test]
443    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
444    fn test_ratio() {
445        let builtin = SignatureBuiltinRunner::new(Some(512), true);
446        assert_eq!(builtin.ratio(), Some(512));
447    }
448
449    #[test]
450    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
451    fn test_base() {
452        let builtin = SignatureBuiltinRunner::new(Some(512), true);
453        assert_eq!(builtin.base(), 0);
454    }
455
456    #[test]
457    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
458    fn get_allocated_memory_min_step_not_reached() {
459        let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
460        let mut vm = vm!();
461        vm.current_step = 500;
462        assert_eq!(
463            builtin.get_allocated_memory_units(&vm),
464            Err(MemoryError::InsufficientAllocatedCells(
465                InsufficientAllocatedCellsError::MinStepNotReached(Box::new((
466                    512,
467                    BuiltinName::ecdsa
468                )))
469            ))
470        )
471    }
472
473    #[test]
474    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
475    fn get_used_cells_and_allocated_size_insufficient_allocated() {
476        let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
477        let mut vm = vm!();
478        vm.segments.segment_used_sizes = Some(vec![50]);
479        vm.current_step = 512;
480        assert_eq!(
481            builtin.get_used_cells_and_allocated_size(&vm),
482            Err(MemoryError::InsufficientAllocatedCells(
483                InsufficientAllocatedCellsError::BuiltinCells(Box::new((
484                    BuiltinName::ecdsa,
485                    50,
486                    2
487                )))
488            ))
489        )
490    }
491
492    #[test]
493    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
494    fn final_stack_invalid_stop_pointer() {
495        let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
496        let mut vm = vm!();
497        vm.segments = segments![((0, 0), (1, 0))];
498        assert_eq!(
499            builtin.final_stack(&vm.segments, (0, 1).into()),
500            Err(RunnerError::InvalidStopPointerIndex(Box::new((
501                BuiltinName::ecdsa,
502                relocatable!(1, 0),
503                0
504            ))))
505        )
506    }
507
508    #[test]
509    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
510    fn final_stack_no_used_instances() {
511        let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into();
512        let mut vm = vm!();
513        vm.segments = segments![((0, 0), (0, 0))];
514        assert_eq!(
515            builtin.final_stack(&vm.segments, (0, 1).into()),
516            Err(RunnerError::Memory(MemoryError::MissingSegmentUsedSizes))
517        )
518    }
519
520    #[test]
521    fn get_additional_data() {
522        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
523        let signatures = HashMap::from([(
524            Relocatable::from((4, 0)),
525            Signature {
526                r: Felt252::from_dec_str("45678").unwrap(),
527                s: Felt252::from_dec_str("1239").unwrap(),
528            },
529        )]);
530        builtin.signatures = Rc::new(RefCell::new(signatures));
531        let signatures = BTreeMap::from([(
532            Relocatable::from((4, 0)),
533            (felt_str!("45678"), felt_str!("1239")),
534        )]);
535        assert_eq!(
536            builtin.get_additional_data(),
537            BuiltinAdditionalData::Signature(signatures)
538        )
539    }
540
541    #[test]
542    fn get_and_extend_additional_data() {
543        let mut builtin_a = SignatureBuiltinRunner::new(Some(512), true);
544        let signatures = HashMap::from([(
545            Relocatable::from((0, 0)),
546            Signature {
547                r: Felt252::from_dec_str("45678").unwrap(),
548                s: Felt252::from_dec_str("1239").unwrap(),
549            },
550        )]);
551        builtin_a.signatures = Rc::new(RefCell::new(signatures));
552        let additional_data = builtin_a.get_additional_data();
553        let mut builtin_b = SignatureBuiltinRunner::new(Some(512), true);
554        builtin_b.extend_additional_data(&additional_data).unwrap();
555        // Signature doesn't implement PartialEq so we can't comapre the list of signatures directly
556        let signatures_a = builtin_a.signatures.borrow();
557        let signatures_b = builtin_b.signatures.borrow();
558        assert_eq!(signatures_a.len(), signatures_b.len());
559        for ((addr_a, signature_a), (addr_b, signature_b)) in
560            signatures_a.iter().zip(signatures_b.iter())
561        {
562            assert_eq!(addr_a, addr_b);
563            assert_eq!(signature_a.r, signature_b.r);
564            assert_eq!(signature_a.s, signature_b.s);
565        }
566    }
567    #[test]
568    fn get_air_private_input() {
569        let mut builtin = SignatureBuiltinRunner::new(Some(512), true);
570
571        builtin.base = 0;
572
573        let signature1_r = Felt252::from(1234);
574        let signature1_s = Felt252::from(5678);
575        let signature2_r = Felt252::from(8765);
576        let signature2_s = Felt252::from(4321);
577
578        let sig1_addr = Relocatable::from((builtin.base as isize, 0));
579        let sig2_addr = Relocatable::from((builtin.base as isize, CELLS_PER_SIGNATURE as usize));
580
581        builtin
582            .add_signature(sig1_addr, &(signature1_r, signature1_s))
583            .unwrap();
584        builtin
585            .add_signature(sig2_addr, &(signature2_r, signature2_s))
586            .unwrap();
587
588        let pubkey1 = Felt252::from(1111);
589        let msg1 = Felt252::from(2222);
590        let pubkey2 = Felt252::from(3333);
591        let msg2 = Felt252::from(4444);
592
593        let segments = segments![
594            ((0, 0), 1111),
595            ((0, 1), 2222),
596            ((0, 2), 3333),
597            ((0, 3), 4444)
598        ];
599        let w1 =
600            Felt252::from(&div_mod(&BigInt::one(), &signature1_s.to_bigint(), &EC_ORDER).unwrap());
601
602        let w2 =
603            Felt252::from(&div_mod(&BigInt::one(), &signature2_s.to_bigint(), &EC_ORDER).unwrap());
604
605        let expected_private_inputs = vec![
606            PrivateInput::Signature(PrivateInputSignature {
607                index: 0,
608                pubkey: pubkey1,
609                msg: msg1,
610                signature_input: SignatureInput {
611                    r: signature1_r,
612                    w: w1,
613                },
614            }),
615            PrivateInput::Signature(PrivateInputSignature {
616                index: 1,
617                pubkey: pubkey2,
618                msg: msg2,
619                signature_input: SignatureInput {
620                    r: signature2_r,
621                    w: w2,
622                },
623            }),
624        ];
625
626        let private_inputs = builtin.air_private_input(&segments.memory);
627
628        assert_eq!(private_inputs, expected_private_inputs);
629    }
630}