Skip to content

Commit b4ccc10

Browse files
committed
blockifier: add unit test to state_reader_and_contract_manager's caching
1 parent f30483a commit b4ccc10

File tree

6 files changed

+210
-5
lines changed

6 files changed

+210
-5
lines changed

crates/apollo_gateway/src/state_reader_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,8 +421,8 @@ async fn test_get_class_hash_at() {
421421
assert_eq!(result, expected_result);
422422
}
423423

424-
// TODO(Arni): Check if any test cases here should move to the tests of
425-
// `StateReaderAndContractManager`.
424+
// TODO(Arni): Convert scenarios that are covered in the blockifier's
425+
// state_reader_and_contract_manager_test.rs to unit tests of sync_state_reader.
426426
#[rstest]
427427
#[case::cairo_0_declared_and_cached(
428428
cairo_0_declared_scenario(),

crates/blockifier/src/state.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub mod state_api;
1111
#[cfg(any(feature = "testing", test))]
1212
pub mod state_api_test_utils;
1313
pub mod state_reader_and_contract_manager;
14+
#[cfg(any(feature = "testing", test))]
15+
pub mod state_reader_and_contract_manager_test_utils;
1416
pub mod stateful_compression;
1517
#[cfg(any(feature = "testing", test))]
1618
pub mod stateful_compression_test_utils;

crates/blockifier/src/state/global_cache.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ impl CompiledClasses {
3636
},
3737
}
3838
}
39+
40+
// Note: For Cairo 1 classes, the Sierra does not match the Casm.
41+
#[cfg(any(feature = "testing", test))]
42+
pub fn from_runnable_for_testing(runnable_compiled_class: RunnableCompiledClass) -> Self {
43+
match runnable_compiled_class {
44+
RunnableCompiledClass::V0(compiled_class_v0) => Self::V0(compiled_class_v0),
45+
RunnableCompiledClass::V1(compiled_class_v1) => {
46+
Self::V1(compiled_class_v1, Arc::new(SierraContractClass::default()))
47+
}
48+
#[cfg(feature = "cairo_native")]
49+
RunnableCompiledClass::V1Native(native_compiled_class_v1) => {
50+
Self::V1Native(CachedCairoNative::Compiled(native_compiled_class_v1))
51+
}
52+
}
53+
}
3954
}
4055

4156
pub type RawClassCache = GlobalContractCache<CompiledClasses>;

crates/blockifier/src/state/state_reader_and_contract_manager.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::state::contract_class_manager::ContractClassManager;
77
use crate::state::errors::StateError;
88
use crate::state::global_cache::CompiledClasses;
99
use crate::state::state_api::{StateReader, StateResult};
10+
1011
#[cfg(test)]
1112
#[path = "state_reader_and_contract_manager_test.rs"]
1213
pub mod state_reader_and_contract_manager_test;

crates/blockifier/src/state/state_reader_and_contract_manager_test.rs

Lines changed: 156 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
use std::sync::LazyLock;
2+
13
use assert_matches::assert_matches;
24
use blockifier_test_utils::cairo_versions::{CairoVersion, RunnableCairo1};
35
use blockifier_test_utils::contracts::FeatureContract;
46
use rstest::rstest;
7+
use starknet_api::class_hash;
58
use starknet_api::contract_class::compiled_class_hash::HashVersion;
9+
use starknet_api::core::ClassHash;
610

7-
use crate::blockifier::config::ContractClassManagerConfig;
11+
use crate::blockifier::config::{CairoNativeRunConfig, ContractClassManagerConfig};
812
use crate::execution::contract_class::RunnableCompiledClass;
913
use crate::state::contract_class_manager::ContractClassManager;
14+
use crate::state::errors::StateError;
1015
#[cfg(feature = "cairo_native")]
11-
use crate::state::global_cache::{CachedCairoNative, CompiledClasses};
12-
use crate::state::state_api::StateReader;
16+
use crate::state::global_cache::CachedCairoNative;
17+
use crate::state::global_cache::CompiledClasses;
18+
use crate::state::state_api::{StateReader, StateResult};
19+
use crate::state::state_api_test_utils::assert_eq_state_result;
1320
use crate::state::state_reader_and_contract_manager::StateReaderAndContractManager;
21+
use crate::state::state_reader_and_contract_manager_test_utils::MockFetchCompiledClasses;
1422
use crate::test_utils::contracts::{FeatureContractData, FeatureContractTrait};
1523
use crate::test_utils::dict_state_reader::DictStateReader;
1624
use crate::test_utils::initial_test_state::state_reader_and_contract_manager_for_testing;
1725

26+
static DUMMY_CLASS_HASH: LazyLock<ClassHash> = LazyLock::new(|| class_hash!(2_u32));
27+
1828
fn build_reader_and_declare_contract(
1929
contract: FeatureContractData,
2030
contract_manager_config: ContractClassManagerConfig,
@@ -115,3 +125,146 @@ fn test_get_compiled_class_when_native_is_cached() {
115125
let compiled_class = state_reader.get_compiled_class(test_class_hash).unwrap();
116126
assert_matches!(compiled_class, RunnableCompiledClass::V1Native(_));
117127
}
128+
129+
struct GetCompiledClassTestScenario {
130+
expectations: GetCompiledClassTestExpectation,
131+
132+
// Test result.
133+
expected_result: StateResult<RunnableCompiledClass>,
134+
}
135+
136+
struct GetCompiledClassTestExpectation {
137+
get_compiled_classes_result: Option<StateResult<CompiledClasses>>,
138+
is_declared_result: Option<StateResult<bool>>,
139+
}
140+
141+
fn add_expectation_to_mock_fetch_compiled_classes(
142+
mock_fetch_compiled_classes: &mut MockFetchCompiledClasses,
143+
expectations: GetCompiledClassTestExpectation,
144+
) {
145+
if let Some(get_compiled_classes_result) = expectations.get_compiled_classes_result {
146+
mock_fetch_compiled_classes
147+
.expect_get_compiled_classes()
148+
.times(1)
149+
.return_once(move |_| get_compiled_classes_result);
150+
}
151+
152+
if let Some(is_declared_result) = expectations.is_declared_result {
153+
mock_fetch_compiled_classes
154+
.expect_is_declared()
155+
.times(1)
156+
.return_once(|_| is_declared_result);
157+
}
158+
}
159+
160+
fn cairo_1_declared_scenario() -> GetCompiledClassTestScenario {
161+
GetCompiledClassTestScenario {
162+
expectations: GetCompiledClassTestExpectation {
163+
get_compiled_classes_result: Some(Ok(CompiledClasses::from_runnable_for_testing(
164+
RunnableCompiledClass::test_casm_contract_class(),
165+
))),
166+
is_declared_result: None,
167+
},
168+
expected_result: Ok(RunnableCompiledClass::test_casm_contract_class()),
169+
}
170+
}
171+
172+
fn cairo_1_cached_scenario() -> GetCompiledClassTestScenario {
173+
GetCompiledClassTestScenario {
174+
expectations: GetCompiledClassTestExpectation {
175+
get_compiled_classes_result: None,
176+
is_declared_result: Some(Ok(true)), // Verification call for cached Cairo1 class.
177+
},
178+
expected_result: Ok(RunnableCompiledClass::test_casm_contract_class()),
179+
}
180+
}
181+
182+
fn cached_but_verification_failed_after_reorg_scenario() -> GetCompiledClassTestScenario {
183+
GetCompiledClassTestScenario {
184+
expectations: GetCompiledClassTestExpectation {
185+
get_compiled_classes_result: None,
186+
is_declared_result: Some(Ok(false)), // Verification fails after reorg.
187+
},
188+
expected_result: Err(StateError::UndeclaredClassHash(*DUMMY_CLASS_HASH)),
189+
}
190+
}
191+
192+
fn cairo_0_declared_scenario() -> GetCompiledClassTestScenario {
193+
GetCompiledClassTestScenario {
194+
expectations: GetCompiledClassTestExpectation {
195+
get_compiled_classes_result: Some(Ok(CompiledClasses::from_runnable_for_testing(
196+
RunnableCompiledClass::test_deprecated_casm_contract_class(),
197+
))),
198+
is_declared_result: None,
199+
},
200+
expected_result: Ok(RunnableCompiledClass::test_deprecated_casm_contract_class()),
201+
}
202+
}
203+
204+
fn cairo_0_cached_scenario() -> GetCompiledClassTestScenario {
205+
GetCompiledClassTestScenario {
206+
expectations: GetCompiledClassTestExpectation {
207+
get_compiled_classes_result: None,
208+
is_declared_result: None,
209+
},
210+
expected_result: Ok(RunnableCompiledClass::test_deprecated_casm_contract_class()),
211+
}
212+
}
213+
214+
fn not_declared_scenario() -> GetCompiledClassTestScenario {
215+
GetCompiledClassTestScenario {
216+
expectations: GetCompiledClassTestExpectation {
217+
get_compiled_classes_result: Some(Err(StateError::UndeclaredClassHash(
218+
*DUMMY_CLASS_HASH,
219+
))),
220+
is_declared_result: None,
221+
},
222+
expected_result: Err(StateError::UndeclaredClassHash(*DUMMY_CLASS_HASH)),
223+
}
224+
}
225+
226+
#[rstest]
227+
#[case::cairo_0_declared_and_cached(cairo_0_declared_scenario(), cairo_0_cached_scenario())]
228+
#[case::cairo_1_declared_and_cached(cairo_1_declared_scenario(), cairo_1_cached_scenario())]
229+
#[case::cairo_1_declared_then_verification_failed_after_reorg(
230+
cairo_1_declared_scenario(),
231+
cached_but_verification_failed_after_reorg_scenario()
232+
)]
233+
#[case::not_declared_then_declared(not_declared_scenario(), cairo_1_declared_scenario())]
234+
#[case::not_declared_both_rounds(not_declared_scenario(), not_declared_scenario())]
235+
fn test_get_compiled_class_caching_scenarios(
236+
#[case] first_scenario: GetCompiledClassTestScenario,
237+
#[case] second_scenario: GetCompiledClassTestScenario,
238+
) {
239+
let contract_class_manager = ContractClassManager::start(ContractClassManagerConfig {
240+
cairo_native_run_config: CairoNativeRunConfig {
241+
wait_on_native_compilation: false,
242+
..Default::default()
243+
},
244+
..Default::default()
245+
});
246+
let class_hash = *DUMMY_CLASS_HASH;
247+
248+
// First execution.
249+
let mut first_reader = MockFetchCompiledClasses::new();
250+
add_expectation_to_mock_fetch_compiled_classes(&mut first_reader, first_scenario.expectations);
251+
let first_state_reader_and_manager =
252+
state_reader_and_contract_manager_for_testing(first_reader, contract_class_manager.clone());
253+
254+
let first_result = first_state_reader_and_manager.get_compiled_class(class_hash);
255+
256+
// Second execution.
257+
let mut second_reader = MockFetchCompiledClasses::new();
258+
add_expectation_to_mock_fetch_compiled_classes(
259+
&mut second_reader,
260+
second_scenario.expectations,
261+
);
262+
let second_state_reader_and_manager =
263+
state_reader_and_contract_manager_for_testing(second_reader, contract_class_manager);
264+
265+
let second_result = second_state_reader_and_manager.get_compiled_class(class_hash);
266+
267+
// Verify results.
268+
assert_eq_state_result(&first_result, &first_scenario.expected_result);
269+
assert_eq_state_result(&second_result, &second_scenario.expected_result);
270+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
2+
use starknet_api::state::StorageKey;
3+
use starknet_types_core::felt::Felt;
4+
5+
use crate::execution::contract_class::RunnableCompiledClass;
6+
use crate::state::global_cache::CompiledClasses;
7+
use crate::state::state_api::{StateReader, StateResult};
8+
use crate::state::state_reader_and_contract_manager::FetchCompiledClasses as FetchCompiledClassesTrait;
9+
10+
mockall::mock! {
11+
pub FetchCompiledClasses {}
12+
13+
impl StateReader for FetchCompiledClasses {
14+
fn get_storage_at(
15+
&self,
16+
contract_address: ContractAddress,
17+
key: StorageKey,
18+
) -> StateResult<Felt>;
19+
20+
fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult<Nonce>;
21+
22+
fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult<ClassHash>;
23+
24+
fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult<RunnableCompiledClass>;
25+
26+
fn get_compiled_class_hash(&self, class_hash: ClassHash) -> StateResult<CompiledClassHash>;
27+
}
28+
29+
// Implement FetchCompiledClasses methods
30+
impl FetchCompiledClassesTrait for FetchCompiledClasses {
31+
fn get_compiled_classes(&self, class_hash: ClassHash) -> StateResult<CompiledClasses>;
32+
fn is_declared(&self, class_hash: ClassHash) -> StateResult<bool>;
33+
}
34+
}

0 commit comments

Comments
 (0)