Skip to content

Commit a649c7a

Browse files
committed
Merge branch 'dict-manager' of github.com:lambdaclass/cairo-rs-py into squash-dict-add-test-tags
2 parents e161553 + 255bc69 commit a649c7a

File tree

13 files changed

+277
-19
lines changed

13 files changed

+277
-19
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ default = ["pyo3/num-bigint", "pyo3/auto-initialize"]
1313

1414
[dependencies]
1515
pyo3 = { version = "0.16.5" }
16-
cairo-rs = { git = "https://github.com/lambdaclass/cairo-rs.git", rev = "4f36aaf46dea8cac158d0da5e80537388e048c01" }
16+
cairo-rs = { git = "https://github.com/lambdaclass/cairo-rs.git", rev = "2ddf78e20cc25e660263a0c9c1b942780d95a0e6" }
1717
num-bigint = "0.4"
1818
lazy_static = "1.4.0"
1919

cairo_programs/ecdsa.cairo

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
%builtins output pedersen ecdsa
2+
3+
from starkware.cairo.common.cairo_builtins import HashBuiltin, SignatureBuiltin
4+
from starkware.cairo.common.hash import hash2
5+
from starkware.cairo.common.signature import verify_ecdsa_signature
6+
7+
func main{output_ptr : felt*, pedersen_ptr : HashBuiltin*, ecdsa_ptr : SignatureBuiltin*}():
8+
alloc_locals
9+
10+
let your_eth_addr = 874739451078007766457464989774322083649278607533249481151382481072868806602
11+
let signature_r = 1839793652349538280924927302501143912227271479439798783640887258675143576352
12+
let signature_s = 1819432147005223164874083361865404672584671743718628757598322238853218813979
13+
let msg = 0000000000000000000000000000000000000000000000000000000000000002
14+
15+
verify_ecdsa_signature(
16+
msg,
17+
your_eth_addr,
18+
signature_r,
19+
signature_s,
20+
)
21+
22+
23+
assert [output_ptr] = your_eth_addr
24+
let output_ptr = output_ptr + 1
25+
26+
return ()
27+
end

comparer_tracer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def new_runner(program_name: str):
88

99
if __name__ == "__main__":
1010
program_name = sys.argv[1]
11-
if program_name in ["blake2s_felt", "blake2s_finalize", "blake2s_integration_tests", "blake2s_hello_world_hash", "dict_squash", "squash_dict", "dict_write"]:
11+
if program_name in ["blake2s_felt", "blake2s_finalize", "blake2s_integration_tests", "blake2s_hello_world_hash", "dict_squash", "squash_dict", "dict_write", "dict_read", "dict_update"]:
1212
pass
1313
else:
1414
new_runner(program_name)

hints_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ def test_program(program_name: str):
6969
test_program("blake2s_finalize")
7070
test_program("blake2s_felt")
7171
test_program("blake2s_integration_tests")
72+
test_program("ecdsa")
7273
print("\nAll test have passed")

src/cairo_runner.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,13 @@ impl PyCairoRunner {
541541
.collect::<Vec<_>>()
542542
.to_object(py))
543543
}
544+
545+
/// Add (or replace if already present) a custom hash builtin.
546+
/// Returns a Relocatable with the new hash builtin base.
547+
pub fn add_additional_hash_builtin(&self) -> PyRelocatable {
548+
let mut vm = (*self.pyvm.vm).borrow_mut();
549+
self.inner.add_additional_hash_builtin(&mut vm).into()
550+
}
544551
}
545552

546553
#[pyclass]
@@ -1221,6 +1228,10 @@ mod test {
12211228
segment_index: 6,
12221229
offset: 0,
12231230
})],
1231+
vec![RelocatableValue(PyRelocatable {
1232+
segment_index: 7,
1233+
offset: 0,
1234+
})],
12241235
];
12251236

12261237
Python::with_gil(|py| {
@@ -1522,4 +1533,43 @@ mod test {
15221533
);
15231534
});
15241535
}
1536+
1537+
/// Test that add_additional_hash_builtin() returns successfully.
1538+
#[test]
1539+
fn add_additional_hash_builtin() {
1540+
Python::with_gil(|_| {
1541+
let program = fs::read_to_string("cairo_programs/fibonacci.json").unwrap();
1542+
let runner = PyCairoRunner::new(
1543+
program,
1544+
Some("main".to_string()),
1545+
Some("small".to_string()),
1546+
false,
1547+
)
1548+
.unwrap();
1549+
1550+
let expected_relocatable = PyRelocatable {
1551+
segment_index: 0,
1552+
offset: 0,
1553+
};
1554+
let relocatable = runner.add_additional_hash_builtin();
1555+
assert_eq!(expected_relocatable, relocatable);
1556+
1557+
assert_eq!(
1558+
(*runner.pyvm.vm)
1559+
.borrow()
1560+
.get_builtin_runners()
1561+
.last()
1562+
.map(|(key, _)| key.as_str()),
1563+
Some("hash_builtin"),
1564+
);
1565+
1566+
let mut vm = (*runner.pyvm.vm).borrow_mut();
1567+
// Check that the segment exists by writing to it.
1568+
vm.insert_value(
1569+
&Relocatable::from((0, 0)),
1570+
MaybeRelocatable::Int(bigint!(42)),
1571+
)
1572+
.expect("memory insert failed");
1573+
});
1574+
}
15251575
}

src/ecdsa.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use std::collections::HashMap;
2+
3+
use cairo_rs::{
4+
types::relocatable::Relocatable,
5+
vm::{errors::vm_errors::VirtualMachineError, runners::builtin_runner::SignatureBuiltinRunner},
6+
};
7+
8+
use num_bigint::BigInt;
9+
use pyo3::prelude::*;
10+
11+
use crate::relocatable::PyRelocatable;
12+
13+
#[pyclass(name = "Signature")]
14+
#[derive(Clone, Debug)]
15+
pub struct PySignature {
16+
signatures: HashMap<PyRelocatable, (BigInt, BigInt)>,
17+
}
18+
19+
#[pymethods]
20+
impl PySignature {
21+
#[new]
22+
pub fn new() -> Self {
23+
Self {
24+
signatures: HashMap::new(),
25+
}
26+
}
27+
28+
pub fn add_signature(&mut self, address: PyRelocatable, pair: (BigInt, BigInt)) {
29+
self.signatures.insert(address, pair);
30+
}
31+
}
32+
33+
impl PySignature {
34+
pub fn update_signature(
35+
&self,
36+
signature_builtin: &mut SignatureBuiltinRunner,
37+
) -> Result<(), VirtualMachineError> {
38+
for (address, pair) in self.signatures.iter() {
39+
signature_builtin
40+
.add_signature(Relocatable::from(address), pair)
41+
.map_err(VirtualMachineError::MemoryError)?
42+
}
43+
Ok(())
44+
}
45+
}
46+
47+
impl Default for PySignature {
48+
fn default() -> Self {
49+
Self::new()
50+
}
51+
}
52+
53+
impl ToPyObject for PySignature {
54+
fn to_object(&self, py: Python<'_>) -> PyObject {
55+
self.clone().into_py(py)
56+
}
57+
}

src/ids.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,14 @@ impl PyIds {
9696
struct_types: Rc::clone(&self.struct_types),
9797
}
9898
.into_py(py));
99-
}
100-
101-
if self
99+
} else if self
102100
.struct_types
103101
.contains_key(cairo_type.trim_end_matches('*'))
104102
{
105103
let addr =
106104
compute_addr_from_reference(hint_ref, &self.vm.borrow(), &self.ap_tracking)?;
107105

108-
let dereferenced_addr = self
106+
let hint_value = self
109107
.vm
110108
.borrow()
111109
.get_relocatable(&addr)
@@ -114,7 +112,7 @@ impl PyIds {
114112

115113
return Ok(PyTypedId {
116114
vm: self.vm.clone(),
117-
hint_value: dereferenced_addr,
115+
hint_value,
118116
cairo_type: cairo_type.trim_end_matches('*').to_string(),
119117
struct_types: Rc::clone(&self.struct_types),
120118
}
@@ -179,11 +177,10 @@ pub struct PyTypedId {
179177
impl PyTypedId {
180178
#[getter]
181179
fn __getattr__(&self, py: Python, name: &str) -> PyResult<PyObject> {
182-
let struct_type = self.struct_types.get(&self.cairo_type).unwrap();
183-
184180
if name == "address_" {
185181
return Ok(PyMaybeRelocatable::from(self.hint_value.clone()).to_object(py));
186182
}
183+
let struct_type = self.struct_types.get(&self.cairo_type).unwrap();
187184

188185
match struct_type.get(name) {
189186
Some(member) => {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod cairo_run;
22
pub mod cairo_runner;
33
mod dict_manager;
4+
mod ecdsa;
45
pub mod ids;
56
mod memory;
67
mod memory_segments;

src/memory.rs

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
use crate::{
22
relocatable::{PyMaybeRelocatable, PyRelocatable},
3+
utils::to_py_error,
34
vm_core::PyVM,
45
};
56
use cairo_rs::{
67
types::relocatable::{MaybeRelocatable, Relocatable},
78
vm::vm_core::VirtualMachine,
89
};
10+
use num_bigint::BigInt;
911
use pyo3::{
1012
exceptions::{PyTypeError, PyValueError},
1113
prelude::*,
1214
};
13-
use std::{cell::RefCell, rc::Rc};
15+
use std::{borrow::Cow, cell::RefCell, rc::Rc};
1416

1517
const MEMORY_GET_ERROR_MSG: &str = "Failed to get value from Cairo memory";
1618
const MEMORY_SET_ERROR_MSG: &str = "Failed to set value to Cairo memory";
@@ -69,6 +71,18 @@ impl PyMemory {
6971
.collect::<Vec<PyMaybeRelocatable>>()
7072
.to_object(py))
7173
}
74+
75+
/// Return a continuous section of memory as a vector of integers.
76+
pub fn get_range_as_ints(&self, addr: PyRelocatable, size: usize) -> PyResult<Vec<BigInt>> {
77+
Ok(self
78+
.vm
79+
.borrow()
80+
.get_integer_range(&Relocatable::from(&addr), size)
81+
.map_err(to_py_error)?
82+
.into_iter()
83+
.map(Cow::into_owned)
84+
.collect())
85+
}
7286
}
7387

7488
#[cfg(test)]
@@ -282,4 +296,104 @@ assert memory[ap] == fp
282296
assert_eq!(range.unwrap_err(), expected_error);
283297
});
284298
}
299+
300+
// Test that get_range_as_ints() works as intended.
301+
#[test]
302+
fn get_range_as_ints() {
303+
let vm = PyVM::new(
304+
BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]),
305+
false,
306+
);
307+
let memory = PyMemory::new(&vm);
308+
309+
let addr = {
310+
let mut vm = vm.vm.borrow_mut();
311+
let addr = vm.add_memory_segment();
312+
313+
vm.load_data(
314+
&MaybeRelocatable::from(&addr),
315+
vec![
316+
bigint!(1).into(),
317+
bigint!(2).into(),
318+
bigint!(3).into(),
319+
bigint!(4).into(),
320+
],
321+
)
322+
.expect("memory insertion failed");
323+
324+
addr
325+
};
326+
327+
assert_eq!(
328+
memory
329+
.get_range_as_ints(addr.into(), 4)
330+
.expect("get_range_as_ints() failed"),
331+
vec![bigint!(1), bigint!(2), bigint!(3), bigint!(4)],
332+
);
333+
}
334+
335+
// Test that get_range_as_ints() fails when not all values are integers.
336+
#[test]
337+
fn get_range_as_ints_mixed() {
338+
let vm = PyVM::new(
339+
BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]),
340+
false,
341+
);
342+
let memory = PyMemory::new(&vm);
343+
344+
let addr = {
345+
let mut vm = vm.vm.borrow_mut();
346+
let addr = vm.add_memory_segment();
347+
348+
vm.load_data(
349+
&MaybeRelocatable::from(&addr),
350+
vec![
351+
bigint!(1).into(),
352+
bigint!(2).into(),
353+
MaybeRelocatable::RelocatableValue((1, 2).into()),
354+
bigint!(4).into(),
355+
],
356+
)
357+
.expect("memory insertion failed");
358+
359+
addr
360+
};
361+
362+
memory
363+
.get_range_as_ints(addr.into(), 4)
364+
.expect_err("get_range_as_ints() succeeded (should have failed)");
365+
}
366+
367+
// Test that get_range_as_ints() fails when the requested range is larger than the available
368+
// segments.
369+
#[test]
370+
fn get_range_as_ints_incomplete() {
371+
let vm = PyVM::new(
372+
BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]),
373+
false,
374+
);
375+
let memory = PyMemory::new(&vm);
376+
377+
let addr = {
378+
let mut vm = vm.vm.borrow_mut();
379+
let addr = vm.add_memory_segment();
380+
381+
vm.load_data(
382+
&MaybeRelocatable::from(&addr),
383+
vec![
384+
bigint!(1).into(),
385+
bigint!(2).into(),
386+
bigint!(3).into(),
387+
bigint!(4).into(),
388+
],
389+
)
390+
.expect("memory insertion failed");
391+
392+
addr
393+
};
394+
395+
memory
396+
.get_range_as_ints(addr.into(), 8)
397+
.expect_err("get_range_as_ints() succeeded (should have failed)");
398+
}
285399
}

src/relocatable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub enum PyMaybeRelocatable {
1818
}
1919

2020
#[pyclass(name = "Relocatable")]
21-
#[derive(Clone, Debug, PartialEq, Eq)]
21+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2222
pub struct PyRelocatable {
2323
#[pyo3(get)]
2424
pub segment_index: isize,

0 commit comments

Comments
 (0)