From 17e0fd55bb25a07249d290a32649a006d246022a Mon Sep 17 00:00:00 2001 From: Arni Hod Date: Mon, 26 Aug 2024 15:58:04 +0300 Subject: [PATCH] feat: use the starknet-sierra-compile downloaded binary to compile --- Cargo.lock | 1 + crates/starknet_sierra_compile/Cargo.toml | 1 + .../src/command_line_compiler.rs | 86 +++++++++++++++++++ .../src/compile_test.rs | 25 ++++-- crates/starknet_sierra_compile/src/errors.rs | 12 +++ crates/starknet_sierra_compile/src/lib.rs | 1 + 6 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 crates/starknet_sierra_compile/src/command_line_compiler.rs diff --git a/Cargo.lock b/Cargo.lock index 0556840fbe..1e2bd0794e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9076,6 +9076,7 @@ dependencies = [ "serde_json", "starknet-types-core", "starknet_api", + "tempfile", "thiserror", "validator", ] diff --git a/crates/starknet_sierra_compile/Cargo.toml b/crates/starknet_sierra_compile/Cargo.toml index 1bcda82fe5..d2c59c4ab2 100644 --- a/crates/starknet_sierra_compile/Cargo.toml +++ b/crates/starknet_sierra_compile/Cargo.toml @@ -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 diff --git a/crates/starknet_sierra_compile/src/command_line_compiler.rs b/crates/starknet_sierra_compile/src/command_line_compiler.rs new file mode 100644 index 0000000000..8a00eb95f9 --- /dev/null +++ b/crates/starknet_sierra_compile/src/command_line_compiler.rs @@ -0,0 +1,86 @@ +use std::env; +use std::io::Write; +use std::path::Path; +use std::process::Command; +use std::sync::OnceLock; + +use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; +use cairo_lang_starknet_classes::contract_class::ContractClass; +use tempfile::NamedTempFile; + +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: &'static str, +} + +impl CommandLineCompiler { + pub fn new(config: SierraToCasmCompilationConfig) -> Self { + Self { config, path_to_starknet_sierra_compile_binary: get_compiler_path() } + } +} + +impl SierraToCasmCompiler for CommandLineCompiler { + fn compile( + &self, + contract_class: ContractClass, + ) -> Result { + // 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::CompilationError("Failed to get temporary file path".to_owned()), + )?; + + // Set the parameters for the compile process. + let mut command = Command::new(self.path_to_starknet_sierra_compile_binary); + 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::(&compile_output.stdout)?) + } +} + +static COMPILER_PATH: OnceLock<&str> = OnceLock::new(); +fn get_compiler_path() -> &'static str { + COMPILER_PATH.get_or_init(|| { + let out_dir = env::var("OUT_DIR").expect("Failed to get the OUT_DIR environment variable"); + let out_dir = Path::new(&out_dir); + + // Navigate up the directory tree until reaching the 'target' directory + let target_dir = out_dir + .ancestors() + .nth(3) + .expect("Failed to navigate up three levels from OUT_DIR") + .join("cairo"); + + let binary_name = "starknet-sierra-compile"; + Box::leak( + target_dir + .join("bin") + .join(binary_name) + .to_str() + .expect("Failed to get compiler path") + .into(), + ) + }) +} diff --git a/crates/starknet_sierra_compile/src/compile_test.rs b/crates/starknet_sierra_compile/src/compile_test.rs index 547b92c497..b0080bdb18 100644 --- a/crates/starknet_sierra_compile/src/compile_test.rs +++ b/crates/starknet_sierra_compile/src/compile_test.rs @@ -1,24 +1,35 @@ use std::env; use std::path::Path; +use std::sync::OnceLock; 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 }; + +const CAIRO_LANG_COMPILER: CairoLangSierraToCasmCompiler = + CairoLangSierraToCasmCompiler { config: SIERRA_TO_CASM_COMPILATION_CONFIG }; + +static COMMAND_LINE_COMPILER: OnceLock = OnceLock::new(); +fn commnad_line_compiler() -> &'static CommandLineCompiler { + COMMAND_LINE_COMPILER + .get_or_init(|| 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; @@ -32,7 +43,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); diff --git a/crates/starknet_sierra_compile/src/errors.rs b/crates/starknet_sierra_compile/src/errors.rs index c1f25719ec..85ca1823c2 100644 --- a/crates/starknet_sierra_compile/src/errors.rs +++ b/crates/starknet_sierra_compile/src/errors.rs @@ -21,3 +21,15 @@ impl From for CompilationUtilError { CompilationUtilError::CompilationError(error.to_string()) } } + +impl From for CompilationUtilError { + fn from(error: serde_json::Error) -> Self { + CompilationUtilError::UnexpectedError(error.to_string()) + } +} + +impl From for CompilationUtilError { + fn from(error: std::io::Error) -> Self { + CompilationUtilError::UnexpectedError(error.to_string()) + } +} diff --git a/crates/starknet_sierra_compile/src/lib.rs b/crates/starknet_sierra_compile/src/lib.rs index e68efcc505..f3d697cefa 100644 --- a/crates/starknet_sierra_compile/src/lib.rs +++ b/crates/starknet_sierra_compile/src/lib.rs @@ -5,6 +5,7 @@ use cairo_lang_starknet_classes::contract_class::ContractClass; use crate::errors::CompilationUtilError; pub mod cairo_lang_compiler; +pub mod command_line_compiler; pub mod config; pub mod errors; pub mod utils;