@@ -946,6 +946,17 @@ impl VirtualMachine {
946946 self . segments . memory . insert_value ( key, val)
947947 }
948948
949+ /// Removes (unsets) a value from a memory cell that was not accessed by the VM.
950+ ///
951+ /// This function can be used to implement lazy opening of merkelized contracts. The full
952+ /// program is initially loaded into memory via a hint. After execution, any entry points to
953+ /// contract segments that were not accessed are replaced with an invalid opcode.
954+ ///
955+ /// [Use case](https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/starknet/core/os/contract_class/compiled_class.cairo#L244-L253)
956+ pub fn delete_unaccessed ( & mut self , addr : Relocatable ) -> Result < ( ) , MemoryError > {
957+ self . segments . memory . delete_unaccessed ( addr)
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,84 @@ 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+
4780+ // Check that the cells were set to NONE.
4781+ assert ! ( vm
4782+ . segments
4783+ . memory
4784+ . get_cell_for_testing( cell0)
4785+ . unwrap( )
4786+ . is_none( ) ) ;
4787+ assert ! ( vm
4788+ . segments
4789+ . memory
4790+ . get_cell_for_testing( cell1)
4791+ . unwrap( )
4792+ . is_none( ) ) ;
4793+ assert ! ( vm
4794+ . segments
4795+ . memory
4796+ . get_cell_for_testing( cell_tmp)
4797+ . unwrap( )
4798+ . is_none( ) ) ;
4799+ // Segment 3 cell was out of offset range, so it should not be modified or allocated.
4800+ assert ! ( vm. segments. memory. get_cell_for_testing( cell3) . is_none( ) ) ;
4801+ // Segment 2 cell was accessed, so attempting to unset the memory should result in error.
4802+ assert_matches ! (
4803+ vm. delete_unaccessed( cell2) . unwrap_err( ) ,
4804+ MemoryError :: UnsetAccessedCell ( relocatable) if relocatable == cell2
4805+ ) ;
4806+ // Segment 3 is unallocated, so attempting to unset the memory should result in error.
4807+ assert_matches ! (
4808+ vm. delete_unaccessed( cell3) . unwrap_err( ) ,
4809+ MemoryError :: UnsetUnallocatedCell ( relocatable) if relocatable == cell3
4810+ ) ;
4811+ // Segment 7 was not allocated, so attempting to unset the memory should result in error.
4812+ assert_matches ! (
4813+ vm. delete_unaccessed( cell7) . unwrap_err( ) ,
4814+ MemoryError :: UnallocatedSegment ( boxed)
4815+ if * boxed == ( cell7. segment_index. try_into( ) . unwrap( ) , vm. segments. memory. data. len( ) )
4816+ ) ;
4817+ }
4818+
47304819 #[ test]
47314820 #[ cfg_attr( target_arch = "wasm32" , wasm_bindgen_test) ]
47324821 fn mark_as_accessed ( ) {
0 commit comments