diff --git a/crates/blockifier/src/test_utils/cairo_compile.rs b/crates/blockifier/src/test_utils/cairo_compile.rs index a0c5f77a8d..640637a1ca 100644 --- a/crates/blockifier/src/test_utils/cairo_compile.rs +++ b/crates/blockifier/src/test_utils/cairo_compile.rs @@ -142,7 +142,7 @@ pub fn starknet_compile( cargo_nightly_arg: Option, base_compile_args: &mut Vec, ) -> Vec { - prepare_cairo1_compiler_deps(git_tag_override); + verify_cairo1_compiler_deps(git_tag_override); let cairo1_compiler_path = local_cairo1_compiler_repo_path(); @@ -204,10 +204,9 @@ fn verify_cairo0_compiler_deps() { ); } -fn prepare_cairo1_compiler_deps(git_tag_override: Option) { - let cairo_repo_path = local_cairo1_compiler_repo_path(); +fn get_tag_and_repo_file_path(git_tag_override: Option) -> (String, PathBuf) { let tag = git_tag_override.unwrap_or(cairo1_compiler_tag()); - + let cairo_repo_path = local_cairo1_compiler_repo_path(); // Check if the path is a directory. assert!( cairo_repo_path.is_dir(), @@ -216,6 +215,11 @@ fn prepare_cairo1_compiler_deps(git_tag_override: Option) { cairo_repo_path.to_string_lossy(), ); + (tag, cairo_repo_path) +} + +pub fn prepare_group_tag_compiler_deps(git_tag_override: Option) { + let (tag, cairo_repo_path) = get_tag_and_repo_file_path(git_tag_override); // Checkout the required version in the compiler repo. run_and_verify_output(Command::new("git").args([ "-C", @@ -223,6 +227,10 @@ fn prepare_cairo1_compiler_deps(git_tag_override: Option) { "checkout", &tag, ])); +} + +fn verify_cairo1_compiler_deps(git_tag_override: Option) { + let (tag, cairo_repo_path) = get_tag_and_repo_file_path(git_tag_override); // Verify that the checked out tag is as expected. run_and_verify_output(Command::new("git").args([ diff --git a/crates/blockifier/src/test_utils/contracts.rs b/crates/blockifier/src/test_utils/contracts.rs index c3d4ea4ce2..d6e092f521 100644 --- a/crates/blockifier/src/test_utils/contracts.rs +++ b/crates/blockifier/src/test_utils/contracts.rs @@ -1,4 +1,7 @@ +use std::collections::HashMap; + use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; +use itertools::Itertools; use starknet_api::abi::abi_utils::selector_from_name; use starknet_api::abi::constants::CONSTRUCTOR_ENTRY_POINT_NAME; use starknet_api::contract_class::{ContractClass, EntryPointType}; @@ -86,6 +89,9 @@ const LEGACY_CONTRACT_RUST_TOOLCHAIN: &str = "2023-07-05"; const CAIRO_STEPS_TEST_CONTRACT_COMPILER_TAG: &str = "v2.7.0"; const CAIRO_STEPS_TEST_CONTRACT_RUST_TOOLCHAIN: &str = "2024-04-29"; +pub type TagAndToolchain = (Option, Option); +pub type TagToContractsMapping = HashMap>; + /// Enum representing all feature contracts. /// The contracts that are implemented in both Cairo versions include a version field. #[derive(Clone, Copy, Debug, EnumIter, Hash, PartialEq, Eq)] @@ -212,7 +218,7 @@ impl FeatureContract { /// Some contracts are designed to test behavior of code compiled with a /// specific (old) compiler tag. To run the (old) compiler, older rust /// version is required. - pub fn fixed_tag_and_rust_toolchain(&self) -> (Option, Option) { + pub fn fixed_tag_and_rust_toolchain(&self) -> TagAndToolchain { match self { Self::LegacyTestContract => ( Some(LEGACY_CONTRACT_COMPILER_TAG.into()), @@ -427,4 +433,13 @@ impl FeatureContract { // ERC20 is a special case - not in the feature_contracts directory. Self::all_contracts().filter(|contract| !matches!(contract, Self::ERC20(_))) } + + pub fn cairo1_feature_contracts_by_tag() -> TagToContractsMapping { + // EnumIter iterates over all variants with Default::default() as the cairo + // version. + Self::all_feature_contracts() + .filter(|contract| contract.cairo_version() != CairoVersion::Cairo0) + .map(|contract| (contract.fixed_tag_and_rust_toolchain(), contract)) + .into_group_map() + } } diff --git a/crates/blockifier/tests/feature_contracts_compatibility_test.rs b/crates/blockifier/tests/feature_contracts_compatibility_test.rs index 845e57b318..e9f2780995 100644 --- a/crates/blockifier/tests/feature_contracts_compatibility_test.rs +++ b/crates/blockifier/tests/feature_contracts_compatibility_test.rs @@ -1,5 +1,6 @@ use std::fs; +use blockifier::test_utils::cairo_compile::prepare_group_tag_compiler_deps; use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::CairoVersion; use pretty_assertions::assert_eq; @@ -36,29 +37,51 @@ const FIX_COMMAND: &str = "FIX_FEATURE_TEST=1 cargo test -p blockifier --test \ // `COMPILED_CONTRACTS_SUBDIR` which equals `starknet-compile-deprecated X.cairo --no_debug_info`. fn verify_feature_contracts_compatibility(fix: bool, cairo_version: CairoVersion) { // TODO(Dori, 1/10/2024): Parallelize this test. - for contract in FeatureContract::all_feature_contracts() - .filter(|contract| contract.cairo_version() == cairo_version) - { - // Compare output of cairo-file on file with existing compiled file. - let expected_compiled_output = contract.compile(); - let existing_compiled_path = contract.get_compiled_path(); - - if fix { - fs::write(&existing_compiled_path, &expected_compiled_output).unwrap(); + match cairo_version { + CairoVersion::Cairo0 => { + for contract in FeatureContract::all_feature_contracts() + .filter(|contract| contract.cairo_version() == cairo_version) + { + verify_feature_contracts_compatibility_logic(&contract, fix); + } } - let existing_compiled_contents = fs::read_to_string(&existing_compiled_path) - .unwrap_or_else(|_| panic!("Cannot read {existing_compiled_path}.")); - - if String::from_utf8(expected_compiled_output).unwrap() != existing_compiled_contents { - panic!( - "{} does not compile to {existing_compiled_path}.\nRun `{FIX_COMMAND}` to fix the \ - existing file according to locally installed `starknet-compile-deprecated`.\n", - contract.get_source_path() - ); + _ => { + for (tag_and_tool_chain, feature_contracts) in + FeatureContract::cairo1_feature_contracts_by_tag() + { + prepare_group_tag_compiler_deps(tag_and_tool_chain.0.clone()); + // TODO(Meshi 01/01/2025) Make this loop concurrent + for contract in feature_contracts + .into_iter() + .filter(|contract| contract.cairo_version() == cairo_version) + { + verify_feature_contracts_compatibility_logic(&contract, fix); + } + } } } } +fn verify_feature_contracts_compatibility_logic(contract: &FeatureContract, fix: bool) { + // Compare output of cairo-file on file with existing compiled file. + let expected_compiled_output = contract.compile(); + let existing_compiled_path = contract.get_compiled_path(); + + if fix { + fs::write(&existing_compiled_path, &expected_compiled_output).unwrap(); + } + let existing_compiled_contents = fs::read_to_string(&existing_compiled_path) + .unwrap_or_else(|_| panic!("Cannot read {existing_compiled_path}.")); + + if String::from_utf8(expected_compiled_output).unwrap() != existing_compiled_contents { + panic!( + "{} does not compile to {existing_compiled_path}.\nRun `{FIX_COMMAND}` to fix the \ + existing file according to locally installed `starknet-compile-deprecated`.\n", + contract.get_source_path() + ); + } +} + /// Verifies that the feature contracts directory contains the expected contents, and returns a list /// of pairs (source_path, base_filename, compiled_path) for each contract. fn verify_and_get_files(cairo_version: CairoVersion) -> Vec<(String, String, String)> {