Skip to content

Commit 7b20139

Browse files
feat: implemented delete_unaccessed function
Signed-off-by: Dori Medini <[email protected]>
1 parent fc100d9 commit 7b20139

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
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 [#2099](https://github.com/lambdaclass/cairo-vm/pull/2099)
6+
57
#### [2.1.0] - 2025-05-21
68

79
* chore: bump pip `cairo-lang` 0.13.5 [#1959](https://github.com/lambdaclass/cairo-vm/pull/1959)

vm/src/vm/errors/memory_errors.rs

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

1616
#[derive(Debug, PartialEq, Error)]
1717
pub enum MemoryError {
18+
#[error("Cell {0} has already been accessed; it cannot be execution-protected.")]
19+
CellAlreadyAccessed(Relocatable),
1820
#[error(transparent)]
1921
Math(#[from] MathError),
2022
#[error(transparent)]

vm/src/vm/vm_core.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,17 @@ impl VirtualMachine {
946946
self.segments.memory.insert_value(key, val)
947947
}
948948

949+
/// Unset (delete) the first cell of the given contract segment, to prevent contract segment
950+
/// execution. Returns an error if the first cell of the contract segment is already accessed.
951+
pub fn delete_unaccessed(
952+
&mut self,
953+
contract_segment_start: Relocatable,
954+
) -> Result<(), MemoryError> {
955+
self.segments
956+
.memory
957+
.delete_unaccessed(contract_segment_start)
958+
}
959+
949960
///Writes data into the memory from address ptr and returns the first address after the data.
950961
pub fn load_data(
951962
&mut self,
@@ -4727,6 +4738,80 @@ mod tests {
47274738
assert_eq!(vm.segments.compute_effective_sizes(), &vec![4]);
47284739
}
47294740

4741+
#[test]
4742+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
4743+
fn test_delete_unaccessed() {
4744+
let mut vm = vm!();
4745+
4746+
let segment0 = vm.segments.add();
4747+
let segment1 = vm.segments.add();
4748+
let segment2 = vm.segments.add();
4749+
let segment3 = vm.segments.add();
4750+
let tmp_segment = vm.add_temporary_segment();
4751+
assert_eq!(segment0.segment_index, 0);
4752+
assert_eq!(segment1.segment_index, 1);
4753+
assert_eq!(segment2.segment_index, 2);
4754+
assert_eq!(segment3.segment_index, 3);
4755+
assert_eq!(tmp_segment.segment_index, -1);
4756+
vm.segments.memory = memory![
4757+
((0, 1), 1),
4758+
((1, 0), 3),
4759+
((1, 1), 4),
4760+
((2, 0), 7),
4761+
((3, 0), 7),
4762+
((-1, 0), 5),
4763+
((-1, 1), 5),
4764+
((-1, 2), 5)
4765+
];
4766+
vm.run_finished = true;
4767+
4768+
vm.mark_address_range_as_accessed((2, 0).into(), 1).unwrap();
4769+
4770+
let cell0 = Relocatable::from((0, 0));
4771+
let cell1 = Relocatable::from((1, 1));
4772+
let cell2 = Relocatable::from((2, 0));
4773+
let cell3 = Relocatable::from((3, 7));
4774+
let cell7 = Relocatable::from((7, 17));
4775+
let cell_tmp = Relocatable::from((-1, 1));
4776+
vm.delete_unaccessed(cell0).unwrap();
4777+
vm.delete_unaccessed(cell1).unwrap();
4778+
vm.delete_unaccessed(cell_tmp).unwrap();
4779+
vm.delete_unaccessed(cell3).unwrap();
4780+
4781+
// Check that the cells were set to NONE.
4782+
assert!(vm
4783+
.segments
4784+
.memory
4785+
.get_cell_for_testing(cell0)
4786+
.unwrap()
4787+
.is_none());
4788+
assert!(vm
4789+
.segments
4790+
.memory
4791+
.get_cell_for_testing(cell1)
4792+
.unwrap()
4793+
.is_none());
4794+
assert!(vm
4795+
.segments
4796+
.memory
4797+
.get_cell_for_testing(cell_tmp)
4798+
.unwrap()
4799+
.is_none());
4800+
// Segment 3 cell was out of offset range, so it should not be modified or allocated.
4801+
assert!(vm.segments.memory.get_cell_for_testing(cell3).is_none());
4802+
// Segment 2 cell was accessed, so attempting to unset the memory should result in error.
4803+
assert_matches!(
4804+
vm.delete_unaccessed(cell2).unwrap_err(),
4805+
MemoryError::CellAlreadyAccessed(relocatable) if relocatable == cell2
4806+
);
4807+
// Segment 7 was not allocated, so attempting to unset the memory should result in error.
4808+
assert_matches!(
4809+
vm.delete_unaccessed(cell7).unwrap_err(),
4810+
MemoryError::UnallocatedSegment(boxed)
4811+
if *boxed == (cell7.segment_index.try_into().unwrap(), vm.segments.memory.data.len())
4812+
);
4813+
}
4814+
47304815
#[test]
47314816
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
47324817
fn mark_as_accessed() {

vm/src/vm/vm_memory/memory.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,37 @@ impl Memory {
237237
self.validate_memory_cell(key)
238238
}
239239

240+
pub(crate) fn delete_unaccessed(
241+
&mut self,
242+
contract_segment_start: Relocatable,
243+
) -> Result<(), MemoryError> {
244+
let data = if contract_segment_start.segment_index.is_negative() {
245+
&mut self.temp_data
246+
} else {
247+
&mut self.data
248+
};
249+
let (index, offset) = from_relocatable_to_indexes(contract_segment_start);
250+
251+
let data_len = data.len();
252+
let segment = data
253+
.get_mut(index)
254+
.ok_or_else(|| MemoryError::UnallocatedSegment(Box::new((index, data_len))))?;
255+
256+
// Check if the offset exists; if not, there is nothing to unset.
257+
if offset >= segment.len() {
258+
return Ok(());
259+
}
260+
261+
// Ensure the cell has not been accessed.
262+
if segment[offset].is_accessed() {
263+
return Err(MemoryError::CellAlreadyAccessed(contract_segment_start));
264+
}
265+
266+
// Unset the cell.
267+
segment[offset] = MemoryCell::NONE;
268+
Ok(())
269+
}
270+
240271
/// Retrieve a value from memory (either normal or temporary) and apply relocation rules
241272
pub(crate) fn get<'a, 'b: 'a, K: 'a>(&'b self, key: &'a K) -> Option<Cow<'b, MaybeRelocatable>>
242273
where
@@ -671,6 +702,11 @@ impl Memory {
671702
data.get(i)?.get(j)
672703
}
673704

705+
#[cfg(test)]
706+
pub(crate) fn get_cell_for_testing(&self, addr: Relocatable) -> Option<&MemoryCell> {
707+
self.get_cell(addr)
708+
}
709+
674710
pub fn is_accessed(&self, addr: &Relocatable) -> Result<bool, MemoryError> {
675711
Ok(self
676712
.get_cell(*addr)

0 commit comments

Comments
 (0)