1- use std:: collections:: { HashMap , HashSet } ;
1+ use core:: panic;
2+ use std:: collections:: { BTreeSet , HashMap , HashSet } ;
23
34use apollo_starknet_os_program:: test_programs:: ALIASES_TEST_BYTES ;
45use blockifier:: state:: stateful_compression:: { ALIAS_COUNTER_STORAGE_KEY , INITIAL_AVAILABLE_ALIAS } ;
@@ -15,20 +16,25 @@ use starknet_types_core::felt::Felt;
1516
1617use crate :: test_utils:: cairo_runner:: {
1718 initialize_and_run_cairo_0_entry_point,
19+ initialize_cairo_runner,
20+ run_cairo_0_entrypoint,
1821 EndpointArg ,
1922 EntryPointRunnerConfig ,
2023 ImplicitArg ,
2124 PointerArg ,
2225 ValueArg ,
2326} ;
2427use crate :: test_utils:: utils:: {
28+ create_squashed_cairo_dict,
29+ flatten_cairo_dict,
2530 get_entrypoint_runner_config,
2631 parse_squashed_cairo_dict,
2732 test_cairo_function,
2833} ;
2934
3035// TODO(Nimrod): Move this next to the stateful compression hints implementation.
31- // TODO(Amos): This test is incomplete. Add the rest of the test cases and remove this todo.
36+
37+ const DEFAULT_CLASS_HASH : u128 = 7777 ;
3238
3339#[ test]
3440fn test_constants ( ) {
@@ -309,3 +315,242 @@ fn allocate_aliases_for_keys_and_replace(
309315 ) ;
310316 }
311317}
318+
319+ #[ rstest]
320+ #[ case:: non_allocation_of_address_lt_16_from_empty_storage(
321+ HashMap :: from( [
322+ (
323+ 15 ,
324+ HashMap :: from( [ ( 5534 , 1 ) , ( 98435 , 1 ) , ( 99999 , 1 ) ] )
325+ ) ,
326+ (
327+ 16 ,
328+ HashMap :: from( [ ( 11 , 1 ) , ( 127 , 1 ) , ( 128 , 1 ) , ( 129 , 1 ) , ( 225 , 1 ) , ( 7659 , 1 ) ] )
329+ ) ,
330+ (
331+ 7659 ,
332+ HashMap :: from( [ ( 12 , 0 ) , ( 200 , 1 ) , ( 300 , 1 ) , ( 1111 , 1 ) ] )
333+ ) ,
334+ (
335+ 99999 ,
336+ HashMap :: from( [ ( 225 , 1 ) ] )
337+ )
338+ ] ) ,
339+ HashMap :: new( ) ,
340+ HashMap :: new( ) ,
341+ HashMap :: new( ) ,
342+ HashMap :: from( [ ( 0 , 136 ) , ( 128 , 128 ) , ( 129 , 129 ) , ( 200 , 132 ) , ( 225 , 130 ) , ( 300 , 133 ) , ( 1111 , 134 ) , ( 7659 , 131 ) , ( 99999 , 135 ) ] )
343+ ) ]
344+ #[ case:: non_allocation_of_address_lt_16_from_non_empty_storage(
345+ HashMap :: from( [
346+ (
347+ 9 ,
348+ HashMap :: from( [ ( 5534 , 1 ) , ( 98435 , 1 ) , ( 99999 , 1 ) ] )
349+ ) ,
350+ (
351+ 44 ,
352+ HashMap :: from( [ ( 11 , 1 ) , ( 129 , 1 ) , ( 225 , 1 ) , ( 7659 , 1 ) ] )
353+ ) ,
354+ (
355+ 400 ,
356+ HashMap :: from( [ ( 225 , 1 ) , ( 400 , 1 ) , ( 700 , 1 ) , ( 701 , 1 ) , ( 1111 , 1 ) ] )
357+ ) ,
358+ ] ) ,
359+ HashMap :: new( ) ,
360+ HashMap :: new( ) ,
361+ HashMap :: from( [ ( 0 , 135 ) , ( 129 , 128 ) , ( 225 , 129 ) , ( 7659 , 130 ) , ( 200 , 131 ) , ( 300 , 132 ) , ( 1111 , 133 ) , ( 99999 , 134 ) ] ) ,
362+ HashMap :: from( [ ( 0 , 138 ) , ( 129 , 128 ) , ( 225 , 129 ) , ( 400 , 135 ) , ( 700 , 136 ) , ( 701 , 137 ) , ( 1111 , 133 ) , ( 7659 , 130 ) ]
363+ )
364+ ) ]
365+ #[ case:: non_allocation_with_only_trivial_updates(
366+ HashMap :: from( [
367+ (
368+ 11 ,
369+ HashMap :: from( [ ( 5534 , 1 ) , ( 98435 , 1 ) , ( 99999 , 1 ) ] )
370+ ) ,
371+ (
372+ 44 ,
373+ HashMap :: from( [ ( 11 , 0 ) , ( 129 , 1 ) , ( 225 , 0 ) , ( 400 , 1 ) , ( 7659 , 1 ) ] )
374+ ) ,
375+ (
376+ 400 ,
377+ HashMap :: from( [ ( 225 , 0 ) , ( 406 , 0 ) , ( 700 , 1 ) , ( 701 , 1 ) , ( 1111 , 1 ) ] )
378+ ) ,
379+ (
380+ 598 ,
381+ HashMap :: from( [ ( 2255 , 0 ) , ( 7008 , 0 ) ] ) // Trivial update.
382+ )
383+ ] ) ,
384+ HashMap :: new( ) ,
385+ HashMap :: new( ) ,
386+ HashMap :: new( ) ,
387+ HashMap :: from( [ ( 0 , 134 ) , ( 129 , 128 ) , ( 400 , 129 ) , ( 700 , 131 ) , ( 701 , 132 ) , ( 1111 , 133 ) , ( 7659 , 130 ) ]
388+ )
389+ ) ]
390+ #[ case:: allocation_with_only_nonce_change(
391+ HashMap :: new( ) ,
392+ HashMap :: from( [
393+ ( 13 , 1 ) ,
394+ ( 58 , 1 ) ,
395+ ( 11111 , DEFAULT_CLASS_HASH ) , // Gets a new nonce.
396+ ( 222222 , 1 ) ,
397+ ( 3333333 , 1 ) ,
398+ ( 3333336 , DEFAULT_CLASS_HASH ) , // Nothing changed.
399+ ] ) ,
400+ HashMap :: from( [ ( 11111 , 1 ) ] ) ,
401+ HashMap :: new( ) ,
402+ HashMap :: from( [ ( 0 , 131 ) , ( 11111 , 128 ) , ( 222222 , 129 ) , ( 3333333 , 130 ) ]
403+ )
404+ ) ]
405+ #[ case:: non_allocation_with_trivial_class_hash_update(
406+ HashMap :: new( ) ,
407+ HashMap :: from( [ ( 24 , 1 ) , ( 5000 , 1 ) , ( 6666 , 1 ) , ( 9999 , 1 ) , ( 11111 , DEFAULT_CLASS_HASH ) ,
408+ ] ) ,
409+ HashMap :: new( ) ,
410+ HashMap :: from( [ ( 0 , 133 ) , ( 5000 , 128 ) , ( 11111 , 129 ) , ( 222222 , 130 ) , ( 3333333 , 131 ) , ( 87777 , 132 ) ] ) ,
411+ HashMap :: from( [ ( 0 , 135 ) , ( 5000 , 128 ) , ( 6666 , 133 ) , ( 9999 , 134 ) ]
412+ )
413+ ) ]
414+ #[ case:: allocation_with_partially_trivial_updates(
415+ HashMap :: from( [
416+ (
417+ 1 ,
418+ HashMap :: from( [ ( 777 , 1 ) , ( 8888 , 1 ) , ( 9999 , 1 ) ] )
419+ ) ,
420+ (
421+ 100 ,
422+ HashMap :: from( [ ( 200 , 1 ) , ( 777 , 1 ) , ( 888 , 0 ) ] )
423+ ) ,
424+ (
425+ 600 ,
426+ HashMap :: from( [ ( 2000 , 1 ) , ( 3000 , 1 ) ] )
427+ ) ,
428+ (
429+ 800 ,
430+ HashMap :: from( [ ( 700 , 1 ) , ( 701 , 1 ) ] )
431+ ) ,
432+ (
433+ 3000 ,
434+ HashMap :: from( [ ( 600 , 1 ) , ( 2000 , 1 ) ] )
435+ ) ,
436+ (
437+ 10000 ,
438+ HashMap :: from( [ ( 34567 , 0 ) , ( 435 , 0 ) ] )
439+ )
440+ ] ) ,
441+ HashMap :: from( [ ( 200 , 1 ) , ( 500 , 1 ) , ( 700 , 1 ) , ( 800 , DEFAULT_CLASS_HASH ) ] ) ,
442+ HashMap :: from( [ ( 700 , 1 ) , ( 10000 , 0 ) ] ) ,
443+ HashMap :: new( ) ,
444+ HashMap :: from( [ ( 0 , 137 ) , ( 200 , 128 ) , ( 500 , 130 ) , ( 600 , 133 ) , ( 700 , 134 ) , ( 701 , 135 ) , ( 777 , 129 ) , ( 800 , 136 ) , ( 2000 , 131 ) , ( 3000 , 132 ) ] )
445+ ) ]
446+ fn test_allocate_addresses_for_state_diff_and_replace (
447+ #[ case] storage_updates : HashMap < u128 , HashMap < u128 , u128 > > ,
448+ #[ case] address_to_class_hash : HashMap < u128 , u128 > ,
449+ #[ case] address_to_nonce : HashMap < u128 , u128 > ,
450+ #[ case] initial_alias_storage : HashMap < u128 , u128 > ,
451+ #[ case] expected_alias_storage : HashMap < u128 , u128 > ,
452+ ) {
453+ let runner_config = get_entrypoint_runner_config ( ) ;
454+ let entrypoint = "__main__.allocate_aliases_and_replace" ;
455+ let implicit_args = [ ImplicitArg :: Builtin ( BuiltinName :: range_check) ] ;
456+ let modified_contracts: BTreeSet < _ > = storage_updates
457+ . keys ( )
458+ . chain ( address_to_class_hash. keys ( ) . chain ( address_to_nonce. keys ( ) ) )
459+ . collect ( ) ;
460+
461+ // Initialize the runner to be able to allocate segments.
462+ let ( mut cairo_runner, program, entrypoint) = initialize_cairo_runner (
463+ & runner_config,
464+ ALIASES_TEST_BYTES ,
465+ entrypoint,
466+ & implicit_args,
467+ HashMap :: new ( ) ,
468+ )
469+ . unwrap ( ) ;
470+
471+ // Construct the contract state changes.
472+ let mut prev_state_entries = HashMap :: new ( ) ;
473+ let mut new_state_entries = HashMap :: new ( ) ;
474+ let n_contracts = modified_contracts. len ( ) ;
475+ for address in modified_contracts {
476+ let inner_updates = storage_updates
477+ . get ( address)
478+ . unwrap_or ( & HashMap :: new ( ) )
479+ . iter ( )
480+ . map ( |( k, v) | ( ( * k) . into ( ) , Felt :: from ( * v) . into ( ) ) )
481+ . collect ( ) ;
482+ let ( new_nonce, prev_nonce) = ( address_to_nonce. get ( address) . copied ( ) . unwrap_or ( 0 ) , 0 ) ;
483+ let ( new_class_hash, prev_class_hash) = (
484+ address_to_class_hash. get ( address) . copied ( ) . unwrap_or ( DEFAULT_CLASS_HASH ) ,
485+ DEFAULT_CLASS_HASH ,
486+ ) ;
487+ let ( prev_storage_ptr, new_storage_ptr) =
488+ create_squashed_cairo_dict ( & HashMap :: new ( ) , & inner_updates, & mut cairo_runner. vm ) ;
489+ let new_state_entry: Vec < MaybeRelocatable > = vec ! [
490+ Felt :: from( new_class_hash) . into( ) ,
491+ new_storage_ptr. into( ) ,
492+ Felt :: from( new_nonce) . into( ) ,
493+ ] ;
494+ let prev_state_entry: Vec < MaybeRelocatable > = vec ! [
495+ Felt :: from( prev_class_hash) . into( ) ,
496+ prev_storage_ptr. into( ) ,
497+ Felt :: from( prev_nonce) . into( ) ,
498+ ] ;
499+ new_state_entries
500+ . insert ( ( * address) . into ( ) , cairo_runner. vm . gen_arg ( & new_state_entry) . unwrap ( ) ) ;
501+ prev_state_entries
502+ . insert ( ( * address) . into ( ) , cairo_runner. vm . gen_arg ( & prev_state_entry) . unwrap ( ) ) ;
503+ }
504+ let flat_contract_state_changes = flatten_cairo_dict ( & prev_state_entries, & new_state_entries) ;
505+ let explicit_args = vec ! [
506+ EndpointArg :: Value ( ValueArg :: Single ( n_contracts. into( ) ) ) ,
507+ EndpointArg :: Pointer ( PointerArg :: Array ( flat_contract_state_changes) ) ,
508+ ] ;
509+ let storage_view = initial_alias_storage
510+ . into_iter ( )
511+ . map ( |( key, value) | ( ( * ALIAS_CONTRACT_ADDRESS , key. into ( ) ) , value. into ( ) ) )
512+ . collect ( ) ;
513+ let state_reader = DictStateReader { storage_view, ..Default :: default ( ) } ;
514+ let expected_aliases_storage_flat_length = expected_alias_storage. len ( ) * DICT_ACCESS_SIZE ;
515+ let expected_explicit_return_values = vec ! [
516+ EndpointArg :: Pointer ( PointerArg :: Array ( vec![
517+ MaybeRelocatable :: Int ( Felt :: ZERO ) ;
518+ expected_aliases_storage_flat_length
519+ ] ) ) ,
520+ EndpointArg :: Pointer ( PointerArg :: Array ( vec![ ] ) ) ,
521+ EndpointArg :: Pointer ( PointerArg :: Array ( vec![ ] ) ) ,
522+ ] ;
523+
524+ let ( _, explicit_return_values) = run_cairo_0_entrypoint (
525+ entrypoint,
526+ & explicit_args,
527+ & implicit_args,
528+ Some ( state_reader) ,
529+ & mut cairo_runner,
530+ & program,
531+ & runner_config,
532+ & expected_explicit_return_values,
533+ )
534+ . unwrap ( ) ;
535+
536+ // TODO(Nimrod): Complete this test to also compare the other return values.
537+ if let [
538+ EndpointArg :: Pointer ( PointerArg :: Array ( aliases_storage_updates) ) ,
539+ EndpointArg :: Pointer ( PointerArg :: Array ( _) ) ,
540+ EndpointArg :: Pointer ( PointerArg :: Array ( _) ) ,
541+ ] = explicit_return_values. as_slice ( )
542+ {
543+ let aliases_storage_updates_as_felts: Vec < Felt > =
544+ aliases_storage_updates. iter ( ) . map ( |f| f. get_int ( ) . unwrap ( ) ) . collect ( ) ;
545+ let actual_alias_storage = parse_squashed_cairo_dict ( & aliases_storage_updates_as_felts) ;
546+ let expected_alias_storage: HashMap < Felt , Felt > = expected_alias_storage
547+ . into_iter ( )
548+ . map ( |( key, value) | ( key. into ( ) , value. into ( ) ) )
549+ . collect ( ) ;
550+ assert_eq ! ( actual_alias_storage, expected_alias_storage) ;
551+ } else {
552+ panic ! (
553+ "The return value doesn't match the given format.\n Got: {explicit_return_values:?}"
554+ ) ;
555+ }
556+ }
0 commit comments