@@ -297,7 +297,7 @@ impl RevertInfo {
297297}
298298
299299/// Represents the call tree of a fuzz test.
300- struct FuzzTestManager {
300+ struct FuzzTestContext {
301301 /// The call tree of the fuzz test.
302302 /// The first frame is the frame called by the orchestrator (it's parent frame is the
303303 /// orchestrator).
@@ -325,12 +325,39 @@ struct FuzzTestManager {
325325 /// Next salt to use for a deploy operation.
326326 pub next_salt : ContractAddressSalt ,
327327
328- pub test_manager : TestBuilder < DictStateReader > ,
329- pub first_called_address : ContractAddress ,
330328 pub orchestrator_contract_address : ContractAddress ,
331329 pub rng : ChaCha8Rng ,
332330}
333331
332+ impl FuzzTestContext {
333+ pub fn init (
334+ seed : u64 ,
335+ orchestrator_contract_address : ContractAddress ,
336+ first_call : FuzzCallInfo ,
337+ deployed_fuzz_contracts : BTreeMap < ContractAddress , ClassHash > ,
338+ ) -> Self {
339+ Self {
340+ calls : vec ! [ first_call] ,
341+ current_call : vec ! [ 0 ] ,
342+ final_state : FinalizedState :: Ongoing ,
343+ operations : vec ! [ ] ,
344+ deployed_contracts : deployed_fuzz_contracts,
345+ class_replaced : false ,
346+ next_storage_write_value : StarknetStorageValue ( Felt :: from ( 1u16 << 12 ) ) ,
347+ next_salt : ContractAddressSalt ( Felt :: from ( 1u32 << 16 ) ) ,
348+ orchestrator_contract_address,
349+ rng : ChaCha8Rng :: seed_from_u64 ( seed) ,
350+ }
351+ }
352+ }
353+
354+ /// Manages the fuzz flow test, with the underlying flow test state.
355+ struct FuzzTestManager {
356+ pub context : FuzzTestContext ,
357+ pub test_manager : TestBuilder < DictStateReader > ,
358+ pub first_called_address : ContractAddress ,
359+ }
360+
334361impl FuzzTestManager {
335362 pub async fn init ( seed : u64 ) -> Self {
336363 // Initialize the state with:
@@ -381,40 +408,36 @@ impl FuzzTestManager {
381408 ParentFailureBehavior :: Cairo1Catching ,
382409 ) ;
383410 Self {
384- calls : vec ! [ first_call] ,
385- current_call : vec ! [ 0 ] ,
386- final_state : FinalizedState :: Ongoing ,
387- operations : vec ! [ ] ,
388- deployed_contracts : deployed_fuzz_contracts,
389- class_replaced : false ,
390- next_storage_write_value : StarknetStorageValue ( Felt :: from ( 1u16 << 12 ) ) ,
391- next_salt : ContractAddressSalt ( Felt :: from ( 1u32 << 16 ) ) ,
411+ context : FuzzTestContext :: init (
412+ seed,
413+ orchestrator_contract_address,
414+ first_call,
415+ deployed_fuzz_contracts,
416+ ) ,
392417 test_manager,
393418 first_called_address,
394- orchestrator_contract_address,
395- rng : ChaCha8Rng :: seed_from_u64 ( seed) ,
396419 }
397420 }
398421
399422 pub fn finalized ( & self ) -> bool {
400- self . final_state . finalized ( )
423+ self . context . final_state . finalized ( )
401424 }
402425
403426 pub fn current_fuzz_call_info ( & self ) -> & FuzzCallInfo {
404- let mut call = & self . calls [ self . current_call [ 0 ] ] ;
405- for index in self . current_call . iter ( ) . skip ( 1 ) {
427+ let mut call = & self . context . calls [ self . context . current_call [ 0 ] ] ;
428+ for index in self . context . current_call . iter ( ) . skip ( 1 ) {
406429 call = & call. inner_calls [ * index] ;
407430 }
408431 call
409432 }
410433
411434 pub fn current_fuzz_call_info_mut ( & mut self ) -> & mut FuzzCallInfo {
412- let current_call = self . current_call . clone ( ) ;
435+ let current_call = self . context . current_call . clone ( ) ;
413436 self . fuzz_call_info_mut ( & current_call)
414437 }
415438
416439 pub fn fuzz_call_info_mut ( & mut self , call_path : & [ usize ] ) -> & mut FuzzCallInfo {
417- let mut call = & mut self . calls [ call_path[ 0 ] ] ;
440+ let mut call = & mut self . context . calls [ call_path[ 0 ] ] ;
418441 for index in call_path. iter ( ) . skip ( 1 ) {
419442 call = & mut call. inner_calls [ * index] ;
420443 }
@@ -453,7 +476,8 @@ impl FuzzTestManager {
453476 // There are two Cairo0 contracts and two Cairo1 contracts that can be called.
454477 // When calling from a Cairo1 context, the caller can unwrap the call result or not.
455478 let current_context_is_cairo1 = self . is_current_context_cairo1 ( ) ;
456- self . deployed_contracts
479+ self . context
480+ . deployed_contracts
457481 . keys ( )
458482 . flat_map ( |address| {
459483 if current_context_is_cairo1 {
@@ -507,13 +531,13 @@ impl FuzzTestManager {
507531 . map ( |storage_key| {
508532 FuzzOperationData :: Write (
509533 StorageKey :: try_from ( * storage_key) . unwrap ( ) ,
510- self . next_storage_write_value ,
534+ self . context . next_storage_write_value ,
511535 )
512536 } )
513537 . collect ( ) ,
514538 FuzzOperation :: ReplaceClass => {
515539 // If class was already replaced, no more replacements are allowed.
516- if self . class_replaced {
540+ if self . context . class_replaced {
517541 // TODO(Dori): In this case, replace back to original class.
518542 return vec ! [ ] ;
519543 }
@@ -525,7 +549,7 @@ impl FuzzTestManager {
525549 . keys ( )
526550 . map ( |class_hash| FuzzOperationData :: Deploy {
527551 class_hash : * class_hash,
528- salt : self . next_salt ,
552+ salt : self . context . next_salt ,
529553 } )
530554 . collect ( )
531555 }
@@ -558,7 +582,7 @@ impl FuzzTestManager {
558582 parent_failure_behavior,
559583 ) ) ;
560584 }
561- self . current_call . push ( next_call_index) ;
585+ self . context . current_call . push ( next_call_index) ;
562586 }
563587
564588 /// Enter a new call context.
@@ -578,10 +602,10 @@ impl FuzzTestManager {
578602
579603 /// Exit the current call context.
580604 pub fn exit_call ( & mut self ) {
581- self . current_call . pop ( ) ;
605+ self . context . current_call . pop ( ) ;
582606 // If we returned to orchestrator context, no more operations can be applied.
583- if self . current_call . is_empty ( ) {
584- self . final_state = FinalizedState :: Succeeded ;
607+ if self . context . current_call . is_empty ( ) {
608+ self . context . final_state = FinalizedState :: Succeeded ;
585609 }
586610 }
587611
@@ -595,7 +619,7 @@ impl FuzzTestManager {
595619 calculate_contract_address (
596620 salt,
597621 class_hash,
598- & calldata ! [ * * self . orchestrator_contract_address] ,
622+ & calldata ! [ * * self . context . orchestrator_contract_address] ,
599623 ContractAddress :: default ( ) ,
600624 )
601625 . unwrap ( )
@@ -630,38 +654,38 @@ impl FuzzTestManager {
630654 // Revert class replacement. Do this before "undeploying" deployed contracts so we don't
631655 // "redeploy" anything when we only intend to revert the class hash change.
632656 if let Some ( ( address, class_hash) ) = revert_info. class_replaced_and_original_class_hash {
633- self . deployed_contracts . insert ( address, class_hash) ;
657+ self . context . deployed_contracts . insert ( address, class_hash) ;
634658 }
635659 // "Undeploy" all deployed contracts.
636660 for address in revert_info. deployed_addresses . iter ( ) {
637661 // Remove without asserting that the address was actually deployed - the
638662 // constructor may have reverted before being finalized.
639- self . deployed_contracts . remove ( address) ;
663+ self . context . deployed_contracts . remove ( address) ;
640664 }
641665 }
642666
643667 /// Remove the entire call tree. Used when an uncatchable error occurs, or we cleanly return
644668 /// to the orchestrator context.
645669 /// State should always be finalized after this.
646670 pub fn pop_entire_call_tree ( & mut self , succeeded : bool ) {
647- self . calls . clear ( ) ;
648- self . current_call . clear ( ) ;
649- self . final_state =
671+ self . context . calls . clear ( ) ;
672+ self . context . current_call . clear ( ) ;
673+ self . context . final_state =
650674 if succeeded { FinalizedState :: Succeeded } else { FinalizedState :: Reverted } ;
651675 }
652676
653677 /// Applies the operation and updates the context.
654678 pub fn apply ( & mut self , operation : FuzzOperationData ) {
655679 assert ! ( !self . finalized( ) ) ;
656- self . operations . push ( operation) ;
680+ self . context . operations . push ( operation) ;
657681 match operation {
658682 FuzzOperationData :: Return => {
659683 // Go up the call tree.
660684 self . exit_call ( )
661685 }
662686 FuzzOperationData :: Call ( call_operation_data) => {
663687 let address = * call_operation_data. address ( ) ;
664- let class_hash = * self . deployed_contracts . get ( & address) . unwrap ( ) ;
688+ let class_hash = * self . context . deployed_contracts . get ( & address) . unwrap ( ) ;
665689 self . enter_call ( address, class_hash, call_operation_data. parent_failure_behavior ( ) ) ;
666690 }
667691 FuzzOperationData :: LibraryCall ( library_call_operation_data) => {
@@ -673,15 +697,15 @@ impl FuzzTestManager {
673697 ) ;
674698 }
675699 FuzzOperationData :: Write ( _, _) => {
676- self . next_storage_write_value . 0 += Felt :: ONE ;
700+ self . context . next_storage_write_value . 0 += Felt :: ONE ;
677701 }
678702 FuzzOperationData :: ReplaceClass ( class_hash) => {
679- assert ! ( !self . class_replaced) ;
703+ assert ! ( !self . context . class_replaced) ;
680704 assert_eq ! ( class_hash, * CAIRO1_REPLACEMENT_CLASS_HASH ) ;
681- self . class_replaced = true ;
705+ self . context . class_replaced = true ;
682706 // Update the mapping from address to class hash, so subsequent calls to this
683707 // address will correctly use the new class hash.
684- self . deployed_contracts . insert ( self . current_address ( ) , class_hash) ;
708+ self . context . deployed_contracts . insert ( self . current_address ( ) , class_hash) ;
685709 // Update the current call to mark that it was replaced at this point, to make it
686710 // easy to track if the change must be reverted mid-test.
687711 self . current_fuzz_call_info_mut ( ) . class_replaced_here = true ;
@@ -692,9 +716,9 @@ impl FuzzTestManager {
692716 FuzzOperationData :: Deploy { class_hash, salt } => {
693717 let deployed_address = self . address_of_deploy ( class_hash, salt) ;
694718 // Increment the salt for the next deploy operation.
695- self . next_salt . 0 += Felt :: ONE ;
719+ self . context . next_salt . 0 += Felt :: ONE ;
696720 // Update the mapping from address to class hash.
697- self . deployed_contracts . insert ( deployed_address, class_hash) ;
721+ self . context . deployed_contracts . insert ( deployed_address, class_hash) ;
698722 // Enter constructor context.
699723 self . enter_deploy ( deployed_address, class_hash) ;
700724 }
@@ -714,7 +738,7 @@ impl FuzzTestManager {
714738 == ParentFailureBehavior :: Cairo1Propagating
715739 {
716740 // No need to finalize deploys here - we are reverting.
717- self . current_call . pop ( ) ;
741+ self . context . current_call . pop ( ) ;
718742 }
719743 match self . current_fuzz_call_info ( ) . parent_failure_behavior {
720744 // The simple case is when the parent is "uncatchable"; the entire tx will be
@@ -739,7 +763,7 @@ impl FuzzTestManager {
739763 // The first panic is caught and will revert the replace class. Unless the
740764 // inner call is popped, the second panic will attempt to revert the replace
741765 // class again.
742- if self . current_call . is_empty ( ) {
766+ if self . context . current_call . is_empty ( ) {
743767 // We are back at the orchestrator context. Pop the entire call tree.
744768 // Tx should be successful.
745769 self . pop_entire_call_tree ( true ) ;
@@ -761,7 +785,7 @@ impl FuzzTestManager {
761785 if valid_operations. is_empty ( ) {
762786 return Err ( ( ) ) ;
763787 }
764- let operation = * valid_operations. iter ( ) . choose ( & mut self . rng ) . unwrap ( ) ;
788+ let operation = * valid_operations. iter ( ) . choose ( & mut self . context . rng ) . unwrap ( ) ;
765789 self . apply ( operation) ;
766790 Ok ( ( ) )
767791 }
@@ -790,7 +814,7 @@ impl FuzzTestManager {
790814 #[ allow( unused) ]
791815 pub fn prettify_operations ( & self ) -> String {
792816 let mut output = vec ! [ ] ;
793- for operation in self . operations . iter ( ) {
817+ for operation in self . context . operations . iter ( ) {
794818 let operation_felt_hexes = operation
795819 . felt_vector ( )
796820 . iter ( )
@@ -801,7 +825,7 @@ impl FuzzTestManager {
801825 FuzzOperationData :: Call ( call_operation_data) => {
802826 // It's possible that the address is no longer deployed (post-revert).
803827 let class_info_string =
804- match self . deployed_contracts . get ( call_operation_data. address ( ) ) {
828+ match self . context . deployed_contracts . get ( call_operation_data. address ( ) ) {
805829 Some ( class_hash) => format ! (
806830 "Cairo{} address, class hash: {}" ,
807831 if self . is_cairo1_class( class_hash) { "1" } else { "0" } ,
@@ -888,13 +912,13 @@ impl FuzzTestManager {
888912 /// the context - if the finalized state is Ongoing it will be converted to Succeeded).
889913 pub async fn run_test ( mut self ) {
890914 if !self . finalized ( ) {
891- self . final_state = FinalizedState :: Succeeded ;
915+ self . context . final_state = FinalizedState :: Succeeded ;
892916 }
893917
894918 // Initialize the orchestrator contract with the scenario data.
895- let scenario_data = Self :: operations_to_scenario_data ( & self . operations ) ;
919+ let scenario_data = Self :: operations_to_scenario_data ( & self . context . operations ) ;
896920 let orchestrator_calldata = create_calldata (
897- self . orchestrator_contract_address ,
921+ self . context . orchestrator_contract_address ,
898922 "initialize" ,
899923 & [ vec ! [ Felt :: from( scenario_data. len( ) ) ] , scenario_data] . concat ( ) ,
900924 ) ;
@@ -903,13 +927,13 @@ impl FuzzTestManager {
903927
904928 // Invoke the test.
905929 let start_test_calldata = create_calldata (
906- self . orchestrator_contract_address ,
930+ self . context . orchestrator_contract_address ,
907931 "start_test" ,
908932 & [ * * self . first_called_address ] ,
909933 ) ;
910934
911935 // Whether or not a revert is expected depends on context.
912- let tx_revert_error = match self . final_state {
936+ let tx_revert_error = match self . context . final_state {
913937 FinalizedState :: Succeeded => None ,
914938 FinalizedState :: Reverted => Some ( "" . to_string ( ) ) ,
915939 FinalizedState :: Ongoing => unreachable ! ( ) ,
0 commit comments