@@ -214,3 +214,335 @@ pub fn flexible_builtin_usage_from_input(
214214 ap_tracking,
215215 )
216216}
217+ #[ cfg( test) ]
218+ mod tests {
219+ use super :: * ;
220+ use crate :: test_utils:: prepare_ids_data_for_test;
221+ use cairo_vm:: serde:: deserialize_program:: OffsetValue ;
222+ use cairo_vm:: types:: relocatable:: MaybeRelocatable ;
223+ use cairo_vm:: types:: relocatable:: Relocatable ;
224+ use cairo_vm:: vm:: runners:: builtin_runner:: {
225+ BuiltinRunner , OutputBuiltinRunner , OutputBuiltinState , SignatureBuiltinRunner ,
226+ } ;
227+ use cairo_vm:: vm:: runners:: cairo_pie:: PublicMemoryPage ;
228+ use cairo_vm:: vm:: vm_core:: VirtualMachine ;
229+ use rstest:: rstest;
230+ use std:: borrow:: Cow ;
231+
232+ fn prepare_vm_for_flexible_builtin_usage_test (
233+ input : & FlexibleBuiltinUsageInput ,
234+ ) -> (
235+ VirtualMachine ,
236+ ExecutionScopes ,
237+ HashMap < String , HintReference > ,
238+ ApTracking ,
239+ ) {
240+ // Prepare a VirtualMachine instance, ids_data, exec_scopes, and ap_tracking for testing.
241+ // Should mimic the vm's state after running the following Cairo0 code:
242+ // alloc_locals;
243+ // local n_output;
244+ // local n_pedersen;
245+ // local n_range_check;
246+ // local n_ecdsa;
247+ // local n_bitwise;
248+ // local n_ec_op;
249+ // local n_keccak;
250+ // local n_poseidon;
251+ // local n_range_check96;
252+ // local n_add_mod;
253+ // local n_mul_mod;
254+ // local n_memory_holes;
255+ // local n_blake2s;
256+ let mut vm = VirtualMachine :: new ( false , false ) ;
257+ let ids_data = prepare_ids_data_for_test ( & [
258+ "n_output" ,
259+ "n_pedersen" ,
260+ "n_range_check" ,
261+ "n_ecdsa" ,
262+ "n_bitwise" ,
263+ "n_ec_op" ,
264+ "n_keccak" ,
265+ "n_poseidon" ,
266+ "n_range_check96" ,
267+ "n_add_mod" ,
268+ "n_mul_mod" ,
269+ "n_memory_holes" ,
270+ "n_blake2s" ,
271+ ] ) ;
272+ let mut exec_scopes = ExecutionScopes :: new ( ) ;
273+ exec_scopes. insert_value ( PROGRAM_INPUT , serde_json:: to_string ( input) . unwrap ( ) ) ;
274+ let ap_tracking = ApTracking :: default ( ) ;
275+ vm. set_fp ( 13 ) ;
276+ vm. set_ap ( 13 ) ;
277+ vm. segments . add ( ) ;
278+ vm. segments . add ( ) ;
279+ ( vm, exec_scopes, ids_data, ap_tracking)
280+ }
281+
282+ /// Test the builtin_usage_add_other_segment function with finalize set to true and false.
283+ /// Checks that other_segment is added correctly and segment is finalized based on the flag.
284+ #[ rstest( finalize, case( true ) , case( false ) ) ]
285+ fn test_builtin_usage_add_other_segment ( finalize : bool ) {
286+ let mut vm = VirtualMachine :: new ( false , false ) ;
287+ let ids_data = prepare_ids_data_for_test ( & [ "other_segment" ] ) ;
288+ vm. segments . add ( ) ;
289+ vm. segments . add ( ) ;
290+ vm. set_fp ( 1 ) ;
291+ let ap_tracking = ApTracking :: default ( ) ;
292+ let result = builtin_usage_add_other_segment ( & mut vm, & ids_data, & ap_tracking, finalize) ;
293+ assert ! ( result. is_ok( ) ) ;
294+
295+ // Check that other_segment was added correctly.
296+ let other_segment = vm
297+ . segments
298+ . memory
299+ . get_relocatable ( Relocatable :: from ( ( 1 , 0 ) ) )
300+ . expect ( "Failed to get other_segment" ) ;
301+ assert_eq ! ( other_segment, Relocatable :: from( ( 2 , 0 ) ) ) ;
302+
303+ // Check that the segment is finalized based on the finalize flag.
304+ if finalize {
305+ assert_eq ! ( vm. segments. get_segment_size( 2 ) , Some ( 1 ) ) ;
306+ } else {
307+ assert_eq ! ( vm. segments. get_segment_size( 2 ) , None ) ;
308+ }
309+ }
310+
311+ /// Test the builtin_usage_add_signature hint with a sample signature.
312+ /// Checks that the signature is added correctly to the VM's state.
313+ #[ test]
314+ fn test_builtin_usage_add_signature ( ) {
315+ let mut vm = VirtualMachine :: new ( false , false ) ;
316+ let ids_data = prepare_ids_data_for_test ( & [ "ecdsa_ptr" ] ) ;
317+ let ap_tracking = ApTracking :: default ( ) ;
318+ vm. segments . add ( ) ;
319+ vm. segments . add ( ) ;
320+ vm. set_fp ( 1 ) ;
321+ vm. set_ap ( 1 ) ;
322+
323+ // Initialize the SignatureBuiltinRunner with a base of 2.
324+ let mut ecdsa_builtin = SignatureBuiltinRunner :: new ( Some ( 512 ) , true ) ;
325+ ecdsa_builtin. initialize_segments ( & mut vm. segments ) ;
326+ assert_eq ! ( ecdsa_builtin. base, 2 ) ;
327+ vm. builtin_runners = vec ! [ ecdsa_builtin. into( ) ] ;
328+
329+ // Load the ecdsa_ptr (2,0) into the VM's memory
330+ let _ = vm
331+ . load_data (
332+ Relocatable :: from ( ( 1 , 0 ) ) ,
333+ & [ MaybeRelocatable :: from ( Relocatable :: from ( ( 2 , 0 ) ) ) ] ,
334+ )
335+ . unwrap ( ) ;
336+
337+ builtin_usage_add_signature ( & mut vm, & ids_data, & ap_tracking)
338+ . expect ( "Failed to add signature" ) ;
339+
340+ // Check that the signature was added correctly.
341+ if let BuiltinRunner :: Signature ( sig_runner) = & vm. builtin_runners [ 0 ] {
342+ let signatures = sig_runner. signatures . try_borrow_mut ( ) . unwrap ( ) ;
343+ let signature = signatures
344+ . get ( & Relocatable :: from ( ( 2 , 0 ) ) )
345+ . expect ( "Signature not found at ecdsa_ptr" ) ;
346+ let expected_signature = (
347+ Felt252 :: from_dec_str (
348+ "3086480810278599376317923499561306189851900463386393948998357832163236918254" ,
349+ )
350+ . unwrap ( ) ,
351+ Felt252 :: from_dec_str (
352+ "598673427589502599949712887611119751108407514580626464031881322743364689811" ,
353+ )
354+ . unwrap ( ) ,
355+ ) ;
356+ assert_eq ! ( signature. r, expected_signature. 0 ) ;
357+ assert_eq ! ( signature. s, expected_signature. 1 ) ;
358+ } else {
359+ panic ! ( "Not a SignatureBuiltinRunner" ) ;
360+ }
361+ }
362+
363+ /// Test the builtin_usage_5_to_ap function - checks that the value 5 is inserted into the AP.
364+ /// Checks that Ap indeed has the value 5 and not 6.
365+ #[ test]
366+ fn test_builtin_usage_5_to_ap ( ) {
367+ let mut vm = VirtualMachine :: new ( false , false ) ;
368+ vm. segments . add ( ) ;
369+ vm. segments . add ( ) ;
370+ vm. set_fp ( 1 ) ;
371+ vm. set_ap ( 1 ) ;
372+ let result = builtin_usage_5_to_ap ( & mut vm) ;
373+ assert ! ( result. is_ok( ) ) ;
374+ // Check that the value 5 was inserted into the AP.
375+ let ap = vm. get_ap ( ) ;
376+ let value = vm
377+ . segments
378+ . memory
379+ . get_integer ( ap)
380+ . expect ( "Failed to get AP value" ) ;
381+ assert_eq ! ( value, Cow :: Borrowed ( & Felt252 :: from( 5 ) ) ) ;
382+
383+ //Check that 6 is not in ap.
384+ assert_ne ! ( value, Cow :: Borrowed ( & Felt252 :: from( 6 ) ) ) ;
385+
386+ //Also check that 5 is still in ap.
387+ assert_eq ! ( value, Cow :: Borrowed ( & Felt252 :: from( 5 ) ) ) ;
388+ }
389+
390+ /// Test the builtin_usage_set_pages_and_fact_topology function.
391+ /// Checks the assertion of the pedersen hash and the addition of pages and fact topology to the
392+ /// output builtin.
393+ #[ rstest(
394+ left_pedersen_hash,
395+ case:: hash_matches( 123_usize ) ,
396+ case:: hash_mismatch( 122_usize )
397+ ) ]
398+ fn test_builtin_usage_set_pages_and_fact_topology ( left_pedersen_hash : usize ) {
399+ let mut vm = VirtualMachine :: new ( false , false ) ;
400+ let ids_data = prepare_ids_data_for_test ( & [ "output_ptr" ] ) ;
401+ let ap_tracking = ApTracking :: default ( ) ;
402+ vm. segments . add ( ) ;
403+ vm. segments . add ( ) ;
404+ vm. set_fp ( 1 ) ;
405+ vm. set_ap ( 1 ) ;
406+ // Load the output_ptr (2,0) into the VM's memory
407+ let _ = vm
408+ . load_data (
409+ Relocatable :: from ( ( 1 , 0 ) ) ,
410+ & [ MaybeRelocatable :: from ( Relocatable :: from ( ( 2 , 0 ) ) ) ] ,
411+ )
412+ . unwrap ( ) ;
413+
414+ // Initialize the OutputBuiltinRunner with a base of 2.
415+ let output_segment = vm. add_memory_segment ( ) ;
416+ let mut output_builtin_runner = OutputBuiltinRunner :: new ( true ) ;
417+ let temp_output_builtin_state = OutputBuiltinState {
418+ base : output_segment. segment_index as usize ,
419+ base_offset : 0 ,
420+ pages : Default :: default ( ) ,
421+ attributes : Default :: default ( ) ,
422+ } ;
423+ output_builtin_runner. set_state ( temp_output_builtin_state) ;
424+ vm. builtin_runners = vec ! [ output_builtin_runner. into( ) ] ;
425+
426+ // Load pedersen hash into output builtin_ptr
427+ let ped_hash = pedersen_hash (
428+ & FieldElement :: from ( left_pedersen_hash) ,
429+ & FieldElement :: from ( 456_usize ) ,
430+ ) ;
431+ let ped_hash_felt = field_element_to_felt ( ped_hash) ;
432+ let _ = vm
433+ . load_data (
434+ Relocatable :: from ( ( 2 , 0 ) ) ,
435+ & [ MaybeRelocatable :: from ( ped_hash_felt) ] ,
436+ )
437+ . unwrap ( ) ;
438+
439+ let result = builtin_usage_set_pages_and_fact_topology ( & mut vm, & ids_data, & ap_tracking) ;
440+
441+ // Hint should succeed if the pedersen hash matches the expected value.
442+ if left_pedersen_hash == 123_usize {
443+ assert ! ( result. is_ok( ) ) ;
444+ } else {
445+ assert ! ( result. is_err( ) ) ;
446+ return ;
447+ }
448+
449+ // Assert that OutputBuiltinRunner has the expected pages and attributes.
450+ if let BuiltinRunner :: Output ( output_runner) = & mut vm. builtin_runners [ 0 ] {
451+ let output_builtin_state = output_runner. get_state ( ) ;
452+ let expected_output_builtin_state = OutputBuiltinState {
453+ base : 2 ,
454+ base_offset : 0 ,
455+ pages : HashMap :: from ( [
456+ ( 1 , PublicMemoryPage { start : 1 , size : 2 } ) ,
457+ ( 2 , PublicMemoryPage { start : 3 , size : 2 } ) ,
458+ ] ) ,
459+ attributes : HashMap :: from ( [ ( GPS_FACT_TOPOLOGY . into ( ) , vec ! [ 3 , 2 , 0 , 1 , 0 , 2 ] ) ] ) ,
460+ } ;
461+ assert_eq ! ( output_builtin_state, expected_output_builtin_state) ;
462+ } else {
463+ panic ! ( "Not an OutputBuiltinRunner" ) ;
464+ }
465+ }
466+
467+ /// Test the flexible_builtin_usage_from_input function with a sample input.
468+ /// Asserts that the hint puts all values into the VM's locals as expected.
469+ #[ test]
470+ fn test_flexible_builtin_usage_from_input ( ) {
471+ let input = FlexibleBuiltinUsageInput {
472+ n_output : 1 ,
473+ n_pedersen : 2 ,
474+ n_range_check : 3 ,
475+ n_ecdsa : 4 ,
476+ n_bitwise : 5 ,
477+ n_ec_op : 6 ,
478+ n_keccak : 7 ,
479+ n_poseidon : 8 ,
480+ n_range_check96 : 9 ,
481+ n_add_mod : 10 ,
482+ n_mul_mod : 11 ,
483+ n_memory_holes : 12 ,
484+ n_blake2s : 13 ,
485+ } ;
486+ let ( mut vm, mut exec_scopes, ids_data, ap_tracking) =
487+ prepare_vm_for_flexible_builtin_usage_test ( & input) ;
488+
489+ let result =
490+ flexible_builtin_usage_from_input ( & mut vm, & mut exec_scopes, & ids_data, & ap_tracking) ;
491+ assert ! ( result. is_ok( ) ) ;
492+
493+ // Check that the VM state was modified as expected.
494+ let local_names = [
495+ "n_output" ,
496+ "n_pedersen" ,
497+ "n_range_check" ,
498+ "n_ecdsa" ,
499+ "n_bitwise" ,
500+ "n_ec_op" ,
501+ "n_keccak" ,
502+ "n_poseidon" ,
503+ "n_range_check96" ,
504+ "n_add_mod" ,
505+ "n_mul_mod" ,
506+ "n_memory_holes" ,
507+ "n_blake2s" ,
508+ ] ;
509+ let expected_values = [
510+ input. n_output ,
511+ input. n_pedersen ,
512+ input. n_range_check ,
513+ input. n_ecdsa ,
514+ input. n_bitwise ,
515+ input. n_ec_op ,
516+ input. n_keccak ,
517+ input. n_poseidon ,
518+ input. n_range_check96 ,
519+ input. n_add_mod ,
520+ input. n_mul_mod ,
521+ input. n_memory_holes ,
522+ input. n_blake2s ,
523+ ] ;
524+
525+ // Check that the VM's locals match the expected values.
526+ // The locals are stored at FP + offset2, where offset2 is the offset in the HintReference.
527+ // The offset2 is always a Value, so we can safely use it
528+ // to calculate the address of the local variable.
529+ for ( i, ( & name, & expected) ) in local_names. iter ( ) . zip ( expected_values. iter ( ) ) . enumerate ( ) {
530+ let hint_ref = ids_data. get ( name) . expect ( "Missing HintReference" ) ;
531+ // Calculate address: FP + offset2
532+ let offset: i32 = match hint_ref. offset1 {
533+ OffsetValue :: Immediate ( _) => panic ! ( "Unexpected Immediate in offset2 for {name}" ) ,
534+ OffsetValue :: Value ( _) => panic ! ( "Unexpected Value in offset2 for {name}" ) ,
535+ OffsetValue :: Reference ( _, offset, _, _) => offset,
536+ } ;
537+ let fp = vm. get_fp ( ) ;
538+ let addr = Relocatable :: from ( ( fp. segment_index , ( fp. offset as i32 + offset) as usize ) ) ;
539+ let value_owned = vm. segments . memory . get_integer ( addr) . unwrap ( ) ;
540+ let value = value_owned. as_ref ( ) ;
541+ assert_eq ! (
542+ value,
543+ & Felt252 :: from( expected) ,
544+ "Mismatch for {name} at local {i}"
545+ ) ;
546+ }
547+ }
548+ }
0 commit comments