diff --git a/Cargo.lock b/Cargo.lock index a77ef9d391..a3851f07ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1020,6 +1020,7 @@ dependencies = [ "starknet_api", "strum 0.25.0", "strum_macros 0.25.3", + "tempfile", "test-case", "thiserror", "tikv-jemallocator", diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index ca1c39fe2c..a9132daeeb 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -51,6 +51,7 @@ starknet-types-core.workspace = true starknet_api = { path = "../starknet_api", version = "0.13.0-rc.0", features = ["testing"] } strum.workspace = true strum_macros.workspace = true +tempfile.workspace = true thiserror.workspace = true tikv-jemallocator = { workspace = true, optional = true } toml.workspace = true diff --git a/crates/blockifier/src/test_utils/cairo_compile.rs b/crates/blockifier/src/test_utils/cairo_compile.rs index 7a7687322f..95e4a3c4ce 100644 --- a/crates/blockifier/src/test_utils/cairo_compile.rs +++ b/crates/blockifier/src/test_utils/cairo_compile.rs @@ -1,9 +1,11 @@ +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; use std::{env, fs}; use cached::proc_macro::cached; use serde::{Deserialize, Serialize}; +use tempfile::NamedTempFile; const CAIRO0_PIP_REQUIREMENTS_FILE: &str = "tests/requirements.txt"; const CAIRO1_REPO_RELATIVE_PATH_OVERRIDE_ENV_VAR: &str = "CAIRO1_REPO_RELATIVE_PATH"; @@ -99,9 +101,42 @@ pub fn cairo0_compile(path: String, extra_arg: Option, debug_info: bool) } /// Compiles a Cairo1 program using the compiler version set in the Cargo.toml. -pub fn cairo1_compile(_path: String) -> Vec { - verify_cairo1_compiler_deps(); - todo!(); +pub fn cairo1_compile( + path: String, + git_tag_override: Option, + force_nightly_version: Option, +) -> Vec { + verify_cairo1_compiler_deps(git_tag_override); + let cairo1_compiler_path = local_cairo1_compiler_repo_path(); + + // Command args common to both compilation phases. + let mut base_compile_args = vec![ + "run".into(), + format!("--manifest-path={}/Cargo.toml", cairo1_compiler_path.to_string_lossy()), + "--bin".into(), + ]; + // Add additional cargo arg if provided. Should be first arg (base command is `cargo`). + if let Some(nightly_version) = force_nightly_version { + base_compile_args.insert(0, format!("+nightly-{nightly_version}")); + } + + // Cairo -> Sierra. + let mut starknet_compile_commmand = Command::new("cargo"); + starknet_compile_commmand.args(base_compile_args.clone()); + starknet_compile_commmand.args(["starknet-compile", "--", "--single-file", &path]); + let sierra_output = run_and_verify_output(&mut starknet_compile_commmand); + + let mut temp_file = NamedTempFile::new().unwrap(); + temp_file.write_all(&sierra_output.stdout).unwrap(); + let temp_path_str = temp_file.into_temp_path(); + + // Sierra -> CASM. + let mut sierra_compile_command = Command::new("cargo"); + sierra_compile_command.args(base_compile_args); + sierra_compile_command.args(["starknet-sierra-compile", temp_path_str.to_str().unwrap()]); + let casm_output = run_and_verify_output(&mut sierra_compile_command); + + casm_output.stdout } /// Verifies that the required dependencies are available before compiling; panics if unavailable. @@ -129,13 +164,15 @@ fn verify_cairo0_compiler_deps() { ); } -fn verify_cairo1_compiler_deps() { +fn verify_cairo1_compiler_deps(git_tag_override: Option) { + // TODO(Dori, 1/6/2024): Check repo exists. + let tag = git_tag_override.unwrap_or(format!("v{}", cairo1_compiler_version())); // Checkout the required version in the compiler repo. run_and_verify_output(Command::new("git").args([ "-C", // TODO(Dori, 1/6/2024): Handle CI case (repo path will be different). local_cairo1_compiler_repo_path().to_str().unwrap(), "checkout", - &format!("v{}", cairo1_compiler_version()), + &tag, ])); } diff --git a/crates/blockifier/src/test_utils/contracts.rs b/crates/blockifier/src/test_utils/contracts.rs index bc73273339..1ba748b9b7 100644 --- a/crates/blockifier/src/test_utils/contracts.rs +++ b/crates/blockifier/src/test_utils/contracts.rs @@ -73,6 +73,9 @@ const ERC20_CAIRO0_CONTRACT_PATH: &str = "./ERC20/ERC20_Cairo0/ERC20_without_som const ERC20_CAIRO1_CONTRACT_SOURCE_PATH: &str = "./ERC20/ERC20_Cairo1/ERC20.cairo"; const ERC20_CAIRO1_CONTRACT_PATH: &str = "./ERC20/ERC20_Cairo1/erc20.casm.json"; +// Legacy contract is compiled with a fixed version of the compiler. +const LEGACY_CONTRACT_COMPILER_TAG: &str = "v2.1.0"; + /// 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)] @@ -275,7 +278,19 @@ impl FeatureContract { }; cairo0_compile(self.get_source_path(), extra_arg, false) } - CairoVersion::Cairo1 => cairo1_compile(self.get_source_path()), + CairoVersion::Cairo1 => { + let (tag_override, force_nightly_version) = match self { + Self::LegacyTestContract => ( + // Legacy contract requires specific compiler tag (which is the point of + // the test contract), + to build the compiler an + // older rust version is required. + Some(LEGACY_CONTRACT_COMPILER_TAG.into()), + Some(String::from("2023-07-05")), + ), + _ => (None, None), + }; + cairo1_compile(self.get_source_path(), tag_override, force_nightly_version) + } } } diff --git a/crates/blockifier/tests/feature_contracts_compatibility_test.rs b/crates/blockifier/tests/feature_contracts_compatibility_test.rs index f72c8c0bc6..bb856a0fc7 100644 --- a/crates/blockifier/tests/feature_contracts_compatibility_test.rs +++ b/crates/blockifier/tests/feature_contracts_compatibility_test.rs @@ -3,6 +3,7 @@ use std::fs; use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::CairoVersion; use pretty_assertions::assert_eq; +use rstest::rstest; const CAIRO0_FEATURE_CONTRACTS_DIR: &str = "feature_contracts/cairo0"; const CAIRO1_FEATURE_CONTRACTS_DIR: &str = "feature_contracts/cairo1"; @@ -40,6 +41,7 @@ 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) { + // TODO(Dori, 1/10/2024): Parallelize this test. for contract in FeatureContract::all_feature_contracts() .filter(|contract| contract.cairo_version() == cairo_version) { @@ -123,9 +125,17 @@ fn verify_feature_contracts_match_enum() { assert_eq!(compiled_paths_from_enum, compiled_paths_on_filesystem); } -#[test] +#[rstest] #[ignore] -fn verify_feature_contracts() { +fn verify_feature_contracts( + #[values(CairoVersion::Cairo0, CairoVersion::Cairo1)] cairo_version: CairoVersion, +) { + // TODO(Dori, 1/9/2024): Support Cairo1 contracts in the CI and remove this `if` statement. + if std::env::var("CI").unwrap_or("false".into()) == "true" + && matches!(cairo_version, CairoVersion::Cairo1) + { + return; + } let fix_features = std::env::var("FIX_FEATURE_TEST").is_ok(); - verify_feature_contracts_compatibility(fix_features, CairoVersion::Cairo0) + verify_feature_contracts_compatibility(fix_features, cairo_version) }