@@ -946,6 +946,15 @@ 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+ pub fn delete_unaccessed ( & mut self , addr : Relocatable ) -> Result < ( ) , MemoryError > {
955+ self . segments . memory . delete_unaccessed ( addr)
956+ }
957+
949958 ///Writes data into the memory from address ptr and returns the first address after the data.
950959 pub fn load_data (
951960 & mut self ,
@@ -4727,6 +4736,84 @@ mod tests {
47274736 assert_eq ! ( vm. segments. compute_effective_sizes( ) , & vec![ 4 ] ) ;
47284737 }
47294738
4739+ #[ test]
4740+ #[ cfg_attr( target_arch = "wasm32" , wasm_bindgen_test) ]
4741+ fn test_delete_unaccessed ( ) {
4742+ let mut vm = vm ! ( ) ;
4743+
4744+ let segment0 = vm. segments . add ( ) ;
4745+ let segment1 = vm. segments . add ( ) ;
4746+ let segment2 = vm. segments . add ( ) ;
4747+ let segment3 = vm. segments . add ( ) ;
4748+ let tmp_segment = vm. add_temporary_segment ( ) ;
4749+ assert_eq ! ( segment0. segment_index, 0 ) ;
4750+ assert_eq ! ( segment1. segment_index, 1 ) ;
4751+ assert_eq ! ( segment2. segment_index, 2 ) ;
4752+ assert_eq ! ( segment3. segment_index, 3 ) ;
4753+ assert_eq ! ( tmp_segment. segment_index, -1 ) ;
4754+ vm. segments . memory = memory ! [
4755+ ( ( 0 , 1 ) , 1 ) ,
4756+ ( ( 1 , 0 ) , 3 ) ,
4757+ ( ( 1 , 1 ) , 4 ) ,
4758+ ( ( 2 , 0 ) , 7 ) ,
4759+ ( ( 3 , 0 ) , 7 ) ,
4760+ ( ( -1 , 0 ) , 5 ) ,
4761+ ( ( -1 , 1 ) , 5 ) ,
4762+ ( ( -1 , 2 ) , 5 )
4763+ ] ;
4764+ vm. run_finished = true ;
4765+
4766+ vm. mark_address_range_as_accessed ( ( 2 , 0 ) . into ( ) , 1 ) . unwrap ( ) ;
4767+
4768+ let cell0 = Relocatable :: from ( ( 0 , 0 ) ) ;
4769+ let cell1 = Relocatable :: from ( ( 1 , 1 ) ) ;
4770+ let cell2 = Relocatable :: from ( ( 2 , 0 ) ) ;
4771+ let cell3 = Relocatable :: from ( ( 3 , 7 ) ) ;
4772+ let cell7 = Relocatable :: from ( ( 7 , 17 ) ) ;
4773+ let cell_tmp = Relocatable :: from ( ( -1 , 1 ) ) ;
4774+ vm. delete_unaccessed ( cell0) . unwrap ( ) ;
4775+ vm. delete_unaccessed ( cell1) . unwrap ( ) ;
4776+ vm. delete_unaccessed ( cell_tmp) . unwrap ( ) ;
4777+
4778+ // Check that the cells were set to NONE.
4779+ assert ! ( vm
4780+ . segments
4781+ . memory
4782+ . get_cell_for_testing( cell0)
4783+ . unwrap( )
4784+ . is_none( ) ) ;
4785+ assert ! ( vm
4786+ . segments
4787+ . memory
4788+ . get_cell_for_testing( cell1)
4789+ . unwrap( )
4790+ . is_none( ) ) ;
4791+ assert ! ( vm
4792+ . segments
4793+ . memory
4794+ . get_cell_for_testing( cell_tmp)
4795+ . unwrap( )
4796+ . is_none( ) ) ;
4797+ // Segment 3 cell was out of offset range, so it should not be modified or allocated.
4798+ assert ! ( vm. segments. memory. get_cell_for_testing( cell3) . is_none( ) ) ;
4799+ // Segment 2 cell was accessed, so attempting to unset the memory should result in error.
4800+ assert_matches ! (
4801+ vm. delete_unaccessed( cell2) . unwrap_err( ) ,
4802+ MemoryError :: UnsetAccessedCell ( relocatable) if relocatable == cell2
4803+ ) ;
4804+ // Segment 3 is unallocated, so attempting to unset the memory should result in error.
4805+ assert_matches ! (
4806+ vm. delete_unaccessed( cell3) . unwrap_err( ) ,
4807+ MemoryError :: UnsetUnallocatedCell ( relocatable) if relocatable == cell3
4808+ ) ;
4809+ // Segment 7 was not allocated, so attempting to unset the memory should result in error.
4810+ assert_matches ! (
4811+ vm. delete_unaccessed( cell7) . unwrap_err( ) ,
4812+ MemoryError :: UnallocatedSegment ( boxed)
4813+ if * boxed == ( cell7. segment_index. try_into( ) . unwrap( ) , vm. segments. memory. data. len( ) )
4814+ ) ;
4815+ }
4816+
47304817 #[ test]
47314818 #[ cfg_attr( target_arch = "wasm32" , wasm_bindgen_test) ]
47324819 fn mark_as_accessed ( ) {
0 commit comments