From 9ae0de0333e1e37bc7e1a0f0453504d849b3187c Mon Sep 17 00:00:00 2001 From: Dori Medini Date: Mon, 29 Jul 2024 18:18:45 +0300 Subject: [PATCH] feat(blockifier): support Cairo1 local recompilation --- Cargo.lock | 1 + crates/blockifier/Cargo.toml | 1 + .../src/test_utils/cairo_compile.rs | 46 +++++++++++++++++-- crates/blockifier/src/test_utils/contracts.rs | 17 ++++++- .../feature_contracts_compatibility_test.rs | 14 ++++-- 5 files changed, 70 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 577c9e454e..1ccc3ac3af 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 9a8126ea89..1a28ba860d 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 ce1ee1fb9d..b0b6c20c2e 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 LOCAL_CAIRO1_REPO_RELATIVE_PATH: &str = "../../../cairo"; @@ -94,9 +96,41 @@ 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, + additional_cargo_arg: 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![]; + if let Some(cargo_arg) = additional_cargo_arg { + base_compile_args.push(cargo_arg); + } + base_compile_args.push("run".into()); + base_compile_args + .push(format!("--manifest-path={}/Cargo.toml", cairo1_compiler_path.to_string_lossy())); + base_compile_args.push("--bin".into()); + + // 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.clone()); + 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. @@ -124,13 +158,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 58683aecd7..da161c7010 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, cargo_arg) = 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("+nightly-2023-07-05")), + ), + _ => (None, None), + }; + cairo1_compile(self.get_source_path(), tag_override, cargo_arg) + } } } diff --git a/crates/blockifier/tests/feature_contracts_compatibility_test.rs b/crates/blockifier/tests/feature_contracts_compatibility_test.rs index f72c8c0bc6..8aa7d78959 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,15 @@ 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").is_ok() && 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) }