@@ -20,7 +20,6 @@ use blockifier::state::errors::StateError;
2020use blockifier:: state:: state_api:: { StateReader , StateResult } ;
2121use blockifier:: state:: state_reader_and_contract_manager:: StateReaderAndContractManager ;
2222use blockifier:: test_utils:: initial_test_state:: state_reader_and_contract_manager_for_testing;
23- use cairo_lang_starknet_classes:: casm_contract_class:: CasmContractClass ;
2423use lazy_static:: lazy_static;
2524use mockall:: predicate;
2625use rstest:: rstest;
@@ -34,7 +33,7 @@ use starknet_api::block::{
3433 GasPrices ,
3534 NonzeroGasPrice ,
3635} ;
37- use starknet_api:: contract_class:: { ContractClass , SierraVersion } ;
36+ use starknet_api:: contract_class:: ContractClass ;
3837use starknet_api:: core:: { ClassHash , SequencerContractAddress } ;
3938use starknet_api:: data_availability:: L1DataAvailabilityMode ;
4039use starknet_api:: state:: SierraContractClass ;
@@ -62,6 +61,178 @@ fn state_reader_and_contract_manager(
6261 )
6362}
6463
64+ struct GetCompiledClassTestScenario {
65+ expectations : GetCompiledClassTestExpectation ,
66+
67+ // Test result.
68+ expected_result : StateResult < RunnableCompiledClass > ,
69+ }
70+
71+ struct GetCompiledClassTestExpectation {
72+ // Class manager client.
73+ get_executable_result : ClassManagerClientResult < Option < ExecutableClass > > ,
74+ n_calls_to_get_executable : usize ,
75+ get_sierra_result : ClassManagerClientResult < Option < SierraContractClass > > ,
76+ n_calls_to_get_sierra : usize ,
77+
78+ // State sync client.
79+ is_class_declared_at_result : Option < StateSyncClientResult < bool > > ,
80+ is_cairo_1_class_declared_at_result : Option < StateSyncClientResult < bool > > ,
81+ }
82+
83+ fn add_expectation_to_mock_state_sync_client_and_mock_class_manager_client (
84+ mock_class_manager_client : & mut MockClassManagerClient ,
85+ mock_state_sync_client : & mut MockStateSyncClient ,
86+ expectation : GetCompiledClassTestExpectation ,
87+ ) {
88+ add_expectation_to_mock_class_manager_client (
89+ mock_class_manager_client,
90+ expectation. get_executable_result ,
91+ expectation. n_calls_to_get_executable ,
92+ expectation. get_sierra_result ,
93+ expectation. n_calls_to_get_sierra ,
94+ ) ;
95+ add_expectation_to_mock_state_sync_client (
96+ mock_state_sync_client,
97+ expectation. is_class_declared_at_result ,
98+ expectation. is_cairo_1_class_declared_at_result ,
99+ ) ;
100+ }
101+
102+ fn add_expectation_to_mock_state_sync_client (
103+ mock_state_sync_client : & mut MockStateSyncClient ,
104+ is_class_declared_at_result : Option < StateSyncClientResult < bool > > ,
105+ is_cairo_1_class_declared_at_result : Option < StateSyncClientResult < bool > > ,
106+ ) {
107+ if let Some ( is_class_declared_at_result) = is_class_declared_at_result {
108+ mock_state_sync_client
109+ . expect_is_class_declared_at ( )
110+ . times ( 1 )
111+ . return_once ( move |_, _| is_class_declared_at_result) ;
112+ }
113+ if let Some ( is_cairo_1_class_declared_at_result) = is_cairo_1_class_declared_at_result {
114+ mock_state_sync_client
115+ . expect_is_cairo_1_class_declared_at ( )
116+ . times ( 1 )
117+ . return_once ( move |_, _| is_cairo_1_class_declared_at_result) ;
118+ }
119+ }
120+
121+ fn add_expectation_to_mock_class_manager_client (
122+ mock_class_manager_client : & mut MockClassManagerClient ,
123+ get_executable_result : ClassManagerClientResult < Option < ExecutableClass > > ,
124+ n_calls_to_get_executable : usize ,
125+ get_sierra_result : ClassManagerClientResult < Option < SierraContractClass > > ,
126+ n_calls_to_get_sierra : usize ,
127+ ) {
128+ mock_class_manager_client
129+ . expect_get_executable ( )
130+ . times ( n_calls_to_get_executable)
131+ . return_once ( move |_| get_executable_result) ;
132+
133+ mock_class_manager_client
134+ . expect_get_sierra ( )
135+ . times ( n_calls_to_get_sierra)
136+ . return_once ( move |_| get_sierra_result) ;
137+ }
138+
139+ const CACHED_EXPECTATION : GetCompiledClassTestExpectation = GetCompiledClassTestExpectation {
140+ get_executable_result : Ok ( None ) , // Not called due to caching.
141+ n_calls_to_get_executable : 0 ,
142+ get_sierra_result : Ok ( None ) , // Not called due to caching.
143+ n_calls_to_get_sierra : 0 ,
144+ is_class_declared_at_result : None , // Not called due to caching.
145+ is_cairo_1_class_declared_at_result : None ,
146+ } ;
147+
148+ // Factory functions for different scenarios.
149+ fn cairo_1_declared_scenario ( ) -> GetCompiledClassTestScenario {
150+ GetCompiledClassTestScenario {
151+ expectations : GetCompiledClassTestExpectation {
152+ get_executable_result : Ok ( Some ( DUMMY_CONTRACT_CLASS . clone ( ) ) ) ,
153+ n_calls_to_get_executable : 1 ,
154+ get_sierra_result : Ok ( Some ( SierraContractClass :: default ( ) ) ) ,
155+ n_calls_to_get_sierra : 1 ,
156+ is_class_declared_at_result : Some ( Ok ( true ) ) ,
157+ is_cairo_1_class_declared_at_result : None ,
158+ } ,
159+ expected_result : Ok ( DUMMY_COMPILED_CLASS . clone ( ) ) ,
160+ }
161+ }
162+
163+ fn cairo_0_declared_scenario ( ) -> GetCompiledClassTestScenario {
164+ GetCompiledClassTestScenario {
165+ expectations : GetCompiledClassTestExpectation {
166+ get_executable_result : Ok ( Some ( DUMMY_CONTRACT_CLASS_V0 . clone ( ) ) ) ,
167+ n_calls_to_get_executable : 1 ,
168+ get_sierra_result : Ok ( None ) , // Cairo 0 doesn't use Sierra.
169+ n_calls_to_get_sierra : 0 ,
170+ is_class_declared_at_result : Some ( Ok ( true ) ) ,
171+ is_cairo_1_class_declared_at_result : None ,
172+ } ,
173+ expected_result : Ok ( DUMMY_COMPILED_CLASS_V0 . clone ( ) ) ,
174+ }
175+ }
176+
177+ fn not_declared_scenario ( ) -> GetCompiledClassTestScenario {
178+ GetCompiledClassTestScenario {
179+ expectations : GetCompiledClassTestExpectation {
180+ get_executable_result : Ok ( None ) , // Not called since not declared.
181+ n_calls_to_get_executable : 0 ,
182+ get_sierra_result : Ok ( None ) , // Not called since not declared.
183+ n_calls_to_get_sierra : 0 ,
184+ is_class_declared_at_result : Some ( Ok ( false ) ) ,
185+ is_cairo_1_class_declared_at_result : None ,
186+ } ,
187+ expected_result : Err ( StateError :: UndeclaredClassHash ( * DUMMY_CLASS_HASH ) ) ,
188+ }
189+ }
190+
191+ fn cached_cairo_1_declared_scenario ( ) -> GetCompiledClassTestScenario {
192+ GetCompiledClassTestScenario {
193+ expectations : GetCompiledClassTestExpectation {
194+ is_cairo_1_class_declared_at_result : Some ( Ok ( true ) ) , // Verification call.
195+ ..CACHED_EXPECTATION
196+ } ,
197+ expected_result : Ok ( DUMMY_COMPILED_CLASS . clone ( ) ) ,
198+ }
199+ }
200+
201+ fn cached_cairo_0_declared_scenario ( ) -> GetCompiledClassTestScenario {
202+ GetCompiledClassTestScenario {
203+ expectations : GetCompiledClassTestExpectation {
204+ is_cairo_1_class_declared_at_result : None , // Not called for Cairo 0.
205+ ..CACHED_EXPECTATION
206+ } ,
207+ expected_result : Ok ( DUMMY_COMPILED_CLASS_V0 . clone ( ) ) ,
208+ }
209+ }
210+
211+ fn cached_but_verification_failed_after_reorg_scenario ( ) -> GetCompiledClassTestScenario {
212+ GetCompiledClassTestScenario {
213+ expectations : GetCompiledClassTestExpectation {
214+ is_cairo_1_class_declared_at_result : Some ( Ok ( false ) ) , // Verification fails.
215+ ..CACHED_EXPECTATION
216+ } ,
217+ expected_result : Err ( StateError :: UndeclaredClassHash ( * DUMMY_CLASS_HASH ) ) ,
218+ }
219+ }
220+
221+ fn not_declared_but_in_manager_scenario ( ) -> GetCompiledClassTestScenario {
222+ GetCompiledClassTestScenario {
223+ expectations : GetCompiledClassTestExpectation {
224+ get_executable_result : Ok ( Some ( DUMMY_CONTRACT_CLASS . clone ( ) ) ) , /* In manager but not
225+ * declared. */
226+ n_calls_to_get_executable : 0 , // Not called since not declared.
227+ get_sierra_result : Ok ( Some ( SierraContractClass :: default ( ) ) ) ,
228+ n_calls_to_get_sierra : 0 , // Not called since not declared.
229+ is_class_declared_at_result : Some ( Ok ( false ) ) ,
230+ is_cairo_1_class_declared_at_result : None ,
231+ } ,
232+ expected_result : Err ( StateError :: UndeclaredClassHash ( * DUMMY_CLASS_HASH ) ) ,
233+ }
234+ }
235+
65236#[ tokio:: test]
66237async fn test_get_block_info ( ) {
67238 let mut mock_state_sync_client = MockStateSyncClient :: new ( ) ;
@@ -234,20 +405,15 @@ async fn test_get_class_hash_at() {
234405 assert_eq ! ( result, expected_result) ;
235406}
236407
237- fn dummy_casm_contract_class ( ) -> CasmContractClass {
238- CasmContractClass {
239- compiler_version : "0.0.0" . to_string ( ) ,
240- prime : Default :: default ( ) ,
241- bytecode : Default :: default ( ) ,
242- bytecode_segment_lengths : Default :: default ( ) ,
243- hints : Default :: default ( ) ,
244- pythonic_hints : Default :: default ( ) ,
245- entry_points_by_type : Default :: default ( ) ,
246- }
247- }
248-
249408lazy_static ! {
250409 static ref DUMMY_CLASS_HASH : ClassHash = class_hash!( "0x2" ) ;
410+ static ref DUMMY_CONTRACT_CLASS : ContractClass = ContractClass :: test_casm_contract_class( ) ;
411+ static ref DUMMY_CONTRACT_CLASS_V0 : ContractClass =
412+ ContractClass :: test_deprecated_casm_contract_class( ) ;
413+ static ref DUMMY_COMPILED_CLASS : RunnableCompiledClass =
414+ DUMMY_CONTRACT_CLASS . clone( ) . try_into( ) . unwrap( ) ;
415+ static ref DUMMY_COMPILED_CLASS_V0 : RunnableCompiledClass =
416+ DUMMY_CONTRACT_CLASS_V0 . clone( ) . try_into( ) . unwrap( ) ;
251417}
252418
253419fn assert_eq_state_result (
@@ -263,24 +429,34 @@ fn assert_eq_state_result(
263429 }
264430}
265431
266- // TODO(Arni): add test for class is Cairo 0.
267432#[ rstest]
268433#[ case:: class_declared(
269- Ok ( Some ( ContractClass :: V1 ( ( dummy_casm_contract_class( ) , SierraVersion :: default ( ) ) ) ) ) ,
434+ Ok ( Some ( DUMMY_CONTRACT_CLASS . clone( ) ) ) ,
435+ 1 ,
270436 Ok ( Some ( SierraContractClass :: default ( ) ) ) ,
271437 1 ,
272438 Ok ( true ) ,
273- Ok ( RunnableCompiledClass :: V1 ( ( dummy_casm_contract_class( ) , SierraVersion :: default ( ) ) . try_into( ) . unwrap( ) ) ) ,
439+ Ok ( DUMMY_COMPILED_CLASS . clone( ) ) ,
440+ ) ]
441+ #[ case:: cairo_0_class_declared(
442+ Ok ( Some ( DUMMY_CONTRACT_CLASS_V0 . clone( ) ) ) ,
443+ 1 ,
444+ Ok ( None ) ,
445+ 0 ,
446+ Ok ( true ) ,
447+ Ok ( DUMMY_COMPILED_CLASS_V0 . clone( ) ) ,
274448) ]
275449#[ case:: class_not_declared_but_in_class_manager(
276- Ok ( Some ( ContractClass :: V1 ( ( dummy_casm_contract_class( ) , SierraVersion :: default ( ) ) ) ) ) ,
450+ Ok ( Some ( DUMMY_CONTRACT_CLASS . clone( ) ) ) ,
451+ 0 ,
277452 Ok ( Some ( SierraContractClass :: default ( ) ) ) ,
278453 0 ,
279454 Ok ( false ) ,
280455 Err ( StateError :: UndeclaredClassHash ( * DUMMY_CLASS_HASH ) ) ,
281456) ]
282457#[ case:: class_not_declared(
283458 Ok ( None ) ,
459+ 0 ,
284460 Ok ( None ) ,
285461 0 ,
286462 Ok ( false ) ,
@@ -294,8 +470,9 @@ fn assert_eq_state_result(
294470/// behavior.
295471async fn test_get_compiled_class (
296472 #[ case] get_executable_result : ClassManagerClientResult < Option < ExecutableClass > > ,
473+ #[ case] n_calls_to_get_executable : usize ,
297474 #[ case] get_sierra_result : ClassManagerClientResult < Option < SierraContractClass > > ,
298- #[ case] n_calls_to_class_manager_client : usize ,
475+ #[ case] n_calls_to_get_sierra : usize ,
299476 #[ case] is_class_declared_at_result : StateSyncClientResult < bool > ,
300477 #[ case] expected_result : StateResult < RunnableCompiledClass > ,
301478) {
@@ -308,13 +485,13 @@ async fn test_get_compiled_class(
308485
309486 mock_class_manager_client
310487 . expect_get_executable ( )
311- . times ( n_calls_to_class_manager_client )
488+ . times ( n_calls_to_get_executable )
312489 . with ( predicate:: eq ( class_hash) )
313490 . return_once ( move |_| get_executable_result) ;
314491
315492 mock_class_manager_client
316493 . expect_get_sierra ( )
317- . times ( n_calls_to_class_manager_client )
494+ . times ( n_calls_to_get_sierra )
318495 . with ( predicate:: eq ( class_hash) )
319496 . return_once ( move |_| get_sierra_result) ;
320497
@@ -347,6 +524,7 @@ async fn test_get_compiled_class(
347524async fn test_get_compiled_class_panics_when_class_exists_in_sync_but_not_in_class_manager ( ) {
348525 test_get_compiled_class (
349526 Ok ( None ) ,
527+ 1 ,
350528 Ok ( None ) ,
351529 1 ,
352530 Ok ( true ) ,
@@ -355,4 +533,84 @@ async fn test_get_compiled_class_panics_when_class_exists_in_sync_but_not_in_cla
355533 . await ;
356534}
357535
358- // TODO(Arni): Add tests that check the caching logic.
536+ // TODO(Arni): Check if any test cases here should move to the tests of
537+ // `StateReaderAndContractManager`.
538+ #[ rstest]
539+ #[ case:: cairo_0_declared_and_cached(
540+ cairo_0_declared_scenario( ) ,
541+ cached_cairo_0_declared_scenario( )
542+ ) ]
543+ #[ case:: cairo_1_declared_and_cached(
544+ cairo_1_declared_scenario( ) ,
545+ cached_cairo_1_declared_scenario( )
546+ ) ]
547+ #[ case:: cairo_1_declared_then_verification_failed_after_reorg(
548+ cairo_1_declared_scenario( ) ,
549+ cached_but_verification_failed_after_reorg_scenario( )
550+ ) ]
551+ #[ case:: not_declared_but_in_manager_then_declared(
552+ not_declared_but_in_manager_scenario( ) ,
553+ cairo_1_declared_scenario( )
554+ ) ]
555+ #[ case:: not_declared_then_declared( not_declared_scenario( ) , cairo_1_declared_scenario( ) ) ]
556+ #[ case:: not_declared_both_rounds( not_declared_scenario( ) , not_declared_scenario( ) ) ]
557+ #[ tokio:: test]
558+ async fn test_get_compiled_class_caching_scenarios (
559+ #[ case] first_scenario : GetCompiledClassTestScenario ,
560+ #[ case] second_scenario : GetCompiledClassTestScenario ,
561+ ) {
562+ let block_number = BlockNumber ( 0 ) ; // Not used in the test.
563+ let class_hash = * DUMMY_CLASS_HASH ;
564+
565+ let contract_class_manager = ContractClassManager :: start ( ContractClassManagerConfig :: default ( ) ) ;
566+
567+ // First execution.
568+ let mut mock_state_sync_client = MockStateSyncClient :: new ( ) ;
569+ let mut mock_class_manager_client = MockClassManagerClient :: new ( ) ;
570+ add_expectation_to_mock_state_sync_client_and_mock_class_manager_client (
571+ & mut mock_class_manager_client,
572+ & mut mock_state_sync_client,
573+ first_scenario. expectations ,
574+ ) ;
575+
576+ let first_state_reader_and_class_manager = state_reader_and_contract_manager (
577+ Arc :: new ( mock_state_sync_client) ,
578+ Arc :: new ( mock_class_manager_client) ,
579+ contract_class_manager. clone ( ) ,
580+ block_number,
581+ tokio:: runtime:: Handle :: current ( ) ,
582+ ) ;
583+ let first_result = tokio:: task:: spawn_blocking ( {
584+ let state_reader = first_state_reader_and_class_manager;
585+ move || state_reader. get_compiled_class ( class_hash)
586+ } )
587+ . await
588+ . unwrap ( ) ;
589+
590+ // Second execution.
591+ let mut mock_state_sync_client = MockStateSyncClient :: new ( ) ;
592+ let mut mock_class_manager_client = MockClassManagerClient :: new ( ) ;
593+ add_expectation_to_mock_state_sync_client_and_mock_class_manager_client (
594+ & mut mock_class_manager_client,
595+ & mut mock_state_sync_client,
596+ second_scenario. expectations ,
597+ ) ;
598+
599+ let second_state_reader_and_class_manager = state_reader_and_contract_manager (
600+ Arc :: new ( mock_state_sync_client) ,
601+ Arc :: new ( mock_class_manager_client) ,
602+ contract_class_manager,
603+ block_number,
604+ tokio:: runtime:: Handle :: current ( ) ,
605+ ) ;
606+ let second_result = tokio:: task:: spawn_blocking ( {
607+ let state_reader = second_state_reader_and_class_manager;
608+ move || state_reader. get_compiled_class ( class_hash)
609+ } )
610+ . await
611+ . unwrap ( ) ;
612+
613+ // Verify results
614+ assert_eq_state_result ( & first_result, & first_scenario. expected_result ) ;
615+ assert_eq_state_result ( & second_result, & second_scenario. expected_result ) ;
616+ }
0 commit comments