Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

feat: cairo1 recompilation support #1879

Open
wants to merge 1 commit into
base: dori/verify-cairo0-compiler-deps
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -47,6 +47,7 @@ starknet-crypto.workspace = true
starknet_api = { workspace = true, features = ["testing"] }
strum.workspace = true
strum_macros.workspace = true
tempfile.workspace = true
thiserror.workspace = true
toml.workspace = true

Expand Down
68 changes: 65 additions & 3 deletions crates/blockifier/src/test_utils/cairo_compile.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::process::Command;
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";

/// Objects for simple deserialization of Cargo.toml to fetch the Cairo1 compiler version.
/// The compiler itself isn't actually a dependency, so we compile by using the version of the
Expand Down Expand Up @@ -52,6 +56,25 @@ pub fn cairo1_compiler_version() -> String {
}
}

/// Returns the path to the local Cairo1 compiler repository.
fn local_cairo1_compiler_repo_path() -> PathBuf {
// Location of blockifier's Cargo.toml.
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

// Returns <blockifier_crate_root>/<RELATIVE_PATH_TO_CAIRO_REPO>.
Path::new(&manifest_dir).join(LOCAL_CAIRO1_REPO_RELATIVE_PATH)
}

/// Run a command, assert exit code is zero (otherwise panic with stderr output).
fn run_and_verify_output(command: &mut Command) -> Output {
let output = command.output().unwrap();
if !output.status.success() {
let stderr_output = String::from_utf8(output.stderr).unwrap();
panic!("{stderr_output}");
}
output
}

/// Compiles a Cairo0 program using the deprecated compiler.
pub fn cairo0_compile(path: String, extra_arg: Option<String>, debug_info: bool) -> Vec<u8> {
verify_cairo0_compiler_deps();
Expand All @@ -69,8 +92,34 @@ 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> {
todo!();
pub fn cairo1_compile(path: String, git_tag_override: Option<String>) -> Vec<u8> {
verify_cairo1_compiler_deps(git_tag_override);
let cairo1_compiler_path = local_cairo1_compiler_repo_path();
let mut cargo_command = Command::new("cargo");
let sierra_output = run_and_verify_output(cargo_command.args([
"run",
&format!("--manifest-path={}/Cargo.toml", cairo1_compiler_path.to_string_lossy()),
"--bin",
"starknet-compile",
"--",
"--single-file",
&path,
]));
let mut temp_file = NamedTempFile::new().unwrap();

temp_file.write_all(&sierra_output.stdout).unwrap();
let temp_path_str = temp_file.into_temp_path();

let mut cargo_command = Command::new("cargo");
let casm_output = run_and_verify_output(cargo_command.args([
"run",
&format!("--manifest-path={}/Cargo.toml", cairo1_compiler_path.to_string_lossy()),
"--bin",
"starknet-sierra-compile",
temp_path_str.to_str().unwrap(),
]));

casm_output.stdout
}

/// Verifies that the required dependencies are available before compiling.
Expand All @@ -97,3 +146,16 @@ fn verify_cairo0_compiler_deps() {
CAIRO0_PIP_REQUIREMENTS_FILE
);
}

fn verify_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",
&tag,
]));
}
11 changes: 10 additions & 1 deletion crates/blockifier/src/test_utils/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ const ERC20_CONTRACT_PATH: &str =
"./ERC20_without_some_syscalls/ERC20/erc20_contract_without_some_syscalls_compiled.json";
const ERC20_CONTRACT_SOURCE_PATH: &str = "./ERC20_without_some_syscalls/ERC20/ERC20.cairo";

// 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)]
Expand Down Expand Up @@ -187,7 +190,13 @@ impl FeatureContract {
};
cairo0_compile(self.get_source_path(), extra_arg, false)
}
CairoVersion::Cairo1 => cairo1_compile(self.get_source_path()),
CairoVersion::Cairo1 => {
let tag_override = match self {
Self::LegacyTestContract => Some(LEGACY_CONTRACT_COMPILER_TAG.into()),
_ => None,
};
cairo1_compile(self.get_source_path(), tag_override)
}
}
}

Expand Down
13 changes: 10 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 @@ -123,9 +124,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,
) {
if std::env::var("CI").is_ok() && matches!(cairo_version, CairoVersion::Cairo1) {
// TODO(Dori, 1/6/2024): Support Cairo1 contracts in the CI and remove this `if` statement.
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)
}
Loading