Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use the starknet-sierra-compile downloaded binary to compile #598

Merged
Merged
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/starknet_sierra_compile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ serde.workspace = true
serde_json.workspace = true
starknet-types-core.workspace = true
starknet_api.workspace = true
tempfile.workspace = true
thiserror.workspace = true
validator.workspace = true

Expand Down
21 changes: 6 additions & 15 deletions crates/starknet_sierra_compile/build.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,22 @@
use std::path::Path;
use std::process::Command;

include!("src/build_utils.rs");

fn main() {
println!("cargo:rerun-if-changed=../../Cargo.lock");
println!("cargo:rerun-if-changed=build.rs");

install_starknet_sierra_compile();
}

const BINARY_NAME: &str = "starknet-sierra-compile";
const REQUIRED_VERSION: &str = "2.7.1";

/// Downloads the Cairo crate from StarkWare's release page and extracts its contents into the
/// `target` directory. This crate includes the `starknet-sierra-compile` binary, which is used to
/// compile Sierra to Casm. The binary is executed as a subprocess whenever Sierra compilation is
/// required.
fn install_starknet_sierra_compile() {
let out_dir = Path::new(
&std::env::var("OUT_DIR").expect("Failed to get the OUT_DIR environment variable"),
)
.to_path_buf();
// Get the crate's `OUT_DIR` and navigate up to reach the `target/BUILD_FLAVOR` directory.
// This directory is shared accross all crates in this project.
let shared_folder_dir = out_dir
.ancestors()
.nth(3)
.expect("Failed to navigate up three levels from OUT_DIR")
.join("shared_executables");
let binary_path = shared_folder_dir.join(BINARY_NAME);
let binary_path = binary_path();

match Command::new(&binary_path).args(["--version"]).output() {
Ok(binary_version) => {
Expand All @@ -52,6 +41,7 @@ fn install_starknet_sierra_compile() {
}
}

let out_dir = out_dir();
let temp_cargo_path = out_dir.join("cargo");
let post_install_file_path = temp_cargo_path.join("bin").join(BINARY_NAME);

Expand All @@ -74,7 +64,8 @@ fn install_starknet_sierra_compile() {
}

// Move the 'starknet-sierra-compile' executable to a shared location
std::fs::create_dir_all(shared_folder_dir).expect("Failed to create shared executables folder");
std::fs::create_dir_all(shared_folder_dir())
.expect("Failed to create shared executables folder");
let move_command_status = Command::new("mv")
.args([post_install_file_path.as_os_str(), binary_path.as_os_str()])
.status()
Expand Down
28 changes: 28 additions & 0 deletions crates/starknet_sierra_compile/src/build_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::path::{Path, PathBuf};

const BINARY_NAME: &str = "starknet-sierra-compile";

fn out_dir() -> PathBuf {
Path::new(&std::env::var("OUT_DIR").expect("Failed to get the OUT_DIR environment variable"))
.to_path_buf()
}

/// Get the crate's `OUT_DIR` and navigate up to reach the `target/BUILD_FLAVOR` directory.
/// This directory is shared accross all crates in this project.
fn target_dir() -> PathBuf {
let out_dir = out_dir();

out_dir
.ancestors()
.nth(3)
.expect("Failed to navigate up three levels from OUT_DIR")
.to_path_buf()
}

fn shared_folder_dir() -> PathBuf {
target_dir().join("shared_executables")
}

pub fn binary_path() -> PathBuf {
shared_folder_dir().join(BINARY_NAME)
}
61 changes: 61 additions & 0 deletions crates/starknet_sierra_compile/src/command_line_compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;

use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass;
use cairo_lang_starknet_classes::contract_class::ContractClass;
use tempfile::NamedTempFile;

use crate::build_utils::binary_path;
use crate::config::SierraToCasmCompilationConfig;
use crate::errors::CompilationUtilError;
use crate::SierraToCasmCompiler;

#[derive(Clone)]
pub struct CommandLineCompiler {
pub config: SierraToCasmCompilationConfig,
path_to_starknet_sierra_compile_binary: PathBuf,
}

impl CommandLineCompiler {
pub fn new(config: SierraToCasmCompilationConfig) -> Self {
Self { config, path_to_starknet_sierra_compile_binary: binary_path() }
}
}

impl SierraToCasmCompiler for CommandLineCompiler {
fn compile(
&self,
contract_class: ContractClass,
) -> Result<CasmContractClass, CompilationUtilError> {
// Create a temporary file to store the Sierra contract class.
let serialized_contract_class = serde_json::to_string(&contract_class)?;

let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(serialized_contract_class.as_bytes())?;
let temp_file_path = temp_file.path().to_str().ok_or(
CompilationUtilError::UnexpectedError("Failed to get temporary file path".to_owned()),
)?;

// Set the parameters for the compile process.
// TODO(Arni): Setup the ulimit for the process.
let mut command = Command::new(self.path_to_starknet_sierra_compile_binary.as_os_str());
command.args([
temp_file_path,
"--add-pythonic-hints",
"--max-bytecode-size",
&self.config.max_bytecode_size.to_string(),
]);

// Run the compile process.
let compile_output = command.output()?;

if !compile_output.status.success() {
let stderr_output = String::from_utf8(compile_output.stderr)
.unwrap_or("Failed to get stderr output".into());
return Err(CompilationUtilError::CompilationError(stderr_output));
};

Ok(serde_json::from_slice::<CasmContractClass>(&compile_output.stdout)?)
}
}
22 changes: 16 additions & 6 deletions crates/starknet_sierra_compile/src/compile_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@ use std::path::Path;

use assert_matches::assert_matches;
use mempool_test_utils::{get_absolute_path, FAULTY_ACCOUNT_CLASS_FILE, TEST_FILES_FOLDER};
use rstest::{fixture, rstest};
use rstest::rstest;

use crate::cairo_lang_compiler::CairoLangSierraToCasmCompiler;
use crate::command_line_compiler::CommandLineCompiler;
use crate::config::SierraToCasmCompilationConfig;
use crate::errors::CompilationUtilError;
use crate::test_utils::contract_class_from_file;
use crate::SierraToCasmCompiler;

#[fixture]
fn compiler() -> impl SierraToCasmCompiler {
CairoLangSierraToCasmCompiler { config: SierraToCasmCompilationConfig::default() }
const SIERRA_TO_CASM_COMPILATION_CONFIG: SierraToCasmCompilationConfig =
SierraToCasmCompilationConfig { max_bytecode_size: 81920 };

fn cairo_lang_compiler() -> CairoLangSierraToCasmCompiler {
CairoLangSierraToCasmCompiler { config: SIERRA_TO_CASM_COMPILATION_CONFIG }
}
fn commnad_line_compiler() -> CommandLineCompiler {
CommandLineCompiler::new(SIERRA_TO_CASM_COMPILATION_CONFIG)
}

// TODO: use the other compiler as well.
#[rstest]
fn test_compile_sierra_to_casm(compiler: impl SierraToCasmCompiler) {
#[case::cairo_lang_compiler(cairo_lang_compiler())]
#[case::command_line_compiler(commnad_line_compiler())]
fn test_compile_sierra_to_casm(#[case] compiler: impl SierraToCasmCompiler) {
env::set_current_dir(get_absolute_path(TEST_FILES_FOLDER)).expect("Failed to set current dir.");
let sierra_path = Path::new(FAULTY_ACCOUNT_CLASS_FILE);
let expected_casm_contract_length = 72304;
Expand All @@ -32,7 +40,9 @@ fn test_compile_sierra_to_casm(compiler: impl SierraToCasmCompiler) {

// TODO(Arni, 1/5/2024): Add a test for panic result test.
#[rstest]
fn test_negative_flow_compile_sierra_to_casm(compiler: impl SierraToCasmCompiler) {
#[case::cairo_lang_compiler(cairo_lang_compiler())]
#[case::command_line_compiler(commnad_line_compiler())]
fn test_negative_flow_compile_sierra_to_casm(#[case] compiler: impl SierraToCasmCompiler) {
env::set_current_dir(get_absolute_path(TEST_FILES_FOLDER)).expect("Failed to set current dir.");
let sierra_path = Path::new(FAULTY_ACCOUNT_CLASS_FILE);

Expand Down
12 changes: 12 additions & 0 deletions crates/starknet_sierra_compile/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,15 @@ impl From<StarknetSierraCompilationError> for CompilationUtilError {
CompilationUtilError::CompilationError(error.to_string())
}
}

impl From<serde_json::Error> for CompilationUtilError {
fn from(error: serde_json::Error) -> Self {
CompilationUtilError::UnexpectedError(error.to_string())
}
}

impl From<std::io::Error> for CompilationUtilError {
fn from(error: std::io::Error) -> Self {
CompilationUtilError::UnexpectedError(error.to_string())
}
}
2 changes: 2 additions & 0 deletions crates/starknet_sierra_compile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use cairo_lang_starknet_classes::contract_class::ContractClass;

use crate::errors::CompilationUtilError;

pub mod build_utils;
pub mod cairo_lang_compiler;
pub mod command_line_compiler;
pub mod config;
pub mod errors;
pub mod utils;
Expand Down
Loading