Skip to content

Commit

Permalink
feat(blockifier): support Cairo1 local recompilation (#184)
Browse files Browse the repository at this point in the history
<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/starkware-libs/sequencer/184)
<!-- Reviewable:end -->
  • Loading branch information
dorimedini-starkware authored Aug 11, 2024
1 parent 0657397 commit e41bfdf
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ starknet-types-core.workspace = true
starknet_api = { workspace = true, features = ["testing"] }
strum.workspace = true
strum_macros.workspace = true
tempfile.workspace = true
thiserror.workspace = true
tikv-jemallocator = { workspace = true, optional = true }
toml.workspace = true
Expand Down
47 changes: 42 additions & 5 deletions crates/blockifier/src/test_utils/cairo_compile.rs
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -100,9 +102,42 @@ pub fn cairo0_compile(path: String, extra_arg: Option<String>, debug_info: bool)
}

/// Compiles a Cairo1 program using the compiler version set in the Cargo.toml.
pub fn cairo1_compile(_path: String) -> Vec<u8> {
verify_cairo1_compiler_deps();
todo!();
pub fn cairo1_compile(
path: String,
git_tag_override: Option<String>,
cargo_nightly_arg: Option<String>,
) -> Vec<u8> {
prepare_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) = cargo_nightly_arg {
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.
Expand Down Expand Up @@ -136,13 +171,15 @@ fn verify_cairo0_compiler_deps() {
);
}

fn verify_cairo1_compiler_deps() {
fn prepare_cairo1_compiler_deps(git_tag_override: Option<String>) {
// 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,
]));
}
19 changes: 18 additions & 1 deletion crates/blockifier/src/test_utils/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ 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. This compiler version no longer
// compiles with stable rust, so the toolchain is also fixed.
const LEGACY_CONTRACT_COMPILER_TAG: &str = "v2.1.0";
const LEGACY_CONTRACT_RUST_TOOLCHAIN: &str = "2023-07-05";

/// 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)]
Expand Down Expand Up @@ -269,7 +274,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_nightly_arg) = match self {
Self::LegacyTestContract => (
// Legacy contract is designed to test behavior of code compiled with a
// specific (old) compiler tag. To run the (old) compiler, older rust
// version is required.
Some(LEGACY_CONTRACT_COMPILER_TAG.into()),
Some(LEGACY_CONTRACT_RUST_TOOLCHAIN.into()),
),
_ => (None, None),
};
cairo1_compile(self.get_source_path(), tag_override, cargo_nightly_arg)
}
}
}

Expand Down
16 changes: 13 additions & 3 deletions crates/blockifier/tests/feature_contracts_compatibility_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
}

0 comments on commit e41bfdf

Please sign in to comment.