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+ allocate_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 ( ) {
@@ -290,22 +296,258 @@ fn allocate_aliases_for_keys_and_replace(
290296 Some ( state_reader) ,
291297 )
292298 . unwrap ( ) ;
293- if let [
299+ let [
294300 EndpointArg :: Value ( ValueArg :: Single ( n_aliases) ) ,
295301 EndpointArg :: Pointer ( PointerArg :: Array ( aliases_storage_updates) ) ,
296302 EndpointArg :: Pointer ( PointerArg :: Array ( alias_per_key) ) ,
297303 ] = explicit_return_values. as_slice ( )
298- {
299- let n_aliases = felt_to_usize ( n_aliases) . unwrap ( ) ;
300- assert_eq ! ( n_aliases, aliases_storage_updates. len( ) / DICT_ACCESS_SIZE ) ;
301- let aliases_storage_updates_as_felts: Vec < Felt > =
302- aliases_storage_updates. iter ( ) . map ( |f| f. get_int ( ) . unwrap ( ) ) . collect ( ) ;
303- let actual_alias_storage = parse_squashed_cairo_dict ( & aliases_storage_updates_as_felts) ;
304- let alias_per_key: Vec < Felt > = alias_per_key. iter ( ) . map ( |f| f. get_int ( ) . unwrap ( ) ) . collect ( ) ;
305- ( actual_alias_storage, alias_per_key)
306- } else {
304+ else {
307305 panic ! (
308306 "The return value doesn't match the given format.\n Got: {explicit_return_values:?}"
309307 ) ;
308+ } ;
309+ let n_aliases = felt_to_usize ( n_aliases) . unwrap ( ) ;
310+ assert_eq ! ( n_aliases, aliases_storage_updates. len( ) / DICT_ACCESS_SIZE ) ;
311+ let aliases_storage_updates_as_felts: Vec < Felt > =
312+ aliases_storage_updates. iter ( ) . map ( |f| f. get_int ( ) . unwrap ( ) ) . collect ( ) ;
313+ let actual_alias_storage = parse_squashed_cairo_dict ( & aliases_storage_updates_as_felts) ;
314+ let alias_per_key: Vec < Felt > = alias_per_key. iter ( ) . map ( |f| f. get_int ( ) . unwrap ( ) ) . collect ( ) ;
315+ ( actual_alias_storage, alias_per_key)
316+ }
317+
318+ #[ rstest]
319+ #[ case:: non_allocation_of_address_lt_16_from_empty_storage(
320+ HashMap :: from( [
321+ (
322+ 15 ,
323+ HashMap :: from( [ ( 5534 , 1 ) , ( 98435 , 1 ) , ( 99999 , 1 ) ] )
324+ ) ,
325+ (
326+ 16 ,
327+ HashMap :: from( [ ( 11 , 1 ) , ( 127 , 1 ) , ( 128 , 1 ) , ( 129 , 1 ) , ( 225 , 1 ) , ( 7659 , 1 ) ] )
328+ ) ,
329+ (
330+ 7659 ,
331+ HashMap :: from( [ ( 12 , 0 ) , ( 200 , 1 ) , ( 300 , 1 ) , ( 1111 , 1 ) ] )
332+ ) ,
333+ (
334+ 99999 ,
335+ HashMap :: from( [ ( 225 , 1 ) ] )
336+ )
337+ ] ) ,
338+ HashMap :: new( ) ,
339+ HashMap :: new( ) ,
340+ HashMap :: new( ) ,
341+ HashMap :: from( [ ( 0 , 136 ) , ( 128 , 128 ) , ( 129 , 129 ) , ( 200 , 132 ) , ( 225 , 130 ) , ( 300 , 133 ) , ( 1111 , 134 ) , ( 7659 , 131 ) , ( 99999 , 135 ) ] )
342+ ) ]
343+ #[ case:: non_allocation_of_address_lt_16_from_non_empty_storage(
344+ HashMap :: from( [
345+ (
346+ 9 ,
347+ HashMap :: from( [ ( 5534 , 1 ) , ( 98435 , 1 ) , ( 99999 , 1 ) ] )
348+ ) ,
349+ (
350+ 44 ,
351+ HashMap :: from( [ ( 11 , 1 ) , ( 129 , 1 ) , ( 225 , 1 ) , ( 7659 , 1 ) ] )
352+ ) ,
353+ (
354+ 400 ,
355+ HashMap :: from( [ ( 225 , 1 ) , ( 400 , 1 ) , ( 700 , 1 ) , ( 701 , 1 ) , ( 1111 , 1 ) ] )
356+ ) ,
357+ ] ) ,
358+ HashMap :: new( ) ,
359+ HashMap :: new( ) ,
360+ HashMap :: from( [ ( 0 , 135 ) , ( 129 , 128 ) , ( 225 , 129 ) , ( 7659 , 130 ) , ( 200 , 131 ) , ( 300 , 132 ) , ( 1111 , 133 ) , ( 99999 , 134 ) ] ) ,
361+ HashMap :: from( [ ( 0 , 138 ) , ( 129 , 128 ) , ( 225 , 129 ) , ( 400 , 135 ) , ( 700 , 136 ) , ( 701 , 137 ) , ( 1111 , 133 ) , ( 7659 , 130 ) ]
362+ )
363+ ) ]
364+ #[ case:: non_allocation_with_only_trivial_updates(
365+ HashMap :: from( [
366+ (
367+ 11 ,
368+ HashMap :: from( [ ( 5534 , 1 ) , ( 98435 , 1 ) , ( 99999 , 1 ) ] )
369+ ) ,
370+ (
371+ 44 ,
372+ HashMap :: from( [ ( 11 , 0 ) , ( 129 , 1 ) , ( 225 , 0 ) , ( 400 , 1 ) , ( 7659 , 1 ) ] )
373+ ) ,
374+ (
375+ 400 ,
376+ HashMap :: from( [ ( 225 , 0 ) , ( 406 , 0 ) , ( 700 , 1 ) , ( 701 , 1 ) , ( 1111 , 1 ) ] )
377+ ) ,
378+ (
379+ 598 ,
380+ HashMap :: from( [ ( 2255 , 0 ) , ( 7008 , 0 ) ] ) // Trivial update.
381+ )
382+ ] ) ,
383+ HashMap :: new( ) ,
384+ HashMap :: new( ) ,
385+ HashMap :: new( ) ,
386+ HashMap :: from( [ ( 0 , 134 ) , ( 129 , 128 ) , ( 400 , 129 ) , ( 700 , 131 ) , ( 701 , 132 ) , ( 1111 , 133 ) , ( 7659 , 130 ) ]
387+ )
388+ ) ]
389+ #[ case:: allocation_with_only_nonce_change(
390+ HashMap :: new( ) ,
391+ HashMap :: from( [
392+ ( 13 , 1 ) ,
393+ ( 58 , 1 ) ,
394+ ( 11111 , DEFAULT_CLASS_HASH ) , // Gets a new nonce.
395+ ( 222222 , 1 ) ,
396+ ( 3333333 , 1 ) ,
397+ ( 3333336 , DEFAULT_CLASS_HASH ) , // Nothing changed.
398+ ] ) ,
399+ HashMap :: from( [ ( 11111 , 1 ) ] ) ,
400+ HashMap :: new( ) ,
401+ HashMap :: from( [ ( 0 , 131 ) , ( 11111 , 128 ) , ( 222222 , 129 ) , ( 3333333 , 130 ) ]
402+ )
403+ ) ]
404+ #[ case:: non_allocation_with_trivial_class_hash_update(
405+ HashMap :: new( ) ,
406+ HashMap :: from( [ ( 24 , 1 ) , ( 5000 , 1 ) , ( 6666 , 1 ) , ( 9999 , 1 ) , ( 11111 , DEFAULT_CLASS_HASH ) ,
407+ ] ) ,
408+ HashMap :: new( ) ,
409+ HashMap :: from( [ ( 0 , 133 ) , ( 5000 , 128 ) , ( 11111 , 129 ) , ( 222222 , 130 ) , ( 3333333 , 131 ) , ( 87777 , 132 ) ] ) ,
410+ HashMap :: from( [ ( 0 , 135 ) , ( 5000 , 128 ) , ( 6666 , 133 ) , ( 9999 , 134 ) ]
411+ )
412+ ) ]
413+ #[ case:: allocation_with_partially_trivial_updates(
414+ HashMap :: from( [
415+ (
416+ 1 ,
417+ HashMap :: from( [ ( 777 , 1 ) , ( 8888 , 1 ) , ( 9999 , 1 ) ] ) // No aliases.
418+ ) ,
419+ (
420+ 100 ,
421+ HashMap :: from( [ ( 200 , 1 ) , ( 777 , 1 ) , ( 888 , 0 ) ] ) // Aliases for non-trivial diffs.
422+ ) ,
423+ (
424+ 600 ,
425+ HashMap :: from( [ ( 2000 , 1 ) , ( 3000 , 1 ) ] )
426+ ) ,
427+ (
428+ 800 ,
429+ HashMap :: from( [ ( 700 , 1 ) , ( 701 , 1 ) ] )
430+ ) ,
431+ (
432+ 3000 ,
433+ HashMap :: from( [ ( 600 , 1 ) , ( 2000 , 1 ) ] )
434+ ) ,
435+ (
436+ 10000 ,
437+ HashMap :: from( [ ( 34567 , 0 ) , ( 435 , 0 ) ] ) // No aliases (all diffs trivial).
438+ )
439+ ] ) ,
440+ HashMap :: from( [ ( 200 , 1 ) , ( 500 , 1 ) , ( 700 , 1 ) , ( 800 , DEFAULT_CLASS_HASH ) ] ) ,
441+ HashMap :: from( [ ( 700 , 1 ) , ( 10000 , 0 ) ] ) ,
442+ HashMap :: new( ) ,
443+ HashMap :: from( [ ( 0 , 137 ) , ( 200 , 128 ) , ( 500 , 130 ) , ( 600 , 133 ) , ( 700 , 134 ) , ( 701 , 135 ) , ( 777 , 129 ) , ( 800 , 136 ) , ( 2000 , 131 ) , ( 3000 , 132 ) ] )
444+ ) ]
445+ fn test_allocate_addresses_for_state_diff_and_replace (
446+ #[ case] storage_updates : HashMap < u128 , HashMap < u128 , u128 > > ,
447+ #[ case] address_to_class_hash : HashMap < u128 , u128 > ,
448+ #[ case] address_to_nonce : HashMap < u128 , u128 > ,
449+ #[ case] initial_alias_storage : HashMap < u128 , u128 > ,
450+ #[ case] expected_alias_storage : HashMap < u128 , u128 > ,
451+ ) {
452+ let runner_config = get_entrypoint_runner_config ( ) ;
453+ let entrypoint = "__main__.allocate_aliases_and_replace" ;
454+ let implicit_args = [ ImplicitArg :: Builtin ( BuiltinName :: range_check) ] ;
455+ let modified_contracts: BTreeSet < _ > = storage_updates
456+ . keys ( )
457+ . chain ( address_to_class_hash. keys ( ) . chain ( address_to_nonce. keys ( ) ) )
458+ . collect ( ) ;
459+
460+ // Initialize the runner to be able to allocate segments.
461+ let ( mut cairo_runner, program, entrypoint) = initialize_cairo_runner (
462+ & runner_config,
463+ ALIASES_TEST_BYTES ,
464+ entrypoint,
465+ & implicit_args,
466+ HashMap :: new ( ) ,
467+ )
468+ . unwrap ( ) ;
469+
470+ // Construct the contract state changes.
471+ let mut prev_state_entries = HashMap :: new ( ) ;
472+ let mut new_state_entries = HashMap :: new ( ) ;
473+ let n_contracts = modified_contracts. len ( ) ;
474+ for address in modified_contracts {
475+ let inner_updates = storage_updates
476+ . get ( address)
477+ . unwrap_or ( & HashMap :: new ( ) )
478+ . iter ( )
479+ . map ( |( k, v) | ( ( * k) . into ( ) , Felt :: from ( * v) . into ( ) ) )
480+ . collect ( ) ;
481+ let ( new_nonce, prev_nonce) = ( address_to_nonce. get ( address) . copied ( ) . unwrap_or ( 0 ) , 0 ) ;
482+ let ( new_class_hash, prev_class_hash) = (
483+ address_to_class_hash. get ( address) . copied ( ) . unwrap_or ( DEFAULT_CLASS_HASH ) ,
484+ DEFAULT_CLASS_HASH ,
485+ ) ;
486+ let ( prev_storage_ptr, new_storage_ptr) =
487+ allocate_squashed_cairo_dict ( & HashMap :: new ( ) , & inner_updates, & mut cairo_runner. vm ) ;
488+ let new_state_entry: Vec < MaybeRelocatable > = vec ! [
489+ Felt :: from( new_class_hash) . into( ) ,
490+ new_storage_ptr. into( ) ,
491+ Felt :: from( new_nonce) . into( ) ,
492+ ] ;
493+ let prev_state_entry: Vec < MaybeRelocatable > = vec ! [
494+ Felt :: from( prev_class_hash) . into( ) ,
495+ prev_storage_ptr. into( ) ,
496+ Felt :: from( prev_nonce) . into( ) ,
497+ ] ;
498+ new_state_entries
499+ . insert ( ( * address) . into ( ) , cairo_runner. vm . gen_arg ( & new_state_entry) . unwrap ( ) ) ;
500+ prev_state_entries
501+ . insert ( ( * address) . into ( ) , cairo_runner. vm . gen_arg ( & prev_state_entry) . unwrap ( ) ) ;
310502 }
503+ let flat_contract_state_changes = flatten_cairo_dict ( & prev_state_entries, & new_state_entries) ;
504+ let explicit_args = vec ! [
505+ EndpointArg :: Value ( ValueArg :: Single ( n_contracts. into( ) ) ) ,
506+ EndpointArg :: Pointer ( PointerArg :: Array ( flat_contract_state_changes) ) ,
507+ ] ;
508+ let storage_view = initial_alias_storage
509+ . into_iter ( )
510+ . map ( |( key, value) | ( ( * ALIAS_CONTRACT_ADDRESS , key. into ( ) ) , value. into ( ) ) )
511+ . collect ( ) ;
512+ let state_reader = DictStateReader { storage_view, ..Default :: default ( ) } ;
513+ let expected_aliases_storage_flat_length = expected_alias_storage. len ( ) * DICT_ACCESS_SIZE ;
514+ let expected_explicit_return_values = vec ! [
515+ EndpointArg :: Pointer ( PointerArg :: Array ( vec![
516+ MaybeRelocatable :: Int ( Felt :: ZERO ) ;
517+ expected_aliases_storage_flat_length
518+ ] ) ) ,
519+ EndpointArg :: Pointer ( PointerArg :: Array ( vec![ ] ) ) ,
520+ EndpointArg :: Pointer ( PointerArg :: Array ( vec![ ] ) ) ,
521+ ] ;
522+
523+ let ( _, explicit_return_values) = run_cairo_0_entrypoint (
524+ entrypoint,
525+ & explicit_args,
526+ & implicit_args,
527+ Some ( state_reader) ,
528+ & mut cairo_runner,
529+ & program,
530+ & runner_config,
531+ & expected_explicit_return_values,
532+ )
533+ . unwrap ( ) ;
534+
535+ // TODO(Nimrod): Complete this test to also compare the other return values.
536+ let [
537+ EndpointArg :: Pointer ( PointerArg :: Array ( aliases_storage_updates) ) ,
538+ EndpointArg :: Pointer ( PointerArg :: Array ( _) ) ,
539+ EndpointArg :: Pointer ( PointerArg :: Array ( _) ) ,
540+ ] = explicit_return_values. as_slice ( )
541+ else {
542+ panic ! (
543+ "The return value doesn't match the given format.\n Got: {explicit_return_values:?}"
544+ ) ;
545+ } ;
546+
547+ let aliases_storage_updates_as_felts: Vec < Felt > =
548+ aliases_storage_updates. iter ( ) . map ( |f| f. get_int ( ) . unwrap ( ) ) . collect ( ) ;
549+ let actual_alias_storage = parse_squashed_cairo_dict ( & aliases_storage_updates_as_felts) ;
550+ let expected_alias_storage: HashMap < Felt , Felt > =
551+ expected_alias_storage. into_iter ( ) . map ( |( key, value) | ( key. into ( ) , value. into ( ) ) ) . collect ( ) ;
552+ assert_eq ! ( actual_alias_storage, expected_alias_storage) ;
311553}
0 commit comments