diff --git a/crates/blockifier/src/test_utils.rs b/crates/blockifier/src/test_utils.rs index 6832b5601c..de9529449a 100644 --- a/crates/blockifier/src/test_utils.rs +++ b/crates/blockifier/src/test_utils.rs @@ -1,3 +1,4 @@ +pub mod cairo_compile; pub mod contracts; pub mod declare; pub mod deploy_account; diff --git a/crates/blockifier/src/test_utils/cairo_compile.rs b/crates/blockifier/src/test_utils/cairo_compile.rs new file mode 100644 index 0000000000..61827ee5e0 --- /dev/null +++ b/crates/blockifier/src/test_utils/cairo_compile.rs @@ -0,0 +1,22 @@ +use std::process::Command; + +/// Compiles a Cairo0 program using the deprecated compiler. +pub fn cairo0_compile(path: String, extra_arg: Option, debug_info: bool) -> Vec { + let mut command = Command::new("starknet-compile-deprecated"); + command.arg(&path); + if let Some(extra_arg) = extra_arg { + command.arg(extra_arg); + } + if !debug_info { + command.arg("--no_debug_info"); + } + let compile_output = command.output().unwrap(); + let stderr_output = String::from_utf8(compile_output.stderr).unwrap(); + assert!(compile_output.status.success(), "{stderr_output}"); + compile_output.stdout +} + +/// Compiles a Cairo1 program using the compiler version set in the Cargo.toml. +pub fn cairo1_compile(_path: String) -> Vec { + todo!(); +} diff --git a/crates/blockifier/src/test_utils/contracts.rs b/crates/blockifier/src/test_utils/contracts.rs index 1a2acfbda2..bc73273339 100644 --- a/crates/blockifier/src/test_utils/contracts.rs +++ b/crates/blockifier/src/test_utils/contracts.rs @@ -20,6 +20,7 @@ use strum_macros::EnumIter; use crate::abi::abi_utils::selector_from_name; use crate::abi::constants::CONSTRUCTOR_ENTRY_POINT_NAME; use crate::execution::contract_class::{ContractClass, ContractClassV0, ContractClassV1}; +use crate::test_utils::cairo_compile::{cairo0_compile, cairo1_compile}; use crate::test_utils::{get_raw_contract_class, CairoVersion}; // This file contains featured contracts, used for tests. Use the function 'test_state' in @@ -253,6 +254,31 @@ impl FeatureContract { } } + /// Compiles the feature contract and returns the compiled contract as a byte vector. + /// Panics if the contract is ERC20, as ERC20 contract recompilation is not supported. + pub fn compile(&self) -> Vec { + if matches!(self, Self::ERC20(_)) { + panic!("ERC20 contract recompilation not supported."); + } + match self.cairo_version() { + CairoVersion::Cairo0 => { + let extra_arg: Option = match self { + // Account contracts require the account_contract flag. + FeatureContract::AccountWithLongValidate(_) + | FeatureContract::AccountWithoutValidations(_) + | FeatureContract::FaultyAccount(_) => Some("--account_contract".into()), + FeatureContract::SecurityTests => Some("--disable_hint_validation".into()), + FeatureContract::Empty(_) + | FeatureContract::TestContract(_) + | FeatureContract::LegacyTestContract => None, + FeatureContract::ERC20(_) => unreachable!(), + }; + cairo0_compile(self.get_source_path(), extra_arg, false) + } + CairoVersion::Cairo1 => cairo1_compile(self.get_source_path()), + } + } + /// Fetch PC locations from the compiled contract to compute the expected PC locations in the /// traceback. Computation is not robust, but as long as the cairo function itself is not /// edited, this computation should be stable. @@ -314,4 +340,9 @@ impl FeatureContract { } }) } + + pub fn all_feature_contracts() -> impl Iterator { + // ERC20 is a special case - not in the feature_contracts directory. + Self::all_contracts().filter(|contract| !matches!(contract, Self::ERC20(_))) + } } diff --git a/crates/blockifier/tests/feature_contracts_compatibility_test.rs b/crates/blockifier/tests/feature_contracts_compatibility_test.rs index b8c0685337..f72c8c0bc6 100644 --- a/crates/blockifier/tests/feature_contracts_compatibility_test.rs +++ b/crates/blockifier/tests/feature_contracts_compatibility_test.rs @@ -1,5 +1,4 @@ use std::fs; -use std::process::Command; use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::CairoVersion; @@ -41,20 +40,12 @@ const FIX_COMMAND: &str = "FIX_FEATURE_TEST=1 cargo test -- --ignored"; // 2. for each `X.cairo` file in `TEST_CONTRACTS` there exists an `X_compiled.json` file in // `COMPILED_CONTRACTS_SUBDIR` which equals `starknet-compile-deprecated X.cairo --no_debug_info`. fn verify_feature_contracts_compatibility(fix: bool, cairo_version: CairoVersion) { - for (path_str, file_name, existing_compiled_path) in verify_and_get_files(cairo_version) { + 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 mut command = Command::new("starknet-compile-deprecated"); - command.args([&path_str, "--no_debug_info"]); - if file_name.starts_with("account") { - command.arg("--account_contract"); - } - if file_name.starts_with("security") { - command.arg("--disable_hint_validation"); - } - let compile_output = command.output().unwrap(); - let stderr_output = String::from_utf8(compile_output.stderr).unwrap(); - assert!(compile_output.status.success(), "{stderr_output}"); - let expected_compiled_output = compile_output.stdout; + 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(); @@ -64,9 +55,9 @@ fn verify_feature_contracts_compatibility(fix: bool, cairo_version: CairoVersion if String::from_utf8(expected_compiled_output).unwrap() != existing_compiled_contents { panic!( - "{path_str} does not compile to {existing_compiled_path}.\nRun `{FIX_COMMAND}` to \ - fix the expected test according to locally installed \ - `starknet-compile-deprecated`.\n" + "{} does not compile to {existing_compiled_path}.\nRun `{FIX_COMMAND}` to fix the \ + expected test according to locally installed `starknet-compile-deprecated`.\n", + contract.get_source_path() ); } } @@ -119,10 +110,7 @@ fn verify_and_get_files(cairo_version: CairoVersion) -> Vec<(String, String, Str #[test] fn verify_feature_contracts_match_enum() { - let mut compiled_paths_from_enum: Vec = FeatureContract::all_contracts() - // ERC20 is a special case - not in the feature_contracts directory. - .filter(|contract| !matches!(contract, FeatureContract::ERC20(CairoVersion::Cairo0) | - FeatureContract::ERC20(CairoVersion::Cairo1))) + let mut compiled_paths_from_enum: Vec = FeatureContract::all_feature_contracts() .map(|contract| contract.get_compiled_path()) .collect(); let mut compiled_paths_on_filesystem: Vec = verify_and_get_files(CairoVersion::Cairo0)