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