Skip to content

Commit

Permalink
feat: use the starknet-sierra-compile binary to compile
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed Sep 2, 2024
1 parent ee1cb43 commit 1ee6ade
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 21 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/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

0 comments on commit 1ee6ade

Please sign in to comment.