Skip to content

Commit 0660acc

Browse files
feat: implemented delete_unaccessed function (#2282)
* feat: implemented delete_unaccessed function (#2099) Signed-off-by: Dori Medini <dori@starkware.co> * Update CHANGELOG --------- Signed-off-by: Dori Medini <dori@starkware.co> Co-authored-by: dorimedini-starkware <dori@starkware.co>
1 parent 97fdaa4 commit 0660acc

File tree

4 files changed

+133
-12
lines changed

4 files changed

+133
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### Upcoming Changes
44

5+
* feat: implemented delete_unaccessed function [#2282](https://github.com/lambdaclass/cairo-vm/pull/2282)
6+
57
* fix: rename y_cube_int to y_square_int in get_point_from_x [#2271](https://github.com/lambdaclass/cairo-vm/pull/2271)
68

79
* fix: correct duplicate tuple index in InvalidTrackingGroup error message [#2276](https://github.com/lambdaclass/cairo-vm/pull/2276)

vm/src/vm/errors/memory_errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ use crate::types::{
1515

1616
#[derive(Debug, PartialEq, Error)]
1717
pub enum MemoryError {
18+
#[error("Cell {0} has already been accessed; it cannot be removed.")]
19+
UnsetAccessedCell(Relocatable),
20+
#[error("Cell {0} is not allocated; it cannot be removed.")]
21+
UnsetUnallocatedCell(Relocatable),
1822
#[error(transparent)]
1923
Math(#[from] MathError),
2024
#[error(transparent)]

vm/src/vm/vm_core.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,17 @@ impl VirtualMachine {
10211021
self.segments.memory.insert_value(key, val)
10221022
}
10231023

1024+
/// Removes (unsets) a value from a memory cell that was not accessed by the VM.
1025+
///
1026+
/// This function can be used to implement lazy opening of merkelized contracts. The full
1027+
/// program is initially loaded into memory via a hint. After execution, any entry points to
1028+
/// contract segments that were not accessed are replaced with an invalid opcode.
1029+
///
1030+
/// [Use case](https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/starknet/core/os/contract_class/compiled_class.cairo#L244-L253)
1031+
pub fn delete_unaccessed(&mut self, addr: Relocatable) -> Result<(), MemoryError> {
1032+
self.segments.memory.delete_unaccessed(addr)
1033+
}
1034+
10241035
///Writes data into the memory from address ptr and returns the first address after the data.
10251036
pub fn load_data(
10261037
&mut self,
@@ -4813,6 +4824,84 @@ mod tests {
48134824
assert_eq!(vm.segments.compute_effective_sizes(), &vec![4]);
48144825
}
48154826

4827+
#[test]
4828+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
4829+
fn test_delete_unaccessed() {
4830+
let mut vm = vm!();
4831+
4832+
let segment0 = vm.segments.add();
4833+
let segment1 = vm.segments.add();
4834+
let segment2 = vm.segments.add();
4835+
let segment3 = vm.segments.add();
4836+
let tmp_segment = vm.add_temporary_segment();
4837+
assert_eq!(segment0.segment_index, 0);
4838+
assert_eq!(segment1.segment_index, 1);
4839+
assert_eq!(segment2.segment_index, 2);
4840+
assert_eq!(segment3.segment_index, 3);
4841+
assert_eq!(tmp_segment.segment_index, -1);
4842+
vm.segments.memory = memory![
4843+
((0, 1), 1),
4844+
((1, 0), 3),
4845+
((1, 1), 4),
4846+
((2, 0), 7),
4847+
((3, 0), 7),
4848+
((-1, 0), 5),
4849+
((-1, 1), 5),
4850+
((-1, 2), 5)
4851+
];
4852+
vm.run_finished = true;
4853+
4854+
vm.mark_address_range_as_accessed((2, 0).into(), 1).unwrap();
4855+
4856+
let cell0 = Relocatable::from((0, 0));
4857+
let cell1 = Relocatable::from((1, 1));
4858+
let cell2 = Relocatable::from((2, 0));
4859+
let cell3 = Relocatable::from((3, 7));
4860+
let cell7 = Relocatable::from((7, 17));
4861+
let cell_tmp = Relocatable::from((-1, 1));
4862+
vm.delete_unaccessed(cell0).unwrap();
4863+
vm.delete_unaccessed(cell1).unwrap();
4864+
vm.delete_unaccessed(cell_tmp).unwrap();
4865+
4866+
// Check that the cells were set to NONE.
4867+
assert!(vm
4868+
.segments
4869+
.memory
4870+
.get_cell_for_testing(cell0)
4871+
.unwrap()
4872+
.is_none());
4873+
assert!(vm
4874+
.segments
4875+
.memory
4876+
.get_cell_for_testing(cell1)
4877+
.unwrap()
4878+
.is_none());
4879+
assert!(vm
4880+
.segments
4881+
.memory
4882+
.get_cell_for_testing(cell_tmp)
4883+
.unwrap()
4884+
.is_none());
4885+
// Segment 3 cell was out of offset range, so it should not be modified or allocated.
4886+
assert!(vm.segments.memory.get_cell_for_testing(cell3).is_none());
4887+
// Segment 2 cell was accessed, so attempting to unset the memory should result in error.
4888+
assert_matches!(
4889+
vm.delete_unaccessed(cell2).unwrap_err(),
4890+
MemoryError::UnsetAccessedCell(relocatable) if relocatable == cell2
4891+
);
4892+
// Segment 3 is unallocated, so attempting to unset the memory should result in error.
4893+
assert_matches!(
4894+
vm.delete_unaccessed(cell3).unwrap_err(),
4895+
MemoryError::UnsetUnallocatedCell(relocatable) if relocatable == cell3
4896+
);
4897+
// Segment 7 was not allocated, so attempting to unset the memory should result in error.
4898+
assert_matches!(
4899+
vm.delete_unaccessed(cell7).unwrap_err(),
4900+
MemoryError::UnallocatedSegment(boxed)
4901+
if *boxed == (cell7.segment_index.try_into().unwrap(), vm.segments.memory.data.len())
4902+
);
4903+
}
4904+
48164905
#[test]
48174906
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
48184907
fn mark_as_accessed() {

vm/src/vm/vm_memory/memory.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ impl Memory {
186186
}
187187
}
188188

189+
fn get_segment(&mut self, key: Relocatable) -> Result<&mut Vec<MemoryCell>, MemoryError> {
190+
let (value_index, _) = from_relocatable_to_indexes(key);
191+
let data = if key.segment_index.is_negative() {
192+
&mut self.temp_data
193+
} else {
194+
&mut self.data
195+
};
196+
let data_len = data.len();
197+
data.get_mut(value_index)
198+
.ok_or_else(|| MemoryError::UnallocatedSegment(Box::new((value_index, data_len))))
199+
}
200+
189201
/// Inserts a value into a memory address
190202
/// Will return an Error if the segment index given by the address corresponds to a non-allocated segment,
191203
/// or if the inserted value is inconsistent with the current value at the memory cell
@@ -195,18 +207,8 @@ impl Memory {
195207
MaybeRelocatable: From<V>,
196208
{
197209
let val = MaybeRelocatable::from(val);
198-
let (value_index, value_offset) = from_relocatable_to_indexes(key);
199-
200-
let data = if key.segment_index.is_negative() {
201-
&mut self.temp_data
202-
} else {
203-
&mut self.data
204-
};
205-
206-
let data_len = data.len();
207-
let segment = data
208-
.get_mut(value_index)
209-
.ok_or_else(|| MemoryError::UnallocatedSegment(Box::new((value_index, data_len))))?;
210+
let segment = self.get_segment(key)?;
211+
let (_, value_offset) = from_relocatable_to_indexes(key);
210212

211213
//Check if the element is inserted next to the last one on the segment
212214
//Forgoing this check would allow data to be inserted in a different index
@@ -238,6 +240,25 @@ impl Memory {
238240
self.validate_memory_cell(key)
239241
}
240242

243+
pub(crate) fn delete_unaccessed(&mut self, addr: Relocatable) -> Result<(), MemoryError> {
244+
let (_, offset) = from_relocatable_to_indexes(addr);
245+
let segment = self.get_segment(addr)?;
246+
247+
// Make sure the offset exists.
248+
if offset >= segment.len() {
249+
return Err(MemoryError::UnsetUnallocatedCell(addr));
250+
}
251+
252+
// Ensure the cell has not been accessed.
253+
if segment[offset].is_accessed() {
254+
return Err(MemoryError::UnsetAccessedCell(addr));
255+
}
256+
257+
// Unset the cell.
258+
segment[offset] = MemoryCell::NONE;
259+
Ok(())
260+
}
261+
241262
/// Retrieve a value from memory (either normal or temporary) and apply relocation rules
242263
pub(crate) fn get<'a, 'b: 'a, K: 'a>(&'b self, key: &'a K) -> Option<Cow<'b, MaybeRelocatable>>
243264
where
@@ -764,6 +785,11 @@ impl Memory {
764785
data.get(i)?.get(j)
765786
}
766787

788+
#[cfg(test)]
789+
pub(crate) fn get_cell_for_testing(&self, addr: Relocatable) -> Option<&MemoryCell> {
790+
self.get_cell(addr)
791+
}
792+
767793
pub fn is_accessed(&self, addr: &Relocatable) -> Result<bool, MemoryError> {
768794
Ok(self
769795
.get_cell(*addr)

0 commit comments

Comments
 (0)