Skip to content

Commit

Permalink
feat: add util starknet-sierra-compile
Browse files Browse the repository at this point in the history
  • Loading branch information
ArniStarkware committed May 27, 2024
1 parent 17a76e4 commit 0bbd647
Show file tree
Hide file tree
Showing 8 changed files with 1,400 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"crates/mempool_types",
"crates/mempool_node",
"crates/mempool_infra",
"crates/starknet_sierra_compile",
"crates/task_executor",
]
resolver = "2"
Expand Down Expand Up @@ -32,7 +33,9 @@ async-trait = "0.1.79"
axum = "0.6.12"
# TODO(YaelD, 1/5/2024): Use a fixed version once the StarkNet API is stable.
blockifier = { git = "https://github.com/starkware-libs/blockifier.git", rev = "fc62b8b8", features = ["testing"] }
cairo-lang-sierra = "2.6.0"
cairo-lang-starknet-classes = "2.6.0"
cairo-lang-utils = "2.6.0"
clap = "4.3.10"
derive_more = "0.99"
const_format = "0.2.30"
Expand Down
21 changes: 21 additions & 0 deletions crates/starknet_sierra_compile/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "starknet_sierra_compile"
version.workspace = true
edition.workspace = true
repository.workspace = true
license.workspace = true

[lints]
workspace = true

[dependencies]
cairo-lang-sierra.workspace = true
cairo-lang-starknet-classes.workspace = true
cairo-lang-utils.workspace = true
serde_json.workspace = true
serde.workspace = true
thiserror.workspace = true
tokio.workspace = true

[dev-dependencies]
assert_matches.workspace = true
61 changes: 61 additions & 0 deletions crates/starknet_sierra_compile/src/compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use cairo_lang_starknet_classes::allowed_libfuncs::{AllowedLibfuncsError, ListSelector};
use cairo_lang_starknet_classes::casm_contract_class::{
CasmContractClass, StarknetSierraCompilationError,
};
use cairo_lang_starknet_classes::contract_class::ContractClass;
use thiserror::Error;
use tokio::task::JoinError;

#[cfg(test)]
#[path = "compile_test.rs"]
pub mod compile_test;
pub struct SierraToCasmCompilationArgs {
list_selector: ListSelector,
add_pythonic_hints: bool,
max_bytecode_size: usize,
}

#[derive(Debug, Error)]
pub enum CompilationUtilError {
#[error(transparent)]
AllowedLibfuncsError(#[from] AllowedLibfuncsError),
// The compilation was cancelled or paniced.
#[error(transparent)]
JoinError(#[from] JoinError),
#[error(transparent)]
StarknetSierraCompilationError(#[from] StarknetSierraCompilationError),
}

pub async fn compile_sierra_to_casm(
contract_class: ContractClass,
) -> Result<CasmContractClass, CompilationUtilError> {
let compilation_args = SierraToCasmCompilationArgs {
list_selector: ListSelector::DefaultList,
add_pythonic_hints: true,
max_bytecode_size: 1000000,
};

// TODO(task_executor).
let result = tokio::task::spawn_blocking(move || {
starknet_sierra_compile(compilation_args, contract_class)
})
.await;

// Converts the JoinError (May arraise from painc) to CompilationUtilError.
result?
}

/// Compiles a Sierra contract to a Casm contract.
/// This function may panic.
fn starknet_sierra_compile(
compilation_args: SierraToCasmCompilationArgs,
contract_class: ContractClass,
) -> Result<CasmContractClass, CompilationUtilError> {
contract_class.validate_version_compatible(compilation_args.list_selector)?;

Ok(CasmContractClass::from_contract_class(
contract_class,
compilation_args.add_pythonic_hints,
compilation_args.max_bytecode_size,
)?)
}
38 changes: 38 additions & 0 deletions crates/starknet_sierra_compile/src/compile_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use std::path::Path;

use assert_matches::assert_matches;
use cairo_lang_starknet_classes::allowed_libfuncs::AllowedLibfuncsError;

use crate::compile::{compile_sierra_to_casm, CompilationUtilError};
use crate::test_utils::contract_class_from_file;

const FAULTY_ACCOUNT_SIERRA_FILE: &str = "account_faulty.sierra.json";
const TEST_FILES_FOLDER: &str = "./tests/fixtures";

#[tokio::test]
async fn test_compile_sierra_to_casm() {
let sierra_path = &Path::new(TEST_FILES_FOLDER).join(FAULTY_ACCOUNT_SIERRA_FILE);
let expected_casm_contract_length = 72304;

let contract_class = contract_class_from_file(sierra_path);
let casm_contract = compile_sierra_to_casm(contract_class).await.unwrap();
let serialized_casm = serde_json::to_string_pretty(&casm_contract).unwrap().into_bytes();

assert_eq!(serialized_casm.len(), expected_casm_contract_length);
}

// TODO(Arni, 1/5/2024): Add a test for panic result test.
#[tokio::test]
async fn test_negative_flow_compile_sierra_to_casm() {
let sierra_path = &Path::new(TEST_FILES_FOLDER).join(FAULTY_ACCOUNT_SIERRA_FILE);

let mut contract_class = contract_class_from_file(sierra_path);
// Truncate the sierra program to trigger an error.
contract_class.sierra_program = contract_class.sierra_program[..100].to_vec();

let result = compile_sierra_to_casm(contract_class).await;
assert_matches!(
result,
Err(CompilationUtilError::AllowedLibfuncsError(AllowedLibfuncsError::SierraProgramError))
);
}
6 changes: 6 additions & 0 deletions crates/starknet_sierra_compile/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! A lib for compiling Sierra into Casm.
pub mod compile;

#[cfg(any(feature = "testing", test))]
pub mod test_utils;
33 changes: 33 additions & 0 deletions crates/starknet_sierra_compile/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::fs;
use std::path::Path;

use cairo_lang_starknet_classes::contract_class::{ContractClass, ContractEntryPoints};
use cairo_lang_utils::bigint::BigUintAsHex;
use serde::Deserialize;

/// Same as `ContractClass` - but ignores unnecessary fields like `abi` in deserialization.
#[derive(Deserialize)]
struct DeserializedContractClass {
pub sierra_program: Vec<BigUintAsHex>,
pub sierra_program_debug_info: Option<cairo_lang_sierra::debug_info::DebugInfo>,
pub contract_class_version: String,
pub entry_points_by_type: ContractEntryPoints,
}

pub(crate) fn contract_class_from_file(file: &Path) -> ContractClass {
let DeserializedContractClass {
sierra_program,
sierra_program_debug_info,
contract_class_version,
entry_points_by_type,
} = serde_json::from_str(&fs::read_to_string(file).expect("Failed to read input file."))
.expect("deserialization Failed.");

ContractClass {
sierra_program,
sierra_program_debug_info,
contract_class_version,
entry_points_by_type,
abi: None,
}
}
Loading

0 comments on commit 0bbd647

Please sign in to comment.